休眠多对多 - 获取方法渴望与懒惰 [英] Hibernate many to many - fetch method eager vs lazy
问题描述
Hibernate 的新手.
New to Hibernate.
我有用户组的多对多关系.三个表: User 、 Group 和 UserGroup 映射表.
I have User Group many to many relation. Three tables : User , Group and UserGroup mapping table.
实体:
@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 中,我使用的是 EAGER 获取方法.现在,当我调用我的 DAO 来检索系统中的所有组时使用以下标准:
Notice that in Group Entity I'm using fetch method EAGER. Now, when I'm calling my DAO to retrive all the groups in the system using the following criteria:
Criteria criteria = session.createCriteria(Group.class);
return criteria.list();
我从 mappgin 表(用户组)中获取所有行,而不是获取实际的组数...
I'm getting all the rows from the mappgin table (usergroup) instead of getting the actual number of groups...
例如,如果我有在用户表中
for example if i have in user table
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}
The result will be the following list - {grp1,grp2,grp2,grp1}
代替{grp1,grp2}
Instead of {grp1,grp2}
如果我在 Group Entity 中将 fetch 方法更改为 LAZY 我会得到正确的结果但是 hibernate 在另一个地方抛出了我的 LazyException ......
If I change in Group Entity the fetch method to LAZY I'm getting the correct results but hibernate throws me LazyException in another place...
请帮助我应该使用什么获取方法以及为什么?
Please assist what fetch method should I use and why ?
谢谢!
推荐答案
懒惰的人会告诉你总是违反直觉地使用 FetchType.EAGER
.这些人通常不担心数据库性能,只关心让他们的开发生活更轻松.我会说你应该使用 FetchType.LAZY
来提高性能.因为数据库访问通常是大多数应用程序的性能瓶颈,所以一点点帮助.
Lazy people will tell you to always use FetchType.EAGER
counter-intuitively. These are the people who generally don't worry about database performance and only care about making their development lives easier. I'm going to say you should be using FetchType.LAZY
for the increased performance benefit. Because database access is usually the performance bottleneck of most applications, every little bit helps.
如果您确实需要获取某个组的用户列表,只要您在事务会话中调用 getUsers()
,您就不会收到 LazyLoadingException
这是所有新 Hibernate 用户的祸根.
If you do actually need to get a list of users for a group, as long as your call getUsers()
from within a transactional session, you won't get that LazyLoadingException
that is the bane of all new Hibernate users.
以下代码将为您提供所有组,而无需填充这些组的用户列表
The following code will get you all groups without populating the list of users for those groups
//Service Class
@Transactional
public List<Group> findAll(){
return groupDao.findAll();
}
以下代码将为您提供所有组以及这些组在 DAO 级别的用户:
The following code will get you all groups with the users for those groups at the DAO level:
//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
的更多信息,请参见此处
For more info on the different types of FetchMode
's see here
如果您不想为了访问集合对象而编写不同的 DAO 方法,只要您在用于获取父对象的同一个 Session
中,您就可以使用 Hibernate.initialize()
方法来强制初始化您的子集合对象.我真的不建议您对父对象的 List
执行此操作.这会给数据库带来相当大的负载.
If you don't want to have to write a different DAO method just to access your collection object, as long as you are in the same Session
that was used to fetch the parent object you can use the Hibernate.initialize()
method to force the initialisation of your child collection object. I would seriously not recommend that you do this for a List<T>
of parent objects. That would put quite a heavy load on the database.
//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()
的更多信息,请参见 这里
I've not come across a situation where I've had to use the above code, but it should be relatively efficient. For more information about Hibernate.initialize()
see here
我在服务层完成了这个,而不是在 DAO 中获取它们,因为那样你只需要在服务中创建一个新方法而不是创建一个单独的 DAO 方法.重要的是您已经在事务中封装了 getUsers()
调用,因此将创建一个会话,Hibernate 可以使用该会话来运行其他查询.这也可以在 DAO 中通过为您的集合编写连接标准来完成,但我自己从来没有这样做过.
I have done this in the service layer rather than fetching them in the DAO, because then you only have to create one new method in the service rather than making a separate DAO method as well. The important thing is that you have wrapped the getUsers()
call within the transaction, so a session will have been created that Hibernate can use to run the additional queries. This could also be done in the DAO by writing join criteria to your collection, but I've never had to do that myself.
也就是说,如果您发现调用第二种方法的次数远远多于调用第一种方法,请考虑将提取类型更改为 EAGER
并让数据库为您完成工作.
That said, if you find that you are calling the second method far more than you are calling the first, consider changing your fetch type to EAGER
and letting the database do the work for you.
这篇关于休眠多对多 - 获取方法渴望与懒惰的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!