I have the next problem. When a controller invokes a service method with #Transactional annotation and this method invokes a #Transactional(readonly=true) method, the changes in the object doesn't get saved in the database. I don't know why. I thought that the first method begins a 'read and write' transaction and the second method uses the same transaction.
Example :
/** Controller class **/
#RestController
#RequestMapping("/path1")
public class MyObjectRestController {
...
#Autowired
MyObjectService myObjectService;
#PostMapping(value = "/path2")
#ResponseStatus(HttpStatus.CREATED)
public void save(#RequestBody MyObject myObject) {
myObjectService.save(myObject);
}
...
}
/** Service class **/
public interface MyObjectService {
public MyObject save(MyObject myObject);
public MyObject findOne(long id);
}
/** Service implementation class **/
#Service
public class MyObjectServiceImpl implements MyObjectService {
/**
* Save Object
*/
#Override
#Transactional
public Object save(Object newObject) {
Object myObject = this.findOne(newObject.getId());
// Modify Object
myObject.setAttribute1(...)
myObject.setAttribute2(...)
// Save Object (using Repository)
return MyObjectDao.save(myObject);
}
/**
* Get Object by Id
*/
#Override
#Transactional(readOnly = true)
public MyObject findOne(long id) {
Optional<MyObject> myObject = MyObjectDao.findById(id);
return myObject.isPresent() ? myObject.get() : null;
}
}
This example doesn't update the object attributes. But if I remove the #Transactional annotation in save(..) method ... this example works successfully! (the object is modified).
Why is this happening?
I see save method is annotated with #Transactional and calls findOne method which is also #Transactional. So in this case the same transaction is getting propagated to findOne method and it applies readonly behaviour to the current transaction. And thus it does not perform Flush and you see entity is not modified.
So in this case, you should use a separate transaction for read and writes.
findOne method can be annotated with below -
#Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
Related
I have just rest controller, service and dao methods and . Checking if there is an existing student in the db if it is present then updating the existing object with current request values and storing in the database. But some how in multi threaded environment the values are getting overwritten for few of the records. My understanding is each thread has its own stack and variables that live inside stack which is specific to thread. In this scenario not understanding how the student object is getting overriden some times. Could any one have any idea could you please provide some inputs.
#RestController
public class HelloWorldController {
#Autowired
private StudentService service;
#RequestMapping(value = "/student", method = RequestMethod.PUT)
public String updateStudent(StudentDto studentDto) {
service.updateStudent(studentDto);
return "Success";
}
}
...
#Service
public class StudentService {
#Autowired
private StudentDao studentDao;
public void updateStudent(StudentDto dto) {
Student student = studentDao.findByStudentId(dto.getId());
if (student != null) {
student.setName(dto.getName());
studentDao.update(student); // dao extends couchbaseReository
}
}
}
I think you can use :
studentDao.save(student);
instead of
studentDao.update(student);
And if you want to handle task in parallel thread you can use the Annotation #Async (Spring)
An interface:
public interface Manager {
Object read(Long id);
}
A class which implements this interface:
#Transactional
Public class ManagerImpl implements Manager {
#Override
public Object read(Long id) {
// Implementation here
}
}
An aspect for ManagerImpl:
#Aspect
public class Interceptor {
#Pointcut("execution(public * manager.impl.*.*(..))")
public void executionAsManager() {
}
#Around("executionAsManager()")
public Object invoke(ProceedingJoinPoint joinPoint) throws Throwable {
// Do some actions
return joinPoint.proceed();
}
}
A controller:
#RestController()
public class Controller {
#Autowired
private Manager manager;
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public Object read(#PathVariable Long id) {
return manager.read(id);
}
#RequestMapping(value = "reflection/{id}", method = RequestMethod.GET)
public Object readViaReflection(#PathVariable Long id) {
return ManagerImpl.class.getMethod("read", Long.class).invoke(manager, id);
}
}
So, when spring injects manager variable within controller proxy created.
When method invoked directly:
manager.read(1L)
aspect is invoked.
However, when I try to do like this (see readViaReflection)
ManagerImpl.class.getMethod("read", Long.class).invoke(manager, 1L);
got java.lang.reflect.InvocationTargetException object is not an instance of declaring class.
Which is reasonable.
The question is: how can I invoke method via reflection on proxy-object created by spring (I have method extracted from target-object and I have instance of proxy created by spring).
Can not do invocation on target because then aspect will not invoke.
As you have noticed, you cannot invoke the method of ManagerImpl on the bean, beacause the Bean is actually implemented by a Proxy.
For me, the solution was to get the invocation handler of the Proxy and call the method.
if (Proxy.isProxyClass(manager.getClass())) {
Method readMethod = ManagerImpl.class.getMethod("read", Long.class);
Proxy.getInvocationHandler(manager).invoke(manager, readMethod, parameter);
} else
info.getMethod().invoke(serviceClass, parameter);
The else part is necessary when the Bean is not a Proxy, but either the bare ManagerImpl class or a CGLib proxy class (which would subclass ManagerImpl in your case).
You must invoke the method from the proxy's class. Try this:
manager.getClass().getMethod("read", Long.class).invoke(manager, 1L);
I don't think java reflection will do the trip, you need to use if() pointcut expression
To do implement it, you can define another boolean argument (named invokeAOP), when your invoke manager with invokeAOP = true, then you'll get your Aspect exectued. Otherwise your Aspect will be omitted.
You can do it without using reflection - it just needs some casting:
((Manager) ((Advised)manager).getTargetSource().getTarget()).read(1L);
The cool thing is that it works with JDK and CGLIB proxies.
If you have to use reflection just use part of this solution:
Manager managerBean = ((Manager) ((Advised)manager).getTargetSource().getTarget());
managerBean.getClass().getMethod("read", Long.class).invoke(managerBean, id)
I have one class with transaction Annotation:
#Service
#EnableTransactionManagement(order=2000)
public class UserManagementServiceImpl implements UserManagementService {
#Transactional
public User addUser(User user) throws AlreadyExistException {
// ...
}
}
I have one Aspect class:
#Component
#Aspect
public class PublisherAspect {
#Around("execution(* com.toi.expresso.service.UserManagementService.addUser(..))")
public User publishAddUserEvent(ProceedingJoinPoint jp) {
// ...
}
}
my #Around Annotation is getting executed before addUser save the data in Database. Please suggest me how #Around will execute after data save in database.
The interesting part of your aspect is the ..., i.e. the advice body. Assuming that your addUser method really returns a User and not void (which I would expect) or anything else, you can narrow down the pointcut to methods actually having a User return type so as to avoid problems with methods returning other types.
#Component
#Aspect
public class PublisherAspect {
#Around("execution(User com.toi.expresso.service.UserManagementService.addUser(..))")
public User publishAddUserEvent(ProceedingJoinPoint jp) {
// Do something before method execution
User user = jp.proceed();
// Do something after method execution
return user;
}
}
The more generic solution if you want to match all return types:
#Component
#Aspect
public class PublisherAspect {
#Around("execution(* com.toi.expresso.service.UserManagementService.addUser(..))")
public Object publishAddUserEvent(ProceedingJoinPoint jp) {
// Do something before method execution
Object result = jp.proceed();
// Do something after method execution
return result;
}
}
yes 'adduser' is returning User object. but just after holding user object if I am checking database for the same user its returning empty set.
#Component
#Aspect
public class PublisherAspect {
#Around("execution(User com.toi.expresso.service.UserManagementService.addUser(..))")
public User publishAddUserEvent(ProceedingJoinPoint jp)
{
// Do something before method execution
User user = jp.proceed();
//checking in database(Sending message to other application but that application didn't find user in same db)
return user;
}
}
}
I am using Spring MVC + Hibernate
//Class for Generic Methods for **save and update**
#Service("PersistenceTemplate")
#Transactional
public class PersistenceTemplate {
#Resource(name = "sessionFactory")
private SessionFactory sessionFactory;
// SAVE
public <T> long save(T entity) throws DataAccessException {
Session session = sessionFactory.getCurrentSession();
long getGenVal=(Long) session.save(entity);
return getGenVal;
}
//UPDATE
public <T> void update(T entity) throws DataAccessException {
sessionFactory.getCurrentSession().update(entity);
}
}
AT Controller
#Resource(name = "PersistenceTemplate")
private PersistenceTemplate pt;
long result=pt.save(receiveTrxObj1);
pt.Update(receiveTrxObj2);
Problem statement
How to roll back save statement if Update fails to update the entity in database ?
You could use application level exception to rollback your entity operations. When this custom exception thrown the related operations rollback. Please see following documents to see how to define custom rollback in Spring.
first your #Service("PersistenceTemplate") should be marked as #Repository because its doing the work of DAO layer.
from the controller you should call a Service which should be annotated with #service and #Transactional and inside this service you create a method which will call a DAO layer.
if save or Update fails to update the entity in database the method from which it is called (ie. the method in service layer) will not complete and the transaction is cancelled automatically because persistence objects are synchronized with database near the end of the completion of method of service layer once the control comes back to it.
See the below example.
#Service("authorLoadService")
#Transactional
#Scope(proxyMode=ScopedProxyMode.TARGET_CLASS,value="request")
public class AuthorEntityLoadService implements EntitiesLoadService{
private AuthorDAO authorDao;//this is my DAO
#Autowired
#Qualifier("authorDAO")
public void setAuthorDao(AuthorDAO authorDao) {
this.authorDao = authorDao;
}
#Override
public void deleteEntities(Object o) {
// TODO Auto-generated method stub
}
#Override
public void loadEntities(Object o) {
Set<author_pojo> author=(Set<author_pojo>)o;
Iterator<author_pojo> itr=author.iterator();
while (itr.hasNext()) {
author_pojo authorPojo = (author_pojo) itr.next();
authorDao.save(authorPojo);
}
}
#Override
#Transactional(readOnly=true)
public List getEntities() {
// TODO Auto-generated method stub
return null;
}
#Override
#Transactional(readOnly=true)
public Object getEntity(Object o) {
String author=(String)o;
author_pojo fetAuthor=authorDao.findOneByName(author);
return fetAuthor;
}
}
My Abstract Generic DAO
public abstract class AbstractHibernateDAO<T extends Serializable> {
public Class<T> clazz;//class object reference
protected SessionFactory mysessionFactory;
#Autowired
public void setMysessionFactory(SessionFactory mysessionFactory) {
this.mysessionFactory = mysessionFactory;
}
public T findOneByName(final String name){
return (T) getCurrentSession().createQuery("from "+clazz.getName()).uniqueResult();
}
public void setClazz(final Class<T> clazzToSet) {
this.clazz = clazzToSet;
}
public T findOne(final Long id) {
return (T) getCurrentSession().get(clazz, id);
}
#SuppressWarnings("unchecked")
public List<T> findAll() {
return getCurrentSession().createQuery("from " + clazz.getName()).list();
}
public void save(final T entity) {
getCurrentSession().merge(entity);
}
public void update(final T entity) {
getCurrentSession().update(entity);
}
public void delete(final T entity) {
getCurrentSession().delete(entity);
}
public void deleteById(final Long entityId) {
final T entity = findOne(entityId);
delete(entity);
}
protected Session getCurrentSession() {
return mysessionFactory.getCurrentSession();
}
}
my concerete DAO
#Repository("authorDAO")
#Scope(proxyMode=ScopedProxyMode.TARGET_CLASS,value="request")
public class AuthorDAO extends AbstractHibernateDAO<author_pojo> {
public AuthorDAO() {
setClazz(author_pojo.class);
}
public author_pojo findOneByName(final String name){
System.out.println(clazz);
return (author_pojo) getCurrentSession().createQuery("from "+clazz.getName() +" where authorName=:name").setParameter("name", name).uniqueResult();
}
}
For you to be able to rollback the save if the update fails, the save and update have to occur within the same transaction. Services are a natural place to put DAO calls that need to execute within the same transaction.
Putting a #Transactional annotation on the controller method would create complications due to proxying the controller, see the Spring MVC documentation, 17.3.2:
A common pitfall when working with annotated controller classes
happens when applying functionality that requires creating a proxy for
the controller object (e.g. #Transactional methods). Usually you will
introduce an interface for the controller in order to use JDK dynamic
proxies. To make this work you must move the #RequestMapping
annotations, as well as any other type and method-level annotations
(e.g. #ModelAttribute, #InitBinder) to the interface as well as the
mapping mechanism can only "see" the interface exposed by the proxy.
Alternatively, you could activate proxy-target-class="true" in the
configuration for the functionality applied to the controller (in our
transaction scenario in ). Doing so indicates
that CGLIB-based subclass proxies should be used instead of
interface-based JDK proxies. For more information on various proxying
mechanisms see Section 9.6, “Proxying mechanisms”.
See this question for what goes in a service as opposed to in a controller.
I have two services, like this (simplified code):
#Service
public class OuterService {
#Autowired
InnerService innerService;
#Transactional
public void doSomething() {
List<SomeEntity> list = entityRepo.findByWhatever(...);
for(SomeEntity listElement : list) {
innerService.processEntity(listElement);
}
}
}
#Service
public class InnerService {
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void processEntity(Entity entity) {
// ...
StatusElement status = new StatusElement(...);
statusElementRepo.save(status);
}
}
The constructed StatusElement is now inserted by exiting InnerService.processEntity() and inserted again by exiting OuterService.doSomething().
If I change the #Transactional annotation of OuterService.doSomething() to #Transactional(readOnly = true), it is inserted just once.
Is it a problem with MySql (because it may not support nested transactions), do I need a special transaction manager, or is there something wrong with my code? TIA!
I solved it by using programmatically transactions using the PlatformTransactionManager.
see: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html#transaction-programmatic-ptm