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 或者是否必须使用代理对其进行初始化.