我必须将一个(非常非规范化的)DB Schema映射到hibernate,由于各种原因,我需要使用回调方法将行集合“解析”到业务对象中。
以下是我的映射的简化示例:
@Table(name = "CONTAINER")
class Container {
@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name = "CONTAINER_ID", referencedColumnName = "ID")
@MapKey(name = "key")
@Fetch(FetchMode.SUBSELECT)
private Map<Longs, MappedAttribute> mappedAttributes;
@Transient
private BusinessAttribute businessAttribute;
@PostLoad
private void postLoad(){
this.businessAttribute = new BusinessAttribute(mappedAttributes);
}
}
@Table(name = "ATTRIBUTES")
class MappedAttribute {
...
}
class BusinessAttribute {
public BusinessAttribute(List<MappedAttribute> args){}
}
映射本身是有效的,我最终得到了我想要的业务对象,但令我困惑的是Hibernate用来获取相关行的SQL查询(简化了你会得到它的要点):
-- First select the root objects
select * from CONTAINER where ID in (1,2,3,4);
-- Then select all the attribute rows for each root object in individual queries
select * from ATTRIBUTES where CONTAINER_ID = 1;
select * from ATTRIBUTES where CONTAINER_ID = 2;
select * from ATTRIBUTES where CONTAINER_ID = 3;
select * from ATTRIBUTES where CONTAINER_ID = 4;
这种行为看起来很像我指定FetchMode.SELECT
时会发生什么。不幸的是,这对我来说是不可接受的,因为我通常需要加载几千个容器,这会产生太多的SQL查询。为避免这种情况,我明确指定了FetchType.EAGER
和FetchMode.SUBSELECT
,但它们似乎被忽略了。
请注意,我也没有触及postLoad()
方法中任何对象的任何持久属性,因为我只设置了@Transient
属性。
有趣的是,如果我只是注释掉@PostLoad
注释,那么查询看起来就像我预期的那样(当然,我的业务对象没有创建):
-- First select the root objects
select * from CONTAINER where ID in (1,2,3,4);
-- The select all the attributes for all root objects at once
select * from ATTRIBUTES where CONTAINER_ID in (
select ID from CONTAINER where ID in (1,2,3,4)
);
当回调存在时,Hibernate 5.2.12 EntityManager是否有任何理由决定忽略FetchMode.SUBSELECT
?
有没有什么方法可以同时获得回调和子选择行为?
答案 0 :(得分:0)
此行为是Hibernate中的错误导致的,该错误报告为HHH-12279。
问题的根源是在加载了实体的所有基本属性之后,但在完全加载任何关联之前调用@PostLoad
带注释的方法。
上面的示例中发生的是mappedAttributes
属性
not null,但在调用@PostLoad
方法时,其内容尚未从DB加载。此时调用该集合上的任何getter方法会触发该集合元素的延迟加载。
由于有多个Container
个实体,并且每个Container
都有自己的子集合,因此每个子集合都会触发自己的SQL select
查询。
我已经尝试了很多变通办法(Interceptor,EventListener,Integrator等等),但是发现没有办法(除了从Hibernate获取实体后手动调用postLoad()
)。没有工作的替代方案都表现出相同的行为(可能是因为它们都挂钩了相同的生命周期)。