我尝试使用JPA 2.1中设置的非唯一值映射Map<String, Entity>
。这种关系现在可以是单向的。有一个万亿解释如何使用唯一值集来映射Map<String, Entity>
,但这个问题与此无关。
我有一个
@Entity
public class Entity2 implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private Long id;
[constructors, getter and setter]
和
@Entity
public class Entity1 implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private Long id;
@OneToMany
@MapKey(name = "id")
private Map<String, Entity2> entity2Map = new HashMap<>();
我的persistence.xml
包含
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="richtercloud_jpa-map-with-non-unique-value-set_war_1.0-SNAPSHOTPU" transaction-type="JTA">
<jta-data-source>jdbc/sample</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.schema-generation.database.action" value="create"/>
</properties>
</persistence-unit>
</persistence>
导致
Entity2 entity2 = new Entity2(1l);
Entity1 entity1 = new Entity1(2l);
entity1.getEntity2Map().put("a", entity2);
entity1.getEntity2Map().put("b", entity2);
entityManager.persist(entity2);
entityManager.persist(entity1);
由于(我拼命试图将输出切换为英语,但似乎无法弄明白;但实质上是两个Entity2
引用会导致主键约束违规),因此失败了:
Caused by: javax.transaction.RollbackException: Transaction marked for rollback.
at com.sun.enterprise.transaction.JavaEETransactionImpl.commit(JavaEETransactionImpl.java:490)
at com.sun.enterprise.transaction.JavaEETransactionManagerSimplified.commit(JavaEETransactionManagerSimplified.java:878)
at com.sun.ejb.containers.EJBContainerTransactionManager.completeNewTx(EJBContainerTransactionManager.java:721)
... 64 more
Caused by: javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.4.qualifier): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: Die Anweisung wurde abgebrochen, weil sie in einer für 'ENTITY1_ENTITY2' definierten Vorgabe für einen eindeutigen oder Primärschlüssel-Constraint bzw. für einen von 'SQL180327114054560' identifizierten und eindeutigen Index zu einem duplizierten Schlüsselwert geführt hätte.
Error Code: 20000
Call: INSERT INTO ENTITY1_ENTITY2 (entity2Map_ID, Entity1_ID) VALUES (?, ?)
bind => [2 parameters bound]
Query: DataModifyQuery(name="entity2Map" sql="INSERT INTO ENTITY1_ENTITY2 (entity2Map_ID, Entity1_ID) VALUES (?, ?)")
at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl$1.handleException(EntityManagerSetupImpl.java:745)
at org.eclipse.persistence.transaction.AbstractSynchronizationListener.handleException(AbstractSynchronizationListener.java:275)
at org.eclipse.persistence.transaction.AbstractSynchronizationListener.beforeCompletion(AbstractSynchronizationListener.java:170)
at org.eclipse.persistence.transaction.JTASynchronizationListener.beforeCompletion(JTASynchronizationListener.java:68)
at com.sun.enterprise.transaction.JavaEETransactionImpl.commit(JavaEETransactionImpl.java:452)
... 66 more
Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.4.qualifier): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: Die Anweisung wurde abgebrochen, weil sie in einer für 'ENTITY1_ENTITY2' definierten Vorgabe für einen eindeutigen oder Primärschlüssel-Constraint bzw. für einen von 'SQL180327114054560' identifizierten und eindeutigen Index zu einem duplizierten Schlüsselwert geführt hätte.
Error Code: 20000
Call: INSERT INTO ENTITY1_ENTITY2 (entity2Map_ID, Entity1_ID) VALUES (?, ?)
bind => [2 parameters bound]
Query: DataModifyQuery(name="entity2Map" sql="INSERT INTO ENTITY1_ENTITY2 (entity2Map_ID, Entity1_ID) VALUES (?, ?)")
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:331)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:902)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeNoSelect(DatabaseAccessor.java:964)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:633)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:560)
at org.eclipse.persistence.internal.sessions.AbstractSession.basicExecuteCall(AbstractSession.java:2056)
at org.eclipse.persistence.sessions.server.ClientSession.executeCall(ClientSession.java:306)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:242)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:228)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeNoSelectCall(DatasourceCallQueryMechanism.java:271)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeNoSelect(DatasourceCallQueryMechanism.java:251)
at org.eclipse.persistence.internal.queries.StatementQueryMechanism.executeNoSelect(StatementQueryMechanism.java:118)
at org.eclipse.persistence.queries.DataModifyQuery.executeDatabaseQuery(DataModifyQuery.java:85)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:904)
at org.eclipse.persistence.internal.sessions.AbstractSession.internalExecuteQuery(AbstractSession.java:3271)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1857)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1839)
at org.eclipse.persistence.mappings.ManyToManyMapping.insertIntoRelationTable(ManyToManyMapping.java:672)
at org.eclipse.persistence.mappings.ManyToManyMapping.performDataModificationEvent(ManyToManyMapping.java:831)
at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:160)
at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:4264)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1441)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1531)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.issueSQLbeforeCompletion(UnitOfWorkImpl.java:3168)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.issueSQLbeforeCompletion(RepeatableWriteUnitOfWork.java:355)
at org.eclipse.persistence.transaction.AbstractSynchronizationListener.beforeCompletion(AbstractSynchronizationListener.java:158)
... 68 more
Caused by: java.sql.SQLIntegrityConstraintViolationException: Die Anweisung wurde abgebrochen, weil sie in einer für 'ENTITY1_ENTITY2' definierten Vorgabe für einen eindeutigen oder Primärschlüssel-Constraint bzw. für einen von 'SQL180327114054560' identifizierten und eindeutigen Index zu einem duplizierten Schlüsselwert geführt hätte.
at org.apache.derby.client.am.SQLExceptionFactory40.getSQLException(Unknown Source)
at org.apache.derby.client.am.SqlException.getSQLException(Unknown Source)
at org.apache.derby.client.am.PreparedStatement.executeUpdate(Unknown Source)
at com.sun.gjc.spi.base.PreparedStatementWrapper.executeUpdate(PreparedStatementWrapper.java:125)
at sun.reflect.GeneratedMethodAccessor117.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.sun.gjc.spi.jdbc40.ProfiledConnectionWrapper40$1.invoke(ProfiledConnectionWrapper40.java:437)
at com.sun.proxy.$Proxy341.executeUpdate(Unknown Source)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:892)
... 92 more
Caused by: org.apache.derby.client.am.SqlException: Die Anweisung wurde abgebrochen, weil sie in einer für 'ENTITY1_ENTITY2' definierten Vorgabe für einen eindeutigen oder Primärschlüssel-Constraint bzw. für einen von 'SQL180327114054560' identifizierten und eindeutigen Index zu einem duplizierten Schlüsselwert geführt hätte.
at org.apache.derby.client.am.Statement.completeExecute(Unknown Source)
at org.apache.derby.client.net.NetStatementReply.parseEXCSQLSTTreply(Unknown Source)
at org.apache.derby.client.net.NetStatementReply.readExecute(Unknown Source)
at org.apache.derby.client.net.StatementReply.readExecute(Unknown Source)
at org.apache.derby.client.net.NetPreparedStatement.readExecute_(Unknown Source)
at org.apache.derby.client.am.PreparedStatement.readExecute(Unknown Source)
at org.apache.derby.client.am.PreparedStatement.flowExecute(Unknown Source)
at org.apache.derby.client.am.PreparedStatement.executeUpdateX(Unknown Source)
... 100 more
在使用EclipseLink和Apache Derby部署到Payara 4.1.2.181的基于JTA的Java EE项目中。
@MapKeyColumn
是基本类型之间的映射,例如Map<String, String>
。
我没有尝试@CollectionTable
,因为它
指定用于映射基本类型或可嵌入类型集合的表。
如果我无法解决这个问题,我可能会将Map<String, Entity>
转换为Set<WrappingEntity>
,其中WrappingEntity
包含一个id,字符串值和{{1}参考甚至可能更有效率,但我也想知道这一点。该方法通常应该有效,因为具有Entity2
ID的表格,字符串地图键和地图Entity1
值可以使用Entity2
的ID和字符串将键映射为主键。
可以在https://gitlab.com/krichter/jpa-map-with-non-unique-value-set找到SSCCE。
答案 0 :(得分:1)
我不确定JPA 2.1规范甚至在多对多关联中提到实体是非唯一值,所以这不是真正的错误,只是行为未指定(某些提供商可能支持这样的功能,偶然或设计)。
话虽如此,对{1}}的唯一约束在一对多关联的连接表中存在是有意义的。我会尝试切换到Entity2.id
(由您自己承担风险,因为......未说明的行为)