一尘不染

Hibernate多对多-渴望获取方法与延迟

hibernate

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

注意,在组实体中,我正在使用获取方法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抛出到另一个地方…

请协助我使用哪种提取方法,为什么?

谢谢!


阅读 254

收藏
2020-06-20

共1个答案

一尘不染

懒惰的人会告诉你要始终FetchType.EAGER凭直觉来使用。这些人通常不担心数据库性能,只关心使开发工作更轻松。我要说的是您应该利用它FetchType.LAZY来提高性能。由于数据库访问通常是大多数应用程序的性能瓶颈,因此一点点帮助。

如果确实需要获取组的用户列表,只要您getUsers()在事务会话中进行呼叫,就不会得到LazyLoadingException所有新Hibernate用户的麻烦。

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

//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并让数据库为您完成工作。

2020-06-20