Blockost
Blockost

Reputation: 583

Use proxies with Hibernate runtime bytecode enhancement

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!

Upvotes: 3

Views: 1086

Answers (1)

Christian Beikov
Christian Beikov

Reputation: 16430

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!

Upvotes: 0

Related Questions