openjpa: много ко многим с дополнительной колонкой - PullRequest
2 голосов
/ 04 марта 2012

Мне нужно создать таблицу со многими ко многим с дополнительным столбцом в 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 Ответ

1 голос
/ 05 марта 2012

Не устанавливайте идентификаторы и ссылки на ключи в таблице LocalizedAnalysisName. Вы устанавливаете их на разные значения:

// 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(...), он находится в состоянии «новый», у него еще нет идентификатора (по умолчанию 0). В то же время вы устанавливаете сущность Analysis в L.A.N., при этом менеджер сущностей получает новый идентификатор. Следовательно, Л.А.Н. имеет 2 разных идентификатора записи Analysis одновременно.

Что касается примечания, аннотация @Embeddable в LocalizedAnalysisNameId не обязательна, так как вы не используете его как @EmbeddedId, но перечисляете его поля напрямую.

...