持久化OneToOne关系时重复键值异常

时间:2018-03-12 19:15:18

标签: java jpa eclipselink derby

我有三种实体类型,彼此之间有OneToOne个关系。该关系映射到连接表。

当坚持三人时,我得到以下例外:

DerbySQLIntegrityConstraintViolationException: The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'SQL180312200409430' defined on 'C2_A2'.
Error Code: 20000
Call: INSERT INTO C2_A2 (a_ID, C2_ID) VALUES (?, ?)
    bind => [1, 3]
Query: DataModifyQuery(name="a" sql="INSERT INTO C2_A2 (a_ID, C2_ID) VALUES (?, ?)")
    at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:159)

实体类型如下所示:

@Entity
public class A2 {
    @OneToOne(mappedBy = "a")
    C2 c;
    @GeneratedValue
    @Id
    long id;

    // public getter/setter
}

@Entity
public class B2 {
    @OneToOne(mappedBy = "b")
    C2 c;
    @GeneratedValue
    @Id
    long id;

    // public getter/setter
}
@Entity
public class C2 {
    @JoinTable
    @OneToOne
    A2 a;

    @JoinTable
    @OneToOne
    B2 b;

    @GeneratedValue
    @Id
    long id;

    // public getter/setter
}

这是一个mcve:

public class QuickTestAB4 {

    private static String dbUrlBase = "jdbc:derby:testData/test.db";

    private static String dbUrlCreate = dbUrlBase + ";create=true";

    private static String dbUrlDrop = dbUrlBase + ";drop=true";

    private EntityManagerFactory factory;

    private EntityManager em;

    public Map<String, String> createPersistenceMap(final String dbUrl) {
        final Map<String, String> persistenceMap = new HashMap<>();
        persistenceMap.put("javax.persistence.jdbc.url", dbUrl);
        return persistenceMap;
    }

    public void dropDatabase() throws Exception {
        if (em != null && em.isOpen()) {
            em.close();
        }
        if (factory != null && factory.isOpen()) {
            factory.close();
        }
        try (Connection conn = DriverManager.getConnection(dbUrlDrop)) {

        } catch (final SQLException e) {
            // always

        }
    }

    public void deleteDatabase() throws Exception {
        dropDatabase();
        final File file = new File("testData/test.db");
        if (file.exists()) {
            FileUtils.forceDelete(file);
        }
    }

    public void createNewDatabase() throws SQLException, IOException {

        FileUtils.forceMkdir(new File("testData"));
        try (Connection conn = DriverManager.getConnection(dbUrlCreate)) {

        }
    }

    @BeforeClass
    public static void setUpBeforeClass01() throws Exception {
        Tests.enableLog4J();
        JPATests.enableJPA();

    }

    @AfterClass
    public static void tearDownAfterClass01() throws Exception {

    }

    @Before
    public void setUp01() throws Exception {

        deleteDatabase();
        createNewDatabase();
        final Map<String, String> map = createPersistenceMap(dbUrlCreate);
        factory = Persistence.createEntityManagerFactory("pu", map);

    }

    @After
    public void tearDown01() throws Exception {
        if (em != null && em.isOpen()) {
            em.close();
        }
        em = null;
        if (factory != null && factory.isOpen()) {
            factory.close();
        }
        factory = null;
    }

    @Test
    public void test01() throws Exception {
        em = factory.createEntityManager();
        final A2 a = new A2();
        final B2 b = new B2();
        final C2 c = new C2();
        a.setC(c);
        b.setC(c);
        c.setA(a);
        c.setB(b);
        try {
            em.getTransaction().begin();
            em.persist(a);
            em.persist(b);
            em.persist(c);
            em.getTransaction().commit();
        } finally {
            em.close();
        }
        factory.close();

    }

    @Test
    public void test02() throws Exception {
        em = factory.createEntityManager();
        A2 a = new A2();
        B2 b = new B2();
        C2 c = new C2();
        try {
            em.getTransaction().begin();
            em.persist(a);
            em.persist(b);
            em.persist(c);
            em.getTransaction().commit();
        } finally {
            em.close();
        }

        em = factory.createEntityManager();
        try {
            em.getTransaction().begin();
            a = em.merge(a);
            b = em.merge(b);
            c = em.merge(c);
            a.setC(c);
            b.setC(c);
            c.setA(a);
            c.setB(b);
            em.getTransaction().commit();
        } finally {
            em.close();
        }

        factory.close();

    }

}

persistence.xml

<properties>
        <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver" />
        <!-- EclipseLink should create the database schema automatically -->
        <property name="eclipselink.ddl-generation" value="create-tables" />
        <property name="eclipselink.create-ddl-jdbc-file-name"
                value="createDDL_ddlGeneration.jdbc" />
        <property name="eclipselink.ddl-generation.output-mode"
                value="both" />
        <property name="eclipselink.target-database" value="Derby" />
        <property name="eclipselink.platform.class.name"
                value="org.eclipse.persistence.platform.database.DerbyPlatform" />
        <property name="eclipselink.persistence-context.flush-mode"
                value="commit" />
        <property name="eclipselink.logging.level" value="FINE" />
        <property name="eclipselink.cache.shared.default" value="false" />
        <!-- <property name="eclipselink.logging.level" value="INFO" /> -->
</properties>

修改

生成以下约束(来自日志):

ALTER TABLE C2_A2 ADD CONSTRAINT FK_C2_A2_a_ID FOREIGN KEY (a_ID) REFERENCES A2 (ID)
ALTER TABLE C2_A2 ADD CONSTRAINT FK_C2_A2_C2_ID FOREIGN KEY (C2_ID) REFERENCES C2 (ID)
ALTER TABLE C2_B2 ADD CONSTRAINT FK_C2_B2_C2_ID FOREIGN KEY (C2_ID) REFERENCES C2 (ID)
ALTER TABLE C2_B2 ADD CONSTRAINT FK_C2_B2_b_ID FOREIGN KEY (b_ID) REFERENCES B2 (ID)
INSERT INTO A2 (ID) VALUES (?)
    bind => [1]
INSERT INTO B2 (ID) VALUES (?)
    bind => [2]
INSERT INTO C2 (ID) VALUES (?)
    bind => [3]
INSERT INTO C2_A2 (C2_ID, a_ID) VALUES (?, ?)
    bind => [3, 1]
INSERT INTO C2_B2 (C2_ID, b_ID) VALUES (?, ?)
    bind => [3, 2]
INSERT INTO C2_A2 (a_ID, C2_ID) VALUES (?, ?)
    bind => [1, 3]

同样的情况发生,当我第一次坚持实体然后建立关系时(参见test02)。

出于历史原因,我使用连接表进行关系映射而不是FK。我认为这之前有很多关系。

0 个答案:

没有答案