Java Hibernate 延迟初始化 - 未能延迟初始化集合

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21220234/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 07:25:50  来源:igfitidea点击:

Hibernate lazy initialization - failed to lazily initialize a collection

javaspringhibernate

提问by MChan

I am getting the following error whenever I try to retrieve data from my Users table, I've searched a web a lot trying to find out what could be wrong but couldn't find anything so can someone please help me by telling me what I am missing / I have wrong here?

每当我尝试从我的用户表中检索数据时,我都会收到以下错误,我在网上搜索了很多,试图找出可能出错的地方,但找不到任何东西,所以有人可以告诉我我是什么来帮助我失踪/我错在这里?

Error:

错误:

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

错误:org.hibernate.LazyInitializationException - 无法延迟初始化角色集合:com.domain.crm.domain.Role.users,没有会话或会话被关闭 org.hibernate.LazyInitializationException:无法延迟初始化角色集合: com.domain.crm.domain.Role.users,没有会话或会话被关闭

User class:

用户类:

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

Role Class:

角色等级:

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

User DAO class Method being invoked to collect all users:

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

User Service class method:

用户服务类方法:

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

Controller class calling the method:

调用方法的控制器类:

@RequestMapping(value = "/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;
}

Finally here is my pom.xml dependencies:

最后这里是我的 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>

Thanks for your time

谢谢你的时间

采纳答案by Vaelyr

I post the example here. The basic idea would be just to have 3 database tables where 2 are for entities Userand Role, and last one as mapping table, say user_role. In user table we store user details, in role table we store role id and name and in our mapping table we map the user to role. I copy from my one own project. The BaseEntity that my classes extend is just a mapped superclass that has general fields for every entity such as id, created/modified date etc.

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

User entity

用户实体

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

Role entity

角色实体

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

Note that ManyToManyby default uses eagerly fetch type so we don't need to set it. Now when I would query for my Userentity from my service. The rolecollection would not get fetched. If I need to get roles that belong to that user then I need to manually fetch them with the query. I use Spring Data JPA repository and the query for example would look like that for a single result.

请注意,ManyToMany默认情况下使用 Eagely fetch 类型,因此我们不需要设置它。现在,当我User从我的服务中查询我的实体时。该role集合不会被获取。如果我需要获取属于该用户的角色,那么我需要通过查询手动获取它们。我使用 Spring Data JPA 存储库,例如查询对于单个结果看起来像这样。

UserRepository class

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!**
}

And then in your UserServicelayer you would use that repository/DAO query. For me using the eagerly fetch on the roles is necessary, because every time I would want to query for userlist, I don't don't need to get the roles, because in my real project the roles also have a set of permissions and it's just not worth to query for stuff you don't need.

然后在您的UserService层中,您将使用该存储库/DAO 查询。对我来说,急切地获取角色是必要的,因为每次我想查询用户列表时,我不需要获取角色,因为在我的实际项目中,角色也有一组权限和查询不需要的东西是不值得的。

回答by isah

The problem is that @OneToManyassociations are lazy by default and by the time you call getUsers()the session is already closed in your service method. One way to solve it is by eagerly loading child records. But be careful, as this might cause you load too many data which will slow it down and kill memory.For example, in your Roleentity, if you set it to eagerly load Users:

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

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

Whenever you load a Role, Hibernate will load all its child Users.

每当您加载 a 时Role,Hibernate 都会加载其所有子项Users

One way to handle your case is do all the work on the service inside @Transactionalmethod which should ensure session is still open(It's better to have service methods @Transactionalinstead of DAOs)

处理您的情况的一种方法是在服务内部@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;
}

回答by Glenn Lane

Somewhere (not shown in your code above) you are accessing some method of the userscollection field in a Roleentity.

某处(未在上面的代码中显示)您正在访问实体中users集合字段的某些方法Role

回答by Vaelyr

You cannot load Rolescollection from the same transaction with eager fetch type. To work around that you need to manually initialize the Roles collection or do fetch joinon your query. This should fix it for you.

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

Also I would recommend having ManyToManyrelationship between Userand Rolesand use the mapping table. Use the below and remove user set from role entity. It's redundant to duplicate role records on the roles table.

此外,我会建议有ManyToMany关系User,并Roles和使用映射表。使用以下内容并从角色实体中删除用户集。在角色表上复制角色记录是多余的。

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