【问题标题】:Hibernate Envers Composite Id Entity Cannot be ResolvedHibernate Envers 复合 Id 实体无法解析
【发布时间】:2017-10-22 16:07:09
【问题描述】:

我的数据库中有一个包含附加字段的复合 ID 表,以及使用 EmbeddedId java 持久性注释配置的相应实体模型。在我的应用程序中执行编辑时,一切正常。但是,当尝试使用 Hibernate Envers 检索审核数据时,代码失败,给我以下堆栈跟踪:

Caused by: org.hibernate.QueryException: could not resolve property: contract_id of: com.mycompany.model.DesignContract_AUD
    at org.hibernate.persister.entity.AbstractPropertyMapping.propertyException(AbstractPropertyMapping.java:62)
    at org.hibernate.persister.entity.AbstractPropertyMapping.toType(AbstractPropertyMapping.java:56)
    at org.hibernate.persister.entity.AbstractEntityPersister.toType(AbstractEntityPersister.java:1801)
    at org.hibernate.hql.internal.ast.tree.FromElementType.getPropertyType(FromElementType.java:393)
    at org.hibernate.hql.internal.ast.tree.FromElement.getPropertyType(FromElement.java:507)
    at org.hibernate.hql.internal.ast.tree.DotNode.getDataType(DotNode.java:660)
    at org.hibernate.hql.internal.ast.tree.DotNode.prepareLhs(DotNode.java:264)
    at org.hibernate.hql.internal.ast.tree.DotNode.resolve(DotNode.java:204)
    at org.hibernate.hql.internal.ast.tree.FromReferenceNode.resolve(FromReferenceNode.java:109)
    at org.hibernate.hql.internal.ast.tree.FromReferenceNode.resolve(FromReferenceNode.java:104)
    at org.hibernate.hql.internal.ast.HqlSqlWalker.resolve(HqlSqlWalker.java:1013)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.expr(HqlSqlBaseWalker.java:1286)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.exprOrSubquery(HqlSqlBaseWalker.java:4699)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.comparisonExpr(HqlSqlBaseWalker.java:4169)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2134)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2059)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2059)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.whereClause(HqlSqlBaseWalker.java:813)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:607)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:311)
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:259)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:262)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:190)

这里有两个主要问题。首先,Contract_Id 不是一个字段。我将 JoinColumn 配置为正确的名称“FK_CONTRACT”。其次,我不明白为什么它试图使用审计表的java类名,而不是使用@Table注释中设置的名称,即“CONTRACT_DESIGNS”。我的课程如下:

@Entity
@DynamicInsert
@DynamicUpdate
@SelectBeforeUpdate
@Table(name="CONTRACTS")
@Audited
public class Contract implements Serializable {
    private static final long serialVersionUID = 1L;

    private List<DesignContract> designs;
    @OneToMany(mappedBy = "pk.contract", fetch = FetchType.EAGER, cascade = {CascadeType.ALL}, orphanRemoval=true)
    @Fetch(value = FetchMode.SUBSELECT)
    public List<DesignContract> getDesigns() {
        return designs;
    }
    public void setDesigns(List<DesignContract> designs) {
        this.designs= designs;
    }
}


@Entity
@AssociationOverrides({
        @AssociationOverride(name = "pk.contract",
                joinColumns = @JoinColumn(name = "FK_CONTRACT")),
        @AssociationOverride(name = "pk.design",
                joinColumns = @JoinColumn(name = "FK_DESIGN")) })
@Table(name="CONTRACT_DESIGNS")
@Audited
public class DesignContract implements Serializable {
    private static final long serialVersionUID = 1L;

    public DesignContract () {

    }
    public DesignContract (Contract contract, Design design) {
        pk.setContract(contract);
        pk.setDesign(design);
    }

    private DesignContractId pk = new DesignContractId();
    @EmbeddedId
    public DesignContractId getPk() {
        return pk;
    }
    public void setPk(DesignContractId pk) {
        this.pk = pk;
    }

    @Transient
    public Contract getContract() {
        return getPk().getContract();
    }
    public void setContract(Contract contract) {
        getPk().setContract(contract);
    }

    @Transient
    public Design getDesign() {
        return getPk().getDesign();
    }
    public void setDesign(Design design) {
        getPk().setDesign(design);
    }

    private Double goal;
    @Column(name = "GOAL", nullable = true, insertable = true, updatable = true, precision = 5, scale = 2)
    @Basic
    public Double getGoal() {
        return this.goal;
    }
    public void setGoal(Double goal) {
        this.goal = goal;
    }
}

@Embeddable
public class DesignContractId implements Serializable {
    private static final long serialVersionUID = 1L;

    private Contract contract;
    private Design design;

    @ManyToOne
    public Contract getContract() {
        return contract;
    }
    public void setContract(Contract contract) {
        this.contract = contract;
    }

    @ManyToOne
    public Design getDesign() {
        return design;
    }
    public void setDesign(Design design) {
        this.design = design;
    }
}

toString()、hashCode() 和 equals() 方法均适用于所有三个模型,为简洁起见,我只是省略了它们。

我用于检索审计信息的测试代码非常基础,因为我主要只是做一个概念证明,证明事情正在运行,并且可以初始化附加到我的主要合同实体的延迟初始化记录。当我调用 DesignContract 列表的 size() 方法时发生错误,根据我发现的其他帖子,这是强制 Hibernate Enver 的 ListProxy 的方法。

    AuditReader reader = AuditReaderFactory.get(sessionFactory.openSession());
    List<Number> revisionsContract = reader.getRevisions(Contract.class, contractId);
    for (Number revisionNum : revisionsContract) {
        System.out.println("    revisionNum = " + revisionNum);
        Contract contract = reader.find(Contract.class, contractId, revisionNum);
        System.out.println(contract.getDesigns().size());
        System.out.println(contract.getDesigns());
        System.out.println(contract);
    }

出于调试目的,我将 show_sql 设置为 true,而当调用 size() 时 Hibernate 尝试的查询没有意义:

select e__ from com.mycompany.model.DesignContract_AUD e__ where e__.contract_id = :contract_id and e__.originalId.REVISION_NUMBER.id = (select max(e2__.originalId.REVISION_NUMBER.id) from com.mycompany.model.DesignContract_AUD e2__ where e2__.originalId.REVISION_NUMBER.id <= :revision and e__.originalId.design = e2__.originalId.design and e__.originalId.contract = e2__.originalId.contract) and REVISION_TYPE != :delrevisiontype

同样,没有contract_id字段,所以我不知道它是从哪里得到的一点也不。我什至添加了@AuditTable 注释,将其指向 CONTRACT_DESIGNS_AUD 表,但这并没有改变任何东西。在我的应用程序中执行诸如编辑和保存合同之类的正常操作时,审计信息会正确添加到 CONTRACT_DESIGNS_AUD 表中,我可以在其中直接通过直接 sql 查询信息。我不知道为什么 AuditReader 会发生这种情况。我的应用程序中有多个复合 ID 实体,它们的配置方式都相同,它们都遇到相同的错误,带有未知的 contract_id 参数,以及用于查询中审计表的 java 类名。我确实尝试将我的模型配置为使用@IdClass 注释而不是@EmbeddedId,但这仅有助于解析查询中的表名 - 它仍然尝试根据contract_id 进行过滤,当然,它指出无法解析。

我正在使用 Hibernate 5.2.10.FINAL,在阅读了复合 Id 实体无法与早期版本的 Hibernate Envers 一起工作后升级到该版本 - https://hibernate.atlassian.net/browse/HHH-7625

任何关于我做错了什么的帮助将不胜感激。

【问题讨论】:

  • 我仍在研究这个问题,但至少要回答您的部分问题,它使用类名的原因是因为它是 HQL,而不是 SQL。我正在调查映射失败,敬请期待。
  • 这对 HQL 来说是有意义的。但是我贴的语句应该是原HQL的SQL翻译。在我之前的所有调试中,将 hibernate.show_sql 设置为 true 会给我从 HQL 生成的实际 SQL 语句。它总是有实体的实际表名,所以我可以从控制台复制,粘贴到 SQL Developer 之类的东西中,然后不加修改地运行查询。查看发布到我的控制台的其他 SQL 语句,我没有看到使用类名的任何其他实例。所有其他语句都有正确的数据库表名。

标签: hibernate hibernate-envers


【解决方案1】:

您面临的问题是一个明确的错误。似乎当我们在HHH-7625 中实现对@IdClass 映射的支持时,我们没有正确考虑@EmbeddedId

我已在此问题上记录了一个 JIRA 问题 HHH-11770,该问题已得到修复并将包含在 5.2.11.Final 版本中。

【讨论】:

  • 非常感谢。我将跟踪该问题并等待下一个版本。
  • 实际上,是否有可以从 Maven 中提取的 Hibernate 5.2.11 的 beta 或 SNAPSHOT,或者我应该只从 GitHub 编译代码?我看到修复程序已经在存储库中。
  • 欢迎你从 GitHub 上拉下来直接编译。构建说明很容易遵循:)。
猜你喜欢
  • 2020-01-26
  • 1970-01-01
  • 1970-01-01
  • 2011-07-10
  • 2015-12-14
  • 2015-03-20
  • 2013-08-27
  • 2012-12-05
  • 1970-01-01
相关资源
最近更新 更多