我有三种实体类型,彼此之间有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。我认为这之前有很多关系。