私の Java EE アプリケーションの一部はユーザー管理です。私の開発環境は、Windows 10 上の Netbeans 12.0、JDK 14、Glassfish サーバー 5.1、Apache Derby DB、および Eclipse 永続性 JPA です。現在、開発段階で、サーバーを再起動するたびにテーブルを削除して作成するように JPA を構成しました。
<?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 ファイルからのテスト データをすべてのテーブルに入力しました。「ユーザー」エンティティ Bean の UserList.xml のサンプルを次に示します。
<?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 つのデータは既にテーブルにロードされており、自動キー ジェネレーターは既に 4 にインクリメントされています。 (JPA が index=1 からキーの生成を開始すると仮定します)? したがって、JSF フロントエンド (XML ファイルからテーブルに初期データを追加するために JAXB が行ったのと同じ永続化コードを共有する) を介して追加する最初のユーザーには、id=5 を割り当てる必要がありますか?
最初は、XML ファイルの Id フィールド (つまり 1) がこのエラーに関係しているのかもしれないと思っていましたが、やはり自動キー生成スキームでは、XML の Id は JPA の自動生成された値で上書きされるべきでしょうか?
このフォーラムや他の場所でエラーに関する多くの投稿を既に読みましたが、エラー以外の私の状況との共通点は見つかりませんでした。このエラーの根本的な原因と、アプリケーションで主キーを生成せずに修正する方法についての明快な説明に感謝します。
更新 1: テーブル内の残りのデータ
徹底的な調査の結果、アプリケーションの起動時に User テーブルに常にデータが残っていることがわかりました。これは、persistence.xml ファイルで選択したドロップ アンド クリエイト ポリシーに違反しています (おそらく JPA の問題でしょうか?)。この場合、コンテナがシングルトン クラスの @postConstruct アノテーション付きメソッド loadData() を呼び出して XML ファイルから初期データをロードすると、自動キー生成が 1 から開始され、同じ ID を持つ残りの行の 1 つと競合が発生します。サーバーの起動時にテーブルからすべての行を削除するようにコードを修正したところ、問題はなくなりましたが、ログ ファイルに次の例外が記録されていますが、これはアプリケーションの問題や中断の原因にはなりません。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
主キーを更新したり改ざんしたりしていないことは間違いなく、そのタスクを自動生成機能に任せたので、なぜこの例外が発生するのかわかりませんか? (ソース コードが必要な場合は、喜んで提供します)。