使用@ChangeTracking关系保存ManyToMany会抛出PersistenceException

时间:2016-02-22 14:58:09

标签: java jpa eclipselink load-time-weaving

当我尝试保存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;
    }
}

的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}"]
}

我的问题是它为什么会发生以及如何避免它。

0 个答案:

没有答案