openjpa:多对多,带有额外的列
Posted
技术标签:
【中文标题】openjpa:多对多,带有额外的列【英文标题】:openjpa: many to many with extra column 【发布时间】:2012-03-04 19:42:53 【问题描述】:我需要在 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;
数据库:
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.
请谁能告诉我如何以正确的方式做到这一点? 谢谢。
【问题讨论】:
【参考方案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。
附带说明,LocalizedAnalysisNameId
上的 @Embeddable
注释不是必需的,因为您不将其用作 @EmbeddedId
,而是直接列出其字段。
【讨论】:
现在的问题是如何在LocalizedAnalysisNameId类中设置analyis_id 我会设置 (locale_id, analysis_id) 或 (Locale, Analysis),但不能同时设置。或者先坚持Analysis
(并刷新),然后设置所有字段。以上是关于openjpa:多对多,带有额外的列的主要内容,如果未能解决你的问题,请参考以下文章