2014-01-19 140 views
2

每当我尝试从我的用户表中检索数据时,我收到以下错误,我搜索了很多网页,试图找出可能是错误的但不能找到任何东西,可以有人请告诉我我失踪/我在这里错了吗?休眠懒惰初始化 - 未能懒惰地初始化集合

错误:

ERROR: org.hibernate.LazyInitializationException - failed to lazily initialize a collection of role: com.domain.crm.domain.Role.users, no session or session was closed org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.domain.crm.domain.Role.users, no session or session was closed

用户等级:

@Entity 
@Table(name="COM_USER") 
public class User { 
@Id 
@GeneratedValue(strategy=GenerationType.AUTO) 
@Column(name="USER_ID") 
private Long id; 

@Column(name="USER_NAME",nullable=false,length=25,unique=true) 
private String userName; 

@ManyToOne(fetch=FetchType.EAGER) 
@JoinColumn(name="ROLE_ID",nullable=false) 
private Role role; 
} 

角色类:

@Entity 
@Table(name="COM_ROLE") 
public class Role { 
@Id 
@GeneratedValue(strategy=GenerationType.AUTO) 
@Column(name="ROLE_ID") 
private Long id; 

@Column(name="ROLE",nullable=false,unique=false) 
private Integer Role; 

@OneToMany(mappedBy="role") 
private Set<User> users=new HashSet<User>(); 
} 

用户DAO类的方法被调用,以收集所有用户:

public List<User> getUsers(Long page, Long pageSize) { 
    Long start = (page-1)*pageSize;  
    return sessionfactory.getCurrentSession().createQuery("from User u ").setFirstResult(start.intValue()).setMaxResults(pageSize.intValue()).list(); 
} 

用户服务类方法:

@Transactional 
public List<User> getUsers(Long page, Long pageSize) { 
    return userdao.getUsers(page, pageSize); 
} 

控制器类调用方法:

@RequestMapping(value = "https://stackoverflow.com/users/list-user-data") 
@ResponseBody 
public UserListData listUserData(HttpServletRequest request, HttpServletResponse response, Model model) throws Exception { 

    UserListData listData = new UserListData(); 

    String page = request.getParameter("page"); 
    Long pageLong = Long.parseLong(page); 
    Long pageSize = (long)15; 

    List<User> searchResults = iuserservice.getUsers(pageLong, pageSize); 

    if(searchResults != null){ 
     List<List<Object>> aaData = new ArrayList<List<Object>>(); 
     List<Object> listItem = null; 

     for(User u : searchResults){ 
      listItem = new ArrayList<Object>(); 
      listItem.add(u.getLastName()); 
      listItem.add(u.getFirstName()); 
      listItem.add(u.getUserName()); 
      listItem.add(u.getEmail()); 
      listItem.add(u.getRole()); 

      aaData.add(listItem); 
     } 
     listData.setAaData(aaData); 
    } 



    int totalCount = iuserservice.getAllUsersCount().intValue(); 

    System.out.println("Number of records in DB: "+totalCount); 
    listData.setiTotalRecords(totalCount); 

    return listData; 
} 

最后,这里是我的pom.xml依赖:

<properties> 
    <java-version>1.6</java-version> 
    <org.springframework-version>3.1.0.RELEASE</org.springframework-version> 
    <org.aspectj-version>1.6.9</org.aspectj-version> 
    <org.slf4j-version>1.5.10</org.slf4j-version> 
</properties> 

<dependencies> 
    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-context</artifactId> 
     <version>${org.springframework-version}</version> 
     <exclusions> 
      <!-- Exclude Commons Logging in favor of SLF4j --> 
      <exclusion> 
       <groupId>commons-logging</groupId> 
       <artifactId>commons-logging</artifactId> 
      </exclusion> 
     </exclusions> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-webmvc</artifactId> 
     <version>${org.springframework-version}</version> 
    </dependency> 
    <!-- AspectJ --> 
    <dependency> 
     <groupId>org.aspectj</groupId> 
     <artifactId>aspectjrt</artifactId> 
     <version>${org.aspectj-version}</version> 
    </dependency> 
    <!-- Logging --> 
    <dependency> 
     <groupId>org.slf4j</groupId> 
     <artifactId>slf4j-api</artifactId> 
     <version>${org.slf4j-version}</version> 
    </dependency> 
    <dependency> 
     <groupId>org.slf4j</groupId> 
     <artifactId>jcl-over-slf4j</artifactId> 
     <version>${org.slf4j-version}</version> 
     <scope>runtime</scope> 
    </dependency> 
    <dependency> 
     <groupId>org.slf4j</groupId> 
     <artifactId>slf4j-log4j12</artifactId> 
     <version>${org.slf4j-version}</version> 
     <scope>runtime</scope> 
    </dependency> 
    <dependency> 
     <groupId>log4j</groupId> 
     <artifactId>log4j</artifactId> 
     <version>1.2.15</version> 
     <exclusions> 
      <exclusion> 
       <groupId>javax.mail</groupId> 
       <artifactId>mail</artifactId> 
      </exclusion> 
      <exclusion> 
       <groupId>javax.jms</groupId> 
       <artifactId>jms</artifactId> 
      </exclusion> 
      <exclusion> 
       <groupId>com.sun.jdmk</groupId> 
       <artifactId>jmxtools</artifactId> 
      </exclusion> 
      <exclusion> 
       <groupId>com.sun.jmx</groupId> 
       <artifactId>jmxri</artifactId> 
      </exclusion> 
     </exclusions> 
     <scope>runtime</scope> 
    </dependency> 

    <!-- @Inject --> 
    <dependency> 
     <groupId>javax.inject</groupId> 
     <artifactId>javax.inject</artifactId> 
     <version>1</version> 
    </dependency> 

    <!-- Servlet --> 
    <dependency> 
     <groupId>javax.servlet</groupId> 
     <artifactId>servlet-api</artifactId> 
     <version>2.5</version> 
     <scope>provided</scope> 
    </dependency> 
    <dependency> 
     <groupId>javax.servlet.jsp</groupId> 
     <artifactId>jsp-api</artifactId> 
     <version>2.1</version> 
     <scope>provided</scope> 
    </dependency> 
    <dependency> 
     <groupId>javax.servlet</groupId> 
     <artifactId>jstl</artifactId> 
     <version>1.2</version> 
    </dependency> 
    <!-- Test --> 
    <dependency> 
     <groupId>junit</groupId> 
     <artifactId>junit</artifactId> 
     <version>4.7</version> 
     <scope>test</scope> 
    </dependency>   
    <!-- Spring Security --> 
    <dependency> 
     <groupId>org.springframework.security</groupId> 
     <artifactId>spring-security-core</artifactId> 
     <version>3.1.3.RELEASE</version> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework.security</groupId> 
     <artifactId>spring-security-web</artifactId> 
     <version>3.1.3.RELEASE</version> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework.security</groupId> 
     <artifactId>spring-security-config</artifactId> 
     <version>3.1.3.RELEASE</version> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework.security</groupId> 
     <artifactId>spring-security-taglibs</artifactId> 
     <version>3.1.3.RELEASE</version> 
     <type>jar</type> 
     <scope>compile</scope> 
    </dependency>  
    <!-- Mysql --> 
    <dependency> 
     <groupId>mysql</groupId> 
     <artifactId>mysql-connector-java</artifactId> 
     <version>5.1.6</version> 
    </dependency>   
    <!-- Hibernate --> 
    <dependency> 
     <groupId>org.hibernate</groupId> 
     <artifactId>hibernate-entitymanager</artifactId> 
     <version>3.6.0.Final</version> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-orm</artifactId> 
     <version>${org.springframework-version}</version> 
    </dependency>  
    <!-- Commons DBCP --> 
    <dependency> 
     <groupId>commons-dbcp</groupId> 
     <artifactId>commons-dbcp</artifactId> 
     <version>1.2.2</version> 
    </dependency> 
    <dependency> 
     <groupId>org.apache.maven.plugins</groupId> 
     <artifactId>maven-resources-plugin</artifactId> 
     <version>2.4.3</version> 
    </dependency> 
    <dependency> 
     <groupId>net.sf.jasperreports</groupId> 
     <artifactId>jasperreports</artifactId> 
     <version>5.2.0</version> 
    </dependency>  
    <dependency> 
     <groupId>commons-collections</groupId> 
     <artifactId>commons-collections</artifactId> 
     <version>3.1</version> 
    </dependency>   
</dependencies> 

感谢您的时间

+3

错误说明了一切。您从数据库加载对象,然后关闭会话。你的对象然后处于分离状态。然后,您尝试访问一个懒惰的集合。您需要扩展会话的范围 - 在视图中打开会话是一个很好的模式。或者你可以急切地加载收藏。 –

+0

@BoristheSpider请花点时间检查用户类,我已经在急切地加载集合 – MChan

+0

@BoristheSpider我也没有关闭会话,无论如何,错误出现在我处于控制器方法的中间时,甚至在它之前完成过程。谢谢 – MChan

回答

4

我在此处发布示例。基本思想是只有3个数据库表,其中2个用于实体UserRole,最后一个用作映射表,如user_role。在用户表中,我们存储用户详细信息,在角色表中存储角色ID和名称,并在我们的映射表中将用户映射到角色。我从我自己的项目中复制。我的类扩展了BaseEntity仅仅是一个映射超具有如ID,创建/修改的日期等等

用户实体

@Entity 
@Table(name = "user_t") 
public class User extends BaseEntity { 

    @Column(name = "username", nullable = false, unique = true) 
    private String userName; 

    @Column(name = "password", nullable = false) 
    private String password; 

    @ManyToMany 
    @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) 
    private Set<Role> role = new HashSet<Role>(); 

    // getters & setters 
} 

角色实体

@Entity(name = "role_t") 
public class Role extends BaseEntity { 

    @Column(name = "role_name", nullable = false) 
    private String roleName; 

    public Role() { 
    } 

    public Role(String roleName) { 
     this.roleName = roleName; 
    } 

    // getters & setters 
} 

为每个实体一般字段请注意,ManyToMany默认使用急切获取类型,所以我们不需要设置它。现在,当我从我的服务中查询我的User实体时。 role集合不会被获取。如果我需要获取属于该用户的角色,那么我需要用查询手动获取它们。我使用Spring Data JPA存储库,查询例如看起来像是单个结果。

UserRepository类

public interface UserRepository extends JpaRepository<User, Long> { 

    @Query("SELECT u FROM User u JOIN FETCH u.role WHERE u.userName = (:userName)") 
    public User findByUserNameAndFetchRoles(@Param("userName") String userName); 

    @Query("FROM User u JOIN FETCH u.role") 
    public List<User> getAllUsersAndFetchRoles(); // **query that you would use!** 
} 

,然后在UserService层,你会使用存储库/ DAO查询。对我来说,使用角色上的热切获取是必要的,因为每次我想查询用户列表时,我都不需要获取角色,因为在我的真实项目中角色也具有一组权限,只是不值得去查询你不需要的东西。

2

问题是@OneToMany默认情况下协会是懒惰的,当您拨打getUsers()时,会话已在服务方法中关闭。 解决它的一种方法是通过热切地加载子记录。 但要小心,因为这可能会导致加载太多的数据,这会减慢速度并杀死内存。例如,在你的Role实体,如果你将其设置为热切负载Users

@OneToMany(mappedBy="role", fetch = FetchType.EAGER) 
private Set<User> users=new HashSet<User>(); 
} 

每当装入Role,Hibernate会加载所有子Users

一到处理您的案件的方式是做内部@Transactional方法,应确保会议服务的所有工作仍然是开放的(最好是有服务方法@Transactional代替的DAO)

@Transactional 
public UserListData getUserListData(Long page, Long pageSize) { 
    List<User> searchResults = userdao.getUsers(page, pageSize); 
    UserListData listData = new UserListData(); 
    if(searchResults != null){ 
     List<List<Object>> aaData = new ArrayList<List<Object>>(); 
     List<Object> listItem = null; 

     for(User u : searchResults){ 
      listItem = new ArrayList<Object>(); 
      listItem.add(u.getLastName()); 
      listItem.add(u.getFirstName()); 
      listItem.add(u.getUserName()); 
      listItem.add(u.getEmail()); 
      listItem.add(u.getRole()); 

      aaData.add(listItem); 
     } 
     listData.setAaData(aaData); 
    } 
return listData; 
} 
+0

我已经移动了服务类中的所有代码,就像您刚刚推荐的那样,但我仍然得到相同的错误,我也设置了fetchtype以提醒 – MChan

+0

这是行不通的。如果您在用户实体上调用getRole(),则角色集合仍然未初始化。 – Vaelyr

0

某处(不如上面的代码所示),您正在访问某个Role实体中users收集字段的某种方法。

0

无法从具有预先获取类型的同一事务中加载Roles集合。要解决此问题,您需要手动初始化Roles集合或在查询上执行fetch join。这应该为你解决它。

public List<User> getUsers(Long page, Long pageSize) { 
    Long start = (page-1)*pageSize;  
    return sessionfactory.getCurrentSession().createQuery("FROM User u JOIN FETCH u.role ").setFirstResult(start.intValue()).setMaxResults(pageSize.intValue()).list(); 
} 

另外我建议具有UserRoles之间ManyToMany关系,并使用该映射表。使用以下并从角色实体中删除用户集。在角色表上复制角色记录是多余的。

@ManyToMany 
@JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) 
private Set<Role> role = new HashSet<Role>(); 
+0

感谢您的回复,但是用户和角色之间多么多人,用户只能有一个角色,但角色可以属于很多用户,所以它是一对多的关系,除非我在这里丢失了某些东西,所以可以你请解释为什么多对多? – MChan

+0

基于角色的访问控制最佳实践不是将角色分配给用户,而是将用户分配给角色。你必须更广泛地考虑你的数据库设计。如果您打算扩展它,那么您会遇到您的解决方案。通过ManyToMany,您可以创建角色,并通过映射表将用户与角色映射。如果您打算添加基于权限/权限的身份验证,则允许您扩展解决方案。 – Vaelyr

+0

据我读的ROLES是非常静态的,这意味着我不能动态地构建新的ROLE_,所以所有的ROLES必须先在代码中定义(例如:ROLE_ADMIN,ROLE_CLIENT ...等),然后为每个ROLE_保存在ROLE表中。如果我在用户和角色之间建立了多对多关系,这意味着用户可以在同一时间有多个角色,但我是否正确地跟着您?好吧,现在就可扩展性而言,我已经限制了静态ROLES,所以它不像可扩展......有意义吗? – MChan

相关问题