HQL 查询为惰性关联添加 JOINS
HQL Query adding JOINS for lazy associations
我有一个实体 'Notifications',它有大约 10 个 ManyToOne
关系,标记为 FetchType.Lazy
,如下所示:
@ManyToOne(fetch = FetchType.LAZY, optional = true)
然而,在构建了一个检索通知列表的 HQL 查询(并且不包括任何这些关系)之后,我查看了生成的 SQL 它包括 LEFT OUTER JOIN
的所有这些惰性关联
为什么 hibernate 在应该延迟加载时包含这些?
(休眠 3.6 | Postgres)
更新:HQL 和 SQL 包括:
public List<Notification> findUserNotifications(User user, int limit, int offset){
String s = "from Notification as n ";
s+="where (n.user = :user or n.pool = :pool or n.client = :client or n.admin is not null ";
s+=") and n.dateCreated > :userStartDate ";
s+="order by dateCreated desc";
Query query = this.getEntityManager().createQuery(s);
query.setParameter("user", user);
query.setParameter("userStartDate", user.getDateCreated());
query.setParameter("pool", user.getLocalPool());
query.setParameter("client", user.getLocalPool().getClient());
query.setFirstResult(offset);
query.setMaxResults(limit);
return query.getResultList();
}
生成SQL:
select notificati0_.notification_id as notifica1_159_, notificati0_.admin_audience as admin7_159_, notificati0_.client_audience as client8_159_, notificati0_.client_tag_audience as client9_159_, notificati0_.custom_message as custom2_159_, notificati0_.date_created as date3_159_, notificati0_.icon as icon159_, notificati0_.message_id as message5_159_, notificati0_.pool_audience as pool10_159_, notificati0_.title_id as title6_159_, notificati0_.user_audience as user11_159_, notificati0_1_.client_user_id as client1_160_, notificati0_2_.swim_mate_id as swim1_165_, notificati0_3_.user_training_plan_id as user1_169_, notificati0_4_.user_challenge_id as user1_167_, notificati0_4_.user_id as user2_167_, notificati0_5_.session_id as session1_164_, notificati0_6_.personal_best_id as personal1_163_, notificati0_7_.competition_user_session_id as competit1_162_, notificati0_8_.user_challenge_id as user1_166_, notificati0_9_.competition_user_id as competit1_161_, notificati0_10_.user_drill_id as user1_168_ from public.notifications notificati0_ left outer join public.notification_client_user_creators notificati0_1_ on notificati0_.notification_id=notificati0_1_.notification_id left outer join public.swim_mate_notifications notificati0_2_ on notificati0_.notification_id=notificati0_2_.notification_id left outer join public.user_training_plan_notifications notificati0_3_ on notificati0_.notification_id=notificati0_3_.notification_id left outer join public.ucu_notifications notificati0_4_ on notificati0_.notification_id=notificati0_4_.notification_id left outer join public.session_notifications notificati0_5_ on notificati0_.notification_id=notificati0_5_.notification_id left outer join public.pb_notifications notificati0_6_ on notificati0_.notification_id=notificati0_6_.notification_id left outer join public.competition_session_notifications notificati0_7_ on notificati0_.notification_id=notificati0_7_.notification_id left outer join public.user_challenge_notifications notificati0_8_ on notificati0_.notification_id=notificati0_8_.notification_id left outer join public.competition_user_notifications notificati0_9_ on notificati0_.notification_id=notificati0_9_.notification_id left outer join public.user_drill_notifications notificati0_10_ on notificati0_.notification_id=notificati0_10_.notification_id where (notificati0_.user_audience=? or notificati0_.pool_audience=1 or notificati0_.client_audience=1 or notificati0_.admin_audience is not null) and notificati0_.date_created>? order by notificati0_.date_created desc limit ?
和实体本身:
@Entity
@Table(name = "notifications", schema = "public")
public class Notification implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private int notificationId;
private User user;
private String titleId;
private String messageId;
private String icon;
private boolean customMessage;
private Date dateCreated;
//Relations
private SwimMate swimMate;
private Session session;
private PersonalBest personalBest;
private UserChallengeUser userChallengeUser;
private UserChallenge userChallenge;
private CompetitionUserSession competitionUserSession;
private CompetitionUser competitionUser;
private UserTrainingPlan userTrainingPlan;
private DockActiveBand dockActiveBand;
private UserDrill userDrill;
private Pool pool;
private Client client;
private ClientTag clientTag;
private Admin admin;
private ClientUser clientUserCreator;
public Notification() {
}
public Notification(User user, String titleId, String messageId, String icon) {
super();
this.user = user;
this.titleId = titleId;
this.messageId = messageId;
this.icon = icon;
this.dateCreated = Calendar.getInstance().getTime();
}
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "notification_id", unique = true, nullable = false)
public int getNotificationId() {
return this.notificationId;
}
public void setNotificationId(int notificationId) {
this.notificationId = notificationId;
}
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "date_created", nullable = false, length = 29)
public Date getDateCreated() {
return this.dateCreated;
}
public void setDateCreated(Date dateCreated) {
this.dateCreated = dateCreated;
}
@Length(min=10,max=40)
@Column(name = "title_id", nullable = false)
public String getTitleId() {
return titleId;
}
public void setTitleId(String titleId) {
this.titleId = titleId;
}
@Column(name = "message_id", nullable = false)
public String getMessageId() {
return this.messageId;
}
public void setMessageId(String messageId) {
this.messageId = messageId;
}
@Column(name = "icon", nullable = false)
public String getIcon() {
return this.icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
@Column(name = "custom_message", nullable = false)
public boolean isCustomMessage() {
return this.customMessage;
}
public void setCustomMessage(boolean customMessage) {
this.customMessage = customMessage;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_audience", nullable = true)
public User getUser(){
return this.user;
}
public void setUser(User user) {
this.user = user;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "pool_audience", nullable = true)
public Pool getPool() {
return pool;
}
public void setPool(Pool pool) {
this.pool = pool;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "client_tag_audience", nullable = true)
public ClientTag getClientTag() {
return clientTag;
}
public void setClientTag(ClientTag clientTag) {
this.clientTag = clientTag;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "client_audience", nullable = true)
public Client getClient() {
return client;
}
public void setClient(Client client) {
this.client = client;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "admin_audience", nullable = true)
public Admin getAdmin() {
return admin;
}
public void setAdmin(Admin admin) {
this.admin = admin;
}
@ManyToOne(fetch = FetchType.LAZY, optional = true)
@JoinTable(name = "swim_mate_notifications", schema = "public",
joinColumns = { @JoinColumn(name = "notification_id") },
inverseJoinColumns = { @JoinColumn(name = "swim_mate_id") })
public SwimMate getSwimMate() {
return swimMate;
}
public void setSwimMate(SwimMate swimMate) {
this.swimMate = swimMate;
}
@ManyToOne(fetch = FetchType.EAGER, optional = true)
@JoinTable(name = "session_notifications", schema = "public",
joinColumns = { @JoinColumn(name = "notification_id", nullable=true)},
inverseJoinColumns = { @JoinColumn(name = "session_id", nullable=true) })
public Session getSession() {
return session;
}
public void setSession(Session session) {
this.session = session;
}
@OneToOne(fetch = FetchType.LAZY, optional = true)
@JoinTable(name = "pb_notifications", schema = "public",
joinColumns = { @JoinColumn(name = "notification_id", nullable=true) },
inverseJoinColumns = { @JoinColumn(name = "personal_best_id", nullable=true) })
public PersonalBest getPersonalBest() {
return personalBest;
}
public void setPersonalBest(PersonalBest personalBest) {
this.personalBest = personalBest;
}
@ManyToOne(fetch = FetchType.LAZY, optional = true)
@JoinTable(name = "ucu_notifications", schema = "public",
joinColumns = { @JoinColumn(name = "notification_id") },
inverseJoinColumns = { @JoinColumn(name = "user_challenge_id"), @JoinColumn(name = "user_id") })
public UserChallengeUser getUserChallengeUser() {
return userChallengeUser;
}
public void setUserChallengeUser(UserChallengeUser userChallengeUser) {
this.userChallengeUser = userChallengeUser;
}
@ManyToOne(fetch = FetchType.LAZY, optional = true)
@JoinTable(name = "user_challenge_notifications", schema = "public",
joinColumns = { @JoinColumn(name = "notification_id") },
inverseJoinColumns = { @JoinColumn(name = "user_challenge_id")})
public UserChallenge getUserChallenge(){
return userChallenge;
}
public void setUserChallenge(UserChallenge userChallenge) {
this.userChallenge = userChallenge;
}
@OneToOne(fetch = FetchType.LAZY, optional = true)
@JoinTable(name = "competition_session_notifications", schema = "public",
joinColumns = { @JoinColumn(name = "notification_id", nullable=true) },
inverseJoinColumns = { @JoinColumn(name = "competition_user_session_id", nullable=true)})
public CompetitionUserSession getCompetitionUserSession() {
return competitionUserSession;
}
public void setCompetitionUserSession(CompetitionUserSession competitionUserSession) {
this.competitionUserSession = competitionUserSession;
}
@OneToOne(fetch = FetchType.LAZY, optional = true)
@JoinTable(name = "competition_user_notifications", schema = "public",
joinColumns = { @JoinColumn(name = "notification_id") },
inverseJoinColumns = { @JoinColumn(name = "competition_user_id")})
public CompetitionUser getCompetitionUser() {
return competitionUser;
}
public void setCompetitionUser(CompetitionUser competitionUser) {
this.competitionUser = competitionUser;
}
@ManyToOne(fetch = FetchType.LAZY, optional = true)
@JoinTable(name = "user_training_plan_notifications", schema = "public",
joinColumns = { @JoinColumn(name = "notification_id") },
inverseJoinColumns = { @JoinColumn(name = "user_training_plan_id") })
public UserTrainingPlan getUserTrainingPlan() {
return userTrainingPlan;
}
public void setUserTrainingPlan(UserTrainingPlan userTrainingPlan) {
this.userTrainingPlan = userTrainingPlan;
}
@ManyToOne(fetch = FetchType.LAZY, optional = true)
@JoinTable(name = "user_drill_notifications", schema = "public",
joinColumns = { @JoinColumn(name = "notification_id") },
inverseJoinColumns = { @JoinColumn(name = "user_drill_id") })
public UserDrill getUserDrill() {
return userDrill;
}
public void setUserDrill(UserDrill userDrill) {
this.userDrill = userDrill;
}
@OneToOne(fetch = FetchType.LAZY, mappedBy = "notification", optional = true)
public DockActiveBand getDockActiveBand() {
return dockActiveBand;
}
public void setDockActiveBand(DockActiveBand dockActiveBand) {
this.dockActiveBand = dockActiveBand;
}
//Log Creator Info
@ManyToOne(fetch = FetchType.LAZY, optional = true)
@Fetch(FetchMode.SELECT)
@JoinTable(name = "notification_client_user_creators", schema = "public",
joinColumns = { @JoinColumn(name = "notification_id") },
inverseJoinColumns = { @JoinColumn(name = "client_user_id") })
public ClientUser getClientUserCreator() {
return clientUserCreator;
}
public void setClientUserCreator(ClientUser clientUserCreator) {
this.clientUserCreator = clientUserCreator;
}
}
您正在使用连接 table 来映射您的 toOne 关联,而不是连接列(这是默认设置)。由于关联是可选的,Hibernate 无法知道是否存在关联实体,仅通过获取 main table 的列即可。使用连接列,它可以:
- 如果该列为空,则 ManyToOne 注释字段为空
- 如果该列不为空,则 ManyToOne 注释字段是包含目标实体 ID 的惰性代理
使用连接 table,Hibernate 被迫从连接 table 中获取列,以便决定是否必须将字段设置为 null 或者是否必须使用代理对其进行初始化.
我有一个实体 'Notifications',它有大约 10 个 ManyToOne
关系,标记为 FetchType.Lazy
,如下所示:
@ManyToOne(fetch = FetchType.LAZY, optional = true)
然而,在构建了一个检索通知列表的 HQL 查询(并且不包括任何这些关系)之后,我查看了生成的 SQL 它包括 LEFT OUTER JOIN
的所有这些惰性关联
为什么 hibernate 在应该延迟加载时包含这些?
(休眠 3.6 | Postgres)
更新:HQL 和 SQL 包括:
public List<Notification> findUserNotifications(User user, int limit, int offset){
String s = "from Notification as n ";
s+="where (n.user = :user or n.pool = :pool or n.client = :client or n.admin is not null ";
s+=") and n.dateCreated > :userStartDate ";
s+="order by dateCreated desc";
Query query = this.getEntityManager().createQuery(s);
query.setParameter("user", user);
query.setParameter("userStartDate", user.getDateCreated());
query.setParameter("pool", user.getLocalPool());
query.setParameter("client", user.getLocalPool().getClient());
query.setFirstResult(offset);
query.setMaxResults(limit);
return query.getResultList();
}
生成SQL:
select notificati0_.notification_id as notifica1_159_, notificati0_.admin_audience as admin7_159_, notificati0_.client_audience as client8_159_, notificati0_.client_tag_audience as client9_159_, notificati0_.custom_message as custom2_159_, notificati0_.date_created as date3_159_, notificati0_.icon as icon159_, notificati0_.message_id as message5_159_, notificati0_.pool_audience as pool10_159_, notificati0_.title_id as title6_159_, notificati0_.user_audience as user11_159_, notificati0_1_.client_user_id as client1_160_, notificati0_2_.swim_mate_id as swim1_165_, notificati0_3_.user_training_plan_id as user1_169_, notificati0_4_.user_challenge_id as user1_167_, notificati0_4_.user_id as user2_167_, notificati0_5_.session_id as session1_164_, notificati0_6_.personal_best_id as personal1_163_, notificati0_7_.competition_user_session_id as competit1_162_, notificati0_8_.user_challenge_id as user1_166_, notificati0_9_.competition_user_id as competit1_161_, notificati0_10_.user_drill_id as user1_168_ from public.notifications notificati0_ left outer join public.notification_client_user_creators notificati0_1_ on notificati0_.notification_id=notificati0_1_.notification_id left outer join public.swim_mate_notifications notificati0_2_ on notificati0_.notification_id=notificati0_2_.notification_id left outer join public.user_training_plan_notifications notificati0_3_ on notificati0_.notification_id=notificati0_3_.notification_id left outer join public.ucu_notifications notificati0_4_ on notificati0_.notification_id=notificati0_4_.notification_id left outer join public.session_notifications notificati0_5_ on notificati0_.notification_id=notificati0_5_.notification_id left outer join public.pb_notifications notificati0_6_ on notificati0_.notification_id=notificati0_6_.notification_id left outer join public.competition_session_notifications notificati0_7_ on notificati0_.notification_id=notificati0_7_.notification_id left outer join public.user_challenge_notifications notificati0_8_ on notificati0_.notification_id=notificati0_8_.notification_id left outer join public.competition_user_notifications notificati0_9_ on notificati0_.notification_id=notificati0_9_.notification_id left outer join public.user_drill_notifications notificati0_10_ on notificati0_.notification_id=notificati0_10_.notification_id where (notificati0_.user_audience=? or notificati0_.pool_audience=1 or notificati0_.client_audience=1 or notificati0_.admin_audience is not null) and notificati0_.date_created>? order by notificati0_.date_created desc limit ?
和实体本身:
@Entity
@Table(name = "notifications", schema = "public")
public class Notification implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private int notificationId;
private User user;
private String titleId;
private String messageId;
private String icon;
private boolean customMessage;
private Date dateCreated;
//Relations
private SwimMate swimMate;
private Session session;
private PersonalBest personalBest;
private UserChallengeUser userChallengeUser;
private UserChallenge userChallenge;
private CompetitionUserSession competitionUserSession;
private CompetitionUser competitionUser;
private UserTrainingPlan userTrainingPlan;
private DockActiveBand dockActiveBand;
private UserDrill userDrill;
private Pool pool;
private Client client;
private ClientTag clientTag;
private Admin admin;
private ClientUser clientUserCreator;
public Notification() {
}
public Notification(User user, String titleId, String messageId, String icon) {
super();
this.user = user;
this.titleId = titleId;
this.messageId = messageId;
this.icon = icon;
this.dateCreated = Calendar.getInstance().getTime();
}
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "notification_id", unique = true, nullable = false)
public int getNotificationId() {
return this.notificationId;
}
public void setNotificationId(int notificationId) {
this.notificationId = notificationId;
}
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "date_created", nullable = false, length = 29)
public Date getDateCreated() {
return this.dateCreated;
}
public void setDateCreated(Date dateCreated) {
this.dateCreated = dateCreated;
}
@Length(min=10,max=40)
@Column(name = "title_id", nullable = false)
public String getTitleId() {
return titleId;
}
public void setTitleId(String titleId) {
this.titleId = titleId;
}
@Column(name = "message_id", nullable = false)
public String getMessageId() {
return this.messageId;
}
public void setMessageId(String messageId) {
this.messageId = messageId;
}
@Column(name = "icon", nullable = false)
public String getIcon() {
return this.icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
@Column(name = "custom_message", nullable = false)
public boolean isCustomMessage() {
return this.customMessage;
}
public void setCustomMessage(boolean customMessage) {
this.customMessage = customMessage;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_audience", nullable = true)
public User getUser(){
return this.user;
}
public void setUser(User user) {
this.user = user;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "pool_audience", nullable = true)
public Pool getPool() {
return pool;
}
public void setPool(Pool pool) {
this.pool = pool;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "client_tag_audience", nullable = true)
public ClientTag getClientTag() {
return clientTag;
}
public void setClientTag(ClientTag clientTag) {
this.clientTag = clientTag;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "client_audience", nullable = true)
public Client getClient() {
return client;
}
public void setClient(Client client) {
this.client = client;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "admin_audience", nullable = true)
public Admin getAdmin() {
return admin;
}
public void setAdmin(Admin admin) {
this.admin = admin;
}
@ManyToOne(fetch = FetchType.LAZY, optional = true)
@JoinTable(name = "swim_mate_notifications", schema = "public",
joinColumns = { @JoinColumn(name = "notification_id") },
inverseJoinColumns = { @JoinColumn(name = "swim_mate_id") })
public SwimMate getSwimMate() {
return swimMate;
}
public void setSwimMate(SwimMate swimMate) {
this.swimMate = swimMate;
}
@ManyToOne(fetch = FetchType.EAGER, optional = true)
@JoinTable(name = "session_notifications", schema = "public",
joinColumns = { @JoinColumn(name = "notification_id", nullable=true)},
inverseJoinColumns = { @JoinColumn(name = "session_id", nullable=true) })
public Session getSession() {
return session;
}
public void setSession(Session session) {
this.session = session;
}
@OneToOne(fetch = FetchType.LAZY, optional = true)
@JoinTable(name = "pb_notifications", schema = "public",
joinColumns = { @JoinColumn(name = "notification_id", nullable=true) },
inverseJoinColumns = { @JoinColumn(name = "personal_best_id", nullable=true) })
public PersonalBest getPersonalBest() {
return personalBest;
}
public void setPersonalBest(PersonalBest personalBest) {
this.personalBest = personalBest;
}
@ManyToOne(fetch = FetchType.LAZY, optional = true)
@JoinTable(name = "ucu_notifications", schema = "public",
joinColumns = { @JoinColumn(name = "notification_id") },
inverseJoinColumns = { @JoinColumn(name = "user_challenge_id"), @JoinColumn(name = "user_id") })
public UserChallengeUser getUserChallengeUser() {
return userChallengeUser;
}
public void setUserChallengeUser(UserChallengeUser userChallengeUser) {
this.userChallengeUser = userChallengeUser;
}
@ManyToOne(fetch = FetchType.LAZY, optional = true)
@JoinTable(name = "user_challenge_notifications", schema = "public",
joinColumns = { @JoinColumn(name = "notification_id") },
inverseJoinColumns = { @JoinColumn(name = "user_challenge_id")})
public UserChallenge getUserChallenge(){
return userChallenge;
}
public void setUserChallenge(UserChallenge userChallenge) {
this.userChallenge = userChallenge;
}
@OneToOne(fetch = FetchType.LAZY, optional = true)
@JoinTable(name = "competition_session_notifications", schema = "public",
joinColumns = { @JoinColumn(name = "notification_id", nullable=true) },
inverseJoinColumns = { @JoinColumn(name = "competition_user_session_id", nullable=true)})
public CompetitionUserSession getCompetitionUserSession() {
return competitionUserSession;
}
public void setCompetitionUserSession(CompetitionUserSession competitionUserSession) {
this.competitionUserSession = competitionUserSession;
}
@OneToOne(fetch = FetchType.LAZY, optional = true)
@JoinTable(name = "competition_user_notifications", schema = "public",
joinColumns = { @JoinColumn(name = "notification_id") },
inverseJoinColumns = { @JoinColumn(name = "competition_user_id")})
public CompetitionUser getCompetitionUser() {
return competitionUser;
}
public void setCompetitionUser(CompetitionUser competitionUser) {
this.competitionUser = competitionUser;
}
@ManyToOne(fetch = FetchType.LAZY, optional = true)
@JoinTable(name = "user_training_plan_notifications", schema = "public",
joinColumns = { @JoinColumn(name = "notification_id") },
inverseJoinColumns = { @JoinColumn(name = "user_training_plan_id") })
public UserTrainingPlan getUserTrainingPlan() {
return userTrainingPlan;
}
public void setUserTrainingPlan(UserTrainingPlan userTrainingPlan) {
this.userTrainingPlan = userTrainingPlan;
}
@ManyToOne(fetch = FetchType.LAZY, optional = true)
@JoinTable(name = "user_drill_notifications", schema = "public",
joinColumns = { @JoinColumn(name = "notification_id") },
inverseJoinColumns = { @JoinColumn(name = "user_drill_id") })
public UserDrill getUserDrill() {
return userDrill;
}
public void setUserDrill(UserDrill userDrill) {
this.userDrill = userDrill;
}
@OneToOne(fetch = FetchType.LAZY, mappedBy = "notification", optional = true)
public DockActiveBand getDockActiveBand() {
return dockActiveBand;
}
public void setDockActiveBand(DockActiveBand dockActiveBand) {
this.dockActiveBand = dockActiveBand;
}
//Log Creator Info
@ManyToOne(fetch = FetchType.LAZY, optional = true)
@Fetch(FetchMode.SELECT)
@JoinTable(name = "notification_client_user_creators", schema = "public",
joinColumns = { @JoinColumn(name = "notification_id") },
inverseJoinColumns = { @JoinColumn(name = "client_user_id") })
public ClientUser getClientUserCreator() {
return clientUserCreator;
}
public void setClientUserCreator(ClientUser clientUserCreator) {
this.clientUserCreator = clientUserCreator;
}
}
您正在使用连接 table 来映射您的 toOne 关联,而不是连接列(这是默认设置)。由于关联是可选的,Hibernate 无法知道是否存在关联实体,仅通过获取 main table 的列即可。使用连接列,它可以:
- 如果该列为空,则 ManyToOne 注释字段为空
- 如果该列不为空,则 ManyToOne 注释字段是包含目标实体 ID 的惰性代理
使用连接 table,Hibernate 被迫从连接 table 中获取列,以便决定是否必须将字段设置为 null 或者是否必须使用代理对其进行初始化.