0

我有一个服务 bean,它在实例化后从数据库中加载对象到对象缓存中。如果我将调用我的 DAO 对象方法的服务方法标记为 @Transactional,那么我会收到“HibernateException: No Session found for current thread”错误。但是,如果我将 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())
    }
}

ApplicationContext.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>
4

2 回答 2

3

使用注入的 bean 实例从 bean 外部调用方法时,方法是事务性的,这实际上是实际 bean 实例周围的事务代理。

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

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

于 2012-06-13T17:31:15.400 回答
0

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

一些解决方案:

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

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

  • 使用ApplicationListener。有了这个,您可以注册一个回调并处理ContextRefreshedEvent作为上下文初始化完成的标志。

于 2012-06-13T21:05:19.963 回答