一般的なHibernateの例外

1. 前書き

このチュートリアルでは、Hibernateでの作業中に発生する可能性のある一般的な例外について説明します。
それらの目的といくつかの一般的な原因を確認します。 さらに、そのソリューションについても検討します。

2. Hibernate例外の概要

多くの条件により、Hibernateの使用中に例外がスローされる可能性があります。 これらには、マッピングエラー、インフラストラクチャの問題、SQLエラー、データ整合性違反、セッションの問題、トランザクションエラーなどがあります。
*これらの例外は主に_https://docs.jboss.org/hibernate/orm/6.0/javadocs/org/hibernate/HibernateException.html [HibernateException] _。*から拡張されます。ただし、JPA永続プロバイダーとしてHibernateを使用している場合、これらの例外は、https://docs.oracle.com/javaee/7/api/javax/persistence/PersistenceException.html [_PersistenceException_]にラップされる場合があります。
これらの基本クラスは両方とも_RuntimeException_から拡張されています。 したがって、それらはすべてチェックされていません。 したがって、使用されるすべての場所でそれらをキャッチまたは宣言する必要はありません。
さらに、*これらのほとんどは回復不能です。 その結果、操作を再試行しても効果はありません。*これは、それらに遭遇したときに現在のセッションを放棄する必要があることを意味します。
次に、これらのそれぞれを1つずつ調べてみましょう。

3. マッピングエラー

オブジェクトリレーショナルマッピングは、Hibernateの大きな利点です。 具体的には、SQLステートメントを手動で作成する必要がなくなります。
同時に、Javaオブジェクトとデータベーステーブル間のマッピングを指定する必要があります。 したがって、アノテーションまたはマッピングドキュメントを使用してそれらを指定します。 これらのマッピングは手動でコーディングできます。 または、ツールを使用して生成することもできます。
これらのマッピングを指定するときに、ミスをする可能性があります。 これらはマッピング仕様に含まれている可能性があります。 または、Javaオブジェクトと対応するデータベーステーブルの間に不一致が存在する可能性があります。
*このようなマッピングエラーは例外を生成します。 最初の開発中に頻繁にそれらに出くわします。 さらに、環境間で変更を移行しているときにそれらに遭遇する可能性があります。*
これらのエラーをいくつかの例で見てみましょう。

3.1. MappingException

*オブジェクトリレーショナルマッピングの問題により、a__MappingException_がスローされます*:
public void whenQueryExecutedWithUnmappedEntity_thenMappingException() {
    thrown.expectCause(isA(MappingException.class));
    thrown.expectMessage("Unknown entity: java.lang.String");

    Session session = sessionFactory.getCurrentSession();
    NativeQuery<String> query = session
      .createNativeQuery("select name from PRODUCT", String.class);
    query.getResultList();
}
上記のコードでは、_createNativeQuery_メソッドは、クエリ結果を指定されたJava型_String._にマップしようとします。__Strings_クラスの暗黙的なマッピングを使用します。 persistence / metamodel / Metamodel.html [Metamodel] _マッピングを行います。
ただし、_String_クラスにはマッピングが指定されていません。 したがって、Hibernateは_name_列を_String_にマップする方法を知らず、例外をスローします。
考えられる原因と解決策の詳細な分析については、https://www.baeldung.com/hibernate-mappingexception-unknown-entity [Hibernate Mapping Exception – Unknown Entity]をご覧ください。
同様に、他のエラーもこの例外を引き起こす可能性があります。
  • フィールドとメソッドに注釈を混在させる

  • _ @ ManyToMany_関連付けにfor_ @ JoinTable_を指定しない

  • マッピングされたクラスのデフォルトコンストラクターは、例外をスローします
    マッピング処理

    さらに、* _MappingException_には、特定のマッピングの問題を示すことができるサブクラスがいくつかあります:*
  • AnnotationException – _アノテーションの問題

  • _DuplicateMappingException –_クラス、テーブル、または重複マッピング
    プロパティ名

  • InvalidMappingException – mappingが無効です

  • MappingNotFoundException – mappingリソースが見つかりませんでした

  • PropertyNotFoundException – an予想されるgetterまたはsetterメソッド
    クラスで見つかりませんでした

    したがって、*この例外に遭遇した場合、まずマッピングを検証する必要があります*。

3.2. _ AnnotationException _

_AnnotationExceptionを理解するために、フィールドまたはプロパティに識別子注釈のないエンティティを作成しましょう。
@Entity
public class EntityWithNoId {
    private int id;
    public int getId() {
        return id;
    }

    // standard setter
}
  • Hibernateはすべてのエンティティがidentifier*を持っていることを想定しているため、エンティティを使用すると_AnnotationException_が発生します。

public void givenEntityWithoutId_whenSessionFactoryCreated_thenAnnotationException() {
    thrown.expect(AnnotationException.class);
    thrown.expectMessage("No identifier specified for entity");

    Configuration cfg = getConfiguration();
    cfg.addAnnotatedClass(EntityWithNoId.class);
    cfg.buildSessionFactory();
}
さらに、他の考えられる原因は次のとおりです。
  • _ @ GeneratedValue_アノテーションで使用される不明なシーケンスジェネレーター

  • Java 8 Date _ / _ Time_クラスで使用される @ Temporal_注釈

  • _ @ ManyToOne_または_ @ OneToMany_のターゲットエンティティが存在しないか存在しない

  • Rawコレクションクラス
    関係注釈_ @ OneToMany_または_ @ ManyToMany_で使用されます

  • コレクションアノテーション_ @ OneToMany_で使用される具象クラス、
    _ @ ManyToMany_または_ @ ElementCollection_は、Hibernateがコレクションインターフェイスを想定しているため

    この例外を解決するには、まずエラーメッセージに記載されている特定の注釈を確認する必要があります。

4. スキーマ管理エラー

Hibernateのもう1つの利点は、自動データベーススキーマ管理です。 たとえば、データベースオブジェクトを作成または検証するDDLステートメントを生成できます。
この機能を使用するには、__ hibernate.hbm2ddl.auto __propertyを適切に設定する必要があります。
スキーマ管理の実行中に問題が発生した場合、例外が発生します。 これらのエラーを調べてみましょう。

4.1. _ SchemaManagementException _

スキーマ管理の実行におけるインフラストラクチャ関連の問題により、_SchemaManagementException_が発生します。
実証するために、データベーススキーマを検証するようにHibernateに指示しましょう。
public void givenMissingTable_whenSchemaValidated_thenSchemaManagementException() {
    thrown.expect(SchemaManagementException.class);
    thrown.expectMessage("Schema-validation: missing table");

    Configuration cfg = getConfiguration();
    cfg.setProperty(AvailableSettings.HBM2DDL_AUTO, "validate");
    cfg.addAnnotatedClass(Product.class);
    cfg.buildSessionFactory();
}
_Product_に対応するテーブルがデータベースに存在しないため、S__essionFactory__の構築中にスキーマ検証例外が発生します。
さらに、この例外には他にも考えられるシナリオがあります。
  • データベースに接続してスキーマ管理タスクを実行できません

  • スキーマがデータベースに存在しません

4.2. _ CommandAcceptanceException _

特定のスキーマ管理コマンドに対応するDDLの実行に問題があると、_CommandAcceptanceException_が発生する可能性があります。
例として、_SessionFactory_のセットアップ中に間違った方言を指定してみましょう。
public void whenWrongDialectSpecified_thenCommandAcceptanceException() {
    thrown.expect(SchemaManagementException.class);

    thrown.expectCause(isA(CommandAcceptanceException.class));
    thrown.expectMessage("Halting on error : Error executing DDL");

    Configuration cfg = getConfiguration();
    cfg.setProperty(AvailableSettings.DIALECT,
      "org.hibernate.dialect.MySQLDialect");
    cfg.setProperty(AvailableSettings.HBM2DDL_AUTO, "update");
    cfg.setProperty(AvailableSettings.HBM2DDL_HALT_ON_ERROR,"true");
    cfg.getProperties()
      .put(AvailableSettings.HBM2DDL_HALT_ON_ERROR, true);

    cfg.addAnnotatedClass(Product.class);
    cfg.buildSessionFactory();
}
ここでは、間違った方言を指定しました:SQL__MySQLDialect。 __また、Hibernateにスキーマオブジェクトを更新するよう指示しています。 その結果、HibernateがH2データベースを更新するために実行するDDLステートメントは失敗し、例外が発生します。
*デフォルトでは、Hibernateはこの例外をサイレントに記録して先に進みます。*後で__SessionFactoryを使用すると、__weが例外を取得します。
このエラーで例外がスローされるように、プロパティTo_HBM2DDL_HALT_ON_ERROR_を_true_に設定しました。
同様に、これらはこのエラーのための他のいくつかの一般的な原因です:
  • マッピングとデータベース間で列名に不一致があります

  • 2つのクラスが同じテーブルにマッピングされます

  • クラスまたはテーブルに使用される名前は、データベース内の予約語です。
    _USER_など

  • データベースへの接続に使用するユーザーに必要なものがありません
    特権

5. SQL実行エラー

  • Hibernateを使用してデータを挿入、更新、削除、またはクエリすると、JDBC *を使用してデータベースに対してDMLステートメントが実行されます。 操作の結果エラーまたは警告が発生した場合、このAPIは_SQLException_を発生させます。

    Hibernateはこの例外を_JDBCException_またはその適切なサブクラスの1つに変換します。
  • ConstraintViolationException

  • DataException

  • _ JDBCConnectionException _

  • _ LockAcquisitionException _

  • _ PessimisticLockException _

  • _ QueryTimeoutException _

  • SQLGrammarException

  • _ GenericJDBCException _

    一般的なエラーについて説明しましょう。

5.1. JDBCException

_JDBCException_は常に特定のSQLステートメントによって発生します。 _getSQL_メソッドを呼び出して、問題のSQLステートメントを取得できます。
さらに、_getSQLException_メソッドを使用して、基になる_SQLException_を取得できます。

5.2. SQLGrammarException

_SQLGrammarException_は、データベースに送信されたSQLが無効だったことを示します。 構文エラーまたは無効なオブジェクト参照が原因である可能性があります。
たとえば、*データのクエリ中に*テーブルが欠落していると、このエラーが発生する可能性があります*:
public void givenMissingTable_whenQueryExecuted_thenSQLGrammarException() {
    thrown.expect(isA(PersistenceException.class));
    thrown.expectCause(isA(SQLGrammarException.class));
    thrown.expectMessage("SQLGrammarException: could not prepare statement");

    Session session = sessionFactory.getCurrentSession();
    NativeQuery<Product> query = session.createNativeQuery(
      "select * from NON_EXISTING_TABLE", Product.class);
    query.getResultList();
}
また、テーブルが欠落している場合、データの保存中にこのエラーが発生する可能性があります。
public void givenMissingTable_whenEntitySaved_thenSQLGrammarException() {
    thrown.expect(isA(PersistenceException.class));
    thrown.expectCause(isA(SQLGrammarException.class));
    thrown
      .expectMessage("SQLGrammarException: could not prepare statement");

    Configuration cfg = getConfiguration();
    cfg.addAnnotatedClass(Product.class);

    SessionFactory sessionFactory = cfg.buildSessionFactory();
    Session session = null;
    Transaction transaction = null;
    try {
        session = sessionFactory.openSession();
        transaction = session.beginTransaction();
        Product product = new Product();
        product.setId(1);
        product.setName("Product 1");
        session.save(product);
        transaction.commit();
    } catch (Exception e) {
        rollbackTransactionQuietly(transaction);
        throw (e);
    } finally {
        closeSessionQuietly(session);
        closeSessionFactoryQuietly(sessionFactory);
    }
}
他の考えられる原因は次のとおりです。
  • 使用される命名戦略は、クラスを正しいテーブルにマップしません

  • _ @ JoinColumn_で指定された列は存在しません

5.3. ConstraintViolationException

_ConstraintViolationException_は、要求されたDML操作が整合性制約の違反を引き起こしたことを示します。 _getConstraintName_メソッドを呼び出すことにより、この制約の名前を取得できます。
*この例外の一般的な原因は、重複レコードを保存しようとしていることです*
public void whenDuplicateIdSaved_thenConstraintViolationException() {
    thrown.expect(isA(PersistenceException.class));
    thrown.expectCause(isA(ConstraintViolationException.class));
    thrown.expectMessage(
      "ConstraintViolationException: could not execute statement");

    Session session = null;
    Transaction transaction = null;

    for (int i = 1; i <= 2; i++) {
        try {
            session = sessionFactory.openSession();
            transaction = session.beginTransaction();
            Product product = new Product();
            product.setId(1);
            product.setName("Product " + i);
            session.save(product);
            transaction.commit();
        } catch (Exception e) {
            rollbackTransactionQuietly(transaction);
            throw (e);
        } finally {
            closeSessionQuietly(session);
        }
    }
}
また、_null_値をデータベースの_NOT NULL_列に保存すると、このエラーが発生する可能性があります。
このエラーを解決するには、*ビジネスレイヤーですべての検証を実行する必要があります*。 さらに、データベースの制約を使用してアプリケーションの検証を行うことはできません。

5.4. DataException

_DataException_は、SQLステートメントの評価の結果、何らかの不正な操作、型の不一致、またはカーディナリティの誤りが生じたことを示します。
たとえば、数値列に対して文字データを使用すると、次のエラーが発生する可能性があります。
public void givenQueryWithDataTypeMismatch_WhenQueryExecuted_thenDataException() {
    thrown.expectCause(isA(DataException.class));
    thrown.expectMessage(
      "org.hibernate.exception.DataException: could not prepare statement");

    Session session = sessionFactory.getCurrentSession();
    NativeQuery<Product> query = session.createNativeQuery(
      "select * from PRODUCT where id='wrongTypeId'", Product.class);
    query.getResultList();
}
このエラーを修正するには、*アプリケーションコードとデータベース間でデータ型と長さが一致することを確認する必要があります*。

5.5. _ JDBCConnectionException _

_JDBCConectionException_は、データベースとの通信の問題を示します。
たとえば、データベースまたはネットワークがダウンすると、この例外がスローされる可能性があります。
また、データベースのセットアップが正しくないと、この例外が発生する可能性があります。 そのようなケースの1つは、長時間アイドル状態だったためにサーバーによって閉じられたデータベース接続です。 これは、https://www.baeldung.com/java-connection-pooling [connection pooling]を使用しており、プールのアイドルタイムアウト設定がデータベースの接続タイムアウト値よりも大きい場合に発生する可能性があります。
この問題を解決するには、まずデータベースホストが存在し、稼働していることを確認する必要があります。 次に、データベース接続に正しい認証が使用されていることを確認する必要があります。 最後に、タイムアウト値が接続プールに正しく設定されていることを確認する必要があります。

5.6. _ QueryTimeoutException _

データベースクエリがタイムアウトすると、この例外が発生します。 テーブルスペースがいっぱいになるなど、他のエラーが原因で表示されることもあります。
これは数少ない回復可能なエラーの1つです。つまり、同じトランザクションでステートメントを再試行できます。
この問題を修正するために、複数の方法で*実行時間の長いクエリのクエリタイムアウトを増やすことができます*:

6. セッション状態関連のエラー

Hibernateセッションの使用エラーによるエラーを調べてみましょう。

6.1. _ NonUniqueObjectException _

  • Hibernateは、単一セッションで同じ識別子を持つ2つのオブジェクトを許可しません。*

    1つのセッションで同じJavaクラスの2つのインスタンスを同じ識別子に関連付けようとすると、_NonUniqueObjectException_が発生します。 entity_getEntityName()_および_getIdentifier()_メソッドを呼び出すことにより、エンティティの名前と識別子を取得できます。
    このエラーを再現するには、セッションで同じIDを持つ_Product_の2つのインスタンスを保存してみましょう。
public void
givenSessionContainingAnId_whenIdAssociatedAgain_thenNonUniqueObjectException() {
    thrown.expect(isA(NonUniqueObjectException.class));
    thrown.expectMessage(
      "A different object with the same identifier value was already associated with the session");

    Session session = null;
    Transaction transaction = null;

    try {
        session = sessionFactory.openSession();
        transaction = session.beginTransaction();

        Product product = new Product();
        product.setId(1);
        product.setName("Product 1");
        session.save(product);

        product = new Product();
        product.setId(1);
        product.setName("Product 2");
        session.save(product);

        transaction.commit();
    } catch (Exception e) {
        rollbackTransactionQuietly(transaction);
        throw (e);
    } finally {
        closeSessionQuietly(session);
    }
}
__NonUniqueObjectException、__が期待どおりに発生します。
*この例外は、_update_メソッドを呼び出して、セッションにデタッチされたオブジェクトを再アタッチするときに頻繁に発生します。*セッションに同じ識別子の別のインスタンスがロードされている場合、このエラーが発生します。 これを修正するには、* _ merge_メソッド*を使用して、デタッチされたオブジェクトを再接続します。

6.2. _ StaleStateException _

バージョン番号またはタイムスタンプのチェックが失敗すると、Hibernateは__StaleStateException__sをスローします。 セッションに古いデータが含まれていたことを示します。
これは時々this_OptimisticLockException_にラップされます。
*このエラーは通常、バージョニングで長時間実行されるトランザクションを使用しているときに発生します。*
また、対応するデータベース行が存在しない場合、エンティティを更新または削除しようとしたときにも発生する可能性があります。
public void whenUpdatingNonExistingObject_thenStaleStateException() {
    thrown.expect(isA(OptimisticLockException.class));
    thrown.expectMessage(
      "Batch update returned unexpected row count from update");
    thrown.expectCause(isA(StaleStateException.class));

    Session session = null;
    Transaction transaction = null;

    try {
        session = sessionFactory.openSession();
        transaction = session.beginTransaction();

        Product product = new Product();
        product.setId(15);
        product.setName("Product1");
        session.update(product);
        transaction.commit();
    } catch (Exception e) {
        rollbackTransactionQuietly(transaction);
        throw (e);
    } finally {
        closeSessionQuietly(session);
    }
}
他のいくつかの可能なシナリオは次のとおりです。
  • エンティティに適切な未保存値戦略を指定しませんでした

  • 2人のユーザーがほぼ同時に同じ行を削除しようとした

  • 自動生成されたIDまたはバージョンフィールドに値を手動で設定します

7. 遅延初期化エラー

通常、アプリケーションのパフォーマンスを向上させるために、アソシエーションが遅延的にロードされるように構成します。 関連付けは、最初に使用されたときにのみ取得されます。
ただし、Hibernateはデータを取得するためにアクティブなセッションを必要とします。 初期化されていない関連付けにアクセスしようとしたときにセッションが既に閉じられている場合、例外が発生します。
この例外とそれを修正するさまざまな方法を見てみましょう。

7.1. LazyInitializationException

  • _LazyInitializationException_は、アクティブなセッション外で初期化されていないデータをロードしようとしたことを示します。*このエラーは多くのシナリオで発生する可能性があります。

    まず、プレゼンテーション層で遅延関係にアクセスしているときにこの例外を取得できます。 その理由は、エンティティがビジネスレイヤーに部分的に読み込まれ、セッションが閉じられたためです。
    第二に、_getOne_メソッドを使用する場合、https://www.baeldung.com/the-persistence-layer-with-spring-data-jpa [Spring Data]でこのエラーを取得できます。 このメソッドは、インスタンスを遅延フェッチします。
    この例外を解決する方法はたくさんあります。
    まず、すべての関係を積極的にロードすることができます。 ただし、使用されないデータをロードするため、これはアプリケーションのパフォーマンスに影響します。
    次に、ビューがレンダリングされるまでセッションを開いたままにすることができます。 これは「https://vladmihalcea.com/the-open-session-in-view-anti-pattern/[*Open Session in View *]」として知られており、アンチパターンです。 これにはいくつかの欠点があるため、これを避ける必要があります。
    第三に、関係を取得するために別のセッションを開いてエンティティを再接続できます。 そのためには、セッションで_merge_メソッドを使用します。
    最後に、ビジネスレイヤーで必要な関連付けを初期化できます。 これについては、次のセクションで説明します。

7.2. ビジネスレイヤーでの関連する遅延関係の初期化

遅延関係を初期化するには多くの方法があります。
1つのオプションは、エンティティの対応するメソッドを呼び出すことによってそれらを初期化することです。 この場合、Hibernateはパフォーマンスを低下させる複数のデータベースクエリを発行します。 これを「N + 1 SELECT」問題と呼びます。
次に、_Fetch Join_を使用して、単一のクエリでデータを取得できます。 ただし、これを実現するにはカスタムコードを記述する必要があります。
最後に、* https://www.baeldung.com/jpa-entity-graph [entity graphs]を使用して、取得するすべての属性を定義できます*。 注釈_ @ NamedEntityGraph、@ NamedAttributeNode_、および_ @ NamedEntitySubgraph_を使用して、エンティティグラフを宣言的に定義できます。 JPA APIを使用してプログラムで定義することもできます。 次に、*フェッチ操作で指定することにより、1回の呼び出しでグラフ全体を取得します*。

8. トランザクションの問題

http://docs.jboss.org/hibernate/orm/6.0/userguide/html_single/Hibernate_User_Guide.html#transactions [トランザクション]は、作業単位と同時アクティビティ間の分離を定義します。 2つの異なる方法でそれらを区別できます。 まず、注釈を使用して宣言的に定義できます。 次に、http://docs.jboss.org/hibernate/orm/6.0/userguide/html_single/Hibernate_User_Guide.html#transactions-api [Hibernate Transaction API]を使用してプログラムで管理できます。
さらに、Hibernateはトランザクション管理をトランザクションマネージャーに委任します。 *何らかの理由でトランザクションを開始、コミット、またはロールバックできなかった場合、Hibernateは例外をスローします。*
*通常、トランザクションマネージャに応じて、__TransactionException __または_IllegalArgumentException_が発生します。*
例として、ロールバック用にマークされたトランザクションをコミットしてみましょう。
public void
givenTxnMarkedRollbackOnly_whenCommitted_thenTransactionException() {
    thrown.expect(isA(TransactionException.class));
    thrown.expectMessage(
        "Transaction was marked for rollback only; cannot commit");

    Session session = null;
    Transaction transaction = null;
    try {
        session = sessionFactory.openSession();
        transaction = session.beginTransaction();

        Product product = new Product();
        product.setId(15);
        product.setName("Product1");
        session.save(product);
        transaction.setRollbackOnly();

        transaction.commit();
    } catch (Exception e) {
        rollbackTransactionQuietly(transaction);
        throw (e);
    } finally {
        closeSessionQuietly(session);
    }
}
同様に、他のエラーも例外を引き起こす可能性があります。
  • 宣言型トランザクションとプログラムトランザクションの混合

  • 別のトランザクションが既にアクティブなときにトランザクションを開始しようとしています
    セッション中

  • トランザクションを開始せずにコミットまたはロールバックしようとしています

  • トランザクションを複数回コミットまたはロールバックしようとしています

9. 並行性の問題

Hibernateは、2つのhttp://docs.jboss.org/hibernate/orm/6.0/userguide/html_single/Hibernate_User_Guide.html#locking[locking]戦略をサポートして、同時トランザクションによるデータベースの不整合を防ぎます– link: / jpa-optimistic-locking [optimistic]およびlink:/jpa-pessimistic-locking[pessimistic]。 ロックの競合が発生した場合、どちらも例外を発生させます。
高い同時実行性と高いスケーラビリティをサポートするために、通常、バージョンチェックを使用したオプティミスティック同時実行制御を使用します。 これは、バージョン番号またはタイムスタンプを使用して、競合する更新を検出します。
  • _OptimisticLockingException_は、楽観的なロックの競合を示すためにスローされます*。 たとえば、最初の操作後に更新せずに同じエンティティの2つの更新または削除を実行すると、このエラーが発生します。

public void whenDeletingADeletedObject_thenOptimisticLockException() {
    thrown.expect(isA(OptimisticLockException.class));
    thrown.expectMessage(
        "Batch update returned unexpected row count from update");
    thrown.expectCause(isA(StaleStateException.class));

    Session session = null;
    Transaction transaction = null;

    try {
        session = sessionFactory.openSession();
        transaction = session.beginTransaction();

        Product product = new Product();
        product.setId(12);
        product.setName("Product 12");
        session.save(product1);
        transaction.commit();
        session.close();

        session = sessionFactory.openSession();
        transaction = session.beginTransaction();
        product = session.get(Product.class, 12);
        session.createNativeQuery("delete from Product where id=12")
          .executeUpdate();
        // We need to refresh to fix the error.
        // session.refresh(product);
        session.delete(product);
        transaction.commit();
    } catch (Exception e) {
        rollbackTransactionQuietly(transaction);
        throw (e);
    } finally {
        closeSessionQuietly(session);
    }
}
同様に、2人のユーザーがほぼ同時に同じエンティティを更新しようとした場合にも、このエラーが発生する可能性があります。 この場合、最初のエラーが成功し、2番目のエラーがこのエラーを発生させます。
したがって、*悲観的ロックを導入しない限り、このエラーを完全に回避することはできません*。 ただし、次の操作を行うことで、発生の確率を最小限に抑えることができます。
  • 更新操作をできるだけ短くする

  • クライアントのエンティティ表現を可能な限り頻繁に更新する

  • エンティティまたはそれを表す値オブジェクトをキャッシュしないでください

  • 更新後、クライアントのエンティティ表現を常に更新する

10. 結論

この記事では、Hibernateの使用中に発生する一般的な例外について調べました。 さらに、考えられる原因と解決策を調査しました。
いつものように、完全なソースコードはhttps://github.com/eugenp/tutorials/tree/master/persistence-modules/hibernate5[GitHubで]にあります。