基于@MapKey的JPA插入

基于@MapKey的JPA插入,jpa,map,eclipselink,Jpa,Map,Eclipselink,我的实体(MyEntity)中有以下字段: 最后,自定义翻译如下所示: @Entity @Table(name="MY_ENTITY_TRANSLATIONS") public class MyEntityTranslations extends CustomTranslations { private String prop1; private String prop2; //..getters and setters } @MappedSuperclass @Ac

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

最后,自定义翻译如下所示:

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

    //..getters and setters
}
@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
    }
}
@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
    }
}
@OneToMany(mappedBy="translationsFor", fetch=FetchType.EAGER, cascade=CascadeType.ALL)
@MapKey(name="localeForDB")
private Map<String, MyEntityTranslations> translations;
当我从数据库中提取数据时,一切都很好。我得到了一个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

根据以下建议,我将自定义翻译更改为如下:

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

    //..getters and setters
}
@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
    }
}
@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
    }
}
@OneToMany(mappedBy="translationsFor", fetch=FetchType.EAGER, cascade=CascadeType.ALL)
@MapKey(name="localeForDB")
private Map<String, MyEntityTranslations> translations;

我肯定我只是误解了什么,但在尝试保存MyEntity实例时,我仍然会遇到与以前完全相同的错误。

您需要在MyEntityTranslations中返回@ManyToOne。您的@OneToMany应该使用mappedBy而不是@JoinColumn

从CustomTranslations中删除id,而是向MyEntity添加一个带有@JoinColumn和@id的@ManyTone

看,,

以及,

这将导致一个错误,因为您有两个映射控制“MY_ENTITY_TRANSLATIONS”表中的“ID”字段。第一个是CustomTranslations类中的ID映射,第二个是MyEntity中的OneToMany映射。您只能有1个可写的,我建议您在映射到表的类中设置一个可访问的,以避免其他问题

做你想做的事情最简单的方法就是让你的“一人一妻”成为一个双向的“一人一妻”。这意味着在CustomTranslations中将一个多通映射添加回MyEntity,该映射使用“ID”字段作为联接列,然后将该多通映射标记为该新映射映射的多通映射。JPA允许您将这个新的manyTone作为CustomTranslations的ID,因此您可以删除长ID,也可以保留它并使用@MapsId注释,以便JPA知道引用的MyEntity中的值也将用于长ID字段。比如:

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

您将需要维护双向关系,而不仅仅是集合,但这将是独立于实现的。

我更新了我的问题,尝试了您所说的内容。我还是会犯同样的错误。你能解释一下我做错了什么吗?在坚持之前,确保你在CustomTranslations中设置了TranslationFor的值是的,当然。我最初用来填充实体的库并没有神奇地为我完成这项工作。:)一旦我得到了它,它就像一个魅力。谢谢,@MapsId如何处理我也希望作为主键一部分的区域设置?