博主上篇文章讲到spring的优点,由于篇幅过长,留下了spring事务这块内容,今天我们继续把spring的事务相关内容简单讲解下。
一:事务的隔离级别和传播行为
讲到spring的事务就离不开事务的隔离级别和传播行为。
事务的特性:
原子性:事务是一个不可分割的工作单位,事务中的操作要么都成功,要么都不成功。
一致性:事务的前后数据的完整性必须保持一致。
隔离性:一个事务的执行,不受其他事务(进程)的干扰。既并发执行的个事务之间互不干扰。
持久性:一个事务一旦提交,它对数据库的改变将是永久的。
事务的隔离级别:
讲隔离级别前,我们首先需要知道在数据库在事务上经常出现的问题:脏读,不可重复读,幻读。
脏读:一个事务读取了另一个事务没有提交的数据,如果这些数据被回滚了,这些读到的数据是无效的。
不可重复读:在同一个事务中,多次读取同一个数据返回的结果不同。
幻读:一个事务读取了几行数据后,另一个事务插入了一些数据,在后来的查询中,第一个事务发现原来没读取到的数据。
default | 使用数据库默认的隔离级别(spring中的一个选择项,数据库是没有的) |
READ_UNCOMMITTED | 允许读取为提交的改变了的数据。可能导致脏读,不可重复读,幻读 |
READ_COMMITTED | 允许在并发事务已经提交后读取。可防止脏读,但不可重复读,幻读仍可发生 |
REPEATABLE_READ | 对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏读,不可重复读,但幻读仍可发生 |
SERIALIZABLE | 序列化的,串行操作,速度也是最慢的,可防止脏读,不可重复读,幻读。 |
所以数据库的隔离级别是四种!mysql默认的是REPEATABLE_READ,oracle是READ_COMMITTED。
事务的传播行为:
先我们来说个简单的例子,最经典的转账操作,你给朋友转2000块钱,我们在数据库中其中是分2步完成的,第一步,你的账户减少2000,第二步,你朋友的账户增加2000。用代码来说就是你的service层的一个方法调用了dao层的两个方法,那么问题就来了第一步成功了,第二步应该如果是失败的,那么第一步的成功也是要回滚到之前的。
传播行为 | 说明 |
PROPAGATION_REQUIRES | 支持当前事务,如果不存在,就新建一个 |
PROPAGATION_SUPPORTS | 支持当前事务,如果不存在,就不使用事务 |
PROPAGATION_MANDATORY | 支持当前事务,如果不存在,就抛出异常 |
PROPAGATION_REQUIRES_NEW | 如果有事务存在,就挂起当前事务,并创建一个新的事务 |
PROPAGATION_NOT_SUPPORTED | 以非事务的方式运行,如果有事务存在,挂起当前事务 |
PROPAGATION_NEVER | 以非事务的方式运行,如果有事务存在,抛出异常 |
PROPAGATION_NESTED | 如果当前事务存在,则嵌套事务执行 |
那么最常用的就是用红色表示出来的这三种。
二:编程式事务管理
mysql数据库先建表插入数据,简单的说一点,金额我们是以分为单位的,这样可以有效的避免小数的问题。
-- ----------------------------
-- Table structure for account
-- ----------------------------
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`id` int(12) NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL,
`money` int(16) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of account
-- ----------------------------
INSERT INTO `account` VALUES ('1', 'julyday', '100000');
INSERT INTO `account` VALUES ('2', 'zhangsan', '100000');
INSERT INTO `account` VALUES ('3', 'lisi', '100000');
创建一个maven工程,导入需要的jar包,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.julyday</groupId>
<artifactId>spring_transaction</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring_transaction</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.1.4.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
</dependency>
<!-- mysql连接 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.34</version>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.12</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>${spring.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
创建相应的类,service和dao,我们这就简单的是java工程就不写control了。
package com.julyday.spring_transaction.service;
public interface AccountService {
public void transfer(String in,String out,int money);
}
package com.julyday.spring_transaction.service;
public class AccountServiceImpl implements AccountService {
@Override
public void transfer( String in, String out, int money) {
}
}
package com.julyday.spring_transaction.dao;
public interface AccountDao {
public void outMoney(String out,int money);
public void inMoney(String in,int money);
}
package com.julyday.spring_transaction.dao;
public class AccountDaoImpl implements AccountDao {
@Override
public void outMoney(String out, int money) {
}
@Override
public void inMoney(String in, int money) {
}
}
里面的业务实现,我们等下再写。jdbc.properties:
validationQuery=SELECT 1
jdbc.url=jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
spring.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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置数据源 使用的是Druid数据源 -->
<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!-- 初始化连接大小 -->
<property name="initialSize" value="0" />
<!-- 连接池最大使用连接数量 -->
<property name="maxActive" value="20" />
<!-- 连接池最小空闲 -->
<property name="minIdle" value="0" />
<!-- 获取连接最大等待时间 -->
<property name="maxWait" value="60000" />
<property name="poolPreparedStatements" value="true" />
<property name="maxPoolPreparedStatementPerConnectionSize"
value="33" />
<!-- 用来检测有效sql -->
<property name="validationQuery" value="${validationQuery}" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="testWhileIdle" value="true" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="25200000" />
<!-- 打开removeAbandoned功能 -->
<property name="removeAbandoned" value="true" />
<!-- 1800秒,也就是30分钟 -->
<property name="removeAbandonedTimeout" value="1800" />
<!-- 关闭abanded连接时输出错误日志 -->
<property name="logAbandoned" value="true" />
<!-- 监控数据库 -->
<property name="filters" value="mergeStat" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="accountService" class="com.julyday.spring_transaction.service.AccountServiceImpl">
<property name="accountDao" ref="accountDao" />
</bean>
<bean id="accountDao" class="com.julyday.spring_transaction.dao.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
</beans>
建出相应的bean后,我们来写我们的业务实现类。
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
@Override
public void outMoney(String out, int money) {
String sql = "update account set money = money - ? where name = ?";
getJdbcTemplate().update(sql, money,out);
}
@Override
public void inMoney(String in, int money) {
String sql = "update account set money = money + ? where name = ?";
this.getJdbcTemplate().update(sql, money,in);
}
}
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String in, String out, int money) {
accountDao.outMoney(out, money);
//int i = 1/0;
accountDao.inMoney(in, money);
}
}
测试下:
package com.julyday.spring_transaction;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.julyday.spring_transaction.service.AccountService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring.xml")
public class TransferTest {
@Resource(name="accountService")
private AccountService accountService;
@Test
public void testService(){
System.out.println("testService begin");
accountService.transfer("julyday", "zhangsan", 20000);
System.out.println("testService finish:");
}
}
OK,正常情况下,是没有任何问题的,接着我们让中间出现异常,显然第一个成功后,第二个是不会执行的。
接着我们用事务来做转账:
package com.julyday.spring_transaction.dao;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class AccountDaoImpl1 extends JdbcDaoSupport implements AccountDao {
@Override
public void outMoney(String out, int money) {
String sql = "update account set money = money - ? where name = ?";
getJdbcTemplate().update(sql, money,out);
}
@Override
public void inMoney(String in, int money) {
String sql = "update account set money = money + ? where name = ?";
this.getJdbcTemplate().update(sql, money,in);
}
}
package com.julyday.spring_transaction.service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import com.julyday.spring_transaction.dao.AccountDao;
public class AccountServiceImpl1 implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
@Override
public void transfer(final String in,final String out,final int money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
accountDao.outMoney(out, money);
//int i = 1/0;
accountDao.inMoney(in, money);
}
});
}
// public boolean transfer(final String in,final String out,final int money){
// return transactionTemplate.execute(new TransactionCallback<Boolean>() {
// @Override
// public Boolean doInTransaction(TransactionStatus transactionStatus) {
// accountDao.outMoney(out, money);
// //int i = 1/0;
// accountDao.inMoney(in, money);
// return true;
// }
// });
// }
}
spring.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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置数据源 使用的是Druid数据源 -->
<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!-- 初始化连接大小 -->
<property name="initialSize" value="0" />
<!-- 连接池最大使用连接数量 -->
<property name="maxActive" value="20" />
<!-- 连接池最小空闲 -->
<property name="minIdle" value="0" />
<!-- 获取连接最大等待时间 -->
<property name="maxWait" value="60000" />
<property name="poolPreparedStatements" value="true" />
<property name="maxPoolPreparedStatementPerConnectionSize"
value="33" />
<!-- 用来检测有效sql -->
<property name="validationQuery" value="${validationQuery}" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="testWhileIdle" value="true" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="25200000" />
<!-- 打开removeAbandoned功能 -->
<property name="removeAbandoned" value="true" />
<!-- 1800秒,也就是30分钟 -->
<property name="removeAbandonedTimeout" value="1800" />
<!-- 关闭abanded连接时输出错误日志 -->
<property name="logAbandoned" value="true" />
<!-- 监控数据库 -->
<property name="filters" value="mergeStat" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="accountService" class="com.julyday.spring_transaction.service.AccountServiceImpl">
<property name="accountDao" ref="accountDao" />
</bean>
<bean id="accountDao" class="com.julyday.spring_transaction.dao.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<!-- 业务层 -->
<bean id="accountService1" class="com.julyday.spring_transaction.service.AccountServiceImpl1">
<property name="accountDao" ref="accountDao1" />
<property name="transactionTemplate" ref="transactionTemplate" />
</bean>
<!-- dao层 -->
<bean id="accountDao1" class="com.julyday.spring_transaction.dao.AccountDaoImpl1">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" 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="transactionManager" />
</bean>
</beans>
AccountDaoImpl1完全一样,只是我们在配置文件中改成了dataSource,而不是jdbcTemplate,其实这两个是一样的,看JdbcDaoSupport源码就知道了。
AccountServiceImpl1的transfer方法我们用了匿名内部类,我们的方法是没有返回值的,我们用的doInTransactionWithoutResult,当然如果有返回值的话,可以用doInTransaction。
接着我们再次测试下:
package com.julyday.spring_transaction;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.julyday.spring_transaction.service.AccountService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring.xml")
public class TransferTest {
@Resource(name="accountService")
private AccountService accountService;
@Resource(name="accountService1")
private AccountService accountService1;
@Test
public void testService(){
System.out.println("testService begin");
accountService.transfer("julyday", "zhangsan", 20000);
System.out.println("testService finish:");
}
@Test
public void testService1(){
System.out.println("testService1 begin");
accountService1.transfer("julyday", "zhangsan", 20000);
System.out.println("testService1 finish");
}
}
OK,之前的错误情况不再出现,编程式事务就这样了。
下面来说声明式事务:
第一种:代理模式
package com.julyday.spring_transaction.service.proxy;
import com.julyday.spring_transaction.dao.AccountDao;
import com.julyday.spring_transaction.service.AccountService;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(final String in,final String out,final int money) {
accountDao.outMoney(out, money);
//int i = 1 / 0;
accountDao.inMoney(in, money);
}
}
spring_proxy.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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置数据源 使用的是Druid数据源 -->
<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!-- 初始化连接大小 -->
<property name="initialSize" value="0" />
<!-- 连接池最大使用连接数量 -->
<property name="maxActive" value="20" />
<!-- 连接池最小空闲 -->
<property name="minIdle" value="0" />
<!-- 获取连接最大等待时间 -->
<property name="maxWait" value="60000" />
<property name="poolPreparedStatements" value="true" />
<property name="maxPoolPreparedStatementPerConnectionSize"
value="33" />
<!-- 用来检测有效sql -->
<property name="validationQuery" value="${validationQuery}" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="testWhileIdle" value="true" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="25200000" />
<!-- 打开removeAbandoned功能 -->
<property name="removeAbandoned" value="true" />
<!-- 1800秒,也就是30分钟 -->
<property name="removeAbandonedTimeout" value="1800" />
<!-- 关闭abanded连接时输出错误日志 -->
<property name="logAbandoned" value="true" />
<!-- 监控数据库 -->
<property name="filters" value="mergeStat" />
</bean>
<!-- 业务层 -->
<bean id="accountService" class="com.julyday.spring_transaction.service.proxy.AccountServiceImpl">
<property name="accountDao" ref="accountDao" />
</bean>
<!-- dao层 -->
<bean id="accountDao" class="com.julyday.spring_transaction.dao.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 代理的目标类 -->
<property name="target" ref="accountService" />
<!-- 事务管理器 -->
<property name="transactionManager" ref="transactionManager" />
<!-- 事务的属性 -->
<property name="transactionAttributes">
<props>
<prop key="transfer">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
</beans>
有了之前的spring入门,这个就比较简单了,如果不知道怎么配置的话,TransactionProxyFactoryBean这个源码里面也有参考的例子,这里的事务的属性里面prop我们就写了一个transfer,小伙伴可以根据自己项目的需要修改。insert* 表示的就是insert开头的。
我们测试下:
package com.julyday.spring_transaction;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.julyday.spring_transaction.service.AccountService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring_proxy.xml")
public class TransferProxyTest {
@Resource(name="accountServiceProxy")
private AccountService accountService;
@Test
public void testService(){
System.out.println("testService begin");
accountService.transfer("julyday", "zhangsan", 20000);
System.out.println("testService finish:");
}
}
测试
没有问题!
声明式事务第二种:基于xml配置的方式:
package com.julyday.spring_transaction.service.schema;
import com.julyday.spring_transaction.dao.AccountDao;
import com.julyday.spring_transaction.service.AccountService;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(final String in,final String out,final int money) {
accountDao.outMoney(out, money);
//int i = 1 / 0;
accountDao.inMoney(in, money);
}
}
spring_schema.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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置数据源 使用的是Druid数据源 -->
<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!-- 初始化连接大小 -->
<property name="initialSize" value="0" />
<!-- 连接池最大使用连接数量 -->
<property name="maxActive" value="20" />
<!-- 连接池最小空闲 -->
<property name="minIdle" value="0" />
<!-- 获取连接最大等待时间 -->
<property name="maxWait" value="60000" />
<property name="poolPreparedStatements" value="true" />
<property name="maxPoolPreparedStatementPerConnectionSize"
value="33" />
<!-- 用来检测有效sql -->
<property name="validationQuery" value="${validationQuery}" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="testWhileIdle" value="true" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="25200000" />
<!-- 打开removeAbandoned功能 -->
<property name="removeAbandoned" value="true" />
<!-- 1800秒,也就是30分钟 -->
<property name="removeAbandonedTimeout" value="1800" />
<!-- 关闭abanded连接时输出错误日志 -->
<property name="logAbandoned" value="true" />
<!-- 监控数据库 -->
<property name="filters" value="mergeStat" />
</bean>
<!-- 业务层 -->
<bean id="accountService" class="com.julyday.spring_transaction.service.schema.AccountServiceImpl">
<property name="accountDao" ref="accountDao" />
</bean>
<!-- dao层 -->
<bean id="accountDao" class="com.julyday.spring_transaction.dao.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置事务增强处理Bean,指定事务管理器 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<!-- Spring aop事务管理 -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="transactionPointcut"
expression="execution(* com.julyday.spring_transaction.service..*Impl.*(..))" />
<!-- 指定在txAdvice切入点应用txAdvice事务增强处理 -->
<aop:advisor pointcut-ref="transactionPointcut" advice-ref="txAdvice" />
</aop:config>
</beans>
这个对于aop比较熟悉的同学肯定就知道了,这里就不多说了。
测试下:
package com.julyday.spring_transaction;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.julyday.spring_transaction.service.AccountService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring_schema.xml")
public class TransferSchemaTest {
@Resource(name="accountService")
private AccountService accountService;
@Test
public void testService(){
System.out.println("testService begin");
accountService.transfer("julyday", "zhangsan", 20000);
System.out.println("testService finish:");
}
}
ok,测试通过!
声明式事务第三种:注解方式
package com.julyday.spring_transaction.service.annotation;
import org.springframework.transaction.annotation.Transactional;
import com.julyday.spring_transaction.dao.AccountDao;
import com.julyday.spring_transaction.service.AccountService;
@Transactional()
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(final String in,final String out,final int money) {
accountDao.outMoney(out, money);
//int i = 1 / 0;
accountDao.inMoney(in, money);
}
}
spring_annotation.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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置数据源 使用的是Druid数据源 -->
<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!-- 初始化连接大小 -->
<property name="initialSize" value="0" />
<!-- 连接池最大使用连接数量 -->
<property name="maxActive" value="20" />
<!-- 连接池最小空闲 -->
<property name="minIdle" value="0" />
<!-- 获取连接最大等待时间 -->
<property name="maxWait" value="60000" />
<property name="poolPreparedStatements" value="true" />
<property name="maxPoolPreparedStatementPerConnectionSize"
value="33" />
<!-- 用来检测有效sql -->
<property name="validationQuery" value="${validationQuery}" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="testWhileIdle" value="true" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="25200000" />
<!-- 打开removeAbandoned功能 -->
<property name="removeAbandoned" value="true" />
<!-- 1800秒,也就是30分钟 -->
<property name="removeAbandonedTimeout" value="1800" />
<!-- 关闭abanded连接时输出错误日志 -->
<property name="logAbandoned" value="true" />
<!-- 监控数据库 -->
<property name="filters" value="mergeStat" />
</bean>
<!-- 业务层 -->
<bean id="accountService" class="com.julyday.spring_transaction.service.annotation.AccountServiceImpl">
<property name="accountDao" ref="accountDao" />
</bean>
<!-- dao层 -->
<bean id="accountDao" class="com.julyday.spring_transaction.dao.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
最后一行千万不要忘记<tx:annotation-driven transaction-manager="transactionManager"/>,@Transactional注解,我们可以点进去看下,这里我们用的全部都是默认值了!首先传播行为默认的是Propagation propagation() default Propagation.REQUIRED;隔离级别Isolation isolation() default Isolation.DEFAULT;只读boolean readOnly() default false;还有异常的rollbackFor,noRollbackFor,rollbackFor就是出现什么异常就回滚,noRollbackFor就是出现什么异常就不回滚。当然一般情况下都是不需要加这些的,有需要的同学可以自己去添加看看,同样的其他的方式也是可以配置这些内容的。
总结
文章中代码的地址:
https://github.com/Julyday/spring_transaction.git