我需要在JPA中使用额外列创建多对多的连接表。
我正在使用:openjpa-maven-plugin
用于在构建期间进行增强,openjpa 2.2.0
,hsql file db
,testng + spring
我有三个表:Analysis
,localizedAnalysisName
(加入表)和Locale
我的代码:
@Entity
@Table(name = "analysis", catalog = "testdb")
public class Analysis {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "analysis_id", unique = true, nullable = false)
private int analysisId;
@OneToMany(mappedBy = "analysis", cascade = CascadeType.PERSIST)
private List<LocalizedAnalysisName> localizedNames = new ArrayList<LocalizedAnalysisName>();
public void addLocalizedName(Locale locale, String localized_text) {
LocalizedAnalysisName localizedAnalysisName = new LocalizedAnalysisName();
localizedAnalysisName.setLocale(locale);
localizedAnalysisName.setAnalysis(Analysis.this);
localizedAnalysisName.setLocaleId(locale.getLocaleId());
localizedAnalysisName.setAnalysisId(this.getAnalysisId());
localizedAnalysisName.setLocalizedText(localized_text);
localizedNames.add(localizedAnalysisName);
}
...
@Entity
@Table(name = "locale", catalog = "testdb")
public class Locale implements Serializable {
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "locale_id", unique = true, nullable = false)
private int localeId;
@Column(name = "language", length = 200)
private String language;
@Column(name = "country", length = 200)
private String country;
@Column(name = "variant", length = 200)
private String variant;
...
@Entity
@Table(name = "localized_analysis_name", catalog = "testdb")
@IdClass(LocalizedAnalysisNameId.class)
public class LocalizedAnalysisName implements Serializable {
@Id
private int analysis_id;
@Id
private int locale_id;
@Column(name = "localized_text", nullable = false, length = 200)
private String localizedText;
@ManyToOne
@PrimaryKeyJoinColumn(name="locale_id", referencedColumnName="locale_id")
private Locale locale;
@ManyToOne
@PrimaryKeyJoinColumn(name="analysis_id", referencedColumnName="analysis_id")
private Analysis analysis;
public LocalizedAnalysisName() {
}
public String getLocalizedText() {
return localizedText;
}
public void setLocalizedText(String localizedText) {
this.localizedText = localizedText;
}
public Locale getLocale() {
return locale;
}
public Analysis getAnalysis() {
return analysis;
}
public void setLocale(Locale locale) {
this.locale = locale;
}
public void setAnalysis(Analysis analysis) {
this.analysis = analysis;
}
public void setAnalysisId(int analysis_id) {
this.analysis_id = analysis_id;
}
public void setLocaleId(int locale_id) {
this.locale_id = locale_id;
}
@Embeddable
public class LocalizedAnalysisNameId implements Serializable {
private int analysis_id;
private int locale_id;
public LocalizedAnalysisNameId() {
}
public int getAnalysisId() {
return analysis_id;
}
public void setAnalysisId(int analysis_id) {
this.analysis_id = analysis_id;
}
public int getLocaleId() {
return locale_id;
}
public void setLocaleId(int locale_id) {
this.locale_id = locale_id;
}
public int hashCode() {
return analysis_id + locale_id;
}
public boolean equals(Object object) {
if (object instanceof LocalizedAnalysisNameId) {
LocalizedAnalysisNameId otherId = (LocalizedAnalysisNameId) object;
return (otherId.analysis_id == this.analysis_id) && (otherId.locale_id == this.locale_id);
}
return false;
}
DB:
CREATE TABLE locale (
locale_id integer NOT NULL IDENTITY,
language varchar(200) DEFAULT NULL,
country varchar(200) DEFAULT NULL,
variant varchar(200) DEFAULT NULL,
PRIMARY KEY (locale_id)
);
CREATE TABLE analysis (
analysis_id integer NOT NULL IDENTITY,
source_id varchar(500) DEFAULT NULL,
locale_id integer,
PRIMARY KEY (analysis_id),
FOREIGN KEY (locale_id)
REFERENCES locale (locale_id)
);
CREATE TABLE localized_analysis_name (
analysis_id integer,
locale_id integer,
localized_text varchar(200) DEFAULT NULL,
PRIMARY KEY (analysis_id, locale_id),
FOREIGN KEY (analysis_id)
REFERENCES analysis (analysis_id),
FOREIGN KEY (locale_id)
REFERENCES locale (locale_id)
);
INSERT INTO LOCALE VALUES(0,'en','US','');
INSERT INTO LOCALE VALUES(1,'de','DE','');
这是我在testng中的测试方法片段:
@Test(enabled = true)
@Rollback(false)
public void testSave() {
Analysis analysis = new Analysis();
Locale locale = localeDao.findById(0);
analysis.addLocalizedName(locale,"localized text");
analysisDao.save(analysis);
}
构建时,测试失败
invalidstateexception:
Caused by: <openjpa-2.2.0-r422266:1244990 fatal user error> org.apache.openjpa.persistence.InvalidStateException:
Attempt to set column "localized_analysis_name.analysis_id" to two different values: (class java.lang.Long)"0", (class java.lang.Integer)"40"
This can occur when you fail to set both sides of a
two-sided relation between objects, or when you map
different fields to the same column, but you do not
keep the values of these fields in synch.
请问有谁可以告诉我如何以正确的方式做到这一点? 谢谢。
答案 0 :(得分:1)
不要在LocalizedAnalysisName
表中设置ID和键引用。您 将它们设置为不同的值:
// The test:
Analysis analysis = new Analysis();
Locale locale = localeDao.findById(0);
analysis.addLocalizedName(locale,"localized text");
// The entity:
localizedAnalysisName.setLocale(locale);
localizedAnalysisName.setAnalysis(Analysis.this);
localizedAnalysisName.setLocaleId(locale.getLocaleId());
localizedAnalysisName.setAnalysisId(this.getAnalysisId());
当你调用setAnalysisId(...)
时,它处于“new”状态,它还没有id(默认为0)。同时,在L.A.N.上设置Analysis
实体,实体管理器将为其分配新的ID。因此,L.A.N。同时有Analysis
条记录的2个不同ID。
在旁注中,@Embeddable
上的LocalizedAnalysisNameId
注释不是必需的,因为您不将其用作@EmbeddedId
,而是直接列出其字段。