Spring 05 -SpringAOP事务管理

1 事务

1.1 事务概念

事务是一个原子操作。是一个最小执行单元。可以由一个或多个SQL语句组成,在同一个事务当中,所有的SQL语句都成功执行时,整个事务成功,有一个SQL语句执行失败,整个事务都执行失败。

1.2 事务的特性

  • Atomicity(原子性)

一个事务中的事务操作,不可再分,要一起成功要么一起失败

  • Consistency(一致性)

一个或多个事务的操作,在操作前后数据是一致的

  • Isolation(隔离性)

一个事务的操作不能影响到另外一个事务

  • Durability(持久性)

一旦事务条件,将会永久保存到数据库中

1.3 并发问题

事务并发时的安全问题

问题描述
脏读一个事务读取到另一个事务还未提交的数据。大于等于 read-commited 可防止
不可重复读一个事务内多次读取一行数据的相同内容,其结果不一致。大于等于 repeatable-read 可防止
幻读一个事务内多次读取一张表中的相同内容,其结果不一致。serialized-read 可防止

1.4 隔离级别

名称描述
default(默认值)(采用数据库的默认的设置) (建议)
read-uncommited读未提交
read-commited读提交 (Oracle数据库默认的隔离级别)
repeatable-read可重复读 (MySQL数据库默认的隔离级别)
serialized-read序列化读

隔离级别由低到高为:read-uncommited < read-commited < repeatable-read < serialized-read

1.5 隔离级别的特性

  • 安全性:级别越高,多事务并发时,越安全。因为共享的数据越来越少,事务间彼此干扰减少。

  • 并发性:级别越高,多事务并发时,并发越差。因为共享的数据越来越少,事务间阻塞情况增多。

2 自定义事务管理

技术:Spring+DBUtils、AOP

1)增强类 (负责对方法进行增强,开启事务、提交事务、回滚事务、关闭资源)

2)配置切面(增强+切入点)

注意:要保证这个操作中的Connetion是同一个

2.1 事务管理类

public class TXAdvice {
    @Autowired
    TXUtils txUtils;
    public Object around(ProceedingJoinPoint joinPoint){
        try {
            //开启事务
            txUtils.start();
            //调用目标对象的方法
            Object obj = joinPoint.proceed();
            //提交事务
            txUtils.commit();
            return obj;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            //回滚事务
            txUtils.rollback();
        }finally {
            //释放资源
            txUtils.close();
        }
        return null;
    }
}

2.2 事务管理工具类

//事务工具类
@Component
public class TXUtils {
    @Autowired
    DataSource dataSource;
    ThreadLocal<Connection> tl = new ThreadLocal<>();
    public Connection getConnection(){
        try {
            Connection conn = tl.get();
            if(conn == null){
                conn = dataSource.getConnection();
                //保存ThreadLocal中
                tl.set(conn);
            }
            return conn;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }
    public void start(){
        Connection connection = getConnection();
        try {
            connection.setAutoCommit(false);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
    public void commit(){
        Connection connection = getConnection();
        try {
            connection.commit();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
    public void rollback(){
        Connection connection = getConnection();
        try {
            connection.rollback();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
    public void close(){
        Connection connection = getConnection();
        try {
            connection.close();
            //在ThreadLocal移除Connection对象
            tl.remove();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

2.3 DBUtils实现的Dao操作

@Repository
public class AccountDaoImpl implements AccountDao {
    @Autowired
    private QueryRunner qr;
    @Autowired
    TXUtils txUtils;

    @Override
    public Account selectAccount(String name) throws SQLException {
        return qr.query(txUtils.getConnection(),
                        "select * from tb_account where name = ?",
                        new BeanHandler<Account>(Account.class),
                        name);
    }

    @Override
    public int updateAccount(String name, double money) throws SQLException {
        String sql = "update tb_account set money = money + ? where name = ?";
        return qr.update(txUtils.getConnection(),
                         sql,
                         money,
                         name);
    }
}

2.4 业务层转账操作

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;
    @Override
    public String zhuanzhang(String fromName, String toName, double money) 
        throws SQLException {
        //1、判断用户是否存在
        Account account = accountDao.selectAccount(fromName);
        if(account == null){
            return "用户不存在";
        }
        //2、判断余额是否足够
        if(account.getMoney() < money){
            return "余额不足";
        }
        //3、判断对方账户是否存在
        Account toAccount = accountDao.selectAccount(toName);
        if(toAccount == null){
            return "对方用户不存在";
        }
        //4、我方扣钱
        accountDao.updateAccount(fromName,-money);
        System.out.println(10/0);
        //5、敌方加钱
        accountDao.updateAccount(toName,money);
        return "转账成功";
    }
}

2.5 SpringAOP事务管理配置

<bean id="advice" class="com.ying.advice.TXAdvice"></bean>

<!-- AOP配置-->
<aop:config>
    <!-- AOP切面配置-->
    <aop:aspect ref="advice">
        <!-- AOP切入点配置-->
        <aop:pointcut id="pc" expression="execution(* com.ying.service..*.*(..))"/>
        <!-- AOP通知配置-->
        <aop:around method="around" pointcut-ref="pc"/>
    </aop:aspect>
</aop:config>

2.6 注解式配置

<!-- 开启springAOP的注解扫描 -->
<aop:aspectj-autoproxy/>
@Component
@Aspect
public class TXAdvice { //通知增强
    @Autowired
    TXUtils txUtils;
    
    @Pointcut("execution(* com.ying.service..*.*(..))")
    public void pc(){}
    
    @Around("pc()")   
    public Object around(ProceedingJoinPoint joinPoint){
        try {
            //开启事务
            txUtils.start();
            //调用目标对象的方法
            Object obj = joinPoint.proceed();
            //提交事务
            txUtils.commit();
            return obj;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            //回滚事务
            txUtils.rollback();
        }finally {
            //释放资源
            txUtils.close();
        }
        return null;
    }    
}

3 声明式事务管理

3.1 导入依赖

<!-- Spring声明式事务管理依赖 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.3.6</version>
</dependency>
<!-- Spring声明式事务织入依赖 -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
</dependency>

3.2 配置事务管理器

 <!-- 配置Spring事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 注入数据源信息-->
    <property name="dataSource" ref="dataSource"/>
</bean>

注意:DataSourceTransactionManager 和 SqlSessionFactoryBean 要注入同一个DataSource,否则事务控制失败!!!

3.3 配置事务通知(增强)

<!-- 配置Spring事务的增强类-->
<tx:advice id="txAdvice" transaction-manager="transactionManager" >
    <!-- 配置Spring事务的增强类的信息-->
    <tx:attributes>
        <!--
           name:Spring:事务增强类增强的方法
           isolation:事务的隔离级别
           propagation:事务传播行为
                REQUIRED(以事务的方式运行,如有事务,加入事务,如无事务,则创建事务)   更新
                SUPPORTS(有事务则以事务方式运行,没有事务以非事务方式运行)            查询
           read-only:是否只读     查询true  更新false
           timeout:超时时间(默认为-1表示永久等待)   设置单位为‘秒’
           rollback-for:设置哪些异常回滚
           no-rollback-for:设置哪些异常不回滚

            增强方法的配置技巧
                通配符匹配
                    get*
                    select*
                    find*
                    query*
                    * 其他所有方法
           -->
        <tx:method name="get*" isolation="DEFAULT" 
                   propagation="SUPPORTS" read-only="true" timeout="-1"/>
        <tx:method name="select*" isolation="DEFAULT" 
                   propagation="SUPPORTS" read-only="true" timeout="-1"/>
        <tx:method name="find*" isolation="DEFAULT" 
                   propagation="SUPPORTS" read-only="true" timeout="-1"/>
        <tx:method name="query*" isolation="DEFAULT"
                   propagation="SUPPORTS" read-only="true" timeout="-1"/>

        <tx:method name="*" isolation="DEFAULT" 
                   propagation="REQUIRED" read-only="false" timeout="-1"/>
    </tx:attributes>
</tx:advice>

3.3.1 属性说明

isolation 隔离级别

名称描述
default(默认值)(采用数据库的默认的设置) (建议)
read-uncommited读未提交
read-commited读提交 (Oracle数据库默认的隔离级别)
repeatable-read可重复读 (MySQL数据库默认的隔离级别)
serialized-read序列化读

propagation 事务传播行为

名称作用
PROPAGATION_REQUIRED如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

当涉及到事务嵌套(Service调用Service)时,可以设置:

  • SUPPORTS = 不存在外部事务,则不开启新事务;存在外部事务,则合并到外部事务中。(适合查询)

  • REQUIRED = 不存在外部事务,则开启新事务;存在外部事务,则合并到外部事务中。 (默认值)(适合增删改)

readonly 读写性

  • true:只读,可提高查询效率。(适合查询)
  • false:可读可写。 (默认值)(适合增删改)

timeout事务超时时间

当前事务所需操作的数据被其他事务占用,则等待。

  • 1000:自定义等待时间1000(秒)。
  • -1:由数据库指定等待时间,默认值。(建议)

rollbackFor 配置回滚的异常

noRollbackFor 配置不回滚的异常

3.4 配置事务切面

<!-- 配置Spring切面-->
<aop:config>
    <!-- 配置Spring切入点-->
    <aop:pointcut id="pc" expression="execution(* com.ying.service..*.*(..))"/>
    <!-- 配置Spring增强类-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
</aop:config>

3.5 基于注解配置

 <!-- Spring声明式事务基于注解的配置-->
<bean id="transactionManager" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 注入数据源信息-->
    <property name="dataSource" ref="dataSource"/>
</bean>

<!--  开启Spring事务的包扫描  -->
<tx:annotation-driven transaction-manager="transactionManager"/>

在service层的方法或者类上添加注解

  • 注解加在类上表示所有的方法都使用事务管理
  • 注解加在方法上仅表示当前方法使用事务管理
@Transactional(isolation = Isolation.DEFAULT,
               propagation = Propagation.REQUIRED,readOnly = false,timeout = -1)
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yinying293

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值