[英]Hibernate many to many - fetch method eager vs lazy
Hibernate新手。
我有多个用户组关系。 三个表:User,Group和UserGroup映射表。
实体:
@Entity
@Table(name = "user")
public class User {
@Id
@Column (name = "username")
private String userName;
@Column (name = "password", nullable = false)
private String password;
@ManyToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
@JoinTable(name="usergroup",
joinColumns={@JoinColumn(name="username")},
inverseJoinColumns={@JoinColumn(name="groupname")})
private Set<Group> userGroups = new HashSet<Group>();
... setter and getters
@Entity
@Table(name = "group")
public class Group {
@Id
@Column(name = "groupname")
private String groupName;
@Column(name = "admin", nullable = false)
private String admin;
@ManyToMany(mappedBy = "userGroups", fetch = FetchType.EAGER)
private Set<User> users = new HashSet<User>();
... setter and getters
请注意,在Group Entity中我使用了fetch方法EAGER。 现在,当我调用DAO以使用以下标准检索系统中的所有组时:
Criteria criteria = session.createCriteria(Group.class);
return criteria.list();
我从mappgin表(用户组)获取所有行,而不是获取实际的组数...
例如,如果我在用户表中
username password
-----------------
user1 user1
user2 user2
在组表中
groupname admin
---------------
grp1 user1
grp2 user2
在用户组表中
username groupname
------------------
user1 grp1
user2 grp2
user1 grp2
user2 grp1
结果将是以下列表 - {grp1,grp2,grp2,grp1}
而不是{grp1,grp2}
如果我将组实体更改为LAZY的获取方法我得到了正确的结果但是hibernate在另一个地方抛出了LazyException ...
请帮助我使用fetch方法以及为什么?
谢谢!
懒惰的人会告诉你总是反直觉地使用FetchType.EAGER
。 这些人通常不担心数据库性能,只关心让他们的开发生活更轻松。 我要说你应该使用FetchType.LAZY
来提高性能。 因为数据库访问通常是大多数应用程序的性能瓶颈,所以每一点都有帮助。
如果确实需要获取组的用户列表,只要从事务会话中调用getUsers()
,就不会得到所有新Hibernate用户的LazyLoadingException
的LazyLoadingException。
以下代码将为您提供所有组,而不会填充这些组的用户列表
//Service Class
@Transactional
public List<Group> findAll(){
return groupDao.findAll();
}
以下代码将为您提供DAO级别的所有组的用户:
//DAO class
@SuppressWarnings("unchecked")
public List<Group> findAllWithUsers(){
Criteria criteria = getCurrentSession().createCriteria(Group.class);
criteria.setFetchMode("users", FetchMode.SUBSELECT);
//Other restrictions here as required.
return criteria.list();
}
编辑1:感谢Adrian Shum提供此代码
有关不同类型的FetchMode
的更多信息,请参见此处
如果您不想只是为了访问您的集合对象而编写不同的DAO方法,只要您处于用于获取父对象的同一个Session
,您就可以使用Hibernate.initialize()
方法来强制初始化您的子集合对象。 我真的不建议您为父对象的List<T>
执行此操作。 这会给数据库带来相当大的负担。
//Service Class
@Transactional
public Group findWithUsers(UUID groupId){
Group group = groupDao.find(groupId);
//Forces the initialization of the collection object returned by getUsers()
Hibernate.initialize(group.getUsers());
return group;
}
我没有遇到过必须使用上述代码的情况,但它应该相对有效。 有关Hibernate.initialize()
更多信息,请参见此处
我在服务层中完成了这个,而不是在DAO中获取它们,因为那样你只需要在服务中创建一个新方法,而不是制作一个单独的DAO方法。 重要的是你已经在事务中包装了getUsers()
调用,因此将创建一个Hibernate可以用来运行其他查询的会话。 这也可以通过在你的收藏中写入连接标准在DAO中完成,但我自己从来没有这样做过。
也就是说,如果您发现调用第二种方法远远超过调用第一种方法,请考虑将获取类型更改为EAGER
并让数据库为您完成工作。
尽管JamesENL的答案几乎是正确的,但缺乏一些非常关键的方面。
他正在做的是在事务仍处于活动状态时强制加载延迟加载代理。 虽然它解决了LazyInitialization错误,但仍然会逐个完成延迟加载,这将导致极差的性能。 从本质上讲,它只是手动实现FetchType.EAGER的相同结果(并且更糟糕的是,因为我们错过了使用JOIN和SUBSELECT策略的可能性),这甚至与性能的关注相矛盾。
为避免混淆:使用LAZY获取类型是正确的。
但是,为了避免延迟加载异常,在大多数情况下,您应该让您的存储库(或DAO?)获取所需的属性。
最低效的方法是通过访问相应的属性并触发延迟加载来实现。 有一些非常大的缺点:
更合适的方法是尝试在一个查询(或几个)中获取所有相关数据。
只是举例说明使用类似Spring的数据语法(应该足够直观地移植到Hibernate Repository / DAO):
interface GroupRepository {
@Query("from Group")
List<Group> findAll();
@Query("from Group g left join fetch g.users")
List<Group> findAllWithUsers();
}
在Criteria API中加入提取同样简单(尽管似乎只有左连接可用),引自Hibernate doc:
List cats = session.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.setFetchMode("mate", FetchMode.EAGER)
.setFetchMode("kittens", FetchMode.EAGER)
.list();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.