How to avoid 1 + n database queries for a bi-directional optional one to one association in Hibernate?

Having the following simplified entities:

@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;
}

and after the repository using spring data

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

when extracting invoices using the 1 + n repository method, the SQL statements are executed as shown in the logs:

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

From this SO answer. I understand that if there is an optional one-to-one association, Hibernate needs to make n queries in the database to determine if the additional account is null or not. What bothers me is that Hibernate already has an invoice in the original request, so why not use the data from the invoice already?

n , @NamedEntityGraph @NamedSubgraph, .

, :

@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 , select sql , :

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 ?)

, , n ?

+4
1

, , Hibernate n , , -

. , hibernate ToOne, .

, , Hibernate , ?

Hibernate , . order_id, OneToOne. OneToOne , .

:

  • (.. - , " - -, invoice.order =?" )
  • , , .. -. , - , , , - , , .

, , . , , , , , , JPA .

+3

Source: https://habr.com/ru/post/1651940/


All Articles