openjpa:多对多,带有额外的列

Posted

技术标签:

【中文标题】openjpa:多对多,带有额外的列【英文标题】:openjpa: many to many with extra column 【发布时间】:2012-03-04 19:42:53 【问题描述】:

我需要在 JPA 中创建一个带有额外列的多对多可连接。

我正在使用:openjpa-maven-plugin 在构建期间进行增强,openjpa 2.2.0hsql file db,testng + spring

我有三个表:AnalysislocalizedAnalysisName(可连接)和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:多对多,带有额外的列的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot JPA - 使用额外的列查询多对多

playframework - 多对多,使用 crud 模块有额外的列

流利的 NHibernate 多对多与额外的列不插入

如何向多对多数据透视表添加额外的列

带有额外列的多对多自引用原则

带有额外列的 Spring Data JPA 多对多