Hibernate - 无法懒加载角色的集合 - 无法初始化代理 - 没有会话。

3
问题:无法通过Spring控制器在User对象中添加Address对象。 UserAddress类均为@EntityUser具有带有FetchType=LAZYList<Address>
@Repository
public class UserDao{
    @Autowired
    private SessionFactory sessionFactory;
    ...
    public User get(String username) {
        Session session = sessionFactory.getCurrentSession();
        return (User)session.get(User.class, username);
    }
    ...
    public void update(User user){
        Session session = sessionFactory.getCurrentSession();
        session.saveOrUpdate(user);
    }
    ...
}


@Service
@Transnational
public class UserService{
    @AutoWired
    private UserDao userDao;
    ...

    @Transactional(readOnly = true)
    public User get(String username) {
        Session session = sessionFactory.getCurrentSession();
        return (User)session.get(User.class, username);
    }

    public void update(User user){
        userDao.update(user);
    } 
    ...
}

@Controller
public class UserController{
    @AutoWired
    private UserService userService;
    ....
    public String update(){
        User user = userService.get("user0001");
        user.getAddressList.add(new Address("new street"));
        return "update";
    }
}

Spring.xml

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

<tx:annotation-driven transaction-manager="transactionManager" />

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location">
        <value>classpath:jdbc.properties</value>
    </property>
</bean>

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${jdbc.driverClassName}" />
    <property name="jdbcUrl" value="${jdbc.url}" />
    <property name="user" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
</bean>

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource">
        <ref bean="dataSource"/>
    </property>
    <property name="packagesToScan" value="com.entity" />
    <property name="hibernateProperties">
       <props>
         <prop key="hibernate.dialect">${hibernate.dialect}</prop>
         <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
       </props>
    </property> 
</bean>

一切正常。但是我无法在@Controller内部更改user对象。当user对象在@Controller级别中被更改时,没有Hibernate会话与该对象相关联。不知何故,该对象已经超出了Hibernate上下文。错误发生在@Controller中的.add(new Address("new street"));语句处。为什么禁止在通过Hibernate会话接收到的Controller内更改对象?我所遵循的方式是不正确的吗?如果不是,我做错了什么?--Spring 4,Hibernate 4
1个回答

3
User有一个List<Address>。当你从数据库中获取用户而不是列表时,Hibernate会插入一个代理来处理地址的获取。
这个代理需要有一个会话才能做任何事情。当您尝试添加地址时,您处于事务注释范围之外,因此没有会话可用。
让你开始的最佳方法是在UserService中添加一个带有@Transactional注释的方法来添加地址。

一旦对象离开该方法,就会产生混淆。一旦它离开方法,包装在该方法周围的事务性代码会进行清理和关闭会话。当Hibernate为您获取对象时,它添加了一个代理,以允许您惰性地获取集合(更多的样板代码包装!)。然而,代理需要此会话才能执行任何操作。但是当对象离开事务范围时,此会话已被关闭。因为会话已关闭,所以无法获取或添加数据。 - M.P. Korstanje
如果您自己编写了事务样板代码,您很快就能看到在关闭会话后尝试对数据进行操作的问题。现在,这个问题被 @Transactional 关键字的魔力所隐藏。 - M.P. Korstanje
1
简短概括:规则是:不要在事务之外修改数据。这也意味着您可能可以放弃DAO和Service,只使用每个用户的控制器处理修改,但这并不是我的专业领域。 - M.P. Korstanje
另一个需要注意的事情是:事务管理是通过一个bean完成的。你不是获取UserService,而是获取一个包装你的用户服务的bean。因此,如果你通过一个被注释为@Transactional的方法进入用户服务,直到你通过bean离开,所有操作都将在事务内部进行。调用另一个方法也会将其放入事务范围内。但是,如果你通过一个没有注释的方法进入UserService,然后尝试使用一个有注释的方法,你将没有会话。 - M.P. Korstanje
我的问题是,我可以使用Spring JDBCHibernate等来获取实体。但为什么Controller需要了解这个懒加载和代理。对于Controller来说,它只是一个对象。你能给我指一些关于“TLDR:规则是:不要在事务外更改数据”的来源吗? - user2172625
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接