Sanjaya Liyanage
Sanjaya Liyanage

Reputation: 4746

What's the JPA equivalent of the Hibernate @LazyToOne annotation?

I am currently using the Hibernate @LazyToOne annotation.

Since I'm switching from Hibernate to JPA, I'd like to know whether @LazyToOne is available in JPA, or is there any other annotation that provides the same functionality in JPA?

Upvotes: 3

Views: 13737

Answers (3)

Vlad Mihalcea
Vlad Mihalcea

Reputation: 153950

@LazyToOne and LazyToOneOption

The legacy @LazyToOne annotation provides the following options:

  • FALSE
  • PROXY
  • NO_PROXY

LazyToOne annotation

LazyToOneOption.FALSE

The FALSE option is equivalent to the JPA FetchType.EAGER annotation, so it should be avoided as it can cause serious performance issues.

LazyToOneOption.PROXY

The PROXY option is equivalent to the JPA [FetchType.LAZY] strategy. However, you should prefer using the fetch attribute of the JPA @OneToOne or @ManyToOne associations instead.

LazyToOneOption.NO_PROXY

This strategy is useful when using the @OneToOne association on the parent side.

So, assuming you have the following Post and PostDetails entities:

Post and PostDetails entities

If the Post maps the parent-side of this one-to-one table relationship:

@OneToOne(
    mappedBy = "post",
    fetch = FetchType.LAZY,
    cascade = CascadeType.ALL
)
private PostDetails details;

Even if the association uses the FetchType.LAZY strategy, we can see that if we want to fetch the Post entity:

Post post = doInJPA(entityManager -> {
    return entityManager.find(Post.class, 1L);
});

The PostDetails is going to be fetched eagerly:

SELECT p.id AS id1_0_0_,
       p.title AS title2_0_0_
FROM post p
WHERE p.id = 1
 
SELECT pd.id AS id1_1_0_,
       pd.created_by AS created_2_1_0_,
       pd.created_on AS created_3_1_0_
FROM post_details pd
WHERE pd.id = 1

This is because Hibernate doesn't know how to initialize the details property since it needs to decide whether to use null or a Proxy, and the only way to find that out is to issue a SELECT query.

So, to fix this issue, we need to things:

  • enable lazy fetching bytecode enhancement
  • use the NO_PROXY strategy

To enable lazy loading bytecode enhancement, we can use this Hibernate Maven plugin:

<plugin>
    <groupId>org.hibernate.orm.tooling</groupId>
    <artifactId>hibernate-enhance-maven-plugin</artifactId>
    <version>${hibernate.version}</version>
    <executions>
        <execution>
            <configuration>
                <enableLazyInitialization>true</enableLazyInitialization>
            </configuration>
            <goals>
                <goal>enhance</goal>
            </goals>
        </execution>
    </executions>
</plugin>

And, we also need to use the NO_PROXY strategy:

@OneToOne(
    mappedBy = "post",
    fetch = FetchType.LAZY,
    cascade = CascadeType.ALL
)
@LazyToOne(LazyToOneOption.NO_PROXY)
private PostDetails details;

With this change in place, the parent-side @OneToOne association is going to be fetched lazily.

Upvotes: 4

xtian
xtian

Reputation: 3169

It should be

@OneToOne(fetch = FetchType.LAZY, optional=false)

Found on this forum: https://forum.hibernate.org/viewtopic.php?p=2388783

The reason, I guess, is that the only way for JPA to know if an attribute has already been fetched is to check it has a value, but if null is a valid value then there's no way to tell. Hence optional attributes are always fetched regardless.

Upvotes: 0

Guilherme A.
Guilherme A.

Reputation: 1

You can create a jpql, like:

User user = em.createQuery("SELECT NEW com.model.User(u.id, u.name, u.password)
    FROM User u WHERE u.id = :id", User.class)
        .setParameter("id", 1L)
        .gerSingleResult();`

It may solve your problem. But, you have to remember to write the default constructor.

Upvotes: 0

Related Questions