0

Before I describe the problem, let me explain that I'm using Playframework 2.4.4, with Hibernate 5.0.5 and using the pure JPA methods, i.e., I don't have access to the Hibernate 'saveOrUpdate' methods, but must use either merge or persist. Also, as per the latest recommendation from playframework (when not using ebean) my Model class does not extend the play.db.ebean.Model class.

The problem:

My call to EntityManager.merge fails with exception "NULL not allowed for column", although the associated member variable in the subject instance is indeed filled!

Details:

  1. The class; three field, two of which form a composite primary key.
  2. There is new instance of that class, filled by binding a Play form. I have checked the content of the instance, and does indeed contain values for the trouble column.
  3. The table is empty.
  4. Although the values are filled, the values don't arrive to the SQL instruction, and since a primary key is involved, this results of course in an exception [error] - org.hibernate.engine.jdbc.spi.SqlExceptionHelper - NULL not allowed for column "IDTYPECODE"; SQL statement:

Question:

Yet, my object is completely filled. So why would this happen? According to all i have read, the merge function should successfully insert a new non-conflicting entry in this case! What have I done wrong? Persist WILL work in this case, but in fact, in my particular situation I am unable to know if this objects exists persisted in the database or not. I would like to avoid a lookup to check for that existence! From my understanding, that should be possible!

Further details:

Here is the class definition

@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;
}

My controller is very simple;

 @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());
 }

The console output from an attempt to save this object in this way is as follows:

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$7.apply(JavaAction.scala:94) ~[play_2.11-2.4.4.jar:2.4.4]

As you can see, the SQL statement being run in this case is exactly what I would hope for, given that the object does not exist:

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

Yet, for some reason, even though I KNOW that there is a value for 'idTypeName' in my object, it tells me that:

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

So, why did it ignore my obviously filled value?

I have read elsewhere that merge should work; i.e, that if the object exists in the database it will perform an update, if not, an insert. Is this strange behaviour a result of Playframework changing the normal behaviour of the JPA library (in this case i'm using Hibernate).

Any ideas?

UPDATE!!!!!

OK, finally got the time to look further into this myself. It seems this has nothing to do with play. I tested just now the same scenario with a straight JPA/Hibernate console app. I get the same problem. The problem occurs as soon as I have a composite key (as I do in the given example). As soon as I remove one of the columns as key, the merge will work. Now I am investigating why that is, and how to avoid the issue (while still having the model I need). I will update this issue as soon as I have more information.

svaens
  • 649
  • 7
  • 23

1 Answers1

0

OK. So I worked it out finally. It seems like I was constructing my JPA compound/composite primary key incorrectly.

I thought I could simply specify my composite key with the Id annotation alone. Like so:

@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;
}

But I was wrong. I need to use a separate IdClass. Otherwise, I get cryptic error messages as described earlier. Now my class(es) look like this:

@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;
}

and it is all happy. Merge goes through without problems. i.e., the first merge causes an insert, the second merge with the same details (and in my case, for example, a different idTypeName) causes an update on the existing row of the table IdType.

I don't really understand why JPA requires the use of a separate 'IdClass' specified table to make a compound key. I would have thought it has all the information it needs through the two (in my case) attributes annotated with as Id. But apparently that is not good enough.

In anycase, problem solved. I hope this helps someone someday.

svaens
  • 649
  • 7
  • 23