spring基础事务学习

Spring事务

1.什么是事务

把一组业务当成一个业务来做;要么都成功,要么都失败,保证业务操作完整性的一种数据库机制

声明式事务

事务的特性ACID

A:原子性,指的是,在一组业务操作下,要么都成功,要么都失败,保证业务组合的完整性;
C:一致性,事务前后的数据要保持一致性
I:隔离性,并发情况下,事务之间要相互隔离
D:持久性,数据一旦保存就是持久的,可反复使用的

在事务控制方面主要分两类
编程式事务

在代码中直接加入事务处理的逻辑,可能需要在代码中显示调用beginTransaction(),commit(),rollback()等事务管理相关的方法
示例:

try{
connetion.autoCommit(false);
...
...
connction.commint();
}catch(Exception e){
connction.rollback();
}

声明式事务

在方法的外部添加注释或者直接在配置文件中定义,将事务管理代码从业务代码中分离出来,以声明的方式来实现事务管理,spring的AOP恰巧可以完成此类功能,事务管理代码的固定模式作为一种横切点关注,通过AOP方法模块化,进而实现声明式事务

spring声明式事务配置

Spring从不同的事务管理API中抽象出了一整套事务管理机制,让事务管理代码从特定的事务技术中独立出来.开发人员通过配置的方式进行事务管理,而不必了解其底层是如何实现的.
Spring的核心事务管理抽象是PlatformTransactionManager。它为事务管理封装了一组独立于技术的方法.无论使用Spring的哪种事务管理策略(编程式或声明式),事务管理器都是必须的.
事务管理器可以以普通的bean的形式声明在Spring IOC容器中

1.在配置文件中增加事务管理器,和数据库相关配置

示例
添加数据库相关配置和jdbcTemplate配置

  <context:property-placeholder location="db.properties"></context:property-placeholder>
    <!--配置第三方bean-->
    <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource" lazy-init="true">
        <property name="username" value="${mysql.username}"></property>
        <property name="password" value="${mysql.password}"></property>
        <property name="url"  value="${mysql.url}"></property>
        <property name="driverClassName" value="${mysql.driver}"></property>
    </bean>
    <!--配置JdbcTemplate Bean组件-->
    <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
        <property name="dataSource" ref="dataSource" ></property>
    </bean>

添加事务控制配置

<!--    配置事务管理器-->
    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="dataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
<!--    基于注解的事务,需要开启事务注解驱动-->
    <tx:annotation-driven transaction-manager="dataSourceTransactionManager"></tx:annotation-driven>

2.对需要进行事务的类,方法使用@Transactional进行标注

-1. @Transactional标注在类上时,表示当前类的所有方法都运用了事务
-2. @Transactional标注在方法上时,表示只有当前方法使用事务

注意:

可以类和方法上面同时都存在, 如果类和方法都存在@Transactional会以方法的为准,如果方法上面没有@Transactional会以类上面的为准

建议:

@Transactional写在方法上面,控制粒度更细, 建议@Transactional写在业务逻辑层上,因为只有业务逻辑层才会有嵌套调用的情况。

示例

    /**
     * 转账
     *
     * @param add_id        转入人id
     * @param sub_id        转出人id
     * @param changeBalance 转账金额
     * @return
     */
    @Override
    @Transactional(isolation = Isolation.REPEATABLE_READ)//给方法执行添加事务
    public Integer transfer(Integer add_id, Integer sub_id, Integer changeBalance) {
        Integer result = accountDao.addBalance(add_id, changeBalance);
        result += accountDao.subBalance(sub_id, changeBalance);
        return result;
    }

事务配置的属性

@Transactional的源码为

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";

    Propagation propagation() default Propagation.REQUIRED;

    Isolation isolation() default Isolation.DEFAULT;

    int timeout() default -1;

    boolean readOnly() default false;

    Class<? extends Throwable>[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};
}

各个属性为:

1.isolation:设置事务的隔离级别
2.propagation:事务的传播行为
3.noRollbackFor:哪些异常事务可以不回滚
4.noRollbackForClassName:发生异常不会回滚的类名,填写的参数是全类名
5.rollbackFor:哪些异常事务需要回滚
6.rollbackForClassName:填写的参数是全类名
7.readOnly:设置事务是否为只读事务
8.timeout:事务超出指定执行时长后自动终止并回滚,单位是秒

1)设置隔离级别:isolation

隔离级别是用来解决并发事务所产生一些问题的一种设定,不同的隔离级别应对不同的并发下产生问题的处理方案;

并发下可能产生的问题
1.脏读:

概念:一个事务,读取了另一个事务中没有提交的数据,会在本事务中产生的数据不
一致的问题
对应隔离级别处理方案:使用隔离级别"读已提交"可以进行处理,使得事务之间只读取其他事务提交后的信息,而不去获取未提交的数据,从而处理脏读问题

@Transactional(isolation = Isolation.READ_COMMITTED)

2.不可从重复读

概念:一个事务中,多次读取相同的数据, 但是读取的结果不一样, 会在本事务中产生数据不一致的问题
对应隔离级别处理方案:使用隔离级别"重复读"可以进行处理,使用行锁的方式,在事务执行期间,禁止其他事务对这个数据进行操作,让事务在一次更新过程中可以读取到相同的值

@Transactional(isolation = Isolation.REPEATABLE_READ)

3.幻读:

概念:一个事务中,多次对数据进行整表数据读取(统计),但是结果不一样, 会在本事务中产生数据不一致的问题。
对应隔离级别处理方案:使用隔离级别"串行化"可以进行处理

@Transactional(isolation = Isolation.SERIALIZABLE)

注意:

串行化事务隔离级别后在事务执行期间,会禁止其他事务对表进行添加,更新,删除等操作,确实避免了任何并发问题,但是效率十分低下

在不设置事务隔离的情况下不同数据库的默认隔离级别是不同的
oracle默认为读已提交,可已通过以下语句查看隔离级别

SELECT s.sid, s.serial#,
CASE BITAND(t.flag, POWER(2, 28))
WHEN 0 THEN 'READ COMMITTED'
ELSE 'SERIALIZABLE'
END AS isolation_level
FROM v$transaction t
JOIN v$session s ON t.addr = s.taddr AND s.sid = sys_context('USERENV', 'SID');

mysql默认隔离级别为重复读,可以通过以下语句查看

SELECT @@tx_isolation;
2)事务的传播性:propagation

事务的传播特性

指的是当一个事务方法被另一个事务方法调用时,这个事务方法的处理方式

spring的事务传播行为有如下属性选择具体如下:

事务传播行为类型 外部不存在事务 外部存在事务 操作方式
REQUIRED(默认) 开启新的事务 融合到外部事务中 @Transactional(propagation = Propagation.REQUIRED)适用增删改查
SUPPORTS 不开启新的事务 融合到外部事务中 @Transactional(propagation = Propagation.SUPPORTS)适用查询
REQUIRES_NEW 开启新的事务 挂起外部事务,创建新的事务 @Transactional(propagation = Propagation.REQUIRES_NEW)适用内部事务和外部事务不存在业务关联情况,如日志
NOT_SUPPORTED 不开启新的事务 挂起外部事务 @Transactional(propagation =Propagation.NOT_SUPPORTED)不常用
NEVER 不开启新的事务 抛出异常 @Transactional(propagation = Propagation.NEVER )不常用
MANDATORY 抛出异常 融合到外部事务中 @Transactional(propagation = Propagation.MANDATORY)不常用

示例

    /**
     * 加钱
     *
     * @param id           人员id
     * @param changBalance 增加金额
     * @return
     */
    @Override
    @Transactional(propagation= Propagation.REQUIRED)
    public Integer addBalance(Integer id, Integer changBalance) {
        return accountDao.addBalance(id, changBalance);
    }
3)超时属性:timeout

概念:

指定事务等待的最长时间单位为秒

作用:

当前事务访问数据时,有可能访问的数据被别的数据进行加锁的处理,那么此时事务就必须等待,如果等待时间过长给用户造成的体验感差

示例

/**
     * 加钱
     *
     * @param id           人员id
     * @param changBalance 增加金额
     * @return
     */
    @Override
    @Transactional(propagation= Propagation.REQUIRED,timeout = 2)
    public Integer addBalance(Integer id, Integer changBalance) {
        return accountDao.addBalance(id, changBalance);
    }
4)设置事务只读:readOnly

使用场景:

基本只会使用在业务查询之中
如果你一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持SQL执行期间的读一致性;
如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询SQL必须保证整体的读一致性,否则,在前条SQL查询之后,后条SQL查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持(如:设置不可重复度、幻影读级别)

示例

@Override
    @Transactional(readOnly = true)
    public Account selectById(Integer id) {
        return accountDao.selectById(id);
    }
5)异常属性noRollbackFor,rollbackFor等

设置当前事务出现的那些异常就进行回滚或者提交
默认对于RuntimeException 及其子类 采用的是回滚的策略
默认对于Exception 及其子类 采用的是提交的策略。

noRollbackFor:设置哪些异常不回滚
rollbackFor:设置哪些异常回滚
示例:

@Override
    @Transactional(readOnly = true,rollbackFor = {FileNotFoundException.class})
    public Account selectById(Integer id) {
        return accountDao.selectById(id);
    }
6)在实战中事务的使用方式

如果当前业务方法是一组 增、改、删 可以这样设置事务

@Transactional

如果当前业务方法是一组 查询 可以这样设置事务

@Transactionl(readOnly=true)

如果当前业务方法是单个 查询 可以这样设置事务

@Transactionl(propagation=propagation.SUPPORTS ,readOnly=true)

3基于xml的事务配置

<bean id="transactionManager" class="org.springframework.jdbc.datasou
rce.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!‐‐
基于xml配置的事务:依赖tx名称空间和aop名称空间
1、spring中提供事务管理器(切面),配置这个事务管理器
2、配置出事务方法
3、告诉spring哪些方法是事务方法(事务切面按照我们的切入点表达式去切入事
务方法)
‐‐>
<bean id="bookService" class="cn.tulingxueyuan.service.BookService">
</bean>
<aop:config>
<aop:pointcut id="txPoint" expression="execution(* cn.learn.service.*.*(..))"/>
        <!‐‐事务建议:advice‐ref:指向事务管理器的配置‐‐>
        <aop:advisor advice‐ref="myAdvice" pointcut‐ref="txPoint"></aop:advisor>
    </aop:config>
    <tx:advice id="myAdvice" transaction‐manager="transactionManager">
        <!‐‐事务属性‐‐>
        <tx:attributes>
            <!‐‐指明哪些方法是事务方法‐‐>
            <tx:method name="*"/>
            <tx:method name="checkout" propagation="REQUIRED"/>
            <tx:method name="get*" read‐only="true"></tx:method>
        </tx:attributes>
    </tx:advice>
</beans>

总结

1.什么是事务
2.声明式事务
3.spring的事务配置
4.spring基于注释的事务实现
5.spring基于xml配置的事务实现
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342