我有一个服务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())
}
}
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>
答案 0 :(得分:3)
当使用bean的注入实例从bean的外部调用方法时,方法是事务性的,实际上这是一个围绕实际bean实例的事务代理。
Spring直接在bean实例上调用initialize方法,而不是在事务代理上调用,因此不会在事务中调用这些方法。
将initialize方法放在另一个bean中,该bean将使用注入的MyServiceBean并调用其initialize()
方法。
答案 1 :(得分:0)
问题是当Spring调用initialize时,bean周围没有事务代理。这是有目的的,因为哲学是bean在初始化之前还没有准备好使用。
一些解决方案:
在初始时数据处理的这种特殊情况下,通过TransactionTemplate或session.beginTransaction()进行手动事务处理。如果您使用JPA / Hibernate,请使用这样的代码将EntityManager添加到事务同步器:
EntityManager em = entityManagerFactory.createEntityManager();
TransactionSynchronizationManager.bindResource(entityManagerFactory, new EntityManagerHolder(em));
为应用程序中的所有初始化类型事件创建另一个bean,并让其调用其他bean。到那时,所有其他bean都将准备就绪,Spring会在它们周围放置一个事务代理。注意:不要将方法集作为init-method从这个bean中调用,因为这样特殊的bean将被初始化两次,这并不总是健康的:)为此目的创建另一种方法。
使用ApplicationListener。有了这个,你可以注册一个回调并处理ContextRefreshedEvent作为上下文初始化完成的标志。