Spring, AOP - how to define the aspect after Hibernate DAO transaction closes - spring

My project is Web app with Spring and Hibernate. I need to perform some routine operation on the data returned by my DAO layer and transaction is committed and session is closed. I try to use aspects with annotations like this:
#AfterReturning(pointcut = "execution(* com.dao..*.*(..))", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result){
processResult(result);
}
or using
#Around("execution(* com.dao..*.*(..))")
but in either event I got Hibernate exceptions like this:
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing
Off course everything works if I just apply that same operation in my service:
public void serviceMethod(Object param) {
Object result = dao.getObject(param);
processResult(result);
}
'Cause I need to use processResult against every DAO method's result I am reluctant to use the latter approach and would be grateful for some advice on how to use either aspects or some other alternative way on doing so....

Related

Hibernate: Detached Entity error on Selects after adding #Transactional

I have a big Service method in my Spring/Hibernate app which previously wasn't transactional, and was working OK. As soon as I added #Transactional(readOnly = false, rollbackFor = {Exception.class}) to it, the Select (Fetch) part of that method started breaking at a Select statement (even prior to any persistence):
org.hibernate.PersistentObjectException: detached entity passed to persist: app.domain.Activities
in this code:
#Transactional(readOnly = false, rollbackFor = {Exception.class})
public String save(PlansModel plan, String id, Model model,HttpServletRequest request) throws IllegalAccessException, InvocationTargetException {
//...
//... some initial Selects - NO Persistence yet
Project proj = planService.getProjectByID(projId); // <-- BROKE HERE "detached entity passed to persist"
// ... NO Persistence yet
// ...
// At the end, some persistence starts
planService.savePlan(plan); //etc.
}
We're maintaining this messy code and it's hard to analyze what may have caused it. Some objects are being modified there.
We decided to work around it by putting #Transactional just around the actual persistence bottom part, which is all that matters for transactionality. (Our goal is to ensure that this whole method has transactionality/rollback.)
But when we did that, we found that in this new design, transactions don't get rolled back in case of errors. We've split it up into 2 methods, and just the 2nd method is #Transactional, but there's no rollback from here, and the Transaction mechanism doesn't kick in. Everywhere else it works.
public String save(PlansModel plan, String id, Model model,HttpServletRequest request) throws IllegalAccessException, InvocationTargetException {
//...
//... some initial Selects - NO Persistence yet
Project proj = planService.getProjectByID(projId); // <-- WORKING AGAIN without #Transactional
// ... NO Persistence yet
// ...
return persistPart(..); // for persistence
}
#Transactional(readOnly = false, rollbackFor = {Exception.class})
public String persistPart(..) {
planService.savePlan(plan); // <-- NO ROLLBACK from here
}
It seems like we're dealing with a method which has some issues as soon as #Transactional is added, but we need to ensure transactionality/rollback is enabled for it. Based on these symptoms, does anyone know what the solution might be?
#Transactional doesn't work when you call this method from inside the service class like you do. It's true not only for #Transactional. It's a limitation of Spring AOP.
Your question is almost a duplicate of this one. So you either need to inject this service into itself and call this transactional method on the injected object OR create another service which will call this transactional method

How to write a pointcut to get an operation on a class method?

I have a class named PersonDAOImpl, where I use Hibernate to persist data on my data base.
In order to audit the class, I created another class using the #Aspect annotation, where I declared some Pointcuts and Advices. Everytime I insert, update or delete a person, I'm able to store on a table proper information about the operation executed.
However now I have more DAO classes, and I don't want to create more Aspects classes or more Advices for every method on every DAO. Instead, I want to "capture" the operations of persist(), update() or delete() in a single pointcut.
For example I have this method on my DAO:
#Override
#Transactional
public void addPerson(Person p) {
Session session = this.sessionFactory.getCurrentSession();
session.persist(p);
session.flush();
logger.info("Person saved successfully, Person Details="+p);
}
And this is how I audit the insertions:
#Pointcut("execution(* com.dacasals.raspertwo.dao.PersonDAOImpl.addPerson(..))")
public void addNewPerson() {
}
#After("addNewPerson()")
public void aP() {
System.out.println("added a new person");
operation = "Insert";
date = Calendar.getInstance().getTime();
register(operation, date);
}
But I want something like #Pointcut("execution(*com.dacasals.raspertwo.dao.anydaoclass.anymethod.persist(..))")
I mean a pointcut for any method on any of my DAO classes where persist() is called. Same thing for update() and delete().
How can I achieve this? I've tried with 'within' and 'withincode' but nothing happens. Thanks in advance.
Instead of execution expression you might want to use the within expression something like this.
within(com.yourpackage..*)
For this you will have to make sure that all your DAO's are within this package. The above expression will match all the methods defined in classes inside package com.yourpackage and classes sub-packages.
Here is a short doc if you would like to configure it some other way,
Pointcut Expressions in Spring
I would use the 'AroundAdvice'.
Quote for documentation:
Around advice runs "around" a matched method execution. It has the opportunity to do work both before and after the method executes, and to determine when, how, and even if, the method actually gets to execute at all.
Around Advice Spring documentation
So, in your example, you would have a method:
#Around("com.dacasals.raspertwo.dao.*.persist(..)")
public Object doSomething(ProceedingJoinPoint pjp) throws Throwable {
}
This way every time someone on 'com.dacasals.raspertwo.dao' calls persist method, Spring Aspect will invoke doSomething method. Around advice works both before and after the method executes, so you should take care to don't do the same thing twice.
I would create a specific method for every operation (one for insert, another for update and delete) but you could use the same method using short-circuit operators. That way, your #Around annotation would be like:
#Around("com.dacasals.raspertwo.dao.*.persist(..) || com.dacasals.raspertwo.dao.*.update(..) || com.dacasals.raspertwo.dao.*.delete(..)")
public Object doSomething(ProceedingJoinPoint pjp) throws Throwable {
}

How to link JPA persistence context with single database transaction

Latest Spring Boot with JPA and Hibernate: I'm struggling to understand the relationship between transactions, the persistence context and the hibernate session and I can't easily avoid the dreaded no session lazy initialization problem.
I update a set of objects in one transaction and then I want to loop through those objects processing them each in a separate transaction - seems straightforward.
public void control() {
List<> entities = getEntitiesToProcess();
for (Entity entity : entities) {
processEntity(entity.getId());
}
}
#Transactional(value=TxType.REQUIRES_NEW)
public List<Entity> getEntitiesToProcess() {
List<Entity> entities = entityRepository.findAll();
for (Entity entity : entities) {
// Update a few properties
}
return entities;
}
#Transactional(value=TxType.REQUIRES_NEW)
public void processEntity(String id) {
Entity entity = entityRepository.getOne(id);
entity.getLazyInitialisedListOfObjects(); // throws LazyInitializationException: could not initialize proxy - no Session
}
However, I get a problem because (I think) the same hibernate session is being used for both transactions. When I call entityRepository.getOne(id) in the 2nd transaction, I can see in the debugger that I am returned exactly the same object that was returned by findAll() in the 1st transaction without a DB access. If I understand this correctly, it's the hibernate cache doing this? If I then call a method on my object that requires a lazy evaluation, I get a "no session" error. I thought the cache and the session were linked so that's my first confusion.
If I drop all the #Transactional annotations or if I put a #Transactional on the control method it all runs fine, but the database commit isn't done until the control method completes which is obviously not what I want.
So, I have a few questions:
How can I make the hibernate session align with my transaction scope?
What is a good pattern for doing the separation transactions in a loop with JPA and declarative transaction management?
I want to retain the declarative style (i.e. no xml), and don't want to do anything Hibernate specific.
Any help appreciated!
Thanks
Marcus
Spring creates a proxy around your service class, which means #Transactional annotations are only applied when annotated methods are called through the proxy (where you have injected this service).
You are calling getEntitiesToProcess() and processEntity() from within control(), which means those calls are not going through proxy but instead have the transactional scope of the control() method (if you aren't also calling control() from another method in the same class).
In order for #Transactional to apply, you need to do something like this
#Autowired
private ApplicationContext applicationContext;
public void control() {
MyService myService = applicationContext.getBean(MyService.class);
List<> entities = myService.getEntitiesToProcess();
for (Entity entity : entities) {
myService.processEntity(entity.getId());
}
}

#transactional in spring jpa not updating table

I am using spring jpa transactions in my project.One Case includes inserting a data in a synchronized method and when another thread accesses it the data is not updated.My code is given below :
public UpdatedDTO parentMethod(){
private UpdatedDTO updatedDTO = getSomeMethod();
childmethod1(inputVal);
return updatedDTO;
}
#Transactional
public synchronized childmethod1(inputVal){
//SomeCodes
//Place where update takes place
TableEntityObject obj = objectRepository.findByInputVal(inputVal);
if(obj == null){
childMethod2(inputVal);
}
}
#Transactional
public void childMethod2(inputVal){
//Code for inserting
TableEntityObject obj = new TableEntityObject();
obj.setName("SomeValue");
obj.setValueSet(inputVal);
objectRepository.save(obj);
}
Now if two threads access at the same time and if first thread completes childmethod2 and childmethod1 and without completing parentMethod() after that if second thread comes to the childMethod1() and checks if data exists,the data is null and is not updated by first thread.I have tried many ways like
#Transactional(propagation = Propagation.REQUIRES_NEW)
public synchronized childmethod1(inputVal){
//SomeCodes
//Place where update takes place
TableEntityObject obj = objectRepository.findByInputVal(inputVal);
if(obj == null){
childMethod2(inputVal);
}
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void childMethod2(inputVal){
//Code for inserting
TableEntityObject obj = new TableEntityObject();
obj.setName("SomeValue");
obj.setValueSet(inputVal);
objectRepository.save(obj);
}
also tried taking off #transactional in the childMethod1() but nothing works out.I know im doing something wrong here , but couldnt figure out where and what exactly i am doing wrong.Can anyone help me out with this
#Transactional is resolved using proxies on spring beans. It means it will have no effect if your method with #Transactional is called from the same class. Take a look at Spring #Transaction method call by the method within the same class, does not work?
The easiest would be moving those methods into separate service.
Typical checklist I follow in cases like these :
If Java based configuration then make sure
#EnableTransactionManagement annocation is present in the class
containing the #Configuration annotation
Make sure the transactionManager bean is created, again this should be mentioned in the configuration class.
Use of #Transactional annocatio over the method which is calling the repository, typically a class in the DAO layer
Adding the #Service annotation for the class which is invoking the methods in the repository
Nice blog which explains the Transaction configuration with JPA in depth --> http://www.baeldung.com/2011/12/26/transaction-configuration-with-jpa-and-spring-3-1/68954

Adding #Transactional causes "collection with cascade="all-delete-orphan" was no longer referenced"

I am upgrading a working project from Spring2+Hibernate3 to Spring3+Hibernate4. Since HibernateTemplate and HibernateDAOSupport have been retired, I did the following
Before (simplified)
public List<Object> loadTable(final Class<?> cls)
{
Session s = getSession(); // was calling the old Spring getSession
Criteria c = s.createCriteria(cls);
List<Object> objects = c.list();
if (objects == null)
{
objects = new ArrayList<Object>();
}
closeSession(s);
return objects;
}
Now (simplified)
#Transactional(propagation=Propagation.REQUIRED)
public List<Object> loadTable(final Class<?> cls)
{
Session s = sessionFactory.getCurrentSession();
Criteria c = s.createCriteria(cls);
List<Object> objects = c.list();
if (objects == null)
{
objects = new ArrayList<Object>();
}
return objects;
}
I also added the transaction annotation declaration to Spring XML and removed this from Hibernate properties
"hibernate.current_session_context_class", "org.hibernate.context.ThreadLocalSessionContext"
The #Transactional annotation seems to have worked as I see this in the stacktrace
at com.database.spring.DatabaseDAOImpl$$EnhancerByCGLIB$$7d20ef95.loadTable(<generated>)
During initialization, the changes outlined above seem to work for a few calls to the loadTable function but when it gets around to loading an entity with a parent, I get the "collection with cascade="all-delete-orphan" was no longer referenced" error. Since I have not touched any other code that sets/gets parents or children and am only trying to fix the DAO method, and the query is only doing a sql SELECT, can anyone see why the code got broken?
The problem seems similar to Spring transaction management breaks hibernate cascade
This is unlikely problem of Spring, but rather issue with your entity handling / definition. When you are using deleteOrphans on a relation, the underlying PersistentSet MUST NOT be removed from the entity itself. You are allowed only to modify the set instance itself. So if you are trying to do anything clever within your entity setters, that is the cause.
Also as far as I remember there are some issues when you have deleteOrphans on both sides of the relation and/or load/manipulate both sides within one session.
Btw. I don't think "hibernate.current_session_context_class", "org.hibernate.context.ThreadLocalSessionContext" is necessary. In our project, this is the only configuration we have:
#Bean
public LocalSessionFactoryBuilder sessionFactoryBuilder() {
return ((LocalSessionFactoryBuilder) new LocalSessionFactoryBuilder(
dataSourceConfig.dataSource()).scanPackages(ENTITY_PACKAGES).
setProperty("hibernate.id.new_generator_mappings", "true").
setProperty("hibernate.dialect", dataSourceConfig.dialect()).
setProperty("javax.persistence.validation.mode", "none"));
}
#Bean
public SessionFactory sessionFactory() {
return sessionFactoryBuilder().buildSessionFactory();
}
The issue was with Session Management. The same block of transactional code was being called by other modules that were doing their own session handling. To add to our woes, some of the calling modules were Spring beans while others were written in direct Hibernate API style. This disorganization was sufficient work to keep us away from moving up to Hibernate 4 immediately.
Moral of the lesson (how do you like that English?): Use a consistent DAO implementation across the entire project and stick to a clearly defined session and transaction management strategy.

Resources