Considering the following model:
@Entity
public class User {
@Id
@Column(name = "USER_ID")
private Long userId;
@Column(name = "FIRST_NAME")
private String firstName;
@Column(name = "LAST_NAME")
private String lastName;
@OneToOne
@PrimaryKeyJoinColumn
private UserExt userExt;
... //getters and setters
}
@Entity
public class UserExt {
@Id
@Column(name="USER_ID")
private Long id;
private String cdpId;
private Date lastChanged;
... //getters and setters
}
when executing :
Query query = session.createQuery("from User");
List<User> list = query.list();
Hibernate executes
Hibernate: select user0_.USER_ID as USER1_0_, user0_.FIRST_NAME as FIRST2_0_, user0_.LAST_NAME as LAST3_0_, user0_.EXT_USERNAME as EXT4_0_ from USER user0_
Hibernate: select userext0_.USER_ID as USER1_1_0_, userext0_.cdpId as cdpId1_0_, userext0_.lastChanged as lastChan3_1_0_ from USER_EXT userext0_ where userext0_.USER_ID=?
Hibernate: select userext0_.USER_ID as USER1_1_0_, userext0_.cdpId as cdpId1_0_, userext0_.lastChanged as lastChan3_1_0_ from USER_EXT userext0_ where userext0_.USER_ID=?
...
...
Using a query with specific properties works (select u.firstName, u.userExt.cdpId).
However since I want the full User Entity ("from User"), hibernate generates one select for each result row in the first.
I don't get it since the default fetch strategy should be LAZY not EAGER. Forcing it to LAZY didn't fix the problem.
The One-To-One mapping represents a single-valued association where an instance of one entity is associated with an instance of another entity. In this type of association one instance of source entity can be mapped atmost one instance of target entity.
Define Hibernate Mapping FileThe <many-to-one> element will be used to define the rule to establish a one-to-one relationship between EMPLOYEE and ADDRESS entities, but column attribute will be set to unique constraint and rest of the mapping file will remain as it was in case of many-to-one association.
According to book Pro JPA 2 the main difference between unidirectional @ManyToOne and @OneToOne is that in @OneToOne: Only one instance of the source entity can refer to the same target entity instance. In other words, the target entity instance is not shared among the source entity instances.
The best way to map a @OneToOne relationship is to use @MapsId . This way, you don't even need a bidirectional association since you can always fetch the PostDetails entity by using the Post entity identifier. This way, the id property serves as both Primary Key and Foreign Key.
There are two problems preventing lazy loading here:
OneToOne
is EAGER
(and keep in mind that LAZY
is just a hint to the persistence provider).LAZY
can only work on a OneToOne
association if the association is non-nullable (at least without using bytecode instrumentation).9.1.23 OneToOne Annotation
The
OneToOne
annotation defines a single-valued association to another entity that has one-to-one multiplicity. It is not normally necessary to specify the associated target entity explicitly since it can usually be inferred from the type of the object being referenced.Table 16 lists the annotation elements that may be specified for a
OneToOne
annotation and their default values.@Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface OneToOne { Class targetEntity() default void.class; CascadeType[] cascade() default {}; FetchType fetch() default EAGER; boolean optional() default true; String mappedBy() default ""; }
I tested the following:
@OneToOne(optional = false, fetch = FetchType.LAZY)
@PrimaryKeyJoinColumn
private UserExt userExt;
And confirm that a simple from User
only loads all the users
Hibernate: select user0_.USER_ID as USER1_0_, user0_.FIRST_NAME as FIRST2_0_, user0_.LAST_NAME as LAST3_0_ from USER user0_
And doesn't perform N additional queries, the UserExt
are loaded lazily.
So, if you association is mandatory, use the appropriate mapping :) And if it is non-mandatory, you'll have to either:
join fetch
to avoid the N subsequent selects (of course, this somehow defeats the point of a separate table)Note that Hibernate >= 3.x ignores the Fetch annotation when you use the Query interface. In that case, you need to write that explicitly. this is an example:
EntityManager em = [...]
[...]
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<User> criteria = builder.createQuery(User.class);
Root<User> usersRoot = criteria.from(User.class);
usersRoot.fetch("Address", JoinType.LEFT);
List<User> users = em.createQuery(criteria).getResultList();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With