您的位置:首页 > 编程语言 > Java开发

spring事务管理

2016-09-19 17:29 309 查看
博主上篇文章讲到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=rootspring.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就是出现什么异常就不回滚。当然一般情况下都是不需要加这些的,有需要的同学可以自己去添加看看,同样的其他的方式也是可以配置这些内容的。

总结

我们一共介绍了四种事务编写的方式,编程式事务优点就是粒度是最小的,但是同样的他需要修改代码,一般用的不多。相对应的,声明式事务,代码修改的很少,很方便查找和修改,但是粒度比较大。代理的方式,我们没写一个事务就要写很多的bean,用的也不是很多;基于xml配置的aop方式,清晰明了,而且多个事务的话配置多个切点就好,使用的比较多;注解的方式,只需要在需要事务的地方加上注解就可以,也很方便,用的也比较多。所有推荐基于xml配置的aop方式及注解方式。

文章中代码的地址:

https://github.com/Julyday/spring_transaction.git
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: