0

If have a JEE6 application that works fine on Glassfish 3.1.1 since years. Now have to move it to JEE7 / Glassfish 4.1.1 and ran into a couple of problems regarding JPA / Eclipselink:

a) I have an entity Applicant that contains a couple of Lists mapped as uni-directional OneToMany relations.

@Entity
@Table(name="applicant")
@AttributeOverride(name="id", column=@Column(name="UserID", insertable=false, updatable=false))
public class Applicant extends BaseEntityVersioned implements Serializable {
..
..
@OneToMany(cascade=CascadeType.ALL, orphanRemoval=true)
@JoinColumn(name="BewerberID", nullable=false)
private List<ITKenntnis> itKenntnisse = new ArrayList<ITKenntnis>();
..

    public List<ITKenntnis> getITKenntnisse() {
        return this.itKenntnisse;
    }

    public void setITKenntnisse(List<ITKenntnis> itKenntnisse) {
        this.itKenntnisse = itKenntnisse;
    }

    public ITKenntnis createITKenntnis(){
        return new ITKenntnis();
    }

    public boolean addITKenntnis(ITKenntnis itKenntnis) {
        getITKenntnisse().add(itKenntnis);
        return true;
    }
...
}

and the entity ITKEnntnis

@Entity
@Table(name="ITKenntnis")
public class ITKenntnis  extends BaseEntityVersioned implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="ID")
    private Integer id;

    @Override
    public Integer getId() {
        return id;
    }

    @Override
    public void setId(Integer id) {
        this.id=id;
    }
}

Until now I simply added a new instance of ITKentnis to the applicant with addKenntnis and persisted the applicant. With Glassfish 4.1.1 I get an error ->

javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.2.qualifier): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: java.sql.SQLException: Field 'BewerberID' doesn't have a default value Error Code: 1364

The field "BewerberID" is a foreign key in my database table that cannot be null, and I can't change this definition.

The only workaround I found so far was to redefine the relationship to bi-directional. Since I have a lot of relations and the code worked with JPA 2.0, I would appreciate if there was a simpler solution.

b) I have an entity Application that defines a uni-directional @ManyToOne relationship to Applicant.

@Entity
@Table(name="bewerbungen")
public class Application extends BaseEntityVersioned implements Serializable {
...
// uni-directional
@ManyToOne (fetch=FetchType.LAZY)
@JoinColumn(name="BewerberID")
private Bewerberprofil bewerberprofil;
...

}

Regardless of wether this is a good design or not (I had reasons to map it that way) -> when I try to delete the entity I see an update that sets the field "BewerberID" to null, instead of a delete. Why is that in JPA 2.1 and how can I change that?

Sorry for the long post.

Thomas

EDIT: I made further investigations with these issues, debugging the SQL statements generated on the Glasfish 3.1.1. server that's working.

Adding a new child entity has the following SQL statements which are the same as on the new server ->

[#|2016-04-26T17:22:55.613+0200|INFO|glassfish3.1.1|javax.enterprise.system.std.com.sun.enterprise.server.logging|_ThreadID=25;_ThreadName=Thread-2;|[EL Fine]: 2016-04-26 17:22:55.613--ClientSession(1645443748)--Connection(1826150217)--Thread(Thread[http-thread-pool-80(2),5,grizzly-kernel])--INSERT INTO bewerberedv (Bearbeitet, Bereich, Bewertung, Dauer, Kenntnis, Version, ZuletztVerwendet) VALUES (?, ?, ?, ?, ?, ?, ?)
    bind => [true, 6, 1, null, 377, 2016-04-26 17:24:08.0, null]
|#]

[#|2016-04-26T17:22:55.628+0200|INFO|glassfish3.1.1|javax.enterprise.system.std.com.sun.enterprise.server.logging|_ThreadID=25;_ThreadName=Thread-2;|[EL Fine]: 2016-04-26 17:22:55.628--ClientSession(1645443748)--Connection(1826150217)--Thread(Thread[http-thread-pool-80(2),5,grizzly-kernel])--SELECT LAST_INSERT_ID()
|#]

[#|2016-04-26T17:22:55.644+0200|INFO|glassfish3.1.1|javax.enterprise.system.std.com.sun.enterprise.server.logging|_ThreadID=25;_ThreadName=Thread-2;|[EL Fine]: 2016-04-26 17:22:55.644--ClientSession(1645443748)--Connection(1826150217)--Thread(Thread[http-thread-pool-80(2),5,grizzly-kernel])--UPDATE bewerberedv SET BewerberID = ? WHERE (ID = ?)
    bind => [2575, 1223970]
|#]

The difference is, that on the new server the INSERT fails because the field "BewerberID" can't be NULL. Somehow the check in the database seems to be different.

The same is for the DELETE which has following output ->

[#|2016-04-26T17:13:39.363+0200|INFO|glassfish3.1.1|javax.enterprise.system.std.com.sun.enterprise.server.logging|_ThreadID=31;_ThreadName=Thread-2;|[EL Fine]: 2016-04-26 17:13:39.363--ClientSession(792940001)--Connection(1806567172)--Thread(Thread[http-thread-pool-80(4),5,grizzly-kernel])--UPDATE bewerbungen SET BewerberID = ?, PositionID = ?, InseratID = ?, Version = ? WHERE ((ID = ?) AND (Version = ?))
    bind => [null, null, null, 2016-04-26 17:14:52.0, 1304157, 2016-04-26 17:14:25.0]
|#]

[#|2016-04-26T17:13:39.363+0200|INFO|glassfish3.1.1|javax.enterprise.system.std.com.sun.enterprise.server.logging|_ThreadID=31;_ThreadName=Thread-2;|[EL Fine]: 2016-04-26 17:13:39.363--ClientSession(792940001)--Connection(1806567172)--Thread(Thread[http-thread-pool-80(4),5,grizzly-kernel])--UPDATE inserateanz SET AnzahlBew = ?, AnzOffeneBewerbung = ?, AnzNeueBewerbung = ? WHERE (ID = ?)
    bind => [0, 0, 0, 61277]
|#]

[#|2016-04-26T17:13:39.363+0200|INFO|glassfish3.1.1|javax.enterprise.system.std.com.sun.enterprise.server.logging|_ThreadID=31;_ThreadName=Thread-2;|[EL Fine]: 2016-04-26 17:13:39.363--ClientSession(792940001)--Connection(1806567172)--Thread(Thread[http-thread-pool-80(4),5,grizzly-kernel])--UPDATE positionenanz SET AnzahlBew = ?, AnzOffeneBewerbung = ?, AnzNeueBewerbung = ? WHERE (ID = ?)
    bind => [0, 0, 0, 44726]
|#]

[#|2016-04-26T17:13:39.363+0200|INFO|glassfish3.1.1|javax.enterprise.system.std.com.sun.enterprise.server.logging|_ThreadID=31;_ThreadName=Thread-2;|[EL Fine]: 2016-04-26 17:13:39.363--ClientSession(792940001)--Connection(1806567172)--Thread(Thread[http-thread-pool-80(4),5,grizzly-kernel])--DELETE FROM bewerbungen WHERE ((ID = ?) AND (Version = ?))
    bind => [1304157, 2016-04-26 17:14:52.0]

Thats interresting because on the new server I have the same update statement "UPDATE bewerbungen SET BewerberID = ?, ...." but there it fails immediately with the null constraint!

  • Unidirectional OneToMany relationships use a join table. Are you suggesting that you had it working without this before? See §2.10.5.1 of the JPA spec (2.0 or 2.1) – Steve C Apr 26 '16 at 07:48
  • Yes, it works since 2011 with JPA2.0. Also check out https://en.wikibooks.org/wiki/Java_Persistence/OneToMany under Advanced. – Thomas Kernstock Apr 26 '16 at 09:14
  • Also see "Pro JPA 2" by Mike Keith chapter "Advanced Object-Relational Mapping": Avoiding Join Tables Up to this point, we have discussed a unidirectional one-to-many mapping in the context of using a join table, but it is also possible to map a unidirectional mapping without using a join table. It requires the foreign key to be in the target table, or “many” side of the relationship, even though the target object does not have any reference to the “one” side. This is called a unidirectional one-to-many target foreign key mapping, because the foreign key is in the target table instead of a ... – Thomas Kernstock Apr 26 '16 at 09:37
  • .. join table. To use this mapping, we first indicate that the one-to-many relationship is unidirectional by not specifying any mappedBy element in the annotation. Then we specify a @JoinColumn annotation on the one-to-many attribute to indicate the foreign key column. The catch is that the join column that we are specifying applies to the table of the target object, not the source object in which the annotation appears. – Thomas Kernstock Apr 26 '16 at 09:37
  • Ok. Found the authoritative reference for this behaviour in §11.1.25 of the spec, which is also mirrored (in summary) in the javadoc. – Steve C Apr 26 '16 at 15:20

1 Answers1

0

As I found out with the help of Chris Delahunt the problem is not a difference between JPA 2.0 and 2.1 but a matter of database(driver) behaviour. This was also implied by the SQL Statements above.

The solution was, that I just changed the JDBC Definition in Glsassfish 4.1.1 from

<jdbc-connection-pool datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource"  name="Test" res-type="javax.sql.DataSource">

to

<jdbc-connection-pool datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource" name="Test" res-type="javax.sql.ConnectionPoolDataSource">

Now the inserts and deletes work as before. :)