我的实体(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。 MyEntity
中的id字段具有以下注释:
@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实例时,我仍然得到与以前完全相同的错误。
答案 0 :(得分:1)
你的MyEntityTranslations需要一个@ManyToOne。你的@OneToMany应该使用mappedBy而不是@JoinColumn。
从CustomTranslations中删除id,然后将@ManyToOne添加到MyEntity,并在其上添加@JoinColumn和@Id。
请参阅, http://en.wikibooks.org/wiki/Java_Persistence/OneToMany
和, http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#JPA_2.0
答案 1 :(得分:0)
这会导致错误,因为您有2个映射来控制&#34; ID&#34; &#34; MY_ENTITY_TRANSLATIONS&#34;中的字段表。第一个是CustomTranslations类中的ID映射,第二个是MyEntity中的OneToMany映射。你只能有1个可写,我建议你可以从映射到表的类中访问它,以避免其他问题。
最简单的方法是让你的OneToMany成为双向的OneToMany。这意味着将CustomTranslations中的ManyToOne映射添加回使用&#34; ID&#34;的MyEntity。 field作为连接列,然后将OneToMany标记为由此新映射映射。 JPA允许您将这个新的ManyToOne作为CustomTranslations的ID,因此您可以删除Long id,或者您可以保留它并使用@MapsId注释,以便JPA知道引用的MyEntity的值将用于长id字段也是如此。类似的东西:
@Id protected Long id;
@MapsId(value="id")
@ManyToOne
MyEntity myEntityBackReference;
您需要维护双向关系而不仅仅是集合,但这将是独立于实现的。