JPA渴望获取不join
JPA的抓取策略究竟控制着什么? 我无法检测到渴望和懒惰之间的任何区别。 在这两种情况下,JPA / Hibernate不会自动join多对一的关系。
例如:人有一个地址。 一个地址可以属于很多人。 JPA注释的实体类看起来像:
@Entity public class Person { @Id public Integer id; public String name; @ManyToOne(fetch=FetchType.LAZY or EAGER) public Address address; } @Entity public class Address { @Id public Integer id; public String name; }
如果我使用JPA查询:
select p from Person p where ...
JPA / Hibernate生成一个SQL查询以从Person表中进行select,然后为每个人生成一个明确的地址查询:
select ... from Person where ... select ... from Address where id=1 select ... from Address where id=2 select ... from Address where id=3
这对于大型结果集是非常糟糕的。 如果有1000个人,则生成1001个查询(1个来自Person,1000个不同于Address)。 我知道这是因为我正在查看MySQL的查询日志。 我的理解是将地址的提取types设置为eager会导致JPA / Hibernate自动使用连接进行查询。 但是,无论提取types如何,它仍然会为关系生成不同的查询。
只有当我明确地告诉它join它实际上join:
select p, a from Person p left join p.address a where ...
我在这里错过了什么? 我现在必须手动编码每个查询,以便它离开join多对一的关系。 我使用Hibernate的JPA实现与MySQL。
编辑:它出现(请参阅Hibernate FAQ 这里和这里 ), FetchType
不影响JPA查询。 所以在我的情况下,我明确告诉它join。
JPA没有提供任何关于将注释映射到select获取策略的规范。 一般来说,相关实体可以通过以下任何一种方式获取
- SELECT =>查询一个根实体+查询一个相关映射实体/每个根实体的集合=(n + 1)个查询
- SUBSELECT =>一个查询根实体+第二个查询的相关映射实体/第一个查询检索的所有根实体的集合= 2查询
- JOIN =>一个查询来获取根实体及其所有映射的实体/集合= 1查询
所以SELECT
和JOIN
是两个极端, SUBSELECT
落在两者之间。 可以根据她/他的领域模型来select合适的策略。
默认情况下, SELECT
由JPA / EclipseLink和Hibernate使用。 这可以通过使用:
@Fetch(FetchMode.JOIN) @Fetch(FetchMode.SUBSELECT)
在hibernate。 它还允许使用@Fetch(FetchMode.SELECT)
显式设置SELECT
模式,可以使用批量resize,如@BatchSize(size=10)
。
EclipseLink中相应的注释是:
@JoinFetch @BatchFetch
“mxc”是正确的。 fetchType
只是指定关系何时应该被parsing。
要通过使用外连接优化加载加载,您必须添加
@Fetch(FetchMode.JOIN)
到你的领域。 这是一个hibernate特定的注释。
fetchType属性控制在获取主实体时是否立即提取带注释的字段。 它并不一定规定如何构buildfetch语句,实际的sql实现取决于您使用的toplink / hibernate等的提供者。
如果设置了fetchType=EAGER
这意味着注释字段与实体中的其他字段同时填充其值。 因此,如果你打开一个entitymanager检索你的person对象,然后closuresentitymanager,那么随后执行一个person.address将不会导致延迟加载exception被抛出。
如果你设置了fetchType=LAZY
那么这个字段只有在被访问时才被填充。 如果您closures了entitymanager,那么如果您执行person.address,则会抛出一个懒加载exception。 要加载字段,需要将实体放回到em.merge()的entitymangers上下文中,然后执行字段访问,然后closuresentitymanager。
在为客户订单构build集合的客户类时,您可能需要延迟加载。 如果您想要获取客户名单时检索了客户的每个订单,那么当您只查找客户名称和联系人详细信息时,这可能是一个昂贵的数据库操作。 最好离开数据库访问,直到以后。
对于问题的第二部分 – 如何让hibernate生成优化的SQL?
Hibernate应该允许你提供关于如何构造最有效的查询的提示,但是我怀疑你的表结构有什么问题。 关系是否build立在表格中? Hibernate可能已经决定,一个简单的查询将比一个连接更快,特别是如果索引等缺失。
试试:
select p from Person p left join FETCH p.address a where...
它对JPA2 / EclipseLink类似,但似乎JPA1中也有这个特性:
如果您使用EclipseLink而不是Hibernate,则可以通过“查询提示”优化查询。 请参阅Eclipse Wiki中的这篇文章: EclipseLink / Examples / JPA / QueryOptimization 。
有一个关于“join阅读”的章节。
join你可以做多件事情(使用eclipselink)
-
在jpql中你可以做左连接获取
-
在命名查询中,您可以指定查询提示
-
在TypedQuery中,你可以这样说
query.setHint("eclipselink.join-fetch", "e.projects.milestones");
-
还有批量提取提示
query.setHint("eclipselink.batch", "e.address");
看到
http://java-persistence-performance.blogspot.com/2010/08/batch-fetching-optimizing-object-graph.html
我有这个问题,除了Person类有一个embedded式的关键类。 我自己的解决scheme是join他们的查询和删除
@Fetch(FetchMode.JOIN)
我的embedded式ID类:
@Embeddable public class MessageRecipientId implements Serializable { @ManyToOne(targetEntity = Message.class, fetch = FetchType.LAZY) @JoinColumn(name="messageId") private Message message; private String governmentId; public MessageRecipientId() { } public Message getMessage() { return message; } public void setMessage(Message message) { this.message = message; } public String getGovernmentId() { return governmentId; } public void setGovernmentId(String governmentId) { this.governmentId = governmentId; } public MessageRecipientId(Message message, GovernmentId governmentId) { this.message = message; this.governmentId = governmentId.getValue(); } }
有两件事情发生在我身上。
首先,你确定你的意思是ManyToOne的地址吗? 这意味着多个人将有相同的地址。 如果编辑了其中的一个,则会对它们进行编辑。 这是你的意图吗? 99%的时间地址是“私人”的(从属于只有一个人)。
其次,在Person实体上还有其他渴望的关系吗? 如果我没有记错,Hibernate只能处理一个实体上的一个渴望的关系,但这可能是过时的信息。
我这样说是因为你从我所坐的位置上理解这应该如何工作。