Spring的编程式事务管理与声明式事务管理(十三)

17 篇文章 0 订阅

搭建Spring事务管理的环境

要进行编程式事务管理或者是声明式事务管理开发,都需要先将开发环境搭建完毕。

构造环境所需要到Jar包,包括Spring环境的基本包和搭建事务所需要的Jar包:
在这里插入图片描述


我们模拟的是银行转账的事务环境,也就是转入转出金钱,还需要搭建一个数据库环境。

建立一个User表,内含id,name,money三个字段:

CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `money` double DEFAULT NULL,
  PRIMARY KEY (`id`)
)

并且引入记录数据库连接信息的外部配置文件 jdbc.properties:

#定义连接参数
jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/an?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
jdbc.username=root
jdbc.password=root

搭建好了数据库,就开始创建Service的接口和实现类,用来操作转账的业务。到时候事务也是要布置在这一层面上。

Service接口:AccountService

/*
* 转账的业务层接口
* */
public interface AccountService {
    public void transfer(String from, String to, double money);
}

Service接口的实现类:AccountServiceImpl

/*
* 转账的业务层的实现类
* */
public class AccountServiceImpl implements AccountService {

    //注入Dao
    @Resource(name="accountDao")
    private AccountDao accountDao;
    
    /*
    * from:转出账号
    * to:转入账号
    * money:转账金额
    * */
    @Override
    public void transfer(String from, String to, double money) {
        accountDao.outMoney(from, money);
        accountDao.inMoney(to,money);
    }
}

配置好了Service业务层,接下来要配置的是Dao层的接口和实现类。

Dao的接口:AccountDao

/*
 * 转账DAO的接口
 * */
public interface AccountDao {
    //转出金钱,减钱
    public void outMoney(String from, double money);

    //转入金钱,加钱
    public void inMoney(String to, double money);
}

Dao接口的实现类:AccountDaoImpl

在实现类中,通过继承JdbcDaoSupport 类的方式,简化了连接数据库的操作,无需再每次定义DataSource和jdbcTemplate对象,再进行数据库的操作。

通过对JdbcDaoSupport 类的dataSource的XML方式属性注入,调用setDataSource方法,set方法中书写了创建jdbcTemplate对象的操作,从而通过getJdbcTemplate方法获取模板操作的对象。

/*
* 转账DAO的实现类
*
* */
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
//    由于AccountDaoImpl类继承了JdbcDaoSupport类,类中有dataSource和jdbcTemplate成员属性
//    然后使用JdbcDaoSupport类中的setDataSource的方法
    //setDataSource方法中会使用注入的dataSource属性,创建jdbcTemplate模板对象,并将其赋值给jdbcTemplate成员
    //我们就可以直接使用jdbcTemplate模板对象了。

    /*
    * from:转出账号
    * money:转出金额
    * */
    @Override
    public void outMoney(String from, double money) {
        this.getJdbcTemplate().update("update account set money = money - ? where name = ?", money, from);
    }

    /*
    *转入账号:to
    * 转入金钱 :money
    * */
    @Override
    public void inMoney(String to, double money) {
        this.getJdbcTemplate().update("update account set money = money + ? where name = ?", money, to);
    }
}

当将Service层和Dao层创建完毕了,就应该进行applicationContext.xml配置文件的配置了,将Service层和Dao层交给Spring来管理

同时将C3PO的连接池对象也交给Spring管理,并且引入数据库连接配置文件,为C3PO类注入相关属性。

并且开启注解的组件扫描

为了减少编写事务的代码量,还需要配置平台事务管理器(DataSourceTransactionManager)和事务管理的模板(TransactionTemplate)

配置文件:tx.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context.xsd
	http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop.xsd
	http://www.springframework.org/schema/tx
	http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--开启注解的组件扫描-->
    <context:component-scan base-package="tx"/>

    <!--配置Service=========-->
    <bean name="accountService" class="tx.AccountServiceImpl"/>

    <!--配置dao-->
    <!--由于AccountDaoImpl类继承了JdbcDaoSupport类,可以使用JdbcDaoSupport类中的setDataSource的方法-->
    <!--使用注入的dataSource属性,创建jdbcTemplate模板对象-->
    <bean name="accountDao" class="tx.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--引入数据库参数文件-->
    <!--第二种方式,通过context标签引入,常用-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--配置C3PO的连接池对象-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

	<!--配置平台事务管理器-->
    <!--同时为其注入一个连接对象,以便帮助我们进行事务操作-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>

    <!--配置事务管理的模板-->
    <!--只是为了方便创建事务,底层实现还是由事务管理器实现-->
    <!--所以需要给此模板注入事务管理器的属性-->
    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="transactionManager"/>
    </bean>


最后编写一个测试类,基本的事务管理环境就搭建好了,就可以进行事务的开发测试了。

SpringTest:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:tx.xml")
public class SpringTest {

    @Resource(name = "accountService")
    private AccountService accountService;
    @Test
    public void test() {
        accountService.transfer("kk","kk2",50);
    }
}

编程式事务管理

环境搭建好后,需要在使用事务管理的地方添加一个事务管理的模板,也就是要在业务层的实现类 AccountServiceImpl 中添加一个成员,并且为其注入属性:

 @Resource(name = "transactionTemplate")
 private TransactionTemplate transactionTemplate;

然后在AccountServiceImpl书写事务操作:

public class AccountServiceImpl implements AccountService {

    //注入Dao
    @Resource(name="accountDao")
    private AccountDao accountDao;

    //注入事务模板

    @Resource(name = "transactionTemplate")
    private TransactionTemplate transactionTemplate;
    /*
    * from:转出账号
    * to:转入账号
    * money:转账金额
    * */
    @Override
    public void transfer(String from, String to, double money) {
        //采用匿名内部类的方式,创建一个事务,抛出异常将会回滚数据,
        // 采用的是PROPAGATION_REQUIRED传播行为(保证多个操作在同一个事务中)
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                accountDao.outMoney(from, money);
                //设置一个bug,数据会回滚,则说明已经启动了事务
                int d = 1/0;
                accountDao.inMoney(to,money);
            }
        });
    }
}

声明式事务管理(通过配置完成)— AOP的思想

声明式事务管理应用的是Spring的AOP的思想

在操作前开启事务,操作完毕后关闭事务(环绕通知),在遇到异常情况的时候回滚数据。相当于是对业务操作进行增强,而且不涉及到源代码的修改。

同样的,声明式事务管理有两种配置方式,同时需要引入AOP的jar包:

  • XML方式

    首先,按照上面的环境配置,重新复制一份新的环境,恢复一下转账的环境。

    复制一份tx2.xml,并删除事务管理模板(TransactionTemplate)的bean。

    配置事务的增强(tx:advice),事务的增强是固定的一种通知,有特定的格式,是一种规则

<!--配置事务的增强-->
<!--需要注入一个事务管理器-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!--事务管理的规则-->
        <!--增强的方法名和传播行为,还可以设置超时时间和隔离级别等-->
        <tx:method name="transfer" propagation="REQUIRED" />

        <!--而实际开发中一般会使用通配符扫描有相同前缀的方法-->
        <!--<tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT"/>-->
        <!--<tx:method name="update*" propagation="REQUIRED" isolation="DEFAULT"/>-->
        <!--<tx:method name="del*" propagation="REQUIRED" isolation="DEFAULT"/>-->
        <!--<tx:method name="find*" read-only="true"/>-->
    </tx:attributes>
</tx:advice>

然后进行AOP的配置,也就是要将增强配置到目标类:

<!--AOP的配置-->
<!--将事务规则应用到指定的切入点-->
<aop:config>
    <aop:pointcut id="pointcut1" expression="execution(* tx2.AccountServiceImpl.transfer(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
</aop:config>

当配置完成以后,就已经为目标方法设定好了事务,只要直接运行测试类,执行这个方法,就会自动的使用事务。

  • 注解方式

注解方式在开始搭建的基本开发环境上只需要做三件事

1.配置事务管理器

<!--配置平台事务管理器-->
<!--同时为其注入一个连接对象,以便帮助我们进行事务操作-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

2.开启注解事务:

 <!--开启注解事务-->
 <tx:annotation-driven transaction-manager="transactionManager"/>

3.在目标类AccountServiceImpl上添加一个@Transactional 注解

//事务的各类属性,就在注解上配置即可,如传播行为,隔离级别,超时时间等等
@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED)
public class AccountServiceImpl implements AccountService {
	............
}

总结

对于上述三种Spring事务管理方式,有一个共同点,都是需要在xml配置文件中配置一个 事务管理器(DataSourceTransactionManager)

<!--配置平台事务管理器-->
<!--同时为其注入一个连接对象,以便帮助我们进行事务操作-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

对于声明式事务来说,两个方式都具有各自的好处

XML方式的好处在于只需要在配置文件中配置完成,就不需要在去理业务层的逻辑,只用再配置文件中修改事务的规则即可,缺点就是配置麻烦。

而注解方式的好处就是在于便捷,极少的代码,只需要三步,配置事务管理器,开启注解事务,类上注解(@Transactional ),就可以为目标类开启事务了(事务的各项属性,都是在注解上配置)。缺点就是不够灵活,在每次书写业务层类的时候,都必须要记得在类上添加注解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值