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é.
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
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é. –