Reputation: 734
When I add an @OneToOne mapping on the non-owning side (User), I am getting extra selects loading Staff when performing a query. Once I remove the @OneToOne in the user class, N+1 problem is resolved.
User.staff is set to lazy, so it should not be trying to load that.
Anything I'm missing, or is this always the case with bidirectional associations?
/* named HQL query Staff.findByName */
/* load com.xxx.Staff */
/* load com.xxx.Staff */
/* load com.xxx.Staff */
PS. A user is not necessarily a staff member, but staff has to be a user.
Staff class
@Entity
@Table(name = "staff")
@NamedQueries({
@NamedQuery(name = Staff.QUERY_BY_NAME, query = "from Staff s left join fetch s.user u left join fetch u.staff where (lower(s.user.displayName) like :key) or (lower(s.user.lastName) like :key)")})
public class Staff extends AbstractDomainObject<Long> {
public static final String QUERY_BY_NAME = "Staff.findByName";
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "user_id")
private User user;
User Class
@Entity
@Table(name = "env_user")
@SecondaryTable(name = "app_user")
public class User extends AbstractUserDetailsDomainObject<Long> {
public static final String QUERY_BY_USERNAME = "User.findByUsername";
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(optional = true, fetch = FetchType.LAZY, mappedBy = "user")
private Staff staff;
}
Upvotes: 2
Views: 1132
Reputation: 734
Credit goes to Alan Hay for providing link with solution. Cleanest solution in my option is to use FieldHandled. @OneToMany compromises logical structure and the instrument stuff is too easy to miss and forget by future developers.
public class User extends AbstractUserDetailsDomainObject<Long> implements FieldHandled {
public static final String QUERY_BY_USERNAME = "User.findByUsername";
/**
* <p>
* Required to allow lazy loading of Staff @OneToOne association
* </p>
*/
private FieldHandler fieldHandler;
@OneToOne(mappedBy = "user", fetch = FetchType.LAZY, optional = true)
@LazyToOne(LazyToOneOption.NO_PROXY)
private Staff staff;
/**
* <p>
* Staff Getter
* </p>
* Uses fieldhandler to force lazy loading
*
* @return the staff
*/
public Staff getStaff() {
Staff result = this.staff;
if (this.fieldHandler != null) {
result = (Staff) this.fieldHandler.readObject(this, "staff", this.staff);
}
return result;
}
/**
* <p>
* Staff Setter
* </p>
* Uses fieldhandler to force lazy loading
*
* @param staff
* the staff to set
*/
public void setStaff(final Staff staff) {
if (this.fieldHandler != null) {
this.staff = (Staff) this.fieldHandler.writeObject(this, "staff", this.staff, staff);
} else {
this.staff = staff;
}
}
/**
* <p>
* FieldHandled interface method.
* </p>
* Required to allow lazy loading of Staff @OneToOne association
*/
public FieldHandler getFieldHandler() {
return this.fieldHandler;
}
/**
* <p>
* FieldHandled interface method.
* </p>
* Required to allow lazy loading of Staff @OneToOne association
*/
public void setFieldHandler(final FieldHandler fieldHandler) {
this.fieldHandler = fieldHandler;
}
}
Upvotes: 1