JAVA 实战篇 Spring中自带的事务管理器的简单应用 编程式事务 声明式事务、 注解声明式事务、AspectJ注解事务

Spring中自带的事务管理器

1、编程式事务

详情可参考官方文档:
https://www.docs4dev.com/docs/zh/spring-framework/5.1.3.RELEASE/reference/data-access.html#transaction-programmatic
优点:不需要spring进行代理,生成代理对象(反射),性能更好
缺点:事务管理的逻辑必须与业务逻辑耦合,重用性差,扩展性也差
应用场景:项目中只有几个业务方法需要事务,则适合。

1.1、 测试代码:

1.1.1、生成事务管理(TransactionTemplate)的xml文件配置:

注意导入相关的包或者依赖喔

下面是我的maven配置文件:(需要的自提)

<dependencies>
    <dependency>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-core</artifactId>
        <version>1.4.0</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.6</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>net.sf.ehcache</groupId>
        <artifactId>ehcache-core</artifactId>
        <version>2.6.11</version>
        <exclusions>
            <exclusion>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.mybatis.caches</groupId>
        <artifactId>mybatis-ehcache</artifactId>
        <version>1.1.0</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.6</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.35</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.taglibs</groupId>
        <artifactId>taglibs-standard-spec</artifactId>
        <version>1.2.5</version>
    </dependency>
    <dependency>
        <groupId>org.apache.taglibs</groupId>
        <artifactId>taglibs-standard-impl</artifactId>
        <version>1.2.5</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.41</version>
    </dependency>
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>5.1.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.3.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>5.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-instrument</artifactId>
        <version>5.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jms</artifactId>
        <version>5.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-messaging</artifactId>
        <version>5.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>5.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-oxm</artifactId>
        <version>5.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.6</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>RELEASE</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.0.25</version>
    </dependency>
    <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
        <version>1.4</version>
    </dependency>
</dependencies>

OK我们来开始整活:
直接上配置文件代码:

数据库配置:

url=jdbc:mysql://localhost:3306/test?useUnicode=true&amp&characterEncoding=utf-8
driver=com.mysql.jdbc.Driver
uName=root
password=123456

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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd">

<!--1、加载数据库的配置信息 -->
<context:property-placeholder
	location="database.properties" /><!--没有配置文件的记得先创建哦 -->
<!--2、datasource数据源 -->
<bean id="dataSource"
	class="org.apache.commons.dbcp.BasicDataSource">
	<property name="driverClassName" value="${driver}" />
	<property name="url" value="${url}" />
	<property name="username" value="${uName}" />
	<property name="password" value="${password}" />
</bean>
<!-- 3、sqlSessionFactory -->
<bean id="sqlSessionFactory"
	class="org.mybatis.spring.SqlSessionFactoryBean">
	<!-- 别名 -->
	<property name="typeAliasesPackage" value="entity"/>
	<!-- mapper XML映射 -->
	<property name="mapperLocations"
		value="classpath*:mapper/*Mapper.xml"></property>
	<!-- 数据源 -->
	<property name="dataSource" ref="dataSource"></property>
</bean>

<!--4、mapper接口的位置 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<property name="basePackage" value="mapper"></property>
</bean>
<!-- 1)、事务管理(增强/通知): -->
<bean id="txManager" 
	  class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
	<property name="dataSource" ref="dataSource"/> 
</bean>
<bean id="transactionTemplate"
	  class="org.springframework.transaction.support.TransactionTemplate ">
	<property name="transactionManager" ref="txManager"/>
</bean>
</beans>

1.1.2、生成Mapper接口的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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd
    ">
<!--  1、启动注解扫描-->
<context:component-scan base-package="change"/>
<!-- 1)目标 -->
<bean id="target" class="change.ChangeMoneyImpl_BC"/>
<!-- 2)代理对象实例 -->
<bean id="proxy_BC" class="proxy.Proxy_BC"/>
<!--3)代理  -->
<bean  id="changeMoney" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="proxyInterfaces"  value="change.ChangeMoney"/><!-- 注意这里是接口哦 -->
    <!-- 1)注入目标对象 -->
    <property name="target"  ref="target"/>
    <!-- 2)代理对象实例  -->
    <property name="interceptorNames">
        <array>
            <value>proxy_BC</value>
        </array>
    </property>
</bean>
</beans>

1.1.3、ChangMoney接口及实现类:

接口:

public interface ChangeMoney {
    
    
    boolean giveMoney(int on, int to, int money);
}
/**
*@author Nical
*/
@Component
public class ChangeMoneyImpl_BC implements ChangeMoney {
    
    
    @Autowired
    private UserinfoMapper mapper;
    @Autowired
    private TransactionTemplate transactionTemplate;

    class Result{
    
    
        boolean result;
    }

    @Override
    public boolean giveMoney(int on, int to, int money) {
    
    
        final  Result result =new Result() ;
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
    
    
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
    
    
                try {
    
    
                    giveMoneyOption(on, to, money);
                    System.out.println("=======事务提交了BC=====");
                    //事务回滚
                    result.result = true;
                }catch (Exception e){
    
    
                    System.out.println("=======事务回滚了BC=====");
                    //事务回滚
                    transactionStatus.setRollbackOnly();
                    result.result = false;
                }
            }
        });
        return result.result;
    }

    public void giveMoneyOption(int on, int to, int money) throws Exception {
    
    
        Userinfo userinfo =mapper .selectByPrimaryKey(on);
        int i=0;
        if (userinfo!=null){
    
    
            //设置转钱人的余额
            userinfo.setMoney(userinfo.getMoney()-money);
            //受影响的行数
            i = mapper.updateByPrimaryKey(userinfo);
        }
        //被转钱人的信息
        Userinfo userinfo2 =mapper .selectByPrimaryKey(to);
        int j=0;
        if (userinfo2!=null){
    
    
            //设置转钱人的余额
            userinfo2.setMoney(userinfo2.getMoney()+money);
            //受影响的行数
            j = mapper.updateByPrimaryKey(userinfo2);
        }
        if (i>0&&j>0){
    
    
            System.out.println("转账成功!!");
        }else {
    
    
            //回滚事务
            System.out.println("转账失败!!");
            throw new Exception("转账失败的异常");
        }
    }
}

1.1.4、 测试类代码:

/**
*@author Nical
*/
//编程式事务
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({
    
    "classpath:beans_proxy_BC.xml","classpath:beans_BC.xml"})
                          //生成代理的                   生成事务管理器(TransactionTemplate)的
public class Test_BC {
    
    
    //编程式事务,自己通过aop实现事务管理器
    @Autowired
    @Qualifier("changeMoney")
    private ChangeMoney changeMoney;
    @Test
    public void test2(){
    
    
        boolean result = changeMoney.giveMoney(1, 3, 300);
        System.out.println(result);
    }
}

1.2、 总结:

我们发现,在进行事务的处理的时候,事务代码与我们的业务代码耦合了,而且非常的不方便,因为每次我要进行一个事务操作都需要写一遍业务代码,非常的麻烦,下面我们就来看看声明式事务,就很好的解决了上述的问题:

2、声明式事务

优点:事务 Management 脱离业务逻辑,并且不难配置
缺点:需要spring进行代理,生成代理对象(反射),性能稍差
应用场景:项目中大量需要事务管理的场景(企业开发中常用)

来我们直接上代码:

2.1、 测试代码:

2.1.1、生成事务管理的xml文件配置:

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
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/tx
    https://www.springframework.org/schema/tx/spring-tx.xsd
     http://www.springframework.org/schema/aop
    https://www.springframework.org/schema/aop/spring-aop.xsd
    ">
<!--1、加载数据库的配置信息 -->
<context:property-placeholder
	location="database.properties" />
<!--2、datasource数据源 -->
<bean id="dataSource"
	class="org.apache.commons.dbcp.BasicDataSource">
	<property name="driverClassName" value="${driver}" />
	<property name="url" value="${url}" />
	<property name="username" value="${uName}" />
	<property name="password" value="${password}" />
</bean>
<!-- 3、sqlSessionFactory -->
<bean id="sqlSessionFactory"
	class="org.mybatis.spring.SqlSessionFactoryBean">
	<!-- 别名 -->
	<property name="typeAliasesPackage" value="entity"/>
	<!-- mapper XML映射 -->
	<property name="mapperLocations"
		value="classpath*:mapper/*Mapper.xml"></property>
	<!-- 数据源 -->
	<property name="dataSource" ref="dataSource"></property>
</bean>

<!--4、mapper接口的位置 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<property name="basePackage" value="mapper"></property>
</bean>
<!-- 1)、事务管理(增强/通知): -->
<bean id="txManager"
	  class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
	<property name="dataSource" ref="dataSource"/> 
</bean>
<!-- 第1步:定义一个通知 advice -->
<tx:advice id="gmAdvice" transaction-manager="txManager">
	<tx:attributes>
	<!-- 这个方法才会使用事务管理器 -->
		<tx:method name="giveMoney*"/>
		<!-- 其他任何方法,使用只读事务
            isolation="DEFAULT"       事务隔离级别
            propagation="REQUIRED"    事务传播特性
            timeout="-1"              事务的超时时间
            no-rollback-for=""        指定某种运行时异常不回滚
            rollback-for=""           指定某种运行时异常才回滚
         -->
	</tx:attributes>
</tx:advice>
<!-- 第2步:将通知植入切点
   public void   giveMoney(int a){}
   public void  change.ChangeMoneyImpl_SM.giveMoney(..)
 -->
<aop:config>
	<!--  1)切点-->
	<aop:pointcut id="pointCut" expression="execution(*  change.ChangeMoneyImpl_SM.*(..))" />
	<!-- 2)植入 :将通知植入到切点中-->
	<aop:advisor advice-ref="gmAdvice"  pointcut-ref="pointCut"/>
</aop:config>

2.1.2、生成Mapper接口的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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd">

<!--  1、启动注解扫描-->
  <context:annotation-config/>
<context:component-scan base-package="change"/>
<!-- 1)目标 -->
<bean id="target_SM" class="change.ChangeMoneyImpl_SM"/>
<!-- 2)黑客 -->
<bean id="proxy_SM" class="proxy.Proxy_SM"/>
<!--3)代理  -->

<bean  id="changeMoney_sm" class="org.springframework.aop.framework.ProxyFactoryBean">
    <!-- 接口-->
    <property name="proxyInterfaces"  value="change.ChangeMoney"/>
    <!-- 1)注入目标对象 -->
    <property name="target"  ref="target_SM"/>
    <!-- 2)黑客对象  -->
    <property name="interceptorNames">
        <array>
            <value>proxy_SM</value>
        </array>
    </property>
</bean>
</beans>

2.1.3、ChangMoney接口的实现类:

/**
*@author Nical
*/
@Component
public class ChangeMoneyImpl_SM implements ChangeMoney {
    
    
    @Autowired
    private UserinfoMapper mapper;
    
    @Override
    public boolean giveMoney(int on, int to, int money) {
    
    
        boolean result=giveMoneyOption(on,to,money);
        return result;
    }

    public boolean giveMoneyOption(int on, int to, int money)  {
    
    
        Userinfo userinfo =mapper .selectByPrimaryKey(on);
        int i=0;
        if (userinfo!=null){
    
    
            //设置转钱人的余额
            userinfo.setMoney(userinfo.getMoney()-money);
            //受影响的行数
            i = mapper.updateByPrimaryKey(userinfo);
        }
        //被转钱人的信息
        Userinfo userinfo2 =mapper .selectByPrimaryKey(to);
        int j=0;
        if (userinfo2!=null){
    
    
            //设置转钱人的余额
            userinfo2.setMoney(userinfo2.getMoney()+money);
            //受影响的行数
            j = mapper.updateByPrimaryKey(userinfo2);
        }
        if (i>0&&j>0){
    
    
            System.out.println("转账成功!!");
            return true;
        }else {
    
    
            //回滚事务
            System.out.println("转账失败!!");
            throw new RuntimeException("转账失败的异常");
        }
    }
}

2.1.4 、测试类代码:

/**
*@author Nical
*/
//声明式事务
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({
    
    "classpath:beans_proxy_SM.xml","classpath:beans_SM.xml"})
                             //生成代理的                 生成事务管理器(TransactionTemplate)的
public class Test_SM {
    
    
    @Autowired
    @Qualifier("changeMoney_sm")
    private ChangeMoney changeMoney;
    @Test
    public void test2(){
    
    
        boolean result = changeMoney.giveMoney(1, 3, 300);
        System.out.println(result);
    }
}

2.2 总结:

我们发现,比较编程式少了代码的侵入性,同时也避免了代码的耦合,事务相关的代码可以分开写,但是还有没有更加简单的写法呢,有的!就是下面的注解声明事务:

3、注解声明式事务

优点:在上述的优点基础上,可以直接在需要的方法上加上注解,非常的方便
缺点:同上
应用场景:同上(企业中常用的方法)

来我们直接上代码:

3.1 、测试代码:

3.1.1、生成事务管理的xml文件配置:

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
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/tx
    https://www.springframework.org/schema/tx/spring-tx.xsd
     http://www.springframework.org/schema/aop
    https://www.springframework.org/schema/aop/spring-aop.xsd
    ">
<!--1、加载数据库的配置信息 -->
<context:property-placeholder
	location="database.properties" />
<!--2、datasource数据源 -->
<bean id="dataSource"
	class="org.apache.commons.dbcp.BasicDataSource">
	<property name="driverClassName" value="${driver}" />
	<property name="url" value="${url}" />
	<property name="username" value="${uName}" />
	<property name="password" value="${password}" />
</bean>
<!-- 3、sqlSessionFactory -->
<bean id="sqlSessionFactory"
	class="org.mybatis.spring.SqlSessionFactoryBean">
	<!-- 别名 -->
	<property name="typeAliasesPackage" value="entity"/>
	<!-- mapper XML映射 -->
	<property name="mapperLocations"
		value="classpath*:mapper/*Mapper.xml"></property>
	<!-- 数据源 -->
	<property name="dataSource" ref="dataSource"></property>
</bean>

<!--4、mapper接口的位置 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<property name="basePackage" value="mapper"></property>
</bean>
<!-- 1)、事务管理(增强/通知): -->
<bean id="txManager"
	  class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
	<property name="dataSource" ref="dataSource"/> 
</bean>
<!-- 声明式事务注解方式:第1步:开启注解事务
    proxy-target-class="true"   指定注解事务是使用cglib动态代理方式来实现
 -->
<tx:annotation-driven  transaction-manager="txManager"/>

3.1.2、生成Mapper接口的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"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd">

<!--  1、启动注解扫描-->
  <context:annotation-config/>
<context:component-scan base-package="change"/>
<!-- 1)目标 -->
<bean id="target_ZJSM" class="change.ChangeMoneyImpl_ZJSM"/>
<!-- 2)代理实例 -->
<bean id="proxy_ZJSM" class="proxy.Proxy_ZJSM"/>
<!--3)代理  -->

<bean  id="changeMoney_zjsm" class="org.springframework.aop.framework.ProxyFactoryBean">
    <!-- 接口-->
    <property name="proxyInterfaces"  value="change.ChangeMoney"/>
    <!-- 1)注入目标对象 -->
    <property name="target"  ref="target_ZJSM"/>
    <!-- 2)代理实例  -->
    <property name="interceptorNames">
        <array>
            <value>proxy_ZJSM</value>
        </array>
    </property>
</bean>
</beans>

3.1.3、ChangMoney接口的实现类:

/**
*@author  Nical
*/
@Component
public class ChangeMoneyImpl_ZJSM implements ChangeMoney {
    
    
    @Autowired
    private UserinfoMapper mapper;
    @Override
    @Transactional
    public boolean giveMoney(int on, int to, int money) {
    
    
        boolean result=giveMoneyOption(on,to,money);
        return result;
    }

    public boolean giveMoneyOption(int on, int to, int money)  {
    
    
        Userinfo userinfo =mapper .selectByPrimaryKey(on);
        int i=0;
        if (userinfo!=null){
    
    
            //设置转钱人的余额
            userinfo.setMoney(userinfo.getMoney()-money);
            //受影响的行数
            i = mapper.updateByPrimaryKey(userinfo);
        }
        //被转钱人的信息
        Userinfo userinfo2 =mapper .selectByPrimaryKey(to);
        int j=0;
        if (userinfo2!=null){
    
    
            //设置转钱人的余额
            userinfo2.setMoney(userinfo2.getMoney()+money);
            //受影响的行数
            j = mapper.updateByPrimaryKey(userinfo2);
        }
        if (i>0&&j>0){
    
    
            System.out.println("转账成功!!");
            return true;
        }else {
    
    
            //回滚事务
            System.out.println("转账失败!!");
            throw new RuntimeException("转账失败的异常");
        }
    }
}

2.1.4 、 测试类代码:

/**
*@author Nical
*/
//注解声明式事务
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({
    
    "classpath:beans_proxy_ZJSM.xml", "classpath:beans_ZJSM.xml"})
                             //生成代理的                 生成事务管理器(TransactionTemplate)的
public class Test_ZJSM {
    
    
    //编程式事务,自己通过aop实现事务管理器
    @Autowired
    @Qualifier("changeMoney_zjsm")
    private ChangeMoney changeMoney;
    @Test
    public void test2(){
    
    
        boolean result = changeMoney.giveMoney(1, 3, 300);
        System.out.println(result);
    }
}

3.2、 总结:

是不是相比较于前两种,注解的方法特别的爽,所以强烈建议掌握该种事务管理的方法,下面还介绍一种另外的可以处理事务管理的方法,AspectJ和JAVA配置我们的事务管理

4、AspectJ面向切面,注解事务

4.1、介绍

AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法,它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。

4.2、 测试代码:

4.2.1、 AspectJ代码:

/*
 * 日志通知(连接点(切点)+增强)
 * @author Nical
 */
@Aspect
@Component
public class LogAspect {
    
    
    // 1.定义一个切点
    @Pointcut("execution(*  change.Impls.ChangeMoneyImpl_Aspect.*(..) )")
    public void pointCut() {
    
    }
    
    //2.前置通知
    @Before("pointCut() ")
    public void beforeAdvice(JoinPoint joinPoint) {
    
    
        System.out.println("=====我是前置通知=====>"+joinPoint);
    }
   //3.@After在调用目标方法之后切入;切入点表达式(指定在哪个方法切入)
    @After("pointCut() ")
    public void  afterAdvice(JoinPoint joinPoint) {
    
    
        System.out.println("=====我是后置通知=====>"+joinPoint);
    }
    
  //4.环绕通知/增强(黑客)
    //@Around("com.cc.dao.impl.UserDaoImpl2.add(..)")
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    
    
        System.out.println("1.鉴权");
        //调用目标方法
        Object result = joinPoint.proceed();
        System.out.println("1.日志留痕==》"+result);
        return result;
    }
    
  //5.JoinPoint一定要出现在参数表的第一位
    @AfterReturning(value="pointCut()",returning="result")
    public Object logReturn(JoinPoint joinPoint, Object result){
    
    
        System.out.println(""+joinPoint.getSignature().getName()+"正常返回。。。@AfterReturning:运行结果:{"+result+"}");
        return result;
    }
    //6.异常通知
    @AfterThrowing(value="pointCut()",throwing="exception")
    public void logException(JoinPoint joinPoint, Exception exception){
    
    
        System.out.println(""+joinPoint.getSignature().getName()+"异常。。。异常信息:{"+exception+"}");
    }
}

4.2.2、 JAVA 注解配置的Bean工厂代码:

/*
 * @author Nical
 */
@Configuration
@ComponentScan(value= {
    
    "aspect","change.Impls"})
@EnableAspectJAutoProxy//启动aspectJ注解aop
@EnableTransactionManagement//启动事务管理
public class AspectJConfig {
    
    
    
    //1.datasource
    @Bean
    public BasicDataSource getBasicDataSource() {
    
    
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/test?useUnicode=true&amp&characterEncoding=utf-8");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        return dataSource;
    }
    //2.sqlSessionFactory
    @Bean
    public SqlSessionFactory getSessionFactoryBean() throws Exception {
    
    
        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(getBasicDataSource());
        Resource[] resources = {
    
    new ClassPathResource("mapper/UserinfoMapper.xml")};
        sessionFactoryBean.setMapperLocations(resources );
        return sessionFactoryBean.getObject();
    }
    //3.Mapper
    @Bean
    public UserinfoMapper userMapper() throws Exception {
    
    
      // when using javaconfig a template requires less lines than a MapperFactoryBean
      SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(getSessionFactoryBean());
      return sqlSessionTemplate.getMapper(UserinfoMapper.class);
    }
    //4、事务 DataSourceTransactionManager
    @Bean(name = "dataSourceTransactionManager")
    public DataSourceTransactionManager getTransactionManager() {
    
    
        DataSourceTransactionManager txManager = new DataSourceTransactionManager();
        txManager.setDataSource(getBasicDataSource());
        return txManager;
    }
}

4.2.3、 接口的实现类:

/*
 * @author Nical
 */
@Component
public class ChangeMoneyImpl_Aspect implements ChangeMoney {
    
    
    @Autowired
    private UserinfoMapper mapper;
    @Transactional
    @Override
    public boolean giveMoney(int on, int to, int money) {
    
    
        boolean result=giveMoneyOption(on,to,money);
        return result;
    }

    public boolean giveMoneyOption(int on, int to, int money)  {
    
    
        Userinfo userinfo =mapper .selectByPrimaryKey(on);
        int i=0;
        if (userinfo!=null){
    
    
            //设置转钱人的余额
            userinfo.setMoney(userinfo.getMoney()-money);
            //受影响的行数
            i = mapper.updateByPrimaryKey(userinfo);
        }
        //被转钱人的信息
        Userinfo userinfo2 =mapper .selectByPrimaryKey(to);
        int j=0;
        if (userinfo2!=null){
    
    
            //设置转钱人的余额
            userinfo2.setMoney(userinfo2.getMoney()+money);
            //受影响的行数
            j = mapper.updateByPrimaryKey(userinfo2);
        }
        if (i>0&&j>0){
    
    
            System.out.println("转账成功!!");
            return true;
        }else {
    
    
            //回滚事务
            System.out.println("转账失败!!");
            throw new RuntimeException("转账失败的异常");
        }
    }
}

4.2.4、 测试代码:

/*
 * @author Nical
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AspectJConfig.class)
public class AspectJTest {
    
    

    @Autowired
    ChangeMoney changeMoney;
    @Test
    public void Test1(){
    
    
        changeMoney.giveMoney(1,2,300);
    }
}

5、 总结:

  • 以上方法中:编程式事务切点显而易见,麻烦,需要在业务代码中进行事务相关的代码拼接,代码重用性差,而且非常的费时间,声明式事务解决了代码耦合的问题,但是同时也要写相应的代码,而注解式事务则完美的解决了上述的问题,同时呢,AspectJ和JAVA注解可以完全不用写Xml文件,也值得推荐,小伙伴使用的时候注意区分哦

  • 本篇文章中的数据库对象及mapper文件都是由逆向工程生成,如果有不清楚的同学请参考
    https://blog.csdn.net/m0_50217781/article/details/111463605

  • 欢迎小伙伴留言,评论,一起探讨相关的问题,博主看到会第一时间与您回复哦

猜你喜欢

转载自blog.csdn.net/m0_50217781/article/details/111378661