使用spring声明式事务首先是配置datasource,这是java sql提供的一个接口(A factory for connections to the physical data source that this DataSource
object represents. ),一般会使用c3p0数据库连接池来作为datasource,它实现了datasource接口。然后是配置sessionFactory,将datasource注入进去,由sessionFactory来管理这些连接,代表连接的是session。最后配置事务管理器,管理session。详细配置网上都有。下面说说大概的过程:
先看看涉及到的几个对象:hibernateTransactionManager,这是spring实现的一个事务管理器
sessionfactory,hibernate的session工厂,里面拥有currentSessionContext来管理Session
springSessionContext,spring实现的一个session上下文,上层接口是hibernate的currentSessionContext,用来管理session的范围(hibernate 中还有三个类实现了此接口,区别是每个实现类中管理的session的范围都不同)
通过上述配置后,hibernateTransactionManager拥有了sessionFactory,sessionFactory里有currentSessionContext,当我们使用@transactional注解的时候,通过aop会生成一个代理类代理当前类(具体得看源码),在要访问的方法开始之前调用hibernateTransactionManager的getTransaction(TransactionDefinition definition)方法将事务的定义传递进去,然后调用doBegin(transaction, definition)方法开启事务,在doBegin里调用sessionfactory的getCurrentSession()或者是调用openSession()方法得到session。openSession是直接开启一个新事物,而getCurrentSession是得到session上下文的session,由currentSessionContext管理,所以getCurrentSession会调用springSessionContext的getCurrentSession方法取得session上下文中的session。springSessionContext会将session放到threadlocal变量中,用来保证在同一个线程下用到的都是同一个session,并且session之间不会互相干扰
总结:如果说session代表一个连接的话,和数据库交互的时候我么就必须取得这个session然后开事务,问题来了我门在什么地方在什么时候取得这个session,spring的做法是:由于每个请求会使用一个线程处理,就在你开事务的地方取得这个session然后调用session的beginTransaction()方法开事务,并把这个session绑定到线程上,以后只要在这个线程上与数据库交互都用这个session