0

我的实体(MyEntity)中有以下字段:

@OneToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
@JoinColumn(name="ID", referencedColumnName="ID")
@MapKey(name="localeForDB")
private Map<String, MyEntityTranslations> translations;

MyEntityTranslations 看起来像这样:

@Entity
@Table(name="MY_ENTITY_TRANSLATIONS")
public class MyEntityTranslations extends CustomTranslations
{
    private String prop1;
    private String prop2;

    //..getters and setters
}

最后 CustomTranslations 看起来像这样:

@MappedSuperclass
@Access(AccessType.FIELD)
public abstract class CustomTranslations
{
    @Id
    protected Long id;
    @Transient
    protected Locale locale;

    @Column(name="LOCALE", nullable=false)
    @Access(AccessType.PROPERTY)
    @Id
    protected String getLocaleForDB()
    {
        return locale.toString();
    }

    protected void setLocaleForDB(String localeFromDB)
    {
        //logic for creating a Locale object from a String
    }
}

当我从数据库中提取数据时,一切都很好。我得到一个 MyEntity 并且地图已按预期填充。但是,当我尝试插入数据(保存 MyEntity)时,我会出错。这是日志中的内容:

[EL Fine]: 2012-09-04 23:40:19.989--ClientSession(101408113)--Connection(1411623120)--Thread(Thread[http-8080-1,5,main])--select nextval(MY_ENTITY_SEQ_GEN)
[EL Fine]: 2012-09-04 23:40:20.048--ClientSession(101408113)--Connection(1411623120)--Thread(Thread[http-8080-1,5,main])--INSERT INTO MY_ENTITY (ID, STUFF, FOR, MY_ENTITY,) VALUES (?, ?, ?, ?)
    bind => [1, foo1, foo2, foo3]
[EL Fine]: 2012-09-04 23:40:20.13--ClientSession(101408113)--Connection(1411623120)--Thread(Thread[http-8080-1,5,main])--INSERT INTO MY_ENTITY_TRANSLATIONS (ID, LOCALE, PROP1, PROP2) VALUES (?, ?, ?, ?)
    bind => [null, en_US, My Prop1, My Prop2]
[EL Fine]: 2012-09-04 23:40:20.135--ClientSession(101408113)--Thread(Thread[http-8080-1,5,main])--SELECT 1
[EL Warning]: 2012-09-04 23:40:20.139--UnitOfWork(1394902291)--Thread(Thread[http-8080-1,5,main])--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: ERROR: null value in column "id" violates not-null constraint
Error Code: 0
Call: INSERT INTO MY_ENTITY_TRANSLATIONS (ID, LOCALE, PROP1, PROP2) VALUES (?, ?, ?, ?)
    bind => [null, en_US, My Prop1, My Prop2]
Query: InsertObjectQuery(com.foo.MyEntityTranslations@53fd3a3b)
Sep 4, 2012 11:40:54 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet appServlet threw exception
org.postgresql.util.PSQLException: ERROR: null value in column "id" violates not-null constraint
    at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2103)

现在,在我尝试保存MyEntity对象之前,我在地图中看到MyEntityTranslations条目的 id 字段为空。我理解这一点,因为MyEntity它本身还没有 id。中的 id 字段MyEntity具有以下注释:

@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="MY_ENTITY_SEQ_GEN")

因此,它在插入时从序列中获取其 id。我的问题是我需要如何注释地图以便该 id 也被MyEntityTranslations记录在案?我在 上尝试了不同的属性@JoinColumn,但似乎没有任何效果。我是一个 JPA 新手,所以我可能遗漏了一些明显的东西。我想保持所有 JPA 实现独立,但如果重要的话,我使用的是 EclipseLink 2.3.2。

根据以下建议,我将 CustomTranslations 更改为如下所示:

@MappedSuperclass
@Access(AccessType.FIELD)
public abstract class CustomTranslations<T>
{
    @Id
    @ManyToOne
    @JoinColumn(name="ID")
    protected T translationsFor;
    @Transient
    protected Locale locale;

    @Column(name="LOCALE", nullable=false)
    @Access(AccessType.PROPERTY)
    @Id
    protected String getLocaleForDB()
    {
        return locale.toString();
    }

    protected void setLocaleForDB(String localeFromDB)
    {
        //logic for creating a Locale object from a String
    }
}

然后我的地图MyEntity看起来像这样:

@OneToMany(mappedBy="translationsFor", fetch=FetchType.EAGER, cascade=CascadeType.ALL)
@MapKey(name="localeForDB")
private Map<String, MyEntityTranslations> translations;

我确定我只是误解了某些东西,但是在尝试保存 MyEntity 实例时,我仍然遇到与以前完全相同的错误。

4

2 回答 2

1

You need a @ManyToOne back in your MyEntityTranslations. Your @OneToMany should use a mappedBy not a @JoinColumn.

Remove the id from CustomTranslations and instead add a @ManyToOne to MyEntity with the @JoinColumn and @Id on it.

See, http://en.wikibooks.org/wiki/Java_Persistence/OneToMany

and, http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#JPA_2.0

于 2012-09-05T13:49:28.457 回答
0

This should result in an error, because you have 2 mappings that control the "ID" field in the "MY_ENTITY_TRANSLATIONS" table. The first being the ID mapping in the CustomTranslations class, the second being the OneToMany mapping in MyEntity. You can only have 1 writable, and I suggest you make it one accessible from within the class mapped to the table to avoid other problems.

The easiest way to do what you want is to make your OneToMany a bidirectional OneToMany. That means add a ManyToOne mapping in CustomTranslations back to MyEntity that uses the "ID" field as the join column, and then mark the OneToMany as mapped by this new mapping. JPA allows you making this new ManyToOne as the ID for the CustomTranslations, so you can remove the Long id, or you can keep it and use the @MapsId annotation so that JPA knows that the value from the referenced MyEntity is to be used in the Long id field as well. Something like:

   @Id protected Long id;  
   @MapsId(value="id")
   @ManyToOne
   MyEntity myEntityBackReference;   

You will need to maintain the bidirectional relationship instead of just the collection, but this will be implementation independent.

于 2012-09-05T13:50:20.767 回答