When I try to maintain the ManyToMany relationship, I get a database exception:
Exception in thread "main" javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.2.v20151217-774c696): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: org.h2.jdbc.JdbcSQLException: NULL not allowed for column "INVERSES_ID"; SQL statement: INSERT INTO m2m_owner_inverse (inverses_ID, owners_ID) VALUES (?, ?) [23502-184] Error Code: 23502 Call: INSERT INTO m2m_owner_inverse (inverses_ID, owners_ID) VALUES (?, ?) bind => [null, 1]
Strange, as I see in the log that both instances are inserted:
[EL Fine]: sql: 2016-02-22 13:18:32.67--ClientSession(1546269015)--Connection(1894667608)--INSERT INTO M2MOWNER (NAME) VALUES (?) bind => [null] [EL Fine]: sql: 2016-02-22 13:18:32.676--ClientSession(1546269015)--Connection(1894667608)--CALL IDENTITY() [EL Fine]: sql: 2016-02-22 13:18:32.716--ClientSession(1546269015)--Connection(1894667608)--INSERT INTO M2MINVERSE (NAME) VALUES (?) bind => [null] [EL Fine]: sql: 2016-02-22 13:18:32.718--ClientSession(1546269015)--Connection(1894667608)--CALL IDENTITY()
ChangeTracking is an eclipselink function, and the application requires javaagent to run:
java -javaagent:eclipselink.jar
Without ChangeTracking and the agent, it works as expected, the insert occurs. It also works if I keep the owner side (see Commented line)
Files can be found on github: https://github.com/zbiro/many2many
Sample can be run using
gradle start
Java files:
public class M2MTest { public static void main(String[] args) { EntityManagerFactory emf = Persistence.createEntityManagerFactory("m2m-pu"); EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); M2MOwner owner = em.merge(new M2MOwner()); em.flush(); M2MInverse inverse = new M2MInverse(); owner.getInverses().add(inverse); inverse.getOwners().add(owner); inverse = em.merge(inverse); // does not work if agent is used //owner = em.merge(owner); // works in all cases em.flush(); em.getTransaction().commit(); em.close(); emf.close(); } } @Entity @ChangeTracking(ChangeTrackingType.ATTRIBUTE) public class M2MOwner { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String name; @ManyToMany(cascade={CascadeType.ALL}) @JoinTable(name="m2m_owner_inverse") private Set<M2MInverse> inverses = new HashSet<>(); public Set<M2MInverse> getInverses() { return inverses; } } @Entity @ChangeTracking(ChangeTrackingType.ATTRIBUTE) public class M2MInverse { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String name; @javax.persistence.ManyToMany( cascade = { CascadeType.ALL }, mappedBy="inverses") private Set<M2MOwner> owners = new HashSet<>(); public Set<M2MOwner> getOwners() { return owners; } }
persistence.xml
<?xml version="1.0" encoding="UTF-8" ?> <persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"> <persistence-unit name="m2m-pu" transaction-type="RESOURCE_LOCAL"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <class>M2MOwner</class> <class>M2MInverse</class> <exclude-unlisted-classes>false</exclude-unlisted-classes> <properties> <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" /> <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:tests;LOCK_TIMEOUT=10000" /> <property name="javax.persistence.jdbc.user" value="sa" /> <property name="eclipselink.ddl-generation" value="drop-and-create-tables" /> <property name="eclipselink.ddl-generation.output-mode" value="database" /> <property name="eclipselink.logging.parameters" value="true" /> <property name="eclipselink.logging.level.sql" value="FINEST" /> </properties> </persistence-unit> </persistence>
build.gradle
apply plugin: "java" repositories { mavenCentral() } configurations { eclipseLink { transitive = false } } ext.libs = [:] libs.eclipseLink = 'org.eclipse.persistence:eclipselink:2.6.2' dependencies { compile 'com.h2database:h2:1.4.191' compile libs.eclipseLink eclipseLink libs.eclipseLink } task start(type: JavaExec) { dependsOn build classpath = sourceSets.main.runtimeClasspath main = 'M2MTest' jvmArgs = ["-javaagent:${configurations.eclipseLink.singleFile}"] }
My question is why this is happening and how I could avoid it.
java jpa eclipselink load-time-weaving
Zbiro
source share