2017-05-22 2 views
0

J'ai une table d'ID composite avec un champ supplémentaire dans ma base de données, et le modèle d'entité correspondant configuré à l'aide de l'annotation de persistance EmbeddedId java. Lorsque j'effectue des modifications dans mon application, tout fonctionne correctement. Cependant, lors d'une tentative de récupérer des données d'audit à l'aide Hibernate Envers, le code échoue, me donner la stacktrace suivante:L'entité d'ID composite Hibernate Envers ne peut pas être résolue

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) 

Il y a deux problèmes majeurs là-bas. Premièrement, Contract_Id n'est pas un champ. J'ai le JoinColumn configuré avec le nom propre de "FK_CONTRACT". Deuxièmement, je ne comprends pas pourquoi il essaie d'utiliser le nom de classe java pour la table d'audit, au lieu d'utiliser le nom défini dans l'annotation @Table, qui est "CONTRACT_DESIGNS". Mes cours sont les suivantes:

@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; 
    } 
} 

Les méthodes toString(), hashCode() et equals() sont mis en œuvre pour les trois modèles, je viens de les omis par souci de concision. Mon code de test pour récupérer les informations d'audit est incroyablement basique, car je fais principalement une preuve de concept que les choses fonctionnent et que les enregistrements paresseux initialisés attachés à mon entité Contract principale peuvent être initialisés. L'erreur se produit lorsque j'appelle la méthode size() de la liste DesignContract, qui selon les autres messages que j'ai trouvé est la façon de forcer l'intilisation de ListProxy de Hibernate Enver.

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); 
    } 

Pour des fins de débogage, je l'ai mis à show_sql vrai, et la requête que Hibernate tente lorsque la taille() est invoquée n'a pas de sens:

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 

Encore une fois, il n'y a Je ne sais pas d'où proviennent ces annotations dans les modèles pour DesignContract et DesignContractId, et l'utilisation de com.mycompany.model.DesignContract_AUD est quelque chose que je ne comprends pas du tout. J'ai même ajouté l'annotation @AuditTable, en la pointant vers la table CONTRACT_DESIGNS_AUD, mais cela n'a rien changé. Lorsque j'effectue des opérations normales telles que l'édition et l'enregistrement d'un contrat dans mon application, les informations d'audit sont correctement ajoutées à la table CONTRACT_DESIGNS_AUD, où je peux interroger les informations directement via SQL direct. Je ne sais pas pourquoi cela se produirait avec AuditReader. J'ai plusieurs entités d'ID composées dans mon application, elles sont toutes configurées de la même manière et toutes connaissent les mêmes erreurs, avec un paramètre contract_id inconnu, et le nom de classe java utilisé pour la table d'audit dans les requêtes. J'ai essayé de configurer mon modèle pour utiliser l'annotation @IdClass au lieu de @EmbeddedId, mais cela n'a aidé qu'à résoudre le nom de la table dans la requête - il essayait toujours de filtrer en fonction de contract_id qui, bien sûr, ne peut être résolu.

J'utilise Hibernate 5.2.10.FINAL, que je l'avais mis à jour après avoir lu que les entités composites Id a échoué à travailler avec les versions antérieures de Hibernate - Envers https://hibernate.atlassian.net/browse/HHH-7625

Toute aide à ce que je fais le tort serait grandement apprécié.

+0

Je suis toujours en train de regarder le problème, mais pour au moins répondre à une partie de votre question, la raison pour laquelle il utilise le nom de la classe est parce que c'est HQL, pas SQL. Je regarde dans l'échec de la cartographie, restez à l'écoute. – Naros

+0

Cela a du sens pour HQL. Mais la déclaration que j'ai posté devrait être la traduction SQL de l'original HQL. Dans tout mon débogage précédent, mettre hibernate.show_sql à true me donne l'instruction SQL réelle générée à partir de HQL. Il a toujours les noms de table réels pour l'entité, donc je peux simplement copier à partir de la console, coller dans quelque chose comme SQL Developer, et exécuter la requête sans modification. En regardant les autres instructions SQL publiées sur ma console, je ne vois aucune autre instance où le nom de classe est utilisé. Toutes les autres instructions ont le nom de table de base de données approprié. –

Répondre

1

Le problème que vous rencontrez un bug défini. Il semble que lorsque nous avons implémenté la prise en charge des mappages @IdClass dans HHH-7625, nous n'avons pas comptabilisé correctement @EmbeddedId.

J'ai enregistré un problème JIRA HHH-11770 sur ce problème, qui a été corrigé et sera inclus dans la version 5.2.11.Final.

+0

Merci beaucoup. Je vais suivre ce problème et attendre la prochaine version. –

+0

En fait, existe-t-il un bêta ou un SNAPSHOT pour Hibernate 5.2.11 qui peut être extrait de Maven, ou devrais-je simplement compiler le code à partir de GitHub? Je vois que le correctif est déjà dans le référentiel. –

+0

Vous êtes invités à le retirer de GitHub et à le compiler directement. Les instructions de construction sont faciles à suivre :). – Naros