Hibernate 为什么TypedQuery.getResultList()使用单独的SELECT解析每个多个关联?
考虑以下简单实体关联: (实体A)*-1(实体B) 在数据库中使用EntityA(entityB_id)中的外键创建 JPA实体正在单向映射此关系:Hibernate 为什么TypedQuery.getResultList()使用单独的SELECT解析每个多个关联?,hibernate,jpa,Hibernate,Jpa,考虑以下简单实体关联: (实体A)*-1(实体B) 在数据库中使用EntityA(entityB_id)中的外键创建 JPA实体正在单向映射此关系: @Entity EntityA { @Id @GeneratedValue private long id; @Column(nullable=false,length=250) private String name; @ManyToOne(optional=false) private
@Entity
EntityA {
@Id
@GeneratedValue
private long id;
@Column(nullable=false,length=250)
private String name;
@ManyToOne(optional=false)
private EntityB entityB;
... getter/setter ...
}
@Entity
EntityB {
@Id
@GeneratedValue
private long id;
@Column(nullable=false,length=250)
private String name;
... getter/setter ...
}
如果进行了简单查询:
EntityManager em = ...;
TypedQuery<EntityA> tq = em.createQuery("from EntityA a", EntityA.class);
tq.getResultList();
即使默认的获取策略是急切的(似乎是这样),EntityB也应该通过implizitJoin获取,不是吗?
怎么了
但它变得更奇怪了——如果只加载了一个EntityA对象:
EntityA a = em.find(EntityA.class, new Long(1));
然后Hibernate似乎理解了这项工作:
Hibernate:
select
entitya0_.id as id1_1_,
entitya0_.entityB_id as entityB3_1_1_,
entitya0_.name as name1_1_,
entityb1_.id as id12_0_,
entityb1_.name as name12_0_
from
EntityA entitya0_
inner join
EntityB entityb1_
on entitya0_.entityB_id=entityb1_.id
where
entitya0_.id=?
上面的测试是用Hibernate 3.5和JPA2.0进行的。首先,你为什么还要担心这个问题?使用ORM库的意义在于,您不必处理这样的细节。如果您看到某些操作运行缓慢,那么您就开始考虑优化查询 我认为Hibernate的策略是有道理的,因为这种关系是多方面的。这意味着查询中返回的EntityB数永远不会大于EntityA数,并且通常会小于EntityA数。因此,试图通过连接从EntityB获取数据可能意味着发送相同数据的冗余副本 即使默认的获取策略是急切的(似乎是这样),EntityB也应该通过隐式连接获取,不是吗?怎么了 实际上,
manytone
的默认FetchType
是EAGER
。但这只是说当加载多个侧时,应该加载一个侧,而不是如何加载。如何操作由持久性提供者决定(JPA不允许调整策略)
Hibernate有一个特定的Fetch
注释,允许调整fetching模式。从文件中:
JPA附带了fetch选项来
定义延迟加载和获取
然而,Hibernate模式有很多优点
此区域中设置了更多选项。罚款
调整延迟加载和获取
战略,一些额外的
已引入注释:
- [……]
@Fetch
:定义用于加载关联的抓取策略。
FetchMode
可以是SELECT
(SELECT是
当关联需要时触发
要加载),请选择子选项(仅限
可用于集合,请使用
子选择策略-请参考
Hibernate参考文档
有关详细信息)或加入(使用
SQLJOIN
加载关联,同时
加载所有者实体)<代码>加入
覆盖任何惰性属性(一个
通过联接加载的关联
战略不能懒惰)
您可能希望尝试以下操作(如果您不介意使用特定于提供程序的注释):
适用于当前用例的解决方案是在语句中包含fetch联接:
select a from entityA left join fetch a.entityB
这将获取所有相关的EntityB(并覆盖FetchType.LAZY)。谢谢Mike的回答。我不熟悉Hibernate/JPA,所以我(假设这应该以一种更有效的方式完成)想知道我是否在注释方面犯了错误。在实际项目中,EntityA有三个关联。对一个返回100个EntityAs的查询进行成像—按照所描述的行为,它将获取400个对象(包含301条SQL语句),可能不需要它。如果不需要相关实体,则应将关联标记为lazy。你知道他们怎么说假设。即使关联被标记为懒惰,也会进行查询。此外,我确实需要关联的对象。很抱歉,这是错误的-查询不是使用FetchType.LAZY进行的。但正如我提到的,我需要以一种有效的方式急切地获取相关对象。谢谢Pascal。这正是我想要的。我明天会尝试。很抱歉,Pascal,但是无论是否使用此注释,我都会得到相同的结果。顺便说一句,我能够通过查询“从实体a中选择a左连接获取a.entityB”获得所需的行为。但我想知道为什么hibernate在默认情况下不这样做(或者如果注释告诉它这样做的话)。有什么想法吗?@Mulmoth:没必要抱歉,我只是指出FetchType.EAGER+FetchMode.JOIN应该根据文档和论坛线程工作,例如。但这并不排除任何bug,我也不负责:)看起来你实际上并不是。检查Jira是否存在问题,如果需要,可能会提交一个。@Mulmoth:我可以理解,当事情没有按照广告所说的那样工作时,这是非常令人不安的,无论你是否有使用工具的经验(我必须再次检查,看看我的建议是否有错,我相信我没有错)。总而言之,你的审问是完全合法的。
@ManyToOne(optional=false)
@Fetch(FetchMode.JOIN)
private EntityB entityB;
select a from entityA left join fetch a.entityB