情景:
两个映射类。
@Entity
@Table(name = "user")
public class User implements Serializable {
...
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private Set<UserFriend> friends = new HashSet<>();
...
}
@Entity
@Table(name = "user_friend")
public class UserFriend implements Serializable {
...
@ManyToOne
@JoinColumn(name = "user_id", nullable = false)
private User user;
...
}
最常见的方法是吸引所有朋友,但在一个特殊情况下,我想只接受被接受的朋友。为了得到这种方法,在我的DAO方法中,我正在做下一步。
Criteria criteria = this.getCurrentSession().createCriteria(User.class)
.createAlias("userAccount", "ua")
.add(Restrictions.like("ua.email", email).ignoreCase())
.createCriteria(
"friends",
"f",
JoinType.LEFT_OUTER_JOIN,
Restrictions.eq("accept", true))
.setCacheRegion(CACHE_REGION)
.setCacheable(true);
User user = (User) criteria.uniqueResult();
这很好。
Hibernate: select ... from user this_
left outer join user_friend fr2_ on this_.id=fr2_.user_id and ( fr2_.accept=? )
left outer join user user9_ on fr2_.user_id=user9_.id
inner join user_account ua1_ on this_.id=ua1_.user_id where lower(ua1_.email) like ?
当从@Transactional范围内的另一个站点调用user.getFriends()时出现问题,然后执行Lazy初始化并覆盖当前的Friends Set状态。
为了避免这种情况,在加载之后,我将用户对象从Hibernate Session中分离出来。
super.getCurrentSession().evict(user);
但是,稍后,尽管Friends Set之前已经初始化,但我得到一个LazyInitializationException并且丢失了朋友集。
我怎样才能避免这种情况并保持好友集数据避免延迟初始化? (我不能改变EAGER而不是LAZY)。
基于@Maarten响应的解决方案
@FilterDef(
name = "friendStatus",
parameters = {@ParamDef(name = "status", type = "boolean")})
public class User implements Serializable {
...
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
@Filter(name = "friendStatus", condition = "accept = :status")
private Set<UserFriend> friends = new HashSet<>();
...
}
在DAO方法中。
public User getUserByEmailWithValidFriends(String email) {
super.getCurrentSession()
.enableFilter("friendStatus").setParameter("status", true);
Criteria criteria = super.getCurrentSession()
.createCriteria(User.class)
.createAlias("userAccount", "ua")
.add(Restrictions.like("ua.email", email).ignoreCase())
.setCacheRegion(CACHE_REGION)
.setCacheable(true);
return criteria.uniqueResult();
}
工作正常。 :)
答案 0 :(得分:1)
有两个(或三个)选项:
第一种选择是最复杂的。您需要在需要时以编程方式在hibernate会话上启用过滤器。从那时起,该集合将像只有被接受的朋友一样运行,不需要编写特定的查询。
第二个选项是编写一个查询,该查询只为用户提取已接受的朋友并返回列表。此列表既不附加到用户也不附加到会话(尽管列表中的用户是),因此您不会有懒惰的初始化问题。这可能是最容易实现的。
第三是显而易见的。获取所有朋友并在内存中过滤。如果用户有许多未被接受的朋友,这可能会受到影响。