【问题标题】:Hibernate 4 lazy loading doesn't workHibernate 4延迟加载不起作用
【发布时间】:2014-01-02 02:59:22
【问题描述】:

我正在使用 spring 3 和 hibernate 4 开发一个 Web 应用程序。 我在延迟加载用户所属组的一组授予权限时遇到问题。 从数据库中检索用户对象时,其 Group 对象没有任何权限,即使 user.getGroup().getPermissions() 被显式调用。

我注意到在调试模式下,如果我将鼠标悬停在 User 对象上,然后导航到 User 对象内的 Group 对象,然后导航到它的权限,我可以看到它的类型显示为 PersistentSet,并展开它将正确加载权限。但在非调试模式下,权限永远不会延迟加载。我在这里想念什么?提前致谢。

以下是实体之间的关系: 用户与组是一对多的关系,组与权限是多对多的关系。

这里是 UserDaoImpl 类的定义

@Repository
@Transactional
public class UserDaoImpl implements UserDao  {

    @Autowired
    private SessionFactory sessionFactory;

    private Session getSession() {
        return sessionFactory.getCurrentSession();
    }

    @Override
    public User get(String username) {
        Session session = getSession();
        User user = (User) session.createCriteria(User.class).add(Restrictions.eq("username", username)).uniqueResult();

        Group g = user.getGroup();

        // calling g.getGrantedPermissions() doesn't load any permission
        Set<Permission> permissions = g.getGrantedPermissions();
        return user;
    }
}

这是 Permission 类的定义

@Entity
public class Permission {
    @Id
    @GeneratedValue
    private Long id;

    @Column
    private String name;

    @Column
    private String description;

    public Permission()
    {

    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

这是 Group 类的定义

@Entity
@Table(name="\"Group\"")
public class Group {
    public static final String ROLE_VALID_USER = "ROLE_VALID_USER";

    @Id
    @GeneratedValue
    private Long id;

    @Column
    private String description;

    @Column
    private String role;

    @ManyToMany
    @JoinTable(name = "granted_permission", 
            joinColumns = {@JoinColumn(name = "GROUP_ID", nullable = false, updatable = false) }, 
            inverseJoinColumns = { @JoinColumn(name = "PERMISSION_ID", nullable = false, updatable = false) })
    private Set<Permission> grantedPermissions;

    public Group()
    {

    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public Set<Permission> getGrantedPermissions() {
        return grantedPermissions;
    }

    public void setGrantedPermissions(Set<Permission> grantedPermissions) {
        this.grantedPermissions = grantedPermissions;
    }
}

这是 User 类的定义

@Entity
public class User {
    @Id
    @Column(name="USERNAME")
    private String username;

    @Column(name="PASSWORD")
    private String password;

    @Transient
    private final boolean enabled = true;

    @Column
    private String name;

    @Column(name="EMAIL")
    private String email;

    @Column(name="PHONE")
    private String phone;

    @ManyToOne
    private Group group;

    @ManyToOne
    @JoinColumn(name="ORIGINAL_BRANCH_ID")
    private Branch originalBranch;

    @ManyToOne
    @JoinColumn(name="ID_OF_RESPONSIBLE_BRANCH")
    private Branch responsibleBranch;

    public User()
    {

    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getRole() {
        return group.getRole();
    }

    public boolean isEnabled() {
        return enabled;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public Group getGroup() {
        return group;
    }

    public void setGroup(Group group) {
        this.group = group;
    }

    public Branch getOriginalBranch() {
        return originalBranch;
    }

    public void setOriginalBranch(Branch originalBranch) {
        this.originalBranch = originalBranch;
    }

    public Branch getResponsibleBranch() {
        return responsibleBranch;
    }

    public void setResponsibleBranch(Branch responsibleBranch) {
        this.responsibleBranch = responsibleBranch;
    }
}

这里是持久化配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                           http://www.springframework.org/schema/tx 
                           http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
                           http://www.springframework.org/schema/context 
                           http://www.springframework.org/schema/context/spring-context-3.0.xsd">

       <tx:annotation-driven />

       <context:annotation-config />

    <bean id="transactionManager"
        class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <context:component-scan base-package="net.acme.prs" />

    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="mysqlDataSource" />
        <property name="packagesToScan" value="net.acme.prs" />
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">
                    org.hibernate.dialect.MySQL5InnoDBDialect
                </prop>
                <prop key="hibernate.show_sql">false</prop>
            </props>
        </property>
    </bean>

    <bean id="mysqlDataSource" class="org.apache.commons.dbcp.BasicDataSource"  destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/prs" />
        <property name="username" value="" />
        <property name="password" value="" />
    </bean>
</beans>

【问题讨论】:

  • 我在任何地方都没有看到FetchType
  • FetchType 默认为 LAZY。见here
  • 不确定。但我相信您需要初始化您的收藏。例如private Set&lt;Permission&gt; grantedPermissions = new TreeSet&lt;&gt;();
  • 初始化grantedPermissions 也无济于事。
  • @5c1260e1c0 嗯,本可以发誓这是相反的。一定是混淆了EAGER 是一项要求这一事实。

标签: java spring hibernate


【解决方案1】:

我不明白你的问题是什么,因为你没有提到你遇到的任何错误。如果单纯发现grantedPermissions没有获取SQL,这就是lazy-fetching的意思:只有被访问时才获取,不被访问则不获取。

您在调试期间看到它被获取的原因是,当您在调试器中检查grantedPermissions 时,它正在访问该属性,这将触发延迟获取。但是,对于非调试模式,如果您没有访问该属性的代码,则不进行获取正是它应该做的。


如果您要问的是,您的代码处于非调试模式,将访问grantedPermissions,但在访问时延迟获取失败,那么这是由于访问超出事务:Hibernate 需要有一个活动会话发生延迟获取。如果延迟获取发生在事务之外,它将失败,因为没有打开的 Session。你应该重新审视你的设计,考虑

  1. 事务范围:不应围绕 DAO 进行事务,而应将事务边界置于适当的工作单元级别:可能是您的应用服务或控制器
  2. 进行正确的join fetch/eager fetch,这样就不会在事务之外发生延迟获取
  3. EclipseLink 是另一个 JPA 实现,它允许在会话结束后进行延迟获取。

【讨论】:

  • 虽然我的回答被接受了,但是我很想知道OP的问题是什么:OP是误解了lazy fetch的意思,还是会话关闭问题?
【解决方案2】:

关注http://docs.oracle.com/javaee/5/api/javax/persistence/FetchType.html
示例:如果要在“组”中自动加载“grantedPermissions”,则必须将注释更改为 @ManyToMany(fetch = FetchType.EAGER)

【讨论】:

  • 添加 (fetch = FetchType.LAZY) 没有帮助。
  • Hibernate 默认获取惰性数据,因此它什么都不添加。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-02
  • 1970-01-01
  • 2018-10-21
  • 2017-11-05
  • 1970-01-01
相关资源
最近更新 更多