coo*_*xie 5 hibernate jpa spring-data-jpa
拥有以下简化实体:
@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;
}
Run Code Online (Sandbox Code Playgroud)
以及使用Spring数据的存储库
public interface InvoiceRepository extends JpaRepository<Invoice, Long> {
List<Invoice> findDistinctByInvoiceDateBetween(LocalDate from, LocalDate until);
}
Run Code Online (Sandbox Code Playgroud)
使用存储库方法获取发票时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
Run Code Online (Sandbox Code Playgroud)
从这个 SO答案我了解到,当具有一对一可选关联时,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;
}
Run Code Online (Sandbox Code Playgroud)
并且存储库中的方法如下所示:
@EntityGraph(value = Invoice.INVOICE_GRAPH, type = EntityGraph.EntityGraphType.LOAD)
List<Invoice> findDistinctByInvoiceDateBetween(LocalDate from, LocalDate until);
Run Code Online (Sandbox Code Playgroud)
但是,即使第一个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 ?)
Run Code Online (Sandbox Code Playgroud)
那么现在我想知道如何避免n次额外调用以按顺序填充发票?
我知道当有一对一的可选关联时,Hibernate 需要进行 n 次数据库调用来确定可选发票是否为空
是的。更准确地说,hibernate 不支持可选 ToOne 关联的惰性,因此它将始终加载关联数据。
让我困惑的是,Hibernate 已经在初始查询中获取了有问题的发票,那么为什么它不使用已获取的发票中的数据呢?
Hibernate 没有意识到它已经加载了该发票。为此,它要么必须按其 order_id 保留 Invoice 对象的映射,要么对相互的 OneToOne 关联进行特殊处理。OneToOne 关联很少见,因此没有这样的处理。
可以通过以下任一方法解决此问题:
对于您的问题,哪一个是更好的解决方案取决于对该数据进行操作的其他查询。一般来说,我倾向于第一个选项,因为对于程序员来说,必须查询未映射的数据比必须执行神秘的技巧来让 JPA 不加载隐式请求的数据更容易理解。
| 归档时间: |
|
| 查看次数: |
418 次 |
| 最近记录: |