3

I'm using Spring Boot 2.7.5 with Hibernate 5.6.12.Final and apply bytecode enhancement at build-time via hibernate-enhance-maven-plugin. It works great and prevents eagerly fetching @OneToOne relationships, as described here https://vladmihalcea.com/hibernate-lazytoone-annotation/.

However, I found that all lazy relationships do not use proxies anymore but are fetched from the database when I use the getter (which has been instrumented by the maven plugin at build time). Basically, what I would like to do is to still use proxies with bytecode enhancement. Something similar is available at runtime https://in.relation.to/2019/07/30/bytecode-proxy/, but I can't find a way to do it at build-time.

For a bit of context, I need proxies for lazy-loaded properties because it allows me to apply custom logic in entity->dto mapper (using Mapstruct) based on whether the proxy has been initialized or not. In some cases, I must trigger an initialization manually if a Hibernate session is available (and prevent LazyInitializationException).

Any suggestion is greatly appreciated!

Blockost
  • 493
  • 1
  • 7
  • 18
  • you can check use properties like -> `lazy="proxy|no-proxy|false"` – muhammed ozbilici Jan 02 '23 at 13:02
  • Can you please indicate where I should set this property ? Also, I tried setting `spring.jpa.hibernate.bytecode.allow_enhancement_as_proxy: true` in application.yml, but it didn't work – Blockost Jan 02 '23 at 13:55
  • 1
    @Blockost have you checked `org.hibernate.Hibernate#isPropertyInitialized` method? – Andrey B. Panfilov Jan 02 '23 at 14:09
  • I did not know about this method, thank you for pointing that out. It will definitely help me at some point! However, my problem is that property getters are called in the mapper and it's not always guaranteed that a Hibernate session is available. So I get a `LazyInitializationException` there. Mappers are generated at build time by Mapstruct so I don't have full control over them (and I don't want to add too much logic on Mapstruct side anyway) – Blockost Jan 02 '23 at 21:32
  • Just to expand on @muhammedozbilici's suggestion, [the docs](https://docs.jboss.org/hibernate/orm/5.4/topical/html_single/bytecode/BytecodeEnhancement.html) actually show you the way in which you can opt-out of each particular enhancement. And, since you're using compile-time enhancement with the Maven plugin, spring properties obviously won't work – crizzis Feb 01 '23 at 12:02
  • Alternatively, [see here](https://medium.com/cloud-workers/mapstruct-and-hibernate-jpa-lazy-loading-1c1c6f416e) for making MapStruct work with Hibernate's lazy associations - in short, you *do* have some control over the mappers, just enough to make it work – crizzis Feb 01 '23 at 12:06

1 Answers1

0

If you want to avoid LazyInitializationException for your DTO mapping, you should rather try to avoid doing the mapping in Java code and instead use a solution that can take the mapping down to the HQL/SQL level.

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 a possible 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