Service class not updating database when called within Service Class - java

I am working on Java Spring MVC project. When the Controller calls the method in ServiceImpl class (updateAttempt()) which in turn calls DAOImpl class, the update happens and I see the updated data in DB.
But when the loadUserByUserName (which is present in ServiceImpl class) calls updateAttempt() method in same ServiceImpl class, it doesn't throw any error or exception, but data never gets updated in DB.
PersonController.java
#Controller
#SessionAttributes({ "mob_Number"})
public class PersonController implements Observer, InitializingBean{
private static final Logger logger = LoggerFactory.getLogger(PersonController.class);
private PersonService personService;
#Autowired(required=true)
#Qualifier(value="personService")
public void setPersonService(PersonService ps){
this.personService = ps;
}
public PersonController(PersonService personService){
this.personService = personService;
}
public PersonController(){
}
#RequestMapping(value="/submitVerificationCode",method = RequestMethod.POST, headers = "Accept=application/json")
#ResponseBody
public String submitVerificationCode(#RequestBody String json){
......
this.personService.update_User_Verification_AttemptCount(userVer.getMobile_Number(), no_Attempts);
//this call updates the data in DB
}
}
PersonServiceImpl.java
#Service
public class PersonServiceImpl implements PersonService, UserDetailsService {
private static final Logger logger = LoggerFactory.getLogger(PersonServiceImpl.class);
private PersonDAO personDAO;
private PersonService personService;
public void setPersonDAO(PersonDAO personDAO) {
this.personDAO = personDAO;
}
#Autowired
private Observer observe;
#Override
#Transactional
public void update_User_Verification_AttemptCount(String mobile_number, int count){
this.personDAO.update_User_Verification_AttemptCount(mobile_number, count);
}
#Override
#Transactional
public UserDetails loadUserByUsername(String mobile_Number)
throws UsernameNotFoundException {
this.update_User_Verification_AttemptCount(mobile_Number, no_Attempts); //but this call doesn't update the data in DB
this.getUserDetails() //but this call returns data from DB
}
PersonDAOImpl.java
#Repository
public class PersonDAOImpl implements PersonDAO {
private static final Logger logger = LoggerFactory.getLogger(PersonDAOImpl.class);
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sf){
this.sessionFactory = sf;
}
#Override
public void update_User_Verification_VerCode(String mob_number, String verCode, Timestamp currentTimestamp){
Session session = this.sessionFactory.getCurrentSession();
Query query = session.createQuery("update UserVerification set ver_Code=:verCode, sent_Time=:currentTimestamp where mobile_Number=:mob_Number");
query.setParameter("verCode", verCode);
query.setParameter("currentTimestamp", currentTimestamp);
query.setParameter("mob_Number", mob_number);
query.executeUpdate();
session.flush();
}
}
NOTE: the get methods residing in ServiceImpl(which does select) also return values properly when the get methods called from loadUserByUsername.

That is beacause your transaction does not commit when you call the methods inside the same service.
The problem there is that Spring enriches the bean with transaction behaviour by wrapping the bean inside the proxy and adding the behaviour to it. The proxy however is always created around the interface, so calling a method with this keyword will not propagate the desired behaviour.
a proper solution would be to repeat the dao call so to avoid the call of the same service method
#Transactional
public UserDetails loadUserByUsername(String mobile_Number)
throws UsernameNotFoundException {
this.personDAO.update_User_Verification_AttemptCount(mobile_number, count);
this.getUserDetails() //but this call returns data from DB
}
One other (hacky thing) that you can do is, since you already have a personService inside PersonServiceImpl, is to, first make sure that its injected, so add #Autowired
#Autowired private PersonService personService;
and than make a call through interface e.g.
personService.update_User_Verification_AttemptCount(mobile_Number, no_Attempts);
personService.getUserDetails()

Related

Correct use of the EntityManager in an #Async call

I am trying to use the #Async capabilities of the Spring framework to perform a simple indexing task.
The problem I'm facing is that I feel that the EntityManager used in my Async function is somehow reused from previous calls so my data is not up to date and sometimes uses old data.
Here is the code I wrote as an example. The goal is to update a product's data and index it asynchronously after I publish an event using Spring's ApplicationEventPublisher:
ProductService
#Service
class ProductService {
private final EntityManager entityManager;
private final ApplicationEventPublisher eventPublisher;
#Autowired
public ProductService(EntityManager entityManager, ApplicationEventPublisher eventPublisher) {
this.entityManager = entityManager;
this.eventPublisher = eventPublisher;
}
#Transactional
public void patchProduct (String id, ProductDto productDto) {
Product product = this.entityManager.find(Product.class, id);
product.setLabel(productDto.getLabel());
this.entityManager.flush();
this.eventPublisher.publishEvent(new ProductEvent(product, ProductEvent.EVENT_TYPE.UPDATED));
}
}
EventListener
#Component
public class ProductEventListener {
private final AsyncProcesses asyncProcesses;
#Autowired
public ProductEventListener (
AsyncProcesses asyncProcesses
) {
this.asyncProcesses = asyncProcesses;
}
#EventListener
public void indexProduct (ProductEvent productEvent) {
this.asyncProcesses.indexProduct(productEvent.getProduct().getPok());
}
}
AsyncProcesses
#Service
public class AsyncProcesses {
private final SlowProcesses slowProcesses;
#Autowired
public AsyncProcesses(SlowProcesses slowProcesses) {
this.slowProcesses = slowProcesses;
}
#Async
public void indexProduct (String id) {
this.slowProcesses.indexProduct(id);
}
}
SlowProcesses
#Service
public class SlowProcesses {
private EntityManager entityManager;
private ProductSearchService productSearchService;
#Autowired
public SlowProcesses(EntityManager entityManager, NewProductSearchService newProductSearchService) {
this.entityManager = entityManager;
this.newProductSearchService = newProductSearchService;
}
#Transactional(readonly = true)
public void indexProduct (String pok) {
Product product = this.entityManager.find(Product.class, pok);
// this.entityManager.refresh(product); -> If I uncomment this line, everything works as expected
this.productSearchService.indexProduct(product);
}
}
As you can see on the SlowProcesses file, if I refresh the product object in the entityManager, I get the correct and up to date data. If I do not, I might get old data from previous calls.
What is the correct way to use the EntityManager in an Asynchronous call? Do I really have to refresh all my objects in order to make everything work? Am I doing something else wrong?
Thank you for reading through
Since instances of EntityManager are not thread-safe as pointed out by Jordie, you may want to try this instead:
Instead of injecting an EntityManager, inject an EntityManagerFactory. Then from the EntityManagerFactory retrieve a new EntityManager instance that is used only for the duration of the method in question.

Getting Hibernate Session inside AsyncUncaughtExceptionHandler

I want to create an exception log in the database when an #Async operation fails with an exception.
You can see the implementation for AsyncExecutorConfiguration and AsyncExceptionHandler classes below.
Inside AsyncExceptionHandler class, when I call a service that tries to access the database, I am getting: org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
#Configuration
#EnableAsync
public class AsyncExecutorConfiguration implements AsyncConfigurer {
#Autowired
private AsyncExceptionHandler asyncExceptionHandler;
private static final int CORE_POOL_SIZE = 3;
private static final int MAX_POOL_SIZE = 3;
private static final int QUEUE_CAPACITY = 24;
private static final String THREAD_NAME_PREFIX = "AsynchThread-";
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(CORE_POOL_SIZE);
executor.setMaxPoolSize(MAX_POOL_SIZE);
executor.setQueueCapacity(QUEUE_CAPACITY);
executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return asyncExceptionHandler;
}
}
#Component
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
#Autowired
private NotificationService notificationService;
#Override
#Transactional(rollbackFor = Exception.class, readOnly = false)
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
AsyncErrorLog log = new AsyncErrorLog(ex);
notificationService.saveLogAndNotify(log); // throws exception "Could not obtain transaction-synchronized Session for current thread"
}
}
#Service
public class MyServiceImpl implements MyService {
#Override
#Async
#Transactional(rollbackFor = Exception.class, readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void doSomething(Long id) {
// I can execute database operations here
}
...
#Async function itself already has a valid session. What should I do to have a valid session in AsyncExceptionHandler class too?
--
UPDATE
Here is the simplified implementations for NotificationServiceImpl and LogDaoImpl.class where we get the error.
#Service
public class NotificationServiceImpl implements NotificationService {
#Autowired
private LogDao logDao;
#Override
#Transactional(rollbackFor = Exception.class, readOnly = false)
public void saveLogAndNotify(Log log) {
return logDao.createLog(log);
}
#Repository
public class LogDaoImpl{
#Autowired
protected SessionFactory sessionFactory;
#Override
public void createLog(Log log) {
sessionFactory.getCurrentSession().saveOrUpdate(log);
}
Per the Hibernate exception; if you're not using Spring Data, you'll have to make sure the notification service explicitly invokes the database calls on the Hibernate session.
On another note, in my experience, the main use cases for the UncaughtExceptionHandler (in general) are used for:
A simple last-resort to handle RuntimeExceptions that may be unknown to the programmer that for some reason cannot (or are not) caught in code
A way to catch exceptions in code that the programmer has no control over (e.g. if you're invoking Async directly on some third party library, etc.)
The commonality between the two is that this Handler is used for something unexpected. In fact, Spring itself accounts for the "unexpectedness" in your own code and Spring Async already sets a default one for you that will log to the console (code here), letting you not have to worry about rogue exceptions killing threads and not knowing why. (Note: The message in the source code says it's catching an "unexpected" exception. Of course exceptions are unexpected, but these are one's that you really didn't know could happen. Spring Async will log it for you.)
That being the case, in your example, since you're doing Spring Database operations and should know exactly what's happening inside of #doSomething, I would just go with removing the AUEH a try-catch (and/or -finally) and handle the exception inside of #doSomething:
#Service
public class MyServiceImpl implements MyService {
// Self autowired class to take advantage of proxied methods in the same class
// See https://stackoverflow.com/questions/51922604/transactional-and-stream-in-spring/51923214#51923214
private MyService myService;
private NotificationService notificationService;
#Override
#Async
public void doSomething(Long id) {
// I can execute database operations here
try {
myService.doDatabaseOperations(...);
} catch(DatabaseAccessException e) {
AsyncErrorLog log = new AsyncErrorLog(ex);
notificationService.saveLogAndNotify(log);
}
// Other exceptions (from DB operations or the notifications service) can be
// handled with other "catches" or to let the SimpleAsyncExHandler log them for you.
// You can also use standard multithreading exception handling for this
}
#Transactional(rollbackFor = Exception.class, readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void doDatabaseOperations(...) {
...
}
}
You can use the applicationContext in your handler to lookup the notificationService. I had the same issue when I used #Autowired for the handler, which in turn injected my LogService. After looking at the logs I saw that the TransactionSynchronizationManager is clearing transaction synchronization after the rollback of the exception and nothing else except the no transaction for ...... error.
After using the applicationContext for looking up the logService bean and changing my handler, I saw the desired result in the logs.
begin
Initializing transaction synchronization
Getting transaction for [....AsyncService.doAsync]
Exception
rolling back
Clearing transaction synchronization
begin
Initializing transaction synchronization
Getting transaction for [.....LogService.save]
Change your config to include the interface ApplicationContextAware which will give you a convenience method to access the applicationContext. Set it as a instance variable.
See my configuration class below.
#Configuration
#EnableAsync
public class AsyncConfig implements AsyncConfigurer, ApplicationContextAware {
private static final int CORE_POOL_SIZE = 3;
private static final int MAX_POOL_SIZE = 3;
private static final int QUEUE_CAPACITY = 24;
private static final String THREAD_NAME_PREFIX = "AsynchThread-";
private ApplicationContext applicationContext;
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(CORE_POOL_SIZE);
executor.setMaxPoolSize(MAX_POOL_SIZE);
executor.setQueueCapacity(QUEUE_CAPACITY);
executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler(this.applicationContext);
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
I have removed the #Component from the handler and use it as a POJO.
Every time getAsyncUncaughtExceptionHandler is called with an exception, a new handler instance is created with the applicationContext as a dependency.
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
private final ApplicationContext applicationContext;
public AsyncExceptionHandler(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
Log log = new Log();
log.setEntry(ex.getMessage());
LogService logService = this.applicationContext.getBean(LogService.class);
logService.save(log);
}
}
The save method on logService requires a new transaction every time it is called.
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void save(Log log)
This will help you:
#Override
public void createLog(Log log) {
try {
session = sessionFactory.getCurrentSession();
} catch (HibernateException e) {
session = sessionFactory.openSession();
}
session.saveOrUpdate(log);
}

how to get transaction support in Spring boot with Solr?

I'm using Spring Boot with Solr.
I can see Spring-tx in my resolved dependencies.
When I call a #Transactional annotated method in a Spring bean,
a) at runtime, i don't see any signs of it being wrapped in any transaction management proxy, and
b) when the method throws a RuntimeException the data is not rolledback.
The email/phone repositories and just interfaces that extend
org.springframework.data.solr.repository.SolrCrudRepository
What am i missing?
At have the method annotated #Transactional in both the interface and the implementation, just in case ;-)
public interface MyServiceInterface {
#Transactional
public CreateDetailsResponse createAllDetails(
final CreateDetailsRequest createDetailsRequest) throws BusinessException;
}
public class MyService implements MyServiceInterface {
#Autowired
private EmailRepository emailRepository;
#Autowired
private EmailRepository phoneRepository;
#Transactional
public CreateDetailsResponse createAllDetails(
final CreateDetailsRequest createDetailsRequest) throws BusinessException {
//method below calls emailRepository.save(emailDocument)
saveEmail();
//method below is supposed to call phoneRepository.save(phoneDocument)
//but throws RuntimeException before save is called
savePhone();
}
//...
}
#Configuration
#EnableSolrRepositories(basePackages = "com.....repository", multicoreSupport = true,
considerNestedRepositories = true, repositoryBaseClass = SoftCommitSimpleSolrRepository.class)
#EnableAutoConfiguration
public class ConsumersServiceConfiguration {
//...
#Bean
public MyServiceInterface myService() {
return new MyService();
}
}
Supposedly Solr supports transactions:

#Transactional not working with Spring Boot and JDBC

My service should save data to a parent and child database tables, and rollback when an error ocurrs. I've tried forcing an error, using a hardcoded RuntimeException, and found the transaction gets commited no matter what.
What I'm I missing?
I'm using Spring Boot 2, including the spring-boot-starter-jdbc dependency.
Database is Oracle 11g.
Main configuration:
#SpringBootApplication
#EnableTransactionManagement
public class MyApplication extends SpringBootServletInitializer{
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MyApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Service layer:
#Service
public class MyBean {
private final NamedParameterJdbcTemplate jdbcTemplate;
private final MyDAO myDao;
#Autowired
public MyBean (NamedParameterJdbcTemplate jdbcTemplate, MyDAO myDao) {
this.jdbcTemplate = jdbcTemplate;
this.myDao= myDao;
}
#Override
#Transactional
public void saveData(...){
myDao.saveData(jdbcTemplate, ...);
}
}
DAO:
public void saveData(jdbcTemplate, ...){
saveDataInParentDatatable(jdbcTemplate, ...);
saveDataInChildDatatable(jdbcTemplate, ...);
}
private void saveDataInChildDatatable(jdbcTemplate, ...){
throw new RuntimeException();
}
I faced a similar issue. I'm assuming that you were calling the MyBean.saveData method at MyBean's another method.
After searching, trying and failing a lot, I found this link: http://ignaciosuay.com/why-is-spring-ignoring-transactional/
In it, it's explained that when the invoked method is in the same class where is it invoked, the #Transactional annotation is ignored. The Spring explanation for it is:
“In proxy mode (which is the default), only external method calls
coming in through the proxy are intercepted. This means that
self-invocation, in effect, a method within the target object calling
another method of the target object, will not lead to an actual
transaction at runtime even if the invoked method is marked with
#Transactional. Also, the proxy must be fully initialized to provide
the expected behaviour so you should not rely on this feature in your
initialization code, i.e. #PostConstruct.”
So I created another class to encapsulate my DAO method calling, used its method instead and it worked.
So for this case, it could be something like:
MyBean:
#Service
public class MyBean {
MyBean2 bean2;
public void saveData(...){
bean2.saveData(jdbcTemplate, ...);
}
}
MyBean2:
#Service
public class MyBean2 {
private final NamedParameterJdbcTemplate jdbcTemplate;
private final MyDAO myDao;
#Autowired
public MyBean2 (NamedParameterJdbcTemplate jdbcTemplate, MyDAO myDao) {
this.jdbcTemplate = jdbcTemplate;
this.myDao= myDao;
}
#Override
#Transactional
public void saveData(...){
myDao.saveData(jdbcTemplate, ...);
}
}
try this:
#Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class)
recommended use:
#Transactional(rollbackFor = {Exception.class, RuntimeException.class})

Understanding of Transactional services - spring

I have a service implementation with a particular method like so:
public class ExampleServiceImpl implements ExampleService {
#AutoWired
#Resource
private RecordRepository recordRepository;
private void processRecord() {
// some code here
}
#Transactional(readOnly=false)
public void processRecord(Record a) {
Record original = getOriginal(a);
recordRepository.saveChanges(a,original);
}
}
Where the Record class is the root object of an object graph. RecordRepository looks something like the following with sub repositories to save various children of the objects in the graph.
public class RecordRepository extends BaseRepository<Record> {
#AutoWired
#Resource
private IDao databaseDao;
#AutoWired
#Resource
private SubRecordRepository subRecordRepository;
public void saveChanges(Record a, Record b) {
//Perform some processing on a, b
for(SubRecord subA : a.getSubRecords()) {
subRecordRepository.saveChanges(subA);
}
databaseDao.updateRecord(a);
}
}
public class DatabaseDao extends NamedParameterJdbcDaoSupport implements IDao {
#Autowired
public DatabaseDao(#Qualifier("org.somewhere.Datasource") DataSource ds) {
super();
this.setDataSource(ds);
}
public void updateRecord(Record inRecord) {
String query = (String) sql.get("updateRecord");
SqlParameterSource parms = new BeanPropertySqlParameterSource(inRecord);
getNamedParameterJdbcTemplate().update(query, parms);
}
public void insertSubRecord(SubRecord inSubRecord) {
String query = (String) sql.get("insertSubRecord");
SqlParameterSource parms = new BeanPropertySqlParameterSource(inSubRecord);
getNamedParameterJdbcTemplate().insert(query, parms);
}
// other update and insert methods
}
Will the transaction be applied across all involved inserts\updates from the processRecord call? In other words, if an insert or update fails, will all previously called inserts and updates from ExampleServiceImpl.processRecord get rolled back?
Yes. The transactional aspect makes sure that a transaction is started before the annotated method is called, and that the transaction (if started by this method) is committed or rollbacked once the method returns.
The transactional interceptor doesn't know (and doesn't care) about which other methods are called inside the annotated method. Every read and write to the DataSource handled by the Spring transaction manager will be included in the same transaction.

Categories