2012-03-04 2 views
2

Мне нужно создать много ко многим соединительным с дополнительным столбцом в JPA.openjpa: многие для многих с дополнительной колонкой

Я использую: openjpa-maven-plugin для повышения во время сборки, openjpa 2.2.0, hsql file db, TestNG + spring

У меня есть три таблицы: Analysis, localizedAnalysisName (jointable) и 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. Вы являются установка их для различных значений:

// 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., ему будет присвоен новый идентификатор менеджером сущности. Следовательно, L.A.N. имеет 2 разных идентификатора записи Analysis в то же время.

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

+0

Вопрос в том, как установить analyis_id в классе LocalizedAnalysisNameId – Andy602

+0

Я бы выбрал для установки либо (locale_id, analysis_id), либо (Locale, Analysis), но не оба. Или сначала сохраните «Анализ» (и выполните флеш), а затем установите все поля. – MaDa

Смежные вопросы