JPA

JPAとの比較で議論された問題は、Hibernateにも当てはまります。 ここでの比較は、より具体的にHibernateに適用されます。

SQLでのmaxRowsの尊重

Ebean ORMは、生成されたSQLで常にfirstRows / maxRowsを尊重します。 @OneToManyへのjoin fetchを含めると、HibernateはSQLでmaxRowsの実装を停止し、代わりにすべての行をクライアント(アプリケーションサーバー)に取り込み、そこで結果をフィルタリングします。

これは、データベースがmaxRows(limit offset句など)を使用してクエリを最適化する機会がないことを意味し、そのため、DBクエリの実行計画は、DBがインデックスを使用する能力を低下させ、フルテーブルスキャンの可能性を高めることで、非常に異なる可能性があります。

これは、DBからクライアントに引き戻されるデータが大幅に増えることも意味します。

maxRowsを使用した@OneToManyの結合

Hibernateはこの場合、maxRowsを尊重しません。

SQLデカルト積

Ebean ORMは、SQLデカルト積を生成することはありません。 Hibernateは、複数の@OneToManyまたは@ManyToManyアソシエーションをjoin fetchする場合、SQLデカルト積を生成します。

複数の@OneToManyのjoin fetch

HibernateはSQLデカルト積を生成します。 Ebeanのアプローチと比較します。

LazyInitialisationException

コンテキストを超えた遅延ロード

EbeanとEclipseLinkはそれを行いますが、HibernateはLazyInitialisationExceptionをスローします。

Hibernateは、セッションスコープの終了を超えた遅延ロードを許可せず、代わりにLazyInitialisationExceptionをスローします。

Ebeanは、最初のスコープを超えた遅延ロードを許可します。

  • 読み取りコミットのトランザクション分離レベルは、JPA仕様に従って使用されます。 これは、Hibernateが「Open session in view」を介して通常必要とするように、トランザクションを開いたままにするのに対して、別のJDBCトランザクションを使用して遅延ロードする場合に実質的な違いがないことを意味します。
  • EbeanのエンティティBeanは、ロードコンテキストへの参照を保持しており、これにより、後続の遅延ロードが同じPersistenceContextで実行され、バッチ遅延ロードも可能になります。 これは、遅延ロードが(あたかも即時ロードされたかのように)一貫性のあるオブジェクトグラフを生成することを意味します。
  • Ebeanで遅延ロードを防ぎたい場合は、query.setDisableLazyLoading(true)を実行できます。 これは、部分的に設定されたBeanを使用し、JSONまたはDTOなどに変換するリフレクションベースのツールに渡したい場合に役立ちます。
Ebeanでは、遅延ロードはドラマなしで機能します。

 

Open session in view

(トランザクションスコープを超えた)遅延ロードをサポートするEbeanの副産物として、Ebeanは、Hibernateで使用されることがある"Open session in view"パターンを必要としません。

Hibernateで使用される「Open session in view」は、Ebeanと比較してより長い時間トランザクションを開いたままにする効果があります。 このパターンは多くの人によってアンチパターンと見なされており、代わりにサービス層のコードがエンティティに必要なものをすべてロードしていることを確認することに重点が置かれています(これはEbeanでは書く必要のないコードです)。

 

SQL2011 @History 対 Hibernate Envers

Ebeanの@Historyは、SQL2011にマッピングされるデータベース中心のアプローチです。 Hibernate Enversはアプリケーション中心のアプローチであるため、@Historyとは異なり、バルク更新や外部更新はEnversでの監査に含まれません。

Hibernate Enversとの比較

Hibernate Enversが採用したアプローチとの比較

 

Ebean(select/fetch)対 Hibernate JPA fetchgraphヒント(EntityGraph)

Hibernateは、プロパティ/カラムレベルでJPA fetchgraphヒントを尊重しないため、SQLクエリを最適化するために使用できません。

 

PagedList / findCount

JPAもHibernateも、findCountによるPagedListの組み込みサポートはありません。つまり、実行する単一のクエリを持ち、そのクエリを適切で最適化された「合計行数を見つける」クエリに自動的に変換します。

FindCountとPagedList

EbeanのfindCountクエリの仕組みとPagedListの使用方法

 

findEach対scroll

JPAには大規模クエリをサポートするための標準的なアプローチはありませんが、Hibernateにはscrollクエリがあります。

EbeanのfindEach()は、永続化コンテキストのオブジェクトグラフスコープごとに機能し、JDBCドライバー(特にMySQLとPostgres)がすべての結果をクライアントにプルしないように、カーソル/スクロールの使用のためにJDBC fetchSizeを自動的に調整します。 MySQLの場合、findEach()クエリを別のトランザクションで実行します(クエリ結合を実行し、複雑なオブジェクトグラフを反復処理できるようにするため)。

Hibernate scrollクエリを使用する場合、以下を確認する必要があります。

  • セッションからBeanを定期的にevict()するか、StatelessSessionを使用します。
  • クエリでfetchSizeを設定します。
  • MySQLを使用している場合、FORWARD_ONLYおよびREAD_ONLYオプションを使用してステートメントを作成し、バッファフェッチサイズとしてInteger.MINを使用し、クエリのスクロール中に他のクエリを実行する場合は、同じjava.sql.Connectionを使用しないでください。

 

Set対List

Hibernateでは、SetList(バッグセマンティクス)でセマンティクスが異なります。 Hibernateの場合、これはSetを優先されるコレクションタイプとして使用することを促進する傾向があります。

Ebeanは、SetListで異なるセマンティクスを適用しません。

Setを使用する場合の問題は、hashCode()/equals()の使用を意味することであり、hashCode()/equals()の実装は、ミューテートし、常に@Id値を持たないエンティティBeanの場合には完璧ではないことです(たとえば、@Id値が生成された値を介して入力されず、したがって保存後にのみ入力されるため、@Id値をhashCode/equalsの実装で使用できません)。

Ebeanの場合、ミューテートするエンティティBeanでのhashCode()/equals()の実装に関する混乱を避けるために、SetよりもListの使用を推奨したいと思います。

 

マッピング

命名規則

EbeanのデフォルトのUnderscoreNamingConventionは、HibernateのImprovedNamingStrategyには一致しますが、JPA標準ベースの(大文字と小文字が混在したカラム名とアンダースコアを持つ)ものには一致しません。 Spring Bootは、HibernateがImprovedNamingStrategyを使用するように自動的に構成することに注意してください。

javax.validation.constraints NotNullとSize

EbeanとHibernateの両方で、マッピングに@NotNullおよび@Sizeバリデーションアノテーションを使用します。

@ManyToOne

JPAはすべての@ManyToOneをデフォルトでフェッチEAGERとして扱うようにします。 Hibernateを使用している場合、ほとんどの場合、これは望ましくありません(これらのEagerフェッチタイプはJPQLまたはfetchgraphヒントを介して遅延にできないため、通常Hibernateでは、ほとんどの@ManyToOneが明示的に... @ManyToOne(fetch = FetchType.LAZY)を介して遅延になることを期待します)。

Ebeanでは、クエリ言語を使用して「何を取得するか」を定義し、@ManyToOneでFetchType.LAZYを指定する必要はありません。

UUID型

Ebeanは、PostgresおよびH2のUUIDをネイティブ型に自動的にマッピングします。そのため、UUIDは必要なように機能します。

Hibernateは、org.hibernate.annotations.Typeを介してPostgresのネイティブ型にUUIDをマッピングすることをサポートしています-@Type(type="pg-uuid")。 ここでの欠点は、これがH2のUUID(代わりにバイナリにマッピング)では機能しないため、SQLテストスクリプトがリテラルUUID値を使用してH2に対して実行されないことです(したがって、ここでH2に対してテストする場合にいくらかの苦痛があります)。

@Idのないエンティティ

Ebeanでは、@Idなしでエンティティをマッピングできます。 これらのエンティティは、通常、SQLまたはDBビューに基づいており、レポート目的で使用されます。 Ebeanはこれを許可し、これらのエンティティBeanには@Id値がないため、永続化コンテキストをバイパスします。

Hibernateでは、おそらくDTOクエリなど、異なるアプローチを使用することになるでしょう。

@View - ビューに基づくエンティティ

Ebeanは、@Viewを介してビューに基づくエンティティを明示的にサポートしています。 これには、DB移行からのビューの作成、テストの観点、および基になるテーブルの依存関係に基づくL2キャッシュ用のDDLが考慮されます。

DDL外部キーと一意制約

Hibernateは現在、FK_edi14sijwrl3p2sf41ls3svkmのような外部キー名と、UK_ajysu81d17lesquo7uqosrtahのような一意制約名を生成します。

Ebeanには、テーブル名とカラム名を含む外部キー名と一意制約名の命名規則があります。 名前が非常に制限されている場合(DB2とOracle)、Ebeanは、適切な外部キー名と一意制約名を生成するために、「母音削除」とトリムを使用します。 私の中のDBAは、これらのHibernate制約名は「まったくひどい!」と思っています。