Playframework:JPA EntityManager 合并函数失败 - 不使用主题成员变量值

Playframework: JPA EntityManager merge function fails - does not use subjects member variable values

在我描述问题之前,让我解释一下,我正在使用 Playframework 2.4.4,Hibernate 5.0.5 并使用纯 JPA 方法,即我无法访问 Hibernate 'saveOrUpdate' 方法,但必须使用 merge 或 persist。此外,根据 playframework 的最新建议(当不使用 ebean 时),我的模型 class 不会扩展 play.db.ebean.Model class。

问题:

我对 EntityManager.merge 的调用失败并出现异常 "NULL not allowed for column",尽管主题实例中的关联成员变量确实已填充!

详情:

  1. class;三个字段,其中两个组成复合主键。
  2. 有一个 class 的新实例,通过绑定 Play 表单填充。我检查了实例的内容,确实包含故障列的值。
  3. table 为空。
  4. 虽然填入了值,但是SQL指令并没有到达,因为涉及到主键,当然会出现异常 [错误] - org.hibernate.engine.jdbc.spi.SqlExceptionHelper - 列 "IDTYPECODE" 不允许为 NULL; SQL 声明:

问题:

然而,我的对象已经完全填满了。那为什么会这样呢?根据我所阅读的所有内容,在这种情况下,合并功能应该成功插入一个新的非冲突条目!我做错了什么? Persist 将在这种情况下工作,但事实上,在我的特定情况下,我无法知道这个对象是否存在于数据库中。 我想避免查找以检查是否存在!以我的理解,应该是可以的!

更多详情:

这里是class定义

@Entity
public class IdType implements Serializable
{
    /**
     * User specified Unique identifier.
     * Both idTypeCode and idCategory are together
     * a compound key, so as to ensure that the 
     * combination of the both can not occur more
     * than once in the table.
     */
    @Id
    public String idTypeCode;
    @Id
    public IdCategory idCategory;

    public String idTypeName;
}

我的控制器很简单;

 @Transactional
 public Result updateIds()
 {
     IdType idType = Form.form(IdType.class).bindFromRequest().get();

     System.out.println("idTypeCode is:  "+idType.idTypeCode);

     EntityManager em = entityManager();
     em.merge(idType);

     return redirect(routes.Application.index());
 }

尝试以这种方式保存此对象的控制台输出如下:

idTypeCode is:  national_number
[error] - org.hibernate.engine.jdbc.spi.SqlExceptionHelper - NULL not allowed for column "IDTYPECODE"; SQL statement:
insert into IdType (idTypeName, idTypeCode, idCategory) values (?, ?, ?) [23502-187]
[error] - play.core.server.netty.PlayDefaultUpstreamHandler - Cannot invoke the action
javax.persistence.RollbackException: Error while committing the transaction
    at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:86) ~[hibernate-entitymanager-5.0.5.Final.jar:5.0.5.Final]
    at play.db.jpa.DefaultJPAApi.withTransaction(DefaultJPAApi.java:142) ~[play-java-jpa_2.11-2.4.4.jar:2.4.4]
    at play.db.jpa.JPA.withTransaction(JPA.java:159) ~[play-java-jpa_2.11-2.4.4.jar:2.4.4]
    at play.db.jpa.TransactionalAction.call(TransactionalAction.java:16) ~[play-java-jpa_2.11-2.4.4.jar:2.4.4]
    at play.core.j.JavaAction$$anonfun.apply(JavaAction.scala:94) ~[play_2.11-2.4.4.jar:2.4.4]

如您所见,假设对象不存在,在这种情况下 SQL 语句 运行 正是我所希望的:

insert into IdType (idTypeName, idTypeCode, idCategory) values (?, ?, ?) [23502-187]

然而,出于某种原因,即使我知道我的对象中有 'idTypeName' 的值,它告诉我:

[error] - org.hibernate.engine.jdbc.spi.SqlExceptionHelper - NULL not allowed for column "IDTYPECODE"; SQL statement:

那么,为什么它忽略了我明显填充的值?

我在别处读到合并应该有效;即,如果对象存在于数据库中,它将执行更新,如果不存在,则执行插入。 这种奇怪的行为是 Playframework 改变 JPA 库的正常行为的结果吗(在这种情况下我使用的是 Hibernate)。

有什么想法吗?

更新!!!!!

好的,终于有时间亲自深入研究一下了。 看来这跟玩没什么关系。 我刚才使用直接 JPA/Hibernate 控制台应用程序测试了相同的场景。我遇到了同样的问题。 一旦我有一个复合键(就像我在给定的示例中所做的那样),就会出现问题。 只要我删除其中一列作为键,合并就会起作用。 现在我正在研究为什么会这样,以及如何避免这个问题(同时仍然拥有我需要的模型)。 我会在获得更多信息后立即更新此问题。

好的。所以我终于解决了。 好像我在错误地构建我的 JPA compound/composite 主键。

我想我可以简单地单独用 Id 注释指定我的组合键。像这样:

@Entity
public class IdType implements Serializable
{
    /**
     * User specified Unique identifier.
     * Both idTypeCode and idCategory are together
     * a compound key, so as to ensure that the 
     * combination of the both can not occur more
     * than once in the table.
     */
    @Id
    public String idTypeCode;
    @Id
    public IdCategory idCategory;

    public String idTypeName;
}

但我错了。 我需要使用单独的 IdClass。否则,我会收到如前所述的神秘错误消息。 现在我的 class(es) 看起来像这样:

@Entity @IdClass(IdTypeCompoundId.class)
public class IdType implements Serializable
{
    /**
     * User specified Unique identifier.
     * Both idTypeCode and idCategory are together
     * a compound key, so as to ensure that the 
     * combination of the both can not occur more
     * than once in the table.
     */
    @Id
    public String idTypeCode;
    @Id
    public IdCategory idCategory;

    public String idTypeName;
}


public class IdTypeCompoundId implements Serializable
{
    public String idTypeCode;

    public IdCategory idCategory;
}

一切都很开心。合并没有问题。 即,第一次合并导致插入,具有相同详细信息的第二次合并(在我的例子中,例如,不同的 idTypeName)导致更新 table IdType 的现有行。

我不太明白为什么 JPA 需要使用单独的 'IdClass' 指定 table 来制作复合键。我原以为它通过两个(在我的例子中)用 id 注释的属性拥有它需要的所有信息。 但显然这还不够好。

总之,问题解决了。 我希望有一天这对某人有所帮助。