Hibernate配置FetchType.LAZY却未生成关联查询的问题咨询
问题背景
实体代码
Person 实体(拥有方)
@Entity @Table(name = "T_PERSON") @Inheritance(strategy = InheritanceType.JOINED) public class Person { // 代码省略... @OneToOne(fetch=FetchType.LAZY) @JoinColumn(name="ID_CARD_ID") public IdCard getIdCard() { return idCard; } // 代码省略... }
IdCard 实体(关联方)
@Entity @Table(name = "T_ID_CARD") public class IdCard { // 代码省略... }
持久化配置
<persistence-unit name = "RestSpringJPAPractice" transaction-type = "RESOURCE_LOCAL"> // 配置省略... </persistence>
查询代码
TypedQuery<Person> query = entitymanager.createQuery("from Person", Person.class); List<Person> resultList = query.getResultList(); for (Person person : resultList) { StringBuilder sb = new StringBuilder(); sb.append(person.getFirstName()).append(" ").append(person.getLastName()).append(" "); IdCard idCard = person.getIdCard(); if(idCard != null) sb.append(idCard.getIdNumber()).append(" ").append(person.getIdCard().getIssueDate()); }
生成的SQL
select person0_.id as id1_2_, person0_.FIRST_NAME as first_na2_2_, person0_.ID_CARD_ID as id_card_4_2_, person0_.LAST_NAME as last_nam3_2_, person0_1_.FAV_LAN as fav_lan1_0_, decode(person0_.id, person0_1_.id, 1, 0) as clazz_ from T_PERSON person0_, T_GEEK person0_1_ where person0_.id=person0_1_.id(+)
咨询问题
- 上述SQL未关联T_ID_CARD表,IdCard的idNumber和issueDate是如何、何时被获取的?
- 为何显式调用person.getIdCard()时,Hibernate未生成获取IdCard的额外SQL?
- 配置为FetchType.LAZY的IdCard是否被强制即时加载?若是,对应的SQL在哪里?
问题解答
针对你的三个问题,逐一解释:
问题1 & 问题2
这两个问题本质关联,分两种核心情况分析:
- 所有Person的ID_CARD_ID字段为null:此时
person.getIdCard()直接返回null,不会进入if分支执行属性获取逻辑,自然不需要查询T_ID_CARD表,也不会生成额外SQL。 - IdCard已存在于Hibernate一级缓存中:如果在执行本次Person查询之前,已经通过其他操作(比如
entitymanager.find(IdCard.class, id))加载过对应的IdCard对象,这些对象会被存储在Hibernate的一级缓存(会话缓存)中。调用person.getIdCard()时,Hibernate直接从缓存取出IdCard实例,无需生成新SQL;访问idNumber、issueDate时也直接读取缓存数据。
另外还有一种可能:你使用了Hibernate字节码增强并开启了无代理懒加载。这种情况下,Hibernate可通过Person的ID_CARD_ID字段创建IdCard代理对象,只有当真正访问IdCard的非主键属性时才会触发SQL查询。如果你的日志仅捕获到初始的Person查询SQL,可能遗漏了后续的IdCard查询日志。
问题3
配置的FetchType.LAZY没有被强制即时加载。如果是即时加载,Hibernate会在初始的Person查询SQL中通过JOIN关联T_ID_CARD表,一次性取出Person和IdCard的所有数据,但你提供的SQL里并没有关联T_ID_CARD,说明懒加载是生效的。
如果后续触发了IdCard的加载(比如缓存中无对应实例且ID_CARD_ID不为null),Hibernate会生成类似如下的SQL:
select idcard0_.id as id1_1_, idcard0_.id_number as id_numbe2_1_, idcard0_.issue_date as issue_da3_1_ from T_ID_CARD idcard0_ where idcard0_.id=?
内容的提问来源于stack exchange,提问作者Debaprasad Jana
相关产品推荐
相关产品推荐

