具有以下简化实体:
@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;
}
并使用 Spring 数据跟踪存储库
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所以回答我明白,当有一对一的可选关联时,Hibernate 需要进行 n 次数据库调用以确定订单中的可选发票是否为空。
让我感到困惑的是,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);
但是,即使第一个 sql select 子句包含两次发票数据,它仍然会调用 n 次数据库,如您所见:
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 个额外调用?
最佳答案
I understand that when having a one to one optional association, Hibernate needs to make the n database calls to determine if the optional invoice in order is null or not
是的。更准确地说,hibernate 不支持可选 ToOne 关联的惰性,因此它将始终加载关联数据。
What confuses me is that Hibernate already has the invoice in question fetched in the initial query, so why would it not use the data from invoice already fetched?
Hibernate 没有意识到它已经加载了该发票。为此,它要么必须通过 order_id 保留 Invoice 对象的映射,要么对相互的 OneToOne 关联进行特殊处理。 OneToOne 关联很少见,它没有这样的处理。
这可以通过以下任何一种方法来解决:
对于您的问题,哪些是更好的解决方案取决于对该数据进行操作的其他查询。一般来说,我更喜欢第一个选项,因为对于程序员来说,查询未映射的数据比使用神秘的技巧让 JPA 不加载隐式请求的数据更容易理解。
关于hibernate - 如何避免 1 + n 数据库调用在 Hibernate 中进行双向可选的一对一关联?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39065065/