0

I have a SpringBoot/Spring Data JPA application. Until recently, we used the default Spring setting "open-in-view". I had to switch this setting off, because as the use cases were going more complexe, managing transactions became a nightmare. Switching the setting off helped indeed getting the control back on transactions.

This has a price though: we have suddenly to deal with some LazyInitializationExceptions. This was expected. But I'd like to know if my way of dealing with these exceptions is ok. In most of the cases we get LazyInitializationException while reading data: the code tried to access nested properties of detached entities. My (lazy?) way of dealing with this situation is to add a

@Transactional(readOnly=true)

in the concerned service methods. Is there anything wrong with this approach?

I'm wondering because in my readings about this topic
https://vladmihalcea.com/the-best-way-to-handle-the-lazyinitializationexception/
https://thorben-janssen.com/lazyinitializationexception/

redesigning the scope of the transactions is never mentioned, when it seems to me the easiest and fastest fix.

Julien Berthoud
  • 721
  • 8
  • 24

1 Answers1

0

In the service methods this is fine. The open-in-view thing is an anti pattern because the initializations happen during view rendering which might keep the transaction longer open than necessary. If you are actually writing in your transaction, you need to keep it open anyway.

The best solution is to IMO use DTOs though and I think this is a perfect use case for Blaze-Persistence Entity Views.

I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.

A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:

@EntityView(User.class)
public interface UserDto {
    @IdMapping
    Long getId();
    String getName();
    Set<RoleDto> getRoles();

    @EntityView(Role.class)
    interface RoleDto {
        @IdMapping
        Long getId();
        String getName();
    }
}

Querying is a matter of applying the entity view to a query, the simplest being just a query by id.

UserDto a = entityViewManager.find(entityManager, UserDto.class, id);

The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

Page<UserDto> findAll(Pageable pageable);

The best part is, it will only fetch the state that is actually necessary!

Christian Beikov
  • 15,141
  • 2
  • 32
  • 58