Getting LazyInitializationException despite being in #Transactional and having no detached entities - spring

I’m using Spring 3.2.11.RELEASE, JPA 2.1, Hibernate 4.3.6.Final, and MySQL 5.5.37. I’m getting the below error in a JUnit test
testSendValidAssignment(org.mainco.subco.thirdParty.service.ThirdPartyServiceIT) Time elapsed: 13.366 sec <<< ERROR!
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: org.mainco.subco.lessonplan.domain.LessonPlan.classrooms, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:575)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:214)
at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:155)
at org.hibernate.collection.internal.PersistentBag.size(PersistentBag.java:278)
at org.mainco.subco.ThirdParty.service.ThirdPartyServiceIT.testSendValidAssignment(ThirdPartyServiceIT.java:102)
However, I don’t know what line is to blame for this error message. In my method I have
#Service
#Transactional
public class MyServiceImpl implements MyService
{
....
#Override
public void sendAssignment(final String assignmentId)
{
final Assignment assignment = m_lessonPlanDao.getAssignment(assignmentId);
if (processData(assignment))
{
// Gather the students who have been assigned this assignment
final List<Classroom> classes = lessonPlan.getClassrooms();
System.out.println("got " + classes.size() + " classes.");
// Send one request for each class assignment
for (final Classroom classroom : classes)
{
final List<User> classStudents = m_classroomSvc.findClassStudents(classroom.getId());
System.out.println("got " + classStudents.size() + " students.");
and both System.out lines print numbers. There are no other references to this lazily-loaded collection in the method so I don’t know what else to check in my data. Any advice is appreciated.

Your service method is transactional, but your entire test method is not. The size checking of the element is performed in an assertion statement in your JUnit test and is done outside of the transaction scope of the service, so it causes lazy initialization exception. There are three ways you can go
you can try call size() method inside your dao method to make
outside call of the size() method safe.
you can force the user of your method to
open transaction(document which objects are detached and make it a
contract/or use infamous open session in view pattern)
you can create a DTO layer and return size as a part of DTO
all three solutions have pros and cons:
the first solution will look weird for the supporters of the code when they'll find that you call a getter for no obvious reson - it needs commenting.
the second makes life harder to the users of the interface
the third violates DRY principle
p.s. you can also disable lazy initialization of course

Related

Spring Transactional on method with multiple db operation

I'm little confused. I though #Transactional on method means all operations or none.
Say I have this method:
#Transactional
public void fewDbOpeations(){
calculation1();
myDao.saveResult();
calculation2();
myDao.saveResult();
}
Say calculation2() throw exception or my second call to myDao.saveResult goes wrong , what I see is the even though the whole method annotated with #Transactional the saving result after calculation1() call is successful.
That is my first interaction with database saved the records I want but the second one failed but I thought because the method is #Transactinal even the first call to save to database should be rolled back.
Do I miss something?
#Transactional (rollbackFor = Exception.class)
public void fewDbOpeations(){
calculation1();
myDao.saveResult();
calculation2();
myDao.saveResult();
}
Try using this as well and throw Exceptions.
It depends on how you handle exceptions and if there are still #Transactional annotated on those internal method calls.
To have "all or nothing" behaviour in fewDbOpeations(), make sure the followings for all the internal method calls :
Do not annotated with #Transactional(propagation=REQUIRES_NEW)
Do not catch the exception inside and not throw out. Throw RuntimeException or Error but not checked Exception (Assume you are using default setting).

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());
}
}

When does Grails assign an ID to an object?

A Grails 2.3.4 application is connecting to an Oracle database using the following domain class:
class Person {
String name
static mapping = {
id column: "PERSON_ID", generator: "sequence", params: [sequence: 'person_seq']
}
}
The PersonController makes a call to a method in PersonService and it makes a call to UtilService. The method in UtilService being called has some logic based on wether this Person object is new:
if (personInstance.id == null) { ... }
What I have found is that the id property of personInstance (which is passed through the method calls described above) is assigned when the UtilService is called.
The controller action calling PersonService is #Transactional, and the services do not have any transaction configuration.
So, a couple of questions:
When is id value assigned by GORM (I assumed at insert but that seems wrong)?
Is there a better way of checking if the object is new (isAttached() returns true so that's not good for me)?
EDIT: save() has not been called on the personInstance when UtilService does the id check.
The id is assigned when you call save(). For most persistence calls, Hibernate delays flushing the change until it feels it has to flush) to ensure correctness. But save() calls are treated differently. I believe the motivation is that even if we know that flush() will eventually be called, even if it's at the very end of the request, we want to retrieve the id early so it doesn't suddenly change.
Note that a service that does "not have any transaction configuration" is transactional - the only way to get a non-transactional service is to remove all #Transactional annotations (the newer Grails annotation and the older Spring annotation) and add
static transactional = false
All other services are transactional, although you can configure individual methods to be ignored ## Headin.
Turns out, I had a findBy which was flushing the session:
utilService.someMethod(Person.findByUsername(username))
It was at this point that the id was populated.
Got around it by using withNewTransaction:
def personInstance = Person.withNewSession { Person.findByUsername(username) }
utilService.someMethod(personInstance)
Which now leads me onto the next question...

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

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....

Do I have to try-catch JpaRepository

I am using JpaRepository from Spring Data JPA framework. I have a snippet of code below:
#Repository
public interface PresetFolderRepository extends JpaRepository<PresetFolder, Integer>{
#Modifying
#Transactional
#Query("update PresetFolder pf set pf.parentId = :parentId where pf.id = :id")
int updateParentId(#Param("id") int id, #Param("parentId") int parentId);
}
When I invoke this method:
#Autowired PresetFolderRepository repo;
repo.updateParentId(1,2);
public void test(){
Do I have to surround it with a try-catch? How can I know if the self-defined method 'updateParentId' has try-catch implementation in it?
Thanks!
EDIT:
My concern is, if my database went down, does this method catch the exception.
Repositories will always tell you something if a problem happens (i.e. they never swallow exceptions). You'll always get a runtime exception if that's the case.
And you should probably not catch such an exception either, except at the very top of the call stack, where you have the possibility to display an error message to the end user.
No you don't need it to surround by try-catch block. Most of the Spring-Data repositories throw runtime exceptions. With respect to your concern, if the database is down then a runtime exception is generated and you can catch it at the controller level(if you are writing a web application).
The test case would throw out an error for unreachable host, if you are executing the test case when the DB is down.

Resources