Java JPA急切获取未加入
JPA的fetch策略究竟控制着什么?我看不出渴望和懒惰有什么区别。在这两种情况下,JPA/Hibernate不会自动加入多对一关系 示例:此人只有一个地址。一个地址可以属于许多人。JPA注释实体类如下所示:Java JPA急切获取未加入,java,hibernate,jpa,join,Java,Hibernate,Jpa,Join,JPA的fetch策略究竟控制着什么?我看不出渴望和懒惰有什么区别。在这两种情况下,JPA/Hibernate不会自动加入多对一关系 示例:此人只有一个地址。一个地址可以属于许多人。JPA注释实体类如下所示: @Entity public class Person { @Id public Integer id; public String name; @ManyToOne(fetch=FetchType.LAZY or EAGER) public A
@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 ... 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的查询日志。我的理解是,将address的fetch类型设置为eager将导致JPA/Hibernate使用连接自动查询。但是,不管获取类型如何,它仍然会为关系生成不同的查询
只有当我明确告诉它加入时,它才会真正加入:
select p, a from Person p left join p.address a where ...
我是不是遗漏了什么?现在,我必须手工编写每个查询的代码,以便它能够连接多对一关系。我在MySQL中使用Hibernate的JPA实现
Edit:看来(参见Hibernate常见问题解答和)FetchType不会影响JPA查询。所以在我的例子中,我明确地告诉它加入。我想到了两件事 首先,你确定你的地址是manytone吗?这意味着多个人将拥有相同的地址。如果为其中一个进行了编辑,则将为所有这些对象进行编辑。这是你的意图吗?99%的时间地址是“私人”的(即它们只属于一个人) 第二,你对个人实体还有其他渴望的关系吗?如果我没记错的话,Hibernate只能处理实体上的一个急切关系,但这可能是过时的信息
我之所以这么说,是因为从我所处的位置来看,您对这应该如何工作的理解基本上是正确的。fetchType属性控制在获取主实体时是否立即获取带注释的字段。它不一定规定fetch语句的构造方式,实际的sql实现取决于您使用的toplink/hibernate等提供程序 如果设置
fetchType=EAGER
,这意味着注释字段与实体中的其他字段同时填充其值。因此,如果打开entitymanager检索person对象,然后关闭entitymanager,则随后执行person.address不会导致引发延迟加载异常
如果设置fetchType=LAZY
,则该字段仅在访问时填充。如果您已在之前关闭entitymanager,则如果执行person.address,将引发延迟加载异常。要加载字段,需要使用em.merge()将实体放回EntityRangers上下文中,然后进行字段访问,然后关闭entitymanager
在构造包含客户订单集合的客户类时,可能需要延迟加载。如果您在想要获取客户列表时检索了客户的每个订单,那么当您只查找客户名称和联系方式时,这可能是一项昂贵的数据库操作。最好将数据库访问权留到以后
对于问题的第二部分-如何让hibernate生成优化的SQL
Hibernate应该允许您提供有关如何构造最有效查询的提示,但我怀疑您的表构造有问题。表中是否建立了关系?Hibernate可能认为一个简单的查询比连接更快,尤其是在缺少索引等的情况下。“mxc”是正确的fetchType
只指定了何时应解析关系
要通过使用外部联接优化即时加载,必须添加
@Fetch(FetchMode.JOIN)
到你的领域去。这是一个特定于hibernate的注释。如果使用EclipseLink而不是hibernate,则可以通过“查询提示”优化查询。请参阅EclipseWiki中的这篇文章: 有一章是关于“联合阅读”的。尝试一下:
select p from Person p left join FETCH p.address a where...
对于我来说,它与JPA2/EclipseLink的工作原理类似,但这一特性似乎存在于:我正好遇到了这个问题,但Person类有一个嵌入的key类。 我自己的解决方案是将它们加入查询并删除
@Fetch(FetchMode.JOIN)
我的嵌入式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();
}
}
JPA没有提供任何关于将注释映射到选择获取策略的规范。通常,相关实体可以通过下面给出的任何一种方式获取
- SELECT=>一个根实体查询+一个相关映射实体/每个根实体集合查询=(n+1)查询
- SUBSELECT=>一个根实体查询+第二个相关映射实体查询/第一个查询中检索到的所有根实体集合=2个查询
- JOIN=>一个查询来获取根实体及其所有映射实体/集合=1个查询
SELECT
和JOIN
是两个极端,SUBSELECT
介于两者之间。人们可以根据自己的领域模型选择合适的策略
默认情况下,JPA/EclipseLink和Hibernate都使用SELECT
。这可以通过使用以下命令来覆盖:
@Fetch(FetchMode.JOIN)
@Fetch(FetchMode.SUBSELECT)
冬眠。它还允许使用@Fetch(FetchMode.SELECT)
显式设置选择
模式,可以使用批大小进行调整,例如@BatchSize(size=10)
EclipseLink中的相应注释为:
@JoinFetch
@BatchFetch
要加入,您可以做多种事情(使用eclipselink)
- 在jpql中,您可以执行左连接获取
- 在命名查询中,可以指定查询提示
- 在TypedQuery中,您可以说
query.setHint(“eclipselink.join-fetch”,“e.proje