休眠多到很多 - 获取方法渴望vs懒 [英] Hibernate many to many - fetch method eager vs lazy

查看:156
本文介绍了休眠多到很多 - 获取方法渴望vs懒的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有User Group多对多的关系。
三个表: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和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和getters

请注意,在Group Entity I' m使用获取方法EAGER。
现在,当我打电话给我的DAO时,使用以下标准检索系统
中的所有组:

  Criteria criteria = session.createCriteria(Group.class); 
return criteria.list();

我从mappgin表(usergroup)获取所有行,而不是获取实际的如果我有
在用户表中

  

code>用户名密码
-----------------
user1 user1
user2 user2



在组表中

 组名admin b $ b --------------- 
grp1 user1
grp2 user2

在用户组表中

 用户名组名
--------- ---------
user1 grp1
user2 grp2
user1 grp2
user2 grp1

结果如下:{grp1,grp2,grp2,grp1}



不是{grp1,grp2如果我在Group Entity中将获取方法更改为LAZY,我将得到正确的结果
,但是hibernate将LazyException引发到另一个地方......

/ p>

请帮助我应该使用哪种获取方法?为什么?

谢谢!

解决方案

懒惰的人会告诉你总是直截了当地使用 FetchType.EAGER 。这些人通常不担心数据库性能,只关心让他们的开发生活更轻松。我想说你应该使用 FetchType.LAZY 来提高性能优势。由于数据库访问通常是大多数应用程序的性能瓶颈,所以每一点都有帮助。



如果您确实需要获取群组的用户列表,只要您的电话 getUsers()在一个事务性会话中,你不会得到 LazyLoadingException 这是所有新Hibernate用户的祸根。



以下代码将为您提供所有组,而不填充这些组的用户列表

  // Service Class 
@交易
公共列表< Group> findAll(){
return groupDao.findAll();

$ / code>

以下代码将为您提供所有组的用户DAO级别:

  // DAO class 
@SuppressWarnings(unchecked)
public List< Group> ; findAllWithUsers(){
Criteria criteria = getCurrentSession()。createCriteria(Group.class);

criteria.setFetchMode(users,FetchMode.SUBSELECT);
//根据需要在此限制其他限制。

return criteria.list();

编辑1:感谢Adrian Shum为此代码



有关不同类型的 FetchMode 的详细信息,请参阅 here



如果你不想写一个不同的DAO方法只是为了访问你的集合对象,只要你在用于获取父对象的同一 Session 中,你可以使用 Hibernate .initialize()方法强制初始化您的子集合对象。我会认真的不建议你为父对象的 List< T> 做这件事。

  //服务类
@Transactional
public Group findWithUsers(UUID groupId){
Group group = groupDao.find(groupId);

//强制getUsers()返回的集合对象的初始化()
Hibernate.initialize(group.getUsers());

返回组;
}

我还没遇到过必须使用上面的代码,但它应该是相对高效的。有关 Hibernate.initialize()的更多信息,请参见 here



我在服务层完成了这项工作,而不是抓取他们在DAO中,因为那样你只需要在服务中创建一个新的方法,而不是另外制作一个单独的DAO方法。重要的是你已经在事务中包装了 getUsers()调用,所以会创建一个Hibernate可以用来运行附加查询的会话。这也可以在DAO中通过向你的收藏中写入连接标准来完成,但我从来没有必要亲自去做。



也就是说,如果你发现你调用第二种方法远远超过了调用第一种方法,考虑将您的获取类型更改为 EAGER ,并让数据库为您完成工作。


New to Hibernate.

I have User Group many to many relation. Three tables : User , Group and UserGroup mapping table.

Entities:

@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

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();

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

in group table

 groupname admin
 ---------------
 grp1      user1
 grp2      user2

in usergroup table

 username groupname
 ------------------
 user1     grp1
 user2     grp2
 user1     grp2
 user2     grp1

The result will be the following list - {grp1,grp2,grp2,grp1}

Instead of {grp1,grp2}

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 ?

Thanks!

解决方案

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.

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();
}

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();
}

Edit 1: Thanks to Adrian Shum for this code

For more info on the different types of FetchMode's see here

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;
}

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

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.

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.

这篇关于休眠多到很多 - 获取方法渴望vs懒的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆