JPA

JPAとの比較で説明されている問題は、Hibernateにも適用されます。 ここでは、Hibernateに特化した比較を行います。

SQLにおけるmaxRowsの尊重

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

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

これはまた、DBからクライアントに大幅に多くのデータがプルバックされることを意味します。

@OneToManyの結合とmaxRows

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

SQLデカルト積

Ebean ORMは、決してSQLデカルト積を生成しません。Hibernateは、複数の@OneToManyまたは@ManyToMany関連付けをjoin fetchするときに、SQLデカルト積を生成します。

複数の@OneToManyの結合フェッチ

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

LazyInitialisationException

コンテキストを超えた遅延読み込み

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

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

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

  • JPA仕様に従って、read committedのトランザクション分離レベルが使用されます。これは、別のJDBCトランザクションを使用して遅延読み込みを行う場合と、トランザクションを開いたままにする場合(通常、Hibernateでは「Open session in view」を介して必要)との間に、実質的な違いがないことを意味します。
  • EbeanのエンティティBeanは、それらのロードコンテキストへの参照を持っており、それにより後続の遅延読み込みが同じPersistenceContextで実行され、バッチ遅延読み込みも可能になります。これは、遅延読み込みが依然として一貫性のあるオブジェクトグラフを生成することを意味します(eagerに読み込まれた場合とまったく同じです)。
  • 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 fetchSizeを自動的に調整するため、JDBCドライバ(特にMySqlとPostgres)はすべての結果をクライアントにプルバックせず、MySqlではfindEach()クエリを別のトランザクションで実行します(クエリ結合を実行し、複雑なオブジェクトグラフを反復処理できるようにするため)。

Hibernateのscrollクエリを使用する場合は、次のことを確認する必要があります。

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

 

SetとListの比較

Hibernateでは、SetListのセマンティクスが異なります(bagセマンティクス)。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をfetch EAGERとして扱うようにデフォルト設定されています。Hibernateを使用する場合、これはほぼ確実に望ましくありません(それらのEager fetch型は、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マイグレーションとテストの観点からのビューの作成のためのDDLだけでなく、基になるテーブルの依存関係に基づくL2キャッシングも考慮に入れています。

DDL外部キーと一意制約

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

Ebeanには、テーブル名と列名を含む外部キー名と一意制約名のための命名規則があります。名前が非常に制限されている場合(DB2とOracle)、Ebeanは「母音除去」とトリミングを使用して、依然として適切な外部キーと一意制約名を作成します。私のDBAとしては、Hibernateの制約名は「最悪」だと思います!