【问题标题】:Hibernate ignoring LEFT JOIN休眠忽略左连接
【发布时间】:2011-10-18 09:13:35
【问题描述】:

我有一个名为 SecurityContact 的表,它与另一个表 Contacts 具有多对一的关系。它有两个连接到联系人的列,其中一个称为 agentContact,另一个称为 auditContact。我正在尝试运行以下 HQL 查询:

SELECT sc FROM SecurityContact sc LEFT JOIN sc.agentContact ac LEFT JOIN sc.auditContact ac2 WHERE sc.securityId=:securityId2

然而,Hibernate 完全忽略了 LEFT JOIN 语句并继续生成利用内连接的 SQL。这根本不适合我的目的。我试过设置 fetch 注释,但没有运气。这个问题已经让我发疯了两天多,所以任何帮助都将不胜感激。

这是我的 SecurityContact 类的代码:

/**
 * The persistent class for the SecurityContact database table.
 * 
 */
@Entity
@FXClass(kind=FXClassKind.REMOTE)
public class SecurityContact implements Serializable {
    private static final long serialVersionUID = 1L;
    @Transient private String uid;
    @FXIgnore
    public String getUid() {
        if (uid == null) {
            uid = "" + securityContactId;
        }
        return uid;
    }

    public void setUid(String uid) {
        this.uid = uid;
    }

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="securityContact_id")
    private Long securityContactId;

    @Column(name="security_id")
    private String securityId;

    @Column(name="create_date")
    private String createDate;

    @Column(name="create_user")
    private String createUser;

    @Column(name="modify_date")
    private String modifyDate;

    @Column(name="modify_user")
    private String modifyUser;

    //uni-directional many-to-one association to AgentContact

    @ManyToOne
    @JoinColumn(name="agent_id", referencedColumnName="contact_id")
    private Contact agentContact;

    //uni-directional many-to-one association to AuditContact
    @ManyToOne
    @JoinColumn(name="audit_id", referencedColumnName="contact_id")
    private Contact auditContact;

    public SecurityContact() {
    }
    @FXKeyColumn
    public Long getSecurityContactId() {
        return this.securityContactId;
    }

    public void setSecurityContactId(Long securityContactId) {
        this.securityContactId = securityContactId;
    }

    public String getSecurityId() {
        return this.securityId;
    }

    public void setSecurityId(String securityId) {
        this.securityId = securityId;
    }

    public String getCreateDate() {
        return this.createDate;
    }

    public void setCreateDate(String createDate) {
        this.createDate = createDate;
    }

    public String getCreateUser() {
        return this.createUser;
    }

    public void setCreateUser(String createUser) {
        this.createUser = createUser;
    }

    public String getModifyDate() {
        return this.modifyDate;
    }

    public void setModifyDate(String modifyDate) {
        this.modifyDate = modifyDate;
    }

    public String getModifyUser() {
        return this.modifyUser;
    }

    public void setModifyUser(String modifyUser) {
        this.modifyUser = modifyUser;
    }
    @FXManyToOne(parent="parent", property="contactId")
    public Contact getAgentContact() {
        return this.agentContact;
    }

    public void setAgentContact(Contact agentContact) {
        this.agentContact = agentContact;
    }
    @FXManyToOne(parent="parent", property="contactId")
    public Contact getAuditContact() {
        return this.auditContact;
    }

    public void setAuditContact(Contact auditContact) {
        this.auditContact = auditContact;
    }

} 

这是从上述 HQL 生成的 SQL:

select securityco0_.agent_id as col_0_0_, securityco0_.audit_id as col_1_0_, securityco0_.create_date as col_2_0_, securityco0_.create_user as col_3_0_, securityco0_.modify_date as col_4_0_, securityco0_.modify_user as col_5_0_, securityco0_.securityContact_id as col_6_0_, securityco0_.security_id as col_7_0_, agentconta3_.contact_id as contact1_0_0_, agentconta4_.contact_id as contact1_0_1_, agentconta3_.bank_id as bank10_0_0_, agentconta3_.create_date as create2_0_0_, agentconta3_.create_user as create3_0_0_, agentconta3_.email as email0_0_, agentconta3_.fax as fax0_0_, agentconta3_.modify_date as modify6_0_0_, agentconta3_.modify_user as modify7_0_0_, agentconta3_.name as name0_0_, agentconta3_.phone as phone0_0_, agentconta4_.bank_id as bank10_0_1_, agentconta4_.create_date as create2_0_1_, agentconta4_.create_user as create3_0_1_, agentconta4_.email as email0_1_, agentconta4_.fax as fax0_1_, agentconta4_.modify_date as modify6_0_1_, agentconta4_.modify_user as modify7_0_1_, agentconta4_.name as name0_1_, agentconta4_.phone as phone0_1_ from SecurityContact securityco0_ left outer join AgentContact agentconta1_ on securityco0_.agent_id=agentconta1_.contact_id left outer join AgentContact agentconta2_ on securityco0_.audit_id=agentconta2_.contact_id inner join AgentContact agentconta3_ on securityco0_.agent_id=agentconta3_.contact_id inner join AgentContact agentconta4_ on securityco0_.audit_id=agentconta4_.contact_id where securityco0_.security_id=?

【问题讨论】:

  • Hibernate 在这里使用左连接有什么关系?您想用 HQL 查询回答什么问题?与简单的from SecurityContact where securityId = ? 相比,您期望得到什么不同的结果?
  • @matt 这很重要,因为即使没有相应的 agentContact 或 auditContact 可用,我也希望 Hibernate 返回条目。它不会通过内部连接来做到这一点。
  • 但这些@ManyToOne 关联默认情况下不是可选的/可为空的吗?这种关系的反面在其他类中是什么样的?您是说简单的session.get(SecurityContact.class, securityId) 不会返回任一字段为空的实例吗?
  • @matt 默认情况下它们确实是可选的/可为空的,这就是让这个问题对我来说如此奇怪的原因。与我在代码中的 cmets 不同,这是一种单向关系。没有办法从 Contact 到 SecurityContact。当 SecurityContact 中的 agentContact 或 auditContact 为 null(或设置为 Contact 表中不存在的值)时,Hibernate 不会拉取记录。
  • 只是为了验证一下,您能否捕获生成的 SQL 的样子,例如 Session.get(SecurityContact.class, id)、hql 和 from SecurityContact where securityId = ? 以及您的左连接尝试?另外,您使用的是什么版本的 Hibernate?

标签: java hibernate


【解决方案1】:

您的HQL join syntax 看起来很不稳定。试试这个:

SELECT sc
FROM SecurityContact sc
LEFT JOIN FETCH sc.agentContact
LEFT JOIN FETCH sc.auditContact
WHERE sc.securityId=:securityId2

【讨论】:

  • 无论出于何种原因,Hibernate 在使用您的查询构建 annotated.xml 时都会引发错误。
  • 它基本上告诉我 annotated.xml 文件未能构建。我发现当 Hibernate 不喜欢查询时会发生这种情况。我使用的是 3.4.0 版。
【解决方案2】:

我已经针对您的问题进行了一些测试,在我看来,您的映射可能有问题,或者您正在使用的 Hibernate(过时)版本中存在错误。

对于可选的@ManyToOne 关联,Hibernate 应该已经使用左连接来查询实体及其关联(因为它是查询根实体的唯一方法,无论是否关联实体为空)。

我在https://github.com/mattnworb/hibernate-sample 整理了一个示例项目来说明这一点。

在我的示例项目中,Employee 类具有 a unidirectional many-to-one mapping to the Team class:

/**
 * Unidirectional relationship between Employee and Team.
 */
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;

在单元测试中,Employee.team 引用 is not-null when it is set in the DB 的测试和 DB 表中断言 Employee.team is null when not set 的另一个测试。

此外,Company 和 Employee 实体之间存在类似的双向关系,并进行了类似的测试。

最后,在日志中我可以看到,当 Hibernate 查询 Employee 实体时(使用简单的session.get(Employee.class, id)),它使用左连接来拉入teams 表(我自己添加了换行符):

调试 org.hibernate.SQL - 选择employee0_.id 作为id1_2_,employee0_.company_id 作为company6_1_2_,employee0_.dateOfBirth 作为dateOfBi2_1_2_,employee0_.employmentStartDate 作为employme3_1_2_,employee0_.firstName 作为firstName1_2_,employee0_.lastName 作为lastName1_2_,employee0_.team_id作为 team7_1_2_,company1_.id 作为 id0_0_,company1_.name 作为 name0_0_,team2_.id 作为 id2_1_,team2_.name 作为 name2_1_
来自员工员工0_
在employee0_.company_id=company1_.id 上离开外部加入公司company1_
left external join teams team2_ on employee0_.team_id=team2_.id where employee0_.id=?

总而言之,为了让 Hibernate 在关联实体之间使用左连接,只要将关系设置为可选,就根本不需要特殊的 HQL 查询 - 一个简单的 session.get() 即可根实体应该使用左连接查询关联实体的表。如果您看到不同的结果,这表明您的 Hibernate 版本中存在错误(我在这里使用的是 3.6.6.Final),或者您的映射中有错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-09-15
    • 2016-05-02
    • 2015-04-17
    • 2014-09-24
    • 2011-12-11
    • 1970-01-01
    • 2012-12-22
    相关资源
    最近更新 更多