把hibernate事务交给spring管理

来源:互联网 发布:防止刷短信验证码 php 编辑:程序博客网 时间:2024/05/29 19:11

一直在用spring管理hibernate的事务,但是一直没太搞清楚,今天梳理一下

版本问题

开始使用的spring 4.2.6.release和hibernate 5.2.5.final结果启动就报错
java.lang.NoSuchMethodError: org.hibernate.Session.getFlushMode()Lorg/hibernate/FlushMode;
有两种方案一种是把hibernate降到5.1,另一种是把spring升到5.3,最后我使用的是spring4.3.2.release和hibernate5.2.5.final.

注解方式

使用注解@Transactional前,需要在xml配置

    <!-- PlatformTransactionMnager -->    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <property name="dataSource" ref="dataSource" />    </bean>    <!-- enable transaction annotation support -->    <tx:annotation-driven transaction-manager="transactionManager" />

然后在需要启用事务的类或方法上加上@Transactional,在类上加对所有方法都有效,在单独方法上加只对这个方法有效.

@Transactional的参数

rollbackFor

定义回滚的抛出类型,默认是RuntimeException.class,设置成@Transactional(rollbackFor = Exception.class)后,Exception和RuntimeException都会回滚,总之就是触发回滚的异常类型.在源码里是这样定义的Class<? extends Throwable>[] noRollbackFor() default {};所以范围限制在Throwable的子类.

noRollbackFor

和rollbackFor相反,定义之后,该种异常不会触发回滚

isolation

事务隔离级别,默认是Isolation.DEFAULT,这里需要先介绍一下三种现象
1、脏读(dirty read):一个事务可以读取另一个尚未提交事务的修改数据。
2、非重复读(nonrepeatable read):在同一个事务中,同一个查询在T1时间读取某一行,在T2时间重新读取这一行时候,这一行的数据已经发生修改,可能被更新了(update),也可能被删除了(delete)。
3、幻像读(phantom read):在同一事务中,同一查询多次进行时候,由于其他插入操作(insert)的事务提交,导致每次返回不同的结果集。
不同级别对应的情况

级别 说明 DEFAULT 使用底层数据存储的默认隔离级别,所有的其他级别与jdbc隔离级别一致 READ_UNCOMMITTED 这种等级允许一个事务改变了一行,在提交之前,另一个事务能够读到这种改变.如果改变被回滚了,第二个事务会获得一行非法的数据 READ_COMMITTED 禁止一个事务读取其他一行数据的未提交的内容 REPEATABLE_READ 禁止事务读取未提交的内容;禁止一个事务读取一行时另一个事务修改这行,然后第一个事务重新读这行,第二次获得一个不一样的值(不可重复读) SERIALIZABLE 除了REPEATABLE_READ禁止的情况外,禁止当一个事务通过where条件读查出的所有行,另一个事务插入一行满足条件的数据,然后第一个事务重新用相同的条件读,在第二次读的时候获得额外的幻像行

级别和现象的关系

隔离级别 脏读 非重复读 幻像读 READ_UNCOMMITTED 允许 允许 允许 READ_COMMITTED 允许 允许 REPEATABLE_READ 允许 SERIALIZABLE

propagation

事务的传播类型

类型 说明 REQUIRED(默认值) 在有transaction状态下执行;如当前没有transaction,则创建新的transaction. SUPPORTS 如当前有transaction,则在transaction状态下执行;如果当前没有transaction,在无transaction状态下执行 MANDATORY 必须在有transaction状态下执行,如果当前没有transaction,则抛出异常IllegalTransactionStateException. REQUIRES_NEW 创建新的transaction并执行;如果当前已有transaction,则将当前transaction挂起. NOT_SUPPORTED 在无transaction状态下执行;如果当前已有transaction,则将当前transaction挂起. NEVER 在无transaction状态下执行;如果当前已有transaction,则抛出异常IllegalTransactionStateException.

readOnly(默认 false)

如果readonly设置成true.当事务里有增删改操作时,会直接抛异常java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed

timeout

事务的超时时间,当为默认值 TransactionDefinition.TIMEOUT_DEFAULT时,会使用底层系统的默认超时时间.

transactionManager

一个特定事务的标识值,可以用来觉得目标transaction manager,匹配一个特定的 {@link org.springframework.transaction.PlatformTransactionManager}的bean definition.

xml方式

 <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">        <property name="sessionFactory" ref="sessionFactory " />    </bean>   <!--  声明式容器事务管理 ,transaction-manager指定事务管理器为transactionManager -->    <tx:advice id="txAdvice" transaction-manager="transactionManager">        <tx:attributes>            <!-- 定义方法的事务参数-->            <tx:method name="add*" propagation="REQUIRED" />            <tx:method name="get*" propagation="REQUIRED" />            <tx:method name="del*" propagation="REQUIRED" />            <tx:method name="do*" propagation="REQUIRED"  />            <tx:method name="*" read-only="true" />        </tx:attributes>    </tx:advice>    <aop:config expose-proxy="true">        <!-- 只对业务逻辑层实施事务 -->        <aop:pointcut id="txPointcut" expression="execution(* com.xxx.ws.service..*.*(..))" />        <!-- Advisor定义,切入点和通知分别为txPointcut、txAdvice -->        <aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice"/>    </aop:config>

这样配置后所有service包下的类和方法都加上事务了.

dao的写法

在把事务交给spring管理后,dao里的查询方法也要采用一定的写法
1.采用sessionFactory.getCurrentSession();的方式获取session
2.操作完成后不要关闭session
3.不要把异常catch掉,要往外抛,否则spring检测不到异常,就无法回滚
4.不要显示的调用Transaction的操作,会与spring的冲突

 public int updateSql(String sql,Object...params)    {        Session session = sf.getCurrentSession();        try        {            NativeQuery nativeQuery = session.createNativeQuery(sql);            for (int i = 0; i <params.length ; i++)            {                nativeQuery.setParameter(i,params[i]);            }            int num = nativeQuery.executeUpdate();            return num;        } catch (Exception e)        {            throw new RuntimeException(e);        }finally        {        }    }

一些问题的处理

java.lang.IllegalStateException: Transaction not successfully started

不要在dao里显示的调用tx的操作,会出现上面的报错移除tx.commit()后修复.

必须要try-catch异常

有时候业务逻辑必须要把异常catch掉,这时候spring因为检测不到异常无法回滚,可以通过调用
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 来手动回滚

org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread

没有配置spring管理的事务,或者配置没生效

0 0