由于标题可能有点不清楚,我会在这里解释一下。
我有一个用JPA映射的实体。在这个实体中,我使用了一个包含属性的可嵌入类。问题是我想在同一个数据库列上映射这两个属性(后面的例子)。为实现这一点,我重写了两个属性并将它们映射到列,对于其中一个我使用insertable = false和updatable = false。
事情是不起作用(得到“重复列映射...”,我找不到解决这个问题的方法。我也找不到有类似问题的人。我希望我搜索得好但是当我搜索了很多时我觉得没关系。
为了您的信息,我正在使用JPA 2.1和Hibernate实现(但我不想使用Hibernate特定的功能)。
这是我这个类的源代码。问题在于“Periode”对象代表一个时期(多种类型:月,年)。问题是我想使用这个类来表示一年(在一个列中存储在数据库中作为整数),因此能够访问Periode开始和结束日期属性。
Periode来自遗留代码,我对它有相对有限的“权力”(因为它在许多其他项目中使用)。数据库也是遗产,因此我无法更改它以便放置两个日期而不仅仅是一年。
@SuppressWarnings("serial")
@Embeddable
public class CandidateId implements Serializable {
// Fields
private Periode period;
private IdentifiantContrat contractId;
// Constructors
@SuppressWarnings("unused")
private CandidateId() {}
public CandidateId(Periode period, IdentifiantContrat contractId) {
this.period = period;
this.contractId = contractId;
}
public CandidateId(Periode period, IdentifiantAffilie employerId) {
this(period, new IdentifiantContrat(employerId, 0));
}
// Getters
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "debut", column = @Column(name = "PERIODE", nullable = false) ),
@AttributeOverride(name = "fin", column = @Column(name = "PERIODE", nullable = false, insertable = false, updatable = false))
})
// @Converts({
// @Convert(attributeName = "debut", converter = FiscalStartDateConverter.class),
// @Convert(attributeName = "fin", converter = FiscalEndDateConverter.class)
// })
public Periode getPeriod() {
return period;
}
@Embedded
public IdentifiantContrat getContractId() {
return contractId;
}
// Setters
public void setPeriod(Periode period) {
this.period = period;
}
public void setContractId(IdentifiantContrat contractId) {
this.contractId = contractId;
}
// Methods
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((contractId == null) ? 0 : contractId.hashCode());
result = prime * result + ((period == null) ? 0 : period.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CandidateId other = (CandidateId) obj;
if (contractId == null) {
if (other.contractId != null)
return false;
} else if (!contractId.equals(other.contractId))
return false;
if (period == null) {
if (other.period != null)
return false;
} else if (!period.equals(other.period))
return false;
return true;
}
@Override
public String toString() {
return "CandidatDeclarationId [period=" + period + ", contractId=" + contractId + "]";
}
}
正如您所看到的,我计划使用转换器将int值从db转换为两个匹配日期,但一次只有一个问题。
在测试我的代码时,我会收到以下信息:
javax.persistence.PersistenceException: [PersistenceUnit: belgium-fiscal-model] Unable to build Hibernate SessionFactory
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException(EntityManagerFactoryBuilderImpl.java:1249)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.access$600(EntityManagerFactoryBuilderImpl.java:120)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:860)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850)
at org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.withTccl(ClassLoaderServiceImpl.java:425)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:849)
at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:75)
at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:54)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:55)
at be.groups.kernel.utils.persistence.JpaUtil.initEntityManagerFactory(JpaUtil.java:168)
at be.groups.kernel.utils.persistence.JpaUtil.createEntityManagerFactory(JpaUtil.java:65)
at be.groups.belgium.fiscal.model.services.CandidateServiceTest.setUp(CandidateServiceTest.java:54)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: be.groups.belgium.fiscal.model.domain.Candidate column: PERIODE (should be mapped with insert="false" update="false")
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:709)
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:750)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:506)
at org.hibernate.mapping.RootClass.validate(RootClass.java:270)
at org.hibernate.cfg.Configuration.validate(Configuration.java:1360)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1851)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:857)
... 25 more
java.lang.NullPointerException
at be.groups.belgium.fiscal.model.services.CandidateServiceTest.tearDown(CandidateServiceTest.java:74)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:33)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
如果某些事情不清楚,请告诉我,我会尽力确定。
编辑: 这是OndrejM提出的候选人课程。但我不认为问题来自它(我已经检查过了,虽然我很想提到它抱歉^^)。但也许我错过了一些东西,如果能有所帮助,这就是上课!
@Entity
@Table(name = "ECHEANCIER_FISCAL_PERSONNE")
public class Candidate {
// Fields
private CandidateId id;
private Date datePassage;
private String justification;
private Integer actionProgramme;
private Integer actionGestionnaire;
private BaseSignaturePersistance persistenceSignature;
// Constructors
private Candidate() {}
// Getters
@EmbeddedId
public CandidateId getId() {
return id;
}
@Column(name = "ACTION_PROGRAMME", nullable = true)
public Integer getActionProgramme() {
return actionProgramme;
}
@Column(name = "ACTION_GESTIONNAIRE", nullable = true)
public Integer getActionGestionnaire() {
return actionGestionnaire;
}
@Column(name = "DT_PASSAGE", nullable = true)
public Date getDatePassage() {
return datePassage;
}
@Column(name = "JUSTIFICATION", nullable = true)
public String getJustification() {
return justification;
}
@Embedded
public BaseSignaturePersistance getPersistenceSignature() {
return persistenceSignature;
}
// Setters
public void setId(CandidateId id) {
this.id = id;
}
public void setActionProgramme(Integer actionProgramme) {
this.actionProgramme = actionProgramme;
}
public void setActionGestionnaire(Integer actionGestionnaire) {
this.actionGestionnaire = actionGestionnaire;
}
public void setDatePassage(Date datePassage) {
this.datePassage = datePassage;
}
public void setJustification(String justification) {
this.justification = justification;
}
public void setPersistenceSignature(BaseSignaturePersistance persistenceSignature) {
this.persistenceSignature = persistenceSignature;
}
// Methods
/**
* Tells if this concerns a single contract or not
*
* @return true if this concerns a single contract, else false
*
* @since 1.0.0
*/
@Transient
public boolean isSingleContract() {
return id.getContractId().getNumero() != 0;
}
/**
* Tells if this concerns an employer (multiple contracts) or not
*
* @return true if this concerns an employer, else false
*
* @since 1.0.0
*/
@Transient
public boolean isWholeEmployer() {
return !isSingleContract();
}
/**
* Tells if the candidate is blocked
*
* @return true if this is blocked, else false
*
* @since 2.0.0
*/
@Transient
public boolean isBlocked() {
return actionGestionnaire == null
? (actionProgramme == null || actionProgramme == StatutCloture.PAS_PRET_POUR_PREPARATION.getValeur())
: (actionGestionnaire == StatutCloture.PAS_PRET_POUR_PREPARATION.getValeur());
}
}
这里也是嵌入在Candidate类中的BaseSignaturePersistence类。很抱歉混合英语和法语代码,但正如我所说,我使用遗留代码,因此有些课程是法语(最近才决定使用英语)。
@Embeddable
public class BaseSignaturePersistance {
/**
* Auteur creation des données.
*/
private String auteurCreation;
/**
* Auteur modification des données.
*/
private String auteurModification;
/**
* Date creation des données
*/
private Date dateCreation;
/**
* Date modification des données.
*/
private Date dateModification;
/**
* Nouvel Objet Signature.
*
* @param lAuteurCreation
* @param lDateCreation
* @param lAuteurModification
* @param lDateModification
*/
@Requires("auteurCreation!=null && dateCreation!=null")
@Ensures({"this.auteurCreation == auteurCreation && this.dateCreation == dateCreation",
"this.dateModification!=null ? this.auteurModification!=null : true",
"this.auteurModification!=null ? this.dateModification!=null : true"})
public BaseSignaturePersistance(String auteurCreation, Date dateCreation, String auteurModification, Date dateModification) {
this.auteurCreation = auteurCreation;
this.dateCreation = dateCreation;
this.auteurModification = auteurModification;
this.dateModification = dateModification;
}
/**
* Nouvel objet signature contenant seulement la partie creation
* @param unAuteurCreation
* @param uneDateCreation
*/
@Requires({
"unAuteurCreation!=null",
"uneDateCreation!=null"
})
public BaseSignaturePersistance(String unAuteurCreation, Date uneDateCreation){
this(unAuteurCreation, uneDateCreation, null, null);
}
/**
* Nouvel objet signature contenant seulement la partie creation
* @param unAuteurCreation
*/
@Requires({
"auteurCreation!=null"
})
public BaseSignaturePersistance(String unAuteurCreation){
this(unAuteurCreation, DateUtility.getNow(), null, null);
}
/**
* Nouvel objet signature.
* Utiliser setModification ou SetSuppression pour compléter ce qui est nécessaire.
*/
public BaseSignaturePersistance(){
this.auteurCreation = null;
this.dateCreation = null;
this.auteurModification = null;
this.dateModification = null;
}
/**
* Affecte {@code auteur} et {@code date} à {@code auteurModification} et {@code dateModification}.
*
* @param auteur
* @param date
*/
@Requires("auteur!=null && date!=null")
@Ensures("auteurModification == auteur && dateModification == date")
public void setModification(String auteur, Date date) {
auteurModification = auteur;
dateModification = date;
}
public void copy(BaseSignaturePersistance other) {
auteurCreation = other.auteurCreation;
dateCreation = other.dateCreation;
auteurModification = other.auteurModification;
dateModification = other.dateModification;
}
@Column(name = "AUTEUR_CREATION", nullable = false)
public String getAuteurCreation() {
return auteurCreation;
}
@Column(name = "AUTEUR_MODIFICATION")
public String getAuteurModification() {
return auteurModification;
}
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "DT_CREATION", nullable = false)
public Date getDateCreation() {
return dateCreation;
}
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "DT_MODIFICATION")
public Date getDateModification() {
return dateModification;
}
public void setAuteurCreation(String auteurCreation) {
this.auteurCreation = auteurCreation;
}
public void setAuteurModification(String auteurModification) {
this.auteurModification = auteurModification;
}
public void setDateCreation(Date dateCreation) {
this.dateCreation = dateCreation;
}
public void setDateModification(Date dateModification) {
this.dateModification = dateModification;
}
/**
* Gives most recent date the object has been modified
*
* @return modification date if any, else creation date
*/
@Transient
public Date getDateDerniereModification() {
return dateModification != null ? dateModification : dateCreation;
}
/**
* Gives most recent author that has modified the object
*
* @return modification author if any, else creation author
*/
@Transient
public String getAuteurDerniereModification() {
return auteurModification != null ? auteurModification : auteurCreation;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((auteurCreation == null) ? 0 : auteurCreation.hashCode());
result = prime
* result
+ ((auteurModification == null) ? 0 : auteurModification
.hashCode());
result = prime * result
+ ((dateCreation == null) ? 0 : dateCreation.hashCode());
result = prime
* result
+ ((dateModification == null) ? 0 : dateModification.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
BaseSignaturePersistance other = (BaseSignaturePersistance) obj;
if (auteurCreation == null) {
if (other.auteurCreation != null)
return false;
} else if (!auteurCreation.equals(other.auteurCreation))
return false;
if (auteurModification == null) {
if (other.auteurModification != null)
return false;
} else if (!auteurModification.equals(other.auteurModification))
return false;
if (dateCreation == null) {
if (other.dateCreation != null)
return false;
} else if (!dateCreation.equals(other.dateCreation))
return false;
if (dateModification == null) {
if (other.dateModification != null)
return false;
} else if (!dateModification.equals(other.dateModification))
return false;
return true;
}
@Override
public String toString() {
return "BaseSignaturePersistance [auteurCreation=" + auteurCreation
+ ", auteurModification=" + auteurModification
+ ", dateCreation=" + dateCreation + ", dateModification="
+ dateModification + "]";
}
}
答案 0 :(得分:2)
我的代码中没有发现问题。唯一不常见的是,您将嵌入式ID的两个属性映射到一个列,但我不会在那里看到问题。
然而,事实是hibernate不支持嵌入式主键中的这种映射(可能是一个bug)。我检查了hibernate的代码,我发现insertable / updatable不考虑嵌入式id,仅用于属性。比较代码properties are checked以及primary key are checked列(第750行)的代码。
现在可能的解决方案:
尝试从CandidadeId中删除Periode类,并将PERIODE列映射到新字段。然后将Periode类直接嵌入到Candidate实体中,并覆盖映射到PERIODE列的两个属性的属性,使其只读(insertable / updatable = false)。
这将从主键中删除遗留实体Periode,并仍然可以从实体候选者中获取它。缺点是您无法修改Periode类,但总是需要修改CandidateId上的period字段。但是,您可以创建一个更新前的侦听器,并在保留实体之前将Periode类的值复制到新的句点字段中。
类似于以下内容(我在字段而不是属性上使用映射使其更具可读性):
@Embeddable
public class CandidateId implements Serializable {
@Column(name = "PERIODE")
// instead of class Periode put here field period of the same type as field debut in class Periode
private Date period;
@Table(name = "ECHEANCIER_FISCAL_PERSONNE")
public class Candidate {
// Fields
@EmbeddedId
private CandidateId id;
// add following:
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "debut", column = @Column(name = "PERIODE", nullable = false, insertable = false, updatable = false) ),
@AttributeOverride(name = "fin", column = @Column(name = "PERIODE", nullable = false, insertable = false, updatable = false))
})
Periode period;