java - hibernate 多对多-获取方法渴望与懒惰

标签 java hibernate orm many-to-many hibernate-mapping

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 ...

请协助我应该使用什么获取方法以及为什么?

谢谢!

最佳答案

懒人会告诉你总是使用FetchType.EAGER违反直觉。这些人通常不担心数据库性能,只关心让他们的开发生活更轻松。我会说你应该使用 FetchType.LAZY以获得更高的性能优势。因为数据库访问通常是大多数应用程序的性能瓶颈,所以一点一滴都有帮助。

如果您确实需要获取某个组的用户列表,只要您调用 getUsers()在交易 session 中,您不会得到 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 提供此代码

有关不同类型的更多信息 FetchModehere

如果您不想为了访问您的集合对象而编写不同的 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() 的更多信息见here

我在服务层完成了这个,而不是在 DAO 中获取它们,因为那样你只需要在服务中创建一个新方法,而不是同时创建一个单独的 DAO 方法。重要的是你已经包装了 getUsers()在事务中调用,因此将创建一个 session ,Hibernate 可以使用该 session 来运行其他查询。这也可以在 DAO 中通过将连接条件写入您的集合来完成,但我自己从来没有这样做过。

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

关于java - hibernate 多对多-获取方法渴望与懒惰,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24150199/

相关文章:

java - Swing 的 Clojure 库 : externalized/runtime editable definition?

hibernate - 如何修复此 "no persistent classes found for query class"消息?

python - 如何在 SQLAlchemy (ORM) 中为 3 个表建立多对多关系模型?

javascript - 使用sequelize(javascript ORM)来编辑,加载数据表而不删除它们(mysql)

ios - DBAccess:提交不起作用

java - Android:ListView 与 TextView header 问题

java - Android - 从 ProGuard 中排除库(jar)类

Java 同步游戏 : synchronized && wait && notify

java - 单个线程中的单个 hibernate session 导致死锁

hibernate - JPQL Join Fetch 在 Spring Data 的 ManyToOne 关联中不起作用