该语句被中止,因为它会导致重复的键值

The statement was aborted because it would have caused a duplicate key value

我的 Java EE 应用程序的一部分是用户管理。我的开发环境是 Windows 10、JDK 14 上的 Netbeans 12.0、Glassfish server 5.1、Apache Derby DB 和 eclipse persistence JPA。目前,在开发阶段,我已将 JPA 配置为删除并创建 tables,每当我重新启动服务器时。

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2" xmlns="http://java.sun.com/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_2.xsd">
  <persistence-unit name="listserviceDB">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <jta-data-source>java:comp/DefaultDataSource</jta-data-source>
    <properties>
      <property name="eclipselink.ddl-generation.output-mode" value="both"/>
      <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
    </properties>
  </persistence-unit>
</persistence>

我有一个实体 bean“用户”,配置为使用自动生成的实体主键:

public class User implements Serializable {

    private static final long serialVersionUID = 1005L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @NotNull
    private String userId;
    @NotNull
    private String password;
    ............................
    @NotNull
    private String userRole;
}

每当服务器启动时,我都会使用 JAXB 机制使用 XML 文件中的测试数据填充所有 table。这是我的 UserList.xml“用户”实体 bean 的示例。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<users>
    <user>
        <id>2</id>
        <userId>AMackinzy</userId>
        <password>password</password>
        <userRole>update</userRole>
        <firstName>Adam</firstName>
        <lastName>Mc Keinzy</lastName>
        <officePhone>(718)-815-8110</officePhone>
        <cellPhone>(845)-340-5410</cellPhone>
        <company>MMC</company>
    </user>
    <user>
        <id>3</id>
        <userId>AOzguer23</userId>
        <password>password</password>
        <userRole>consumer</userRole>
        <firstName>Albert</firstName>
        <lastName>Ozgur</lastName>
        <officePhone>(213)-567-2390</officePhone>
        <cellPhone>(917)-301-3491</cellPhone>
        <company>Bernstern</company>
    </user>
    <user>
        <id>1</id>
        <userId>admin</userId>
        <password>password</password>
        <userRole>superadmin</userRole>
        <firstName>Joseph</firstName>
        <lastName>Ottaviano</lastName>
        <officePhone>718-815-8111</officePhone>
        <cellPhone>917-971-6854</cellPhone>
        <company>Lemmen Inc.</company>
    </user>
</users>

当应用程序启动时 运行,我可以通过前端 JSF(Java Server Faces)页面直观地验证数据,一切看起来都很好,这表明 JAXB 代码已成功将初始用户数据加载到数据库中。当我尝试从以以下异常终止的前端 JSF 页面添加新用户时,可怕的问题就开始了:

Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.7.0.v20170811-d680af5): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.apache.derby.shared.common.error.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 'SQL201230210444600' defined on 'PERSISTENCE_USER'.
Error Code: 20000
Call: INSERT INTO PERSISTENCE_USER (ID, CELLPHONE, COMPANY, DATEOFBIRTH, FIRSTNAME, LASTNAME, OFFICEPHONE, PASSWORD, USERID, USERROLE) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    bind => [10 parameters bound]
Query: InsertObjectQuery(org.me.mavenlistservicedb.entity.User@79460d95)
    at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:331)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:905)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeNoSelect(DatabaseAccessor.java:967)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:637)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:564)
    at org.eclipse.persistence.internal.sessions.AbstractSession.basicExecuteCall(AbstractSession.java:2093)
    at org.eclipse.persistence.sessions.server.ClientSession.executeCall(ClientSession.java:309)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:270)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:256)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.insertObject(DatasourceCallQueryMechanism.java:405)
    at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:165)
    at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:180)
    at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.insertObjectForWrite(DatabaseQueryMechanism.java:502)
    at org.eclipse.persistence.queries.InsertObjectQuery.executeCommit(InsertObjectQuery.java:80)
    at org.eclipse.persistence.queries.InsertObjectQuery.executeCommitWithChangeSet(InsertObjectQuery.java:90)
    at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:314)
    at org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58)
    at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:911)

我不确定为什么当“用户”实体利用自动密钥生成时出现此错误,最初的 3 条数据已经加载到 table,并且自动-key 生成器已经递增到 4(假设 JPA 从 index=1 开始生成密钥)?因此,我通过 JSF 前端添加的第一个用户(它与 JAXB 共享相同的持久性代码,将 XML 文件中的初始数据添加到 table)应该分配给 id=5?

最初,我想,也许 XML 文件中的 Id 字段(即 1)与此错误有关,但同样与自动密钥生成方案有关,[=46= 中的 Id ] 应该被 JPA 自动生成的值覆盖吗?

我已经在这个论坛和其他地方阅读了很多关于这个错误的帖子,但除了这个错误之外,我没有找到任何与我的情况有共同之处的帖子。感谢您对此错误的根本原因以及我如何在没有应用程序生成主键的情况下修复它的任何清晰解释。

更新 1:table

中的剩余数据 查了下,发现在应用程序启动时Usertable里面总是有剩余数据,这违反了我在persistence.xml文件中选择的drop-and-create策略(可能是JPA 问题?)在这种情况下,当容器调用我的 Singleton class @postConstruct 注释方法 loadData() 以从 XML 文件加载初始数据时,自动密钥生成从 1 开始,并且导致与具有相同 ID 的剩余行之一发生冲突。我修改了代码以在服务器启动时从 table 中删除所有行并且问题消失了,但是,我在日志文件中看到以下异常不会导致应用程序出现任何问题或中断:
Caused by: Exception [EclipseLink-7251] (Eclipse Persistence Services - 2.7.0.v20170811-d680af5): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The attribute [id] of class [org.me.mavenlistservicedb.entity.User] is mapped to a primary key column in the database. Updates are not allowed.
    at org.eclipse.persistence.exceptions.ValidationException.primaryKeyUpdateDisallowed(ValidationException.java:2551)
    at org.eclipse.persistence.mappings.foundation.AbstractDirectMapping.writeFromObjectIntoRowWithChangeRecord(AbstractDirectMapping.java:1265)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildRowForUpdateWithChangeSet(ObjectBuilder.java:1773)
    at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.updateObjectForWriteWithChangeSet(DatabaseQueryMechanism.java:1043)
    at org.eclipse.persistence.queries.UpdateObjectQuery.executeCommitWithChangeSet(UpdateObjectQuery.java:84)
    at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:314)
    at org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58)
    at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:911)
    at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:810)
    at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:108)
    at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:85)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2979)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1892)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1874)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1824)
    at org.eclipse.persistence.internal.sessions.CommitManager.commitChangedObjectsForClassWithChangeSet(CommitManager.java:273)
    at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:131)
    at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:4384)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1491)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1581)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.issueSQLbeforeCompletion(UnitOfWorkImpl.java:3256)
    at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.issueSQLbeforeCompletion(RepeatableWriteUnitOfWork.java:355)
    at org.eclipse.persistence.transaction.AbstractSynchronizationListener.beforeCompletion(AbstractSynchronizationListener.java:158)
    ... 29 more

我绝对不会更新或篡改任何主键,我将该任务留给了自动生成功能,所以我不确定为什么会收到此异常? (如果需要源代码,我很乐意提供)

在这种情况下,Eclipse JPA 选择基于序列的策略(在数据库中创建的 SEQ_GEN 序列;它对该序列调用 next value),它不是自动生成的列.

所以,在这种情况下,违反约束的异常是可以理解的——首先,在启动时,应用程序创建了 3 个用户,id 为:1、2、3,但没有触及序列,然后您尝试创建第 4 个用户, 但序列中的第一个值是 1.

您可以启用 show-sql jpa 属性 来跟踪发送到数据库的 SQL。