注意
在阅读之前,我想要找到的是避免在Hibernate上进行N + 1查询
结束注释
我有3张桌子。客户,商家和交易。
客户可以进行交易。客户可以有很多交易,但每笔交易只能有一个商家。
这是交易表的样子:
private Long transaction_id;
/** removed other stuff for brevity **/
@ManyToOne(optional=false)
@JoinColumn(name="merchantId", referencedColumnName="merchantId")
private Merchant merchant;
@ManyToOne(optional=false)
@JoinColumn(name="customerId", referencedColumnName="customerId")
private Customer customer;
表:
`Transaction` (
`sequenceId` bigint(20) NOT NULL AUTO_INCREMENT,
`amountDue` decimal(19,2) DEFAULT NULL,
`merchantId` varchar(255) DEFAULT NULL,
`customerId` varchar(255) NOT NULL
)
`Customer` (
`customerId` bigint(20) NOT NULL AUTO_INCREMENT,
`dateCreated` datetime DEFAULT NULL,
`dateUpdated` datetime DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`customerName` varchar(255) DEFAULT NULL,
`subscriberNum` varchar(255) DEFAULT NULL
)
`Merchant` (
`sequenceId` bigint(20) NOT NULL AUTO_INCREMENT,
`merchantId` varchar(255) DEFAULT NULL,
`merchantName` varchar(255) DEFAULT NULL
)
我需要查询结果:
merchantName Trend Holomes
customerName Paul Florence
amountDue 20000.00
我可以在一个查询中执行此操作。基本上我可以创建一个子查询
SELECT
(SELECT m.merchantname
FROM merchant m WHERE m.id = t.merchantId) AS merchantname,
(SELECT c.customername
FROM customer c WHERE c.id = t.customerId) AS customername,
t.amountDue
FROM transaction t
或加入
select m.merchantname, c.customername, t.amountDue
from transaction t
inner join customer c on t.customerId = c.id
inner join merchant m on t.merchantId = m.id
我能够使用Spring Data JPA + Hibernate创建我想要的输出,但我注意到日志中有些奇怪。这就是我检索数据的方式:
transactionRepository.findAll();
执行两个选择查询。
select customer0_.customerId as sequence1_0_0_, customer0_.dateCreated as dateCrea2_0_0_, customer0_.dateUpdated as dateUpda3_0_0_, customer0_.email as email4_0_0_, customer0_.customerName as customerNam5_0_0_, customer0_.subscriberNum as subscrib9_0_0_ from Customer customer0_ where customer0_.customerId=?
和
select merchant0_.sequenceId as sequence1_2_0_, merchant0_.merchantId as merchant2_2_0_, merchant0_.merchantName as merchant3_2_0_, merchant0_.merchantOptInCode as merchant4_2_0_, merchant0_.merchantOptOutCode as merchant5_2_0_ from Merchant merchant0_ where merchant0_.merchantId=?
这是否意味着每次使用内部联接时,都会为每个引用的表执行select查询?这有点贵吗?如果需要加入更多实体怎么办?
在hibernate中是否有办法像上面的子查询或内部联接一样执行单个执行连接,以避免为每个引用的实体发送一个选择查询?
TL;博士
Hibernate是否使用外键创建select语句作为每个连接实体的参数?
更多外键意味着发送更多选择语句?
答案 0 :(得分:1)
请不要在生产代码中使用findAll()
。它仅可用于测试目的。如果您想要一个最佳查询 - 使用具有别名(在条件中)的投影或使用结果转换器连接(在HQL中)。您可以使用转换器直接在您的Transaction对象的属性中设置值。而且你需要一种使用关联的策略。请参阅this以获取参考。
<强>更新强>
您可以使用fetching strategies。因此,您可以调整关联以不使用单独的选择。并且,请检查hibernate.properties的值hibernate.max_fetch_depth
。
还有一件事 - 当没有懒惰的双向关联时,你可以有额外的子选择。但看起来不是你的情况。
答案 1 :(得分:0)
除了v.ladynev的回答,这就是我解决问题的方法:
<强>更新强>
@Repository
public interface TransactionRepository extends PagingAndSortingRepository<TransactionRepository, Long>{
@Query("from Transaction AS t inner join "
+ "fetch t.customer "
+ "inner join "
+ "fetch t.merchant "
+ "where t.optStatus = ?1")
public List<TransactionRepository> findByOptStatus(boolean optStatus);
注1
我的问题是尝试解决Hibernate上的N + 1问题,因为Lazy Loading。
注2
请注意,知道我使用Spring Data + Hibernate + Jersey + Jackson非常重要。这可能就是为什么Fetch.Eager的注释没有工作的原因,因为它在没有Spring Boot的Hibernate和Spring Data之间有很多东西。
不,我没有使用Spring启动,因为Jackson与Spring Boot + Jersey不搭配。