当我尝试保存 ManyToMany 关系时,我得到一个数据库异常:
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]
这很奇怪,因为我在日志中看到两个实例都已插入:
[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 是一个 eclipselink 功能,应用程序需要一个 javaagent 才能运行:
java -javaagent:eclipselink.jar
如果没有 ChangeTracking 和它按预期工作的代理,插入就会发生。如果我保存所有者方,它也可以工作(参见注释行)
这些文件可以在 github 上找到:https ://github.com/zbiro/many2many
该示例可以从
gradle start
java文件:
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;
}
}
持久性.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>
构建.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}"]
}
我的问题是它为什么会发生以及如何避免它。