spring 事物(二)—— 编程式事物实现与扩展

简介

使用TransactionTemplate 不需要显式地开始事务,甚至不需要显式地提交事务。这些步骤都由模板完成。但出现异常时,应通过TransactionStatus 的setRollbackOnly 显式回滚事务。 
TransactionTemplate 的execute 方法接收一个TransactionCallback 实例。Callback 也是Spring 的经典设计,用于简化用户操作。

TransactionCallback 包含如下方法。 

• Object dolnTransaction(TransactionStatus status) 。 
该方法的方法体就是事务的执行体。 
如果事务的执行体没有返回值,则可以使用TransactionCallbackWithoutResultl类的实例。这是个抽象类,不能直接实例化,只能用于创建匿名内部类。它也是TransactionCallback 接口的子接口,该抽象类包含一个抽象方法: 

• void dolnTransactionWithoutResult(TransactionStatus status)该方法与dolnTransaction 的效果非常相似,区别在于该方法没有返回值,即事务执行体无须返回值。 

transactionTemplate.execute(new TransactionCallback() { 
    public Object doInTransaction(TransactionStatus status) { 
        try{ 

        } 
        catch (Exception e) {
             status.setRollbackOnly(); 
        } 
    }
}); 

简单实现

SQL:


create table `account` (
    `username` varchar (99),
    `salary` int (11)
); 
insert into `account` (`username`, `salary`) values('小王','3000');
insert into `account` (`username`, `salary`) values('小马','3000');

注意: 通过添加/删除accountMoney() 方法中int i = 10 / 0这个语句便可验证事务管理是否配置正确。

OrdersDao.java(Dao层)

package com.gm.dao;

import org.springframework.jdbc.core.JdbcTemplate;

public class OrdersDao {
    // 注入jdbcTemplate模板对象
    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    // 对数据操作的方法不包含业务操作
    /**
     * 小王少钱的方法
     */
    public void reduceMoney() {
        String sql = "update account set salary=salary-? where username=?";
        jdbcTemplate.update(sql, 1000, "小王");
    }

    /**
     * 小马多钱的方法
     */
    public void addMoney() {
        String sql = "update account set salary=salary+? where username=?";
        jdbcTemplate.update(sql, 1000, "小马");
    }
}

OrdersService.java(业务逻辑层)

package com.gm.service;

import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import com.gm.dao.OrdersDao;

public class OrdersService {

    private final static Logger LOG = LoggerFactory.getLogger(OrdersService.class);
    
    // 注入Dao层对象
    private OrdersDao ordersDao;

    public void setOrdersDao(OrdersDao ordersDao) {
        this.ordersDao = ordersDao;
    }

    // 注入TransactionTemplate对象
    private TransactionTemplate transactionTemplate;

    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }

    // 调用dao的方法
    // 业务逻辑,写转账业务
    public void accountMoney() {
        transactionTemplate.execute(new TransactionCallback<Object>() {

            @Override
            public Object doInTransaction(TransactionStatus status) {
                Object result = null;
                try {
                    // 小马多1000
                    ordersDao.addMoney();
                    // 加入出现异常如下面int
                    // i=10/0(银行中可能为突然停电等。。。);结果:小马账户多了1000而小王账户没有少钱
                    // 解决办法是出现异常后进行事务回滚
                    int i = 10 / 0;// 事务管理配置后异常已经解决
                    // 小王 少1000
                    ordersDao.reduceMoney();
                } catch (Exception e) {
                    status.setRollbackOnly();
                    result = false;
                    LOG.error("Transfer Error!,"+e.getMessage());
                }

                return result;
            }
        });

    }
}

TestService.java(测试方法)

package com.gm.service;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestService {
    @Test
    public void testAdd() {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "beans.xml");
        OrdersService userService = (OrdersService) context
                .getBean("ordersService");
        userService.accountMoney();
    }
}

配置文件:

<?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-2.5.xsd  
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd  
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd  
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
    <!-- 配置c3po连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!-- 注入属性值 -->
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mydb"></property>
        <property name="user" value="root"></property>
        <property name="password" value="153963"></property>
    </bean>
    <!-- 编程式事务管理 -->
    <!-- 配置事务管理器 -->
    <bean id="dataSourceTransactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 注入dataSource -->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 配置事务管理器模板 -->
    <bean id="transactionTemplate"
        class="org.springframework.transaction.support.TransactionTemplate">
        <!-- 注入真正进行事务管理的事务管理器,name必须为 transactionManager否则无法注入 -->
        <property name="transactionManager" ref="dataSourceTransactionManager"></property>
    </bean>

    <!-- 对象生成及属性注入 -->
    <bean id="ordersService" class="com.gm.service.OrdersService">
        <property name="ordersDao" ref="ordersDao"></property>
        <!-- 注入事务管理的模板 -->
        <property name="transactionTemplate" ref="transactionTemplate"></property>
    </bean>

    <bean id="ordersDao" class="com.gm.dao.OrdersDao">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>
    <!-- JDBC模板对象 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans> 

log4j.properties

#Level:ERROR,WARN,INFO,DEBUG
log4j.rootLogger=ERROR, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

执行,输出

[ERROR] 2018-09-06 14:03:06,937 method:com.gm.service.OrdersService$1.doInTransaction(OrdersService.java:54)
Transfer Error!,/ by zero

扩展

首先定义了两个接口:

ServiceTemplate ---->   对TransactionTemplate进行了封装

public interface ServiceTemplate {

    /**
     * <pre> 无事务模板执行业务处理
     * 1. 异常捕获,及分类处理
     * 2. 业务日志记录
     * </pre>
     *
     * @param <T>
     * @param clazz  返回对象
     * @param action 业务操作回调的接口
     * @return       服务返回对象
     */
    <T> T executeWithoutTransaction(Class<? extends Result> clazz, ServiceCallback action);

    /**
     * <pre> 支持本地事务模板执行业务处理
     * 1. 本地事务封装
     * 2. 异常捕获,及分类处理
     * 3. 业务日志记录
     * </pre>
     *
     * @param <T>
     * @param clazz  返回对象
     * @param action 业务操作回调的接口
     * @return       服务返回对象
     */
    <T> T execute(Class<? extends Result> clazz, ServiceCallback action);

}

ServiceCallback  ---->  对TransactionCallBack进行了封装 

public interface ServiceCallback {
    /**
     * <pre> 校验
     * 对于校验不通过,异常驱动返回
     * </pre>
     */
    void doLock();

    /**
     * <pre> 校验
     * 对于校验不通过,异常驱动返回
     * </pre>
     */
    void check();

    /**
     * <pre> 执行业务逻辑
     * </pre>
     * @return
     */
    Result executeService();
}

ServiceTemplate的具体实现ServiceTemplateImpl.java

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

public class ServiceTemplateImpl implements ServiceTemplate {

	private final static Logger LOG = LoggerFactory.getLogger(ServiceTemplateImpl.class);
	
	// 注入TransactionTemplate对象
	private TransactionTemplate transactionTemplate;

	public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
		this.transactionTemplate = transactionTemplate;
	}

	@Override
	public <T> T executeWithoutTransaction(Class<? extends Result> clazz,
			ServiceCallback action) {
		Result result = null;
		try {
			// 执行校验
			action.check();
			// 锁操作
			action.doLock();
			// 执行处理逻辑
			result = action.executeService();
			// 可以对结果进行初步校验TODO

		} catch (Exception e) {//此处可换成具体异常或子定义异常
			LOG.error("Transfer Error!,"+e.getMessage());
			// 打日志TODO
			return (T) result;

		} catch (Throwable e2) {
			LOG.error(e2.getMessage());
			// 打日志TODO
			return (T) result;
		}

		return (T) result;
	}

	@Override
	@SuppressWarnings("unchecked")
	public <T> T execute(final Class<? extends Result> clazz,
			final ServiceCallback action) {
		T acResult = (T) transactionTemplate.execute(new TransactionCallback() {
			/**
			 * @see org.springframework.transaction.support.TransactionCallback#doInTransaction(org.springframework.transaction.TransactionStatus)
			 */
			public Object doInTransaction(TransactionStatus status) {
				Result result = null;
				try {
					result = clazz.newInstance();
					// 执行校验逻辑
					action.check();
					// 锁操作
					action.doLock();
					// 执行处理逻辑
					result = action.executeService();
					// 返回值异常处理
					if (result == null || !(result instanceof Result)) {
						throw new Exception();
					}
				} catch (Exception e) {//此处可换成具体异常或子定义异常
					// 业务异常捕获, 回滚, 打日志TODO
					LOG.error("Transfer Error!,"+e.getMessage());
					status.setRollbackOnly();
					return result;
				} catch (Throwable e2) {
					// 系统异常捕获, 回滚, 打日志TODO
					LOG.error(e2.getMessage());
					status.setRollbackOnly();
					return result;
				}
				return result;
			}
		});
		return acResult;
	}
}

在业务方法中使用ServiceTemplate, 通过构建ServiceCallBack匿名内部类的方式, 传递具体的业务代码

OrdersService.java新增以下方法

public void addMoney() {
		serviceTemplate.execute(Result.class, new ServiceCallback() {
			@Override
			public void doLock() {
				// 进行锁操作
			}

			@Override
			public void check() {
				// 进行校验
			}

			@Override
			public Result executeService() {
				Result result = new Result();
				// 具体的业务代码
				// 小马多1000
				ordersDao.addMoney();
				// 加入出现异常如下面int
				// i=10/0(银行中可能为突然停电等。。。);结果:小马账户多了1000而小王账户没有少钱
				// 解决办法是出现异常后进行事务回滚
				int i = 10 / 0;// 事务管理配置后异常已经解决
				// 小王 少1000
				ordersDao.reduceMoney();
				return result;
			}
		});
	}

修改beans.xml 

    <!-- 对象生成及属性注入 -->
    <bean id="ordersService" class="com.gm.service.OrdersService">
        <property name="ordersDao" ref="ordersDao"></property>
        <!-- 注入事务管理的模板 -->
        <property name="transactionTemplate" ref="transactionTemplate"></property>
        <property name="serviceTemplate" ref="serviceTemplate"></property>
    </bean>
    
    <bean id="serviceTemplate" class="com.gm.service.ServiceTemplateImpl">
        <!-- 注入事务管理的模板 -->
        <property name="transactionTemplate" ref="transactionTemplate"></property>
    </bean>

修改TestService.java 

public class TestService {
    @Test
    public void testAdd() {
        ApplicationContext context = new ClassPathXmlApplicationContext("dbBeans.xml");
        OrdersService userService = (OrdersService) context.getBean("ordersService");
        //userService.accountMoney();
        userService.addMoney();
    }
}

执行,输出

[ERROR] 2018-09-06 14:08:51,524 method:com.gm.service.ServiceTemplateImpl$1.doInTransaction(ServiceTemplateImpl.java:71)
Transfer Error!,/ by zero

注意: 如果是不需要加事务的方法, 如查询 ,那么调用serviceTemplate的executeWithoutTransaction即可

补充pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.test</groupId>
	<artifactId>SpringTest</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<build />

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<!-- spring版本号 -->
		<spring.version>4.1.5.RELEASE</spring.version>
		<!-- mybatis版本号 -->
		<mybatis.version>3.2.6</mybatis.version>
		<!-- log4j日志文件管理包版本 -->
		<slf4j.version>1.7.7</slf4j.version>
		<log4j.version>1.2.17</log4j.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.11</version>
			<!-- 表示开发的时候引入,发布的时候不会加载此包 -->
			<scope>test</scope>
		</dependency>
		<!-- spring核心包 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-oxm</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<!-- aop -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.8.4</version>
		</dependency>
		<!-- mybatis核心包 -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>${mybatis.version}</version>
		</dependency>
		<!-- mybatis/spring包 -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>1.2.2</version>
		</dependency>
		<!-- 导入java ee jar 包 -->
		<dependency>
			<groupId>javax</groupId>
			<artifactId>javaee-api</artifactId>
			<version>7.0</version>
		</dependency>
		<!-- 导入Mysql数据库链接jar包 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.30</version>
		</dependency>
		<!-- 导入dbcp的jar包,用来在applicationContext.xml中配置数据库 -->
		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.2.2</version>
		</dependency>
		<!-- JSTL标签类 -->
		<dependency>
			<groupId>jstl</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
		<!-- 日志文件管理包 -->
		<!-- log start -->
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>${log4j.version}</version>
		</dependency>
		<!-- 格式化对象,方便输出日志 -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.1.41</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${slf4j.version}</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>${slf4j.version}</version>
		</dependency>
		<!-- log end -->
		<!-- 映入JSON -->
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-core</artifactId>
			<version>2.7.5</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-annotations</artifactId>
			<version>2.7.5</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.7.5</version>
		</dependency>

		<dependency>
			<groupId>org.codehaus.jackson</groupId>
			<artifactId>jackson-mapper-asl</artifactId>
			<version>1.9.4</version>
		</dependency>

		<dependency>
			<groupId>org.codehaus.jackson</groupId>
			<artifactId>jackson-core-asl</artifactId>
			<version>1.9.4</version>
		</dependency>
		<!-- 上传组件包 -->
		<dependency>
			<groupId>commons-fileupload</groupId>
			<artifactId>commons-fileupload</artifactId>
			<version>1.3.1</version>
		</dependency>
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>2.4</version>
		</dependency>
		<dependency>
			<groupId>commons-codec</groupId>
			<artifactId>commons-codec</artifactId>
			<version>1.9</version>
		</dependency>

		<dependency>
			<groupId>org.apache.axis2</groupId>
			<artifactId>axis2-adb</artifactId>
			<version>1.6.3</version>
		</dependency>
		<dependency>
			<groupId>org.apache.axis2</groupId>
			<artifactId>axis2-kernel</artifactId>
			<version>1.6.3</version>
		</dependency>
		<dependency>
			<groupId>org.apache.axis2</groupId>
			<artifactId>axis2-transport-local</artifactId>
			<version>1.6.3</version>
		</dependency>
		<dependency>
			<groupId>org.apache.axis2</groupId>
			<artifactId>axis2-transport-http</artifactId>
			<version>1.6.3</version>
			<exclusions>
				<exclusion>
					<artifactId>httpcore</artifactId>
					<groupId>org.apache.httpcomponents</groupId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.jdom</groupId>
			<artifactId>jdom</artifactId>
			<version>1.1.3</version>
		</dependency>
		<dependency>
			<groupId>axis</groupId>
			<artifactId>axis-jaxrpc</artifactId>
			<version>1.4</version>
		</dependency>
		<dependency>
			<groupId>axis</groupId>
			<artifactId>axis</artifactId>
			<version>1.4</version>
		</dependency>
		<dependency>
			<groupId>org.freemarker</groupId>
			<artifactId>freemarker</artifactId>
			<version>2.3.23</version>
		</dependency>
		<dependency>
			<groupId>org.codehaus.jettison</groupId>
			<artifactId>jettison</artifactId>
			<version>1.3.8</version>
		</dependency>

		<!--axis2 begin -->
		<dependency>
			<groupId>org.apache.axis2</groupId>
			<artifactId>axis2</artifactId>
			<version>1.6.3</version>
		</dependency>
		<dependency>
			<groupId>org.apache.axis2</groupId>
			<artifactId>axis2-spring</artifactId>
			<version>1.6.3</version>
		</dependency>
		<dependency>
			<groupId>org.apache.axis2</groupId>
			<artifactId>axis2-xmlbeans</artifactId>
			<version>1.6.3</version>
		</dependency>
		<!--axis2 end -->

		<dependency>
			<groupId>org.kie.modules</groupId>
			<artifactId>org-apache-commons-net</artifactId>
			<version>6.4.0.Final</version>
			<type>pom</type>
		</dependency>

		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
			<version>4.5.2</version>
		</dependency>
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient-cache</artifactId>
			<version>4.5.2</version>
		</dependency>
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpcore</artifactId>
			<version>4.4.5</version>
		</dependency>
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpcore-nio</artifactId>
			<version>4.4.5</version>
		</dependency>
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpcore-ab</artifactId>
			<version>4.4.5</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.eclipse.paho/org.eclipse.paho.client.mqttv3 -->
		<dependency>
			<groupId>org.eclipse.paho</groupId>
			<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
			<version>1.2.0</version>
		</dependency>

		<!-- oracle数据库驱动 -->
		<dependency>
			<groupId>oracle.driver</groupId>
			<artifactId>oracle-driver</artifactId>
			<version>11.2.0.4</version>
		</dependency>

		<dependency>
			<groupId>dom4j</groupId>
			<artifactId>dom4j</artifactId>
			<version>1.6.1</version>
		</dependency>

		<dependency>
			<groupId>net.sf.json-lib</groupId>
			<artifactId>json-lib</artifactId>
			<version>2.4</version>
			<classifier>jdk15</classifier>
		</dependency>

		<dependency>
			<groupId>c3p0</groupId>
			<artifactId>c3p0</artifactId>
			<version>0.9.1.2</version>
		</dependency>
	</dependencies>
</project>

 

posted on 2018-09-06 11:49  疯狂的小萝卜头  阅读(358)  评论(0编辑  收藏  举报