Marcus
Marcus

Reputation: 170

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:

  1. How can I make the hibernate session align with my transaction scope?
  2. 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

Upvotes: 1

Views: 261

Answers (1)

Predrag Maric
Predrag Maric

Reputation: 24433

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

Upvotes: 1

Related Questions