Spring事务管理的三种方式

整合spring事务控制的三种方式

  1. 手动控制事务
  2. 使用切面aop控制
  3. 声明式事务控制
    a) 三种方式当属手动模式较原始且麻烦,未经框架整合,但是最能凸显底层原理所在,作为了解底层结构有帮助
    b) 切面是spring的特色了,可以做实现外加业务而无需对源码做更改,让人很省心,当然优秀的框架自然更加整合度更高,不至于让人从底层逻辑开始开发,而忽略业务逻辑,对于事务控制还需要整合更多.
    c) 声明式事务控制可以使用jdbc模板,虽然这个模板无法实现大规模业务爆发,但是实现小的需求还是很方便的,通过spring整合mybatis框架会更加优秀.
    d) 介绍中决定使用部分注解和部分配置文件结合,方便也现实些,毕竟即不建议全程xml配置狂撸,硬伤,也不建议全程注解裸奔,这么多我也记不过来!
    e) 还有一种就是编程式事务控制,其类似于声明式事务控制,又比声明式事务控制麻烦,不实用,就不介绍了!

大概业务逻辑:

  1. 创建工程(略)
  2. 导入依赖(lue)
  3. 容器(配置文件)
  4. 日志配置文件和jdbc配置文件(略)
  5. 原始数据(略)
  6. 数据封装类(略)
  7. 数据持久层—整合DBUtils框架(或者使用Spring提供的JdbcTemplate模板)
  8. 业务层
  9. (手动实现事务控制还需要创建连接和事务管理)
  10. 测试—整合junit框架

------------------------------------------------------华丽的分割线------------------------------------------------------------------------------------
第一种方式:手动控制事务

1.工程框架
在这里插入图片描述
2.容器

 <!--开启注解扫描 -->
    <context:component-scan base-package="java"></context:component-scan>
    <!--导入数据库配置文件-->
    <context:property-placeholder location="jdbc.properties"></context:property-placeholder>
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <!--导入数据源-->
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>
    <!--数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <!--Bean注入,这里使用注解更方便-->
    <bean id="accountService" class="service.impl.AccountServiceImpl"></bean>
    <bean id="accountDao" class="dao.impl.AccountDaoImpl"></bean>
    <bean id="connectionUtils" class="utils.ConnectionUtils"></bean>
    <bean id="transactionManager" class="utils.TransactionManager"></bean>

3.dao层

public class AccountDaoImpl implements AccountDao {

    @Autowired
    //注入连接,绑定连接和线程,为了让每次进行CRUD都使用同一个连接
    ConnectionUtils connectionUtils;

    @Autowired
    QueryRunner queryRunner;

    public Account findByName(String name) {
        List<Account> list = null;
        try {
            list = queryRunner.query(connectionUtils.getConnection(),"select * from account where name = ?", new BeanListHandler<Account>(Account.class), name);
            if (list.isEmpty()){
                return null;
            }else if (list.size()>1){
                throw new RuntimeException("账户不唯一,无法确定");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return list.get(0);
    }

    public void update(Account account) {
        try {
            queryRunner.update(connectionUtils.getConnection(),"update account set name = ?,money = ? where id = ?",account.getName(),account.getMoney(),account.getId());
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }
}

4.service层

public class AccountServiceImpl implements AccountService {

    @Autowired
    //导入事务管理器
    TransactionManager transactionManager;

    @Autowired
    AccountDao accountDao;

    public Account findByName(String name) {
        return accountDao.findByName(name);
    }

    public void update(Account account) {
        accountDao.update(account);

    }

    public void updateAccount(String name1, String name2, Float money) {
        try {
            //开启事务
            transactionManager.begin();
            Account account1 = accountDao.findByName(name1);
            Account account2 = accountDao.findByName(name2);
            Float money1 = account1.getMoney();
            if (money1 >= 100f){
                account1.setMoney(account1.getMoney()-money);
            }else {
                throw new RuntimeException("账户余额不足,转账不成功");
            }
            account2.setMoney(account2.getMoney()+money);
            accountDao.update(account1);
            //在中间加入异常,测试事务管理是否成功
            int i= 1/0;
            accountDao.update(account2);
            //提交事务
            transactionManager.commit();
        } catch (RuntimeException e) {
            //回滚事务
            transactionManager.rollback();
            e.printStackTrace();
        } finally {
            //关闭事务
            transactionManager.close();
        }

    }
}

5.自定义连接

public class ConnectionUtils {
    ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

    @Autowired
    //注入数据源
    DataSource dataSource;

    public Connection getConnection(){
        //从本地容器中获取连接
        Connection conn = tl.get();
        if (conn==null){
            try {
              //如果没有连接就从数据源中获取
             conn = dataSource.getConnection();
                tl.set(conn);

            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return conn;
    }
    //释放连接
    public void release(){
        tl.remove();
    }

}

6.自定义事务管理器

public class TransactionManager {

    @Autowired
    ConnectionUtils connectionUtils;

    //手动开启事务,把自动提交改为手动提交
    public void begin(){
        try {
            connectionUtils.getConnection().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    //事务提交方法
    public void commit(){
        try {
            connectionUtils.getConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    //事务回滚方法
    public void rollback(){
        try {
            connectionUtils.getConnection().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    //事务关闭和释放连接方法
    public void close(){
        try {
            connectionUtils.getConnection().close();
            connectionUtils.release();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

7.测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:ApplicationContext.xml")
public class AccountTest {

    @Autowired
    AccountService accountService;

    @Test
    public void function(){
        accountService.updateAccount("bbb","aaa",100f);
        
    }
    
}

--------------------------------------------------------------华丽的分割线----------------------------------------------------------------------------

第二种方式:AOP切面
切面的方式相比较手动控制事务,无需更改业务层代码,减少代码侵入
切面的方式需要明确切面,切入点和通知等
这里我只写更改后的代码吧,免得太冗余

1.容器:添加aop相关配置

 <!-- 配置spring开启注解AOP的支持 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    <!--设置aop配置-->
    <aop:config>
        <!--配置切入点-->
        <aop:pointcut id="pointcut" expression="execution(* service..*.*(..))"></aop:pointcut>
        <!--配置切面(这里事务管理器就是切面)和通知-->
        <aop:aspect id="manager" ref="transactionManager">
            <aop:around method="around" pointcut-ref="pointcut"></aop:around>
        </aop:aspect>
    </aop:config>

2.service层把之前的事务控制代码去掉,还原成原始代码(只拿修改过的这个方法)

//转账方法
 public void updateAccount(String name1, String name2, Float money) {
            Account account1 = accountDao.findByName(name1);
            Account account2 = accountDao.findByName(name2);
            Float money1 = account1.getMoney();
            if (money1 >= 100f){
                account1.setMoney(account1.getMoney()-money);
            }else {
                throw new RuntimeException("账户余额不足,转账不成功");
            }
            account2.setMoney(account2.getMoney()+money);
            accountDao.update(account1);
            //在中间加入异常,测试事务管理是否成功
            //int i= 1/0;
            accountDao.update(account2);
    }

3.事务管理器:只添加了一个环绕通知,其余不变
创建环绕通知,在使用注解的方式的前提下建议选择环绕通知,因为可以控制通知的执行顺序,避免关闭事务在提交事务之前就执行,造成事务无法回滚

public class TransactionManager {

    @Autowired
    ConnectionUtils connectionUtils;

    //环绕通知
    public Object around(ProceedingJoinPoint pjp){
        Object obj = null;
        try {
            //开启事务
            this.begin();
             obj = pjp.proceed(pjp.getArgs());
             //提交事务
            this.commit();
        } catch (Throwable throwable) {
            //回滚事务
            this.rollback();
            throwable.printStackTrace();
        }finally {
            //关闭事务
            this.close();
        }
        return obj;
    }

--------------------------------------------------------华丽的分割线----------------------------------------------------------------------------------

第三种方式:声明式事务控制
声明式事务控制相对比aop又更加方便一些,不再需要自己创建连接和事务管理器,如果使用注解就更加简洁明了了,为了方便这里dao层就使用jdbc模板了

1.容器:需要添加事务管理和事务控制等,如果不使用注解还可以选择aop的方式

<!--开启注解扫描 -->
    <context:component-scan base-package="java"></context:component-scan>
    <!-- 配置spring开启注解AOP的支持 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    <!--导入数据库配置文件-->
    <context:property-placeholder location="jdbc.properties"></context:property-placeholder>

    <!--数据源不再使用c3p0,更换为模板-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

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

    <!--事务配置,基于注解,也可以使用aop,只是aop配置相对麻烦-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

    <!--Bean注入-->
    <bean id="accountService" class="service.impl.AccountServiceImpl"></bean>
    <bean id="accountDao" class="dao.impl.AccountDaoImpl"></bean>

2.dao层:使用jdbc模板

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {

    //导入数据源--通过set方法的形参
    @Autowired
    public void setDs(DataSource dataSource){
        super.setDataSource(dataSource);
    }
    public Account findByName(String name) {
        List<Account> list = super.getJdbcTemplate().query("select * from account where name = ?", new BeanPropertyRowMapper<Account>(Account.class), name);
            if (list.isEmpty()){
                return null;
            }else if (list.size()>1){
                throw new RuntimeException("账户不唯一,无法确定");
            }
        return list.get(0);
    }

    public void update(Account account) {
        super.getJdbcTemplate().update("update account set name = ?,money = ? where id = ?",account.getName(),account.getMoney(),account.getId());

    }
}

3.service层:业务层只是多添加了一行注解即可

@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED,readOnly = false)
public class AccountServiceImpl implements AccountService {

    @Autowired
    AccountDao accountDao;

    public Account findByName(String name) {
        return accountDao.findByName(name);
    }

    public void update(Account account) {
        accountDao.update(account);

    }

    public void updateAccount(String name1, String name2, Float money) {
            Account account1 = accountDao.findByName(name1);
            Account account2 = accountDao.findByName(name2);
            Float money1 = account1.getMoney();
            if (money1 >= 100f){
                account1.setMoney(account1.getMoney()-money);
            }else {
                throw new RuntimeException("账户余额不足,转账不成功");
            }
            account2.setMoney(account2.getMoney()+money);
            accountDao.update(account1);
            //在中间加入异常,测试事务管理是否成功
            //int i= 1/0;
            accountDao.update(account2);
    }
}

4.测试还是一样,就不写了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值