Spring 05 -SpringAOP事务管理

SpringAOP事务管理

  • 1 事务
    • 1.1 事务概念
    • 1.2 事务的特性
    • 1.3 并发问题
    • 1.4 隔离级别
    • 1.5 隔离级别的特性
  • 2 自定义事务管理
    • 2.1 事务管理类
    • 2.2 事务管理工具类
    • 2.3 DBUtils实现的Dao操作
    • 2.4 业务层转账操作
    • 2.5 SpringAOP事务管理配置
    • 2.6 注解式配置
  • 3 声明式事务管理
    • 3.1 导入依赖
    • 3.2 配置事务管理器
    • 3.3 配置事务通知(增强)
      • 3.3.1 属性说明
    • 3.4 配置事务切面
    • 3.5 基于注解配置

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:config>
    
    <aop:aspect ref="advice">
        
        <aop:pointcut id="pc" expression="execution(* com.ying.service..*.*(..))"/>
        
        <aop:around method="around" pointcut-ref="pc"/>
    aop:aspect>
aop:config>

2.6 注解式配置


<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 导入依赖


<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-txartifactId>
    <version>5.3.6version>
dependency>

<dependency>
    <groupId>org.aspectjgroupId>
    <artifactId>aspectjweaverartifactId>
    <version>1.9.6version>
dependency>

3.2 配置事务管理器

 
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    
    <property name="dataSource" ref="dataSource"/>
bean>

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

3.3 配置事务通知(增强)


<tx:advice id="txAdvice" transaction-manager="transactionManager" >
    
    <tx:attributes>
        
        <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 配置事务切面


<aop:config>
    
    <aop:pointcut id="pc" expression="execution(* com.ying.service..*.*(..))"/>
    
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
aop:config>

3.5 基于注解配置

 
<bean id="transactionManager" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    
    <property name="dataSource" ref="dataSource"/>
bean>


<tx:annotation-driven transaction-manager="transactionManager"/>

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

  • 注解加在类上表示所有的方法都使用事务管理
  • 注解加在方法上仅表示当前方法使用事务管理
@Transactional(isolation = Isolation.DEFAULT,
               propagation = Propagation.REQUIRED,readOnly = false,timeout = -1)

你可能感兴趣的:(Java,#,框架演进,spring,数据库,java)