2016-08-21 4 views
4

有する以下の単純化エンティティ:Hibernateでの双方向のオプションの1対1関連付けの1 + nデータベース呼び出しを避けるには?

@MappedSuperclass 
public abstract class AbstractEntity implements Serializable { 
    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    protected Long id; 
} 

@Entity 
@Table(name = "t_invoice") 
public class Invoice extends AbstractEntity { 
    @OneToOne(optional = false, fetch = FetchType.EAGER) 
    @JoinColumn(name = "order_id") 
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) 
    private Order order; 
} 

@Entity 
@Table(name = "t_order") 
public class Order extends AbstractEntity { 
    @OneToMany(mappedBy = "order", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) 
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) 
    @SortNatural 
    private SortedSet<OrderLine> orderLines = new TreeSet<>(); 

    @OneToOne(optional = true, mappedBy = "order", fetch = FetchType.EAGER) 
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) 
    private Invoice invoice; 
} 

と使用してリポジトリ以下スプリングデータ

public interface InvoiceRepository extends JpaRepository<Invoice, Long> { 
    List<Invoice> findDistinctByInvoiceDateBetween(LocalDate from, LocalDate until); 
} 

リポジトリ方法を使用して請求書をフェッチ1 + N SQL文がログに示されているように実行されます。

SELECT DISTINCT i.id, ... FROM t_invoice i WHERE i.invoice_date BETWEEN ? AND ?; 
SELECT i.id, ... FROM t_invoice i WHERE i.order_id = ?; 
SELECT i.id, ... FROM t_invoice i WHERE i.order_id = ?; 
... n 

投稿者this回答私は1対1のオプションのアソシエートHibernateはn個のデータベース呼び出しを行って、オプションの請求書がnullかどうかを判断する必要があります。 私に混乱を招くのは、Hibernateがすでに質問のインボイスを初期クエリで取得していることです。なぜ、すでにフェッチされた請求書のデータを使用しないのですか?

また、@ NamedEntityGraphと@NamedSubgraphを使用してn個の呼び出しを避け、熱心に請求書を順番に取り込もうとしました。

したがって、今請求書エンティティは次のようになります。

@Entity 
@NamedEntityGraph(
     name = Invoice.INVOICE_GRAPH, 
     attributeNodes = { 
       @NamedAttributeNode(value = "order", subgraph = "order.subgraph") 
     }, 
     subgraphs = { 
       @NamedSubgraph(name = "order.subgraph", attributeNodes = { 
         @NamedAttributeNode("invoice"), 
         @NamedAttributeNode("orderLines") 
       }), 
     } 
) 
@Table(name = "t_invoice") 
public class Invoice extends AbstractEntity { 
@OneToOne(optional = false, fetch = FetchType.EAGER) 
    @JoinColumn(name = "order_id") 
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) 
    private Order order; 
} 

とリポジトリ内のメソッドは次のようになります。

@EntityGraph(value = Invoice.INVOICE_GRAPH, type = EntityGraph.EntityGraphType.LOAD) 
List<Invoice> findDistinctByInvoiceDateBetween(LocalDate from, LocalDate until); 

しかし、それはまだ、n個のデータベースでも最初のSQL SELECT句ものの呼び出しを行います表示される2倍の請求書データが表示されます。

SELECT DISTINCT 
    invoice0_.id      AS id1_13_0_, 
    order1_.id      AS id1_14_2_, 
    orderlines4_.id     AS id1_15_4_, 
    invoice5_.id      AS id1_13_5_, 
    invoice0_.created     AS created2_13_0_, 
    order1_.created     AS created2_14_2_, 
    orderlines4_.created    AS created2_15_4_, 
    invoice5_.created     AS created2_13_5_, 
FROM t_invoice invoice0_ ... more join clausules ... 
WHERE invoice0_.order_id = order1_.id AND (invoice0_.invoice_date BETWEEN ? AND ?) 

だから私はwonderiです注文書に請求書を取り込むための追加の呼び出しを避ける方法は?

答えて

3

私は一対一の任意の関連を有する場合ために任意の請求書がnullである場合、または、Hibernateは、N個のデータベースを決定するために呼び出しを行う必要があることを理解していない

はい。もっと正確に言えば、HibernateはオプションのToOneアソシエーションのために怠惰をサポートしないので、常に関連するデータをロードします。

私が混乱しているのは、Hibernateはすでに問題のインボイスを初期クエリでフェッチしています。なぜ、すでにフェッチされた請求書のデータを使用しないのですか?

Hibernateは、すでに請求書を読み込んでいることを認識しません。そのためには、order_idでInvoiceオブジェクトのマップを保持するか、相互OneToOneの関連付けを特別に処理する必要があります。 OneToOneの関連はまれであり、そのような処理はありません。

これは、次の方法のいずれかによって解決することができます:

  • は、協会の一端をマッピングし、注文の請求書​​が必要なときに、他の方向(すなわちにナビゲートするためにクエリを使用invoice.order =? ")
  • アソシエーションの両端をマッピングしますが、オプションで、プライマリマッピングを終了します。つまり、外部キーを注文から請求書に移動します。そうすることで、オーダーの請求書はその主キーによって参照されます。この場合、休止状態は永続コンテキストで最初に検索するほどスマートになり、請求書の順序は遅れます。したがって、休止状態では冗長クエリのみが発行されますプロパティは実際にアクセスされます。

これらのうち、問題の解決策として、どちらが適しているかは、そのデータで動作する他のクエリによって異なります。一般に、JPAに暗黙的に要求されたデータをロードさせないための秘訣を行うよりも、マップされていないデータを照会するほうがプログラマにとって理解しやすいため、最初のオプションを優先します。

関連する問題