从事务服务方法调用 DAO 时"HibernateException: No Session found for current thread"



我有一个服务 Bean,它在实例化后将数据库中的对象加载到对象缓存中。如果我将调用我的 DAO 对象方法的服务方法标记为 @Transactional,则会收到"HibernateException:找不到当前线程的会话"错误。但是,如果我将 DAO 类标记为 @Transactional,则不会收到此类错误,并且它工作正常。

问题是我不能从服务对象中的同一方法进行多个 DAO 调用并将其作为一个事务。对可能导致这种情况的原因有什么想法吗?

我正在使用Spring 3.1和Hibernate 4。

DAO 示例:

@Repository
public class HibernateObjectDao implements ObjectDao {
    SessionFactory sessionFactory;
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
    public List<Object> getObjects() {
        return sessionFactory.getCurrentSession()
            .createQuery("from Object").list();
    }
}

服务Bean示例:

@Service
public class MyServiceBean implements AbstractMyServiceBean
{

    @Resource
    private ObjectDao objectDao;
    private HashMap<String,Object> objectCache;

    public MyServiceBean() {
        this.objectCache = new HashMap<String,Object>();
    }

    @Autowired
    public void setObjectDao(ObjectDao objectDao) {
        this.objectDao = objectDao;
    }

    @Transactional
    public void initialize() {
        loadObjectCache();
    }
    public void loadObjectCache() {
        objectCache.put("stuff",this.objectDao.getObjects())
    }
}

应用程序上下文.xml摘录:

<bean id="objectDao" class="com.example.persistence.HibernateObjectDao">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="myServiceBean"
    class="com.example.service.MyServiceBean" 
    init-method="initialize">
    <property name="objectDao" ref="objectDao" />
</bean>

当使用注入的 Bean 实例从 Bean 外部调用方法时,它们是事务性的,这实际上是围绕实际 Bean 实例的事务代理。

Spring 直接在 Bean 实例上调用 initialize 方法,而不是在事务代理上调用,因此不会在事务中调用这些方法。

将初始化方法放在另一个 Bean 中,该 Bean 将使用注入的 MyServiceBean 并调用其 initialize() 方法。

问题是当 Spring 调用初始化时,bean 周围没有事务代理。这是故意的,因为哲学是 bean 在初始化之前还没有准备好使用。

一些解决方案:

  • 在这种初始时间数据处理的特殊情况下,通过 TransactionTemplate 或 session.beginTransaction() 进行手动事务处理。如果使用 JPA/Hibernate,请使用如下代码将 EntityManager 添加到事务同步器:

    EntityManager em = entityManagerFactory.createEntityManager();
    TransactionSynchronizationManager.bindResource(entityManagerFactory, new EntityManagerHolder(em));
    
  • 为应用程序中的所有初始化类型事件创建另一个 bean,并让它调用其他 bean。到那时,所有其他的 bean 都将准备就绪,Spring 将在它们周围放置一个事务代理。注意:不要从这个 bean 调用设置为 init 方法的方法,因为这样特定 bean 将被初始化两次,这并不总是健康的:)为此创建另一种方法。

  • 使用应用程序侦听器。有了这个,你可以注册一个回调并将 ContextRefreshedEvent 处理为上下文初始化完成的标志。

相关内容

最新更新