学习目标
-
能够说出事务的ACID原则
-
能够说出spring的事务管理方式
-
能够说出spring事务管理常用接口
-
能够理解事务的隔离级别
-
能够理解事务的传播行为
-
能够通过xml实现声明式事务控制
-
能够通过注解实现声明式事务控制
-
了解spring编程式事务控制
spring中声明式事务控制【掌握】
事务回顾
事务定义
事务(transaction),一般是指要做的或者所做的事情。在程序中,尤其是在操作数据库的程序中,指的是访问并且可能更新数据库中数据项的一个执行单元(unit),这个执行单元由事务开始(begin transaction)和事务结束(end transaction)之间执行的全部操作组成。
事务特性(ACID原则)【记住】
事务具有4个基本特性:原子性、一致性、隔离性、持久性。也就是我们常说的ACID原则。
-
原子性(Atomicity):一个事务已经是一个不可再分割的工作单位。事务中的全部操作要么都做;要么都不做。
-
一致性(Consistency):事务必须是使得数据库状态从一个一致性状态,转变到另外一个一致性状态。也就是说在事务前,和事务后,被操作的目标资源状态一致。比如银行转账案例中,转账前和转账后,总账不变。
-
隔离性(Isolation):一个事务的执行不能被其他事务的影响。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,多个并发事务之间不能相互干扰。
-
持久性(Durability):一个事务一旦提交,它对数据库中数据的改变会永久存储起来。其他操作不会对它产生影响。
事务案例:账户名单插入
案例需求
使用JdbcTemplate+druid操作账户表(account),模拟实现银行转账操作。
准备数据
create table account(
id int primary key auto_increment,
name varchar(40),
money float
)ENGINE=InnoDB character set utf8 collate utf8_general_ci;
insert into account(name,money) values('小明',1000);
insert into account(name,money) values('小花',1000);
insert into account(name,money) values('小王',1000);
案例需求准备
创建项目
配置pom.xml,加入依赖
<?xml version="1.0" encoding="UTF-8"?>
<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>Spring04</groupId>
<artifactId>tx_xml</artifactId>
<version>1.0-SNAPSHOT</version>
<!--spring核心包-->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--spring AOP包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--aspectj依赖导入-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>
<!--spring事务包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--jdbctemplate包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--mysql驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--数据库连接池依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!--spring测试包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--junit测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
编写账户实体类对象
package com.po;
public class Account {
private int id;
private String name;
private double money;
public Account() {
}
public Account(int id, String name, double money) {
this.id = id;
this.name = name;
this.money = money;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
编写账户持久层对象
编写账户dao接口
package com.dao;
import com.po.Account;
public interface AccountDao {
void insert(Account account);
}
编写账户dao实现类
package com.dao.impl;
import com.dao.AccountDao;
import com.po.Account;
import org.springframework.jdbc.core.JdbcTemplate;
public class AccountDaoImpl implements AccountDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public void insert(Account account) {
String sql = "INSERT INTO account values(null,?,?)";
jdbcTemplate.update(sql, account.getName(), account.getMoney());
}
}
编写账户业务层对象
编写账户service接口
package com.server;
import com.po.Account;
public interface Server {
void insert(Account account);
}
编写账户service实现类
package com.server.impl;
import com.dao.AccountDao;
import com.po.Account;
import com.server.Server;
public class ServerImpl implements Server {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void insert(Account account) {
accountDao.insert(account);
// int i = 1 / 0;
accountDao.insert(account);
}
}
spring事务控制中需要明确的事项
第一件事
j2EE体系中项目按照分层进行设计开发,有表现层、业务层、持久层。事务处理位于业务层。spring提供了分层设计业务层的事务处理解决方案。
第二件事
spring框架为我们提供了一组事务控制接口,该组接口位于spring-tx-xxx.RELEASE.jar包中。具体我们在后面的内容中详细介绍。
第三件事
spring的事务都是基于AOP的实现,它既可以使用编程的方式实现,也可以使用配置的方式实现。我们这里重点是使用配置的方式来实现。
spring中事务控制API介绍
PlatformTransactionManager接口
它是一个接口,是spring的事务管理器核心接口,spring并不实现具体的事务,而是负责包装底层事务,提供规范。应用底层支持什么样的事务策略,spring就支持什么样的事务策略。该接口提供了我们操作事务的常用方法。如下:
我们在开发中都是直接使用它的实现类,有如下常用实现类:
org.springframework.jdbc.datasource.DataSourceTransactionManager实现类
–支持使用spring JDBC 或者Mybatis框架的事务管理器
TransactionDefinition接口
它是一个接口,是定义事务的信息对象,提供了如下常用的方法:
事务隔离级别 【理解】
说明:事务隔离级别,反应了事务在并发访问时的处理态度。
ISOLATION_DEFAULT:
默认级别,归属于下列某一种隔离级别。在项目中使用默认值即可。
ISOLATION_READ_UNCOMMITTED:
可以读取其他事务未提交的数据(脏读)
ISOLATION_READ_COMMITTED:
只读取其他事务已经提交的数据,解决脏读的问题。有可能两次读取不一致的问题,不可重复读(oracle数据库默认级别)
ISOLATION_REPEATABLE_READ:
是否读取其他事务提交修改后的数据,解决不可重复读的问题。保证两次读取一致,可重复读(mysql数据库默认级别)
**ISOLATION_SERIALIZABLE:**是否读取其他事务添加后的数据,解决幻影读的问题
细节:
1.事务级别从低到高:脏读->不可重复读->可重复读->解决幻读
2.事务级别越高,数据越安全,消耗的资源越多,数据库操作性能越低
3.在企业项目中,使用哪一种级别的事务,需要根据业务需求来确定
事务传播行为【理解】
说明:事务传播行为,决定在不同的执行环境中,事务该如何传递。
REQUIRED:
如果已经有事务,就加入该事务中执行;如果没有事务,则新建一个事务。对应增/删/改操作(默认值)
SUPPORTS:
如果已经有事务,支持当前事务的执行;如果没有事务,就以非事务的方式执行。对应查询操作
MANDATORY:
要求在事务环境下执行,如果当前没有事务,则抛出异常
REQUIRES_NEW:
新建事务,如果当前已经存在事务,则把当前事务挂起
NOT_SUPPORTED:
以非事务方式执行,如果当前有事务,则把当前事务挂起
NEVER:
以非事务方式执行,如果当前有事务,则抛出异常
NESTED:
如果当前已经有事务,嵌套在当前事务中执行;如果当前没有事务,则类似于REQUIRED操作
事务超时时间
以秒为单位进行设置。如果设置为-1(默认值),表示没有超时限制。在企业项目中使用默认值即可。
是否只读事务
只读事务比读写事务性能要高,实际项目中,查询一般建议设置为只读。
TransactionStatus
它是一个接口,提供了事务具体的执行状态,描述了某一个时间点上事务对象的状态信息。提供了如下常用的方法:
基于xml的声明式事务配置【掌握】
创建项目
事务配置环境准备
配置pom.xml,加入依赖包
说明:spring的事务配置,是通过我们第三天知识点aop实现的。需要导入aop开发相关的jar包,以及jdbc和tx包。
<?xml version="1.0" encoding="UTF-8"?>
<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>cn.itheima</groupId>
<artifactId>spring-day04-02bankcase-trans-xml</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<!--spring版本-->
<spring.version>5.0.2.RELEASE</spring.version>
<!--druid版本-->
<druid.version>1.0.29</druid.version>
<!--mysql驱动版本-->
<mysql.version>5.1.6</mysql.version>
<!--aopalliance版本-->
<aopalliance.version>1.0</aopalliance.version>
</properties>
<dependencies>
<!--spring ioc依赖包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring jdbc包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring aspects包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<!--aopalliance包-->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>${aopalliance.version}</version>
</dependency>
<!--数据库连接池druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!--mysql驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
</project>
配置步骤【重点】
第一步:配置事务管理器
<bean id="txm" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入连接池-->
<constructor-arg ref="dataSource"/>
</bean>
第二步:配置事务通知规则
<!--事务通知规则(拦截到指定的方法后如何管理事务:读写、只读)-->
<tx:advice id="txAdvice" transaction-manager="txm">
<tx:attributes>
<!--tx:method标签:配置业务方法的名称规则,说明:
name:方法名称规则,可以使用通配符*
isolation:事务隔离级别,使用默认值即可
propagation:事务传播行为,增/删/改方法使用REQUIRED。查询方法使用SUPPORTS。
read-only:是否只读事务。增/删/改方法使用false。查询方法使用true。
timeout:配置事务超时。使用默认值-1即可。永不超时。
rollback-for:发生某种异常时,回滚;发生其它异常不回滚。没有默认值,任何异常都回滚
no-rollback-for:发生某种异常时不回滚;发生其它异常时回滚。没有默认值,任何异常都回 滚。-->
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
</tx:attributes>
</tx:advice>
第三步:Aop配置:建立切入点表达式与事务通知规则的关系
<aop:config>
<!--切入点表达式-->
<aop:pointcut id="pt" expression="execution(* com.server.impl.ServerImpl.insert(..))"/>
<!--建立关联-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
完整配置
<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--设置组件扫描-->
<context:component-scan base-package="com"/>
<!--扫描配置文件-->
<context:property-placeholder location="jdbc.properties"/>
<!--开始注入AccountDaoImpl依赖-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="jdbcTamplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"/>
</bean>
<bean id="dao" class="com.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTamplate"/>
</bean>
<!--结束注入AccountDaoImpl依赖-->
<!--开始注入ServerImpl依赖-->
<bean id="sever" class="com.server.impl.ServerImpl">
<property name="accountDao" ref="dao"/>
</bean>
<!--结束注入ServerImpl依赖-->
<!--开始Spring声明式事务控制的配置-->
<!-- 配置事务管理器(事务切面类)-->
<bean id="txm" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入连接池-->
<constructor-arg ref="dataSource"/>
</bean>
<!--事务通知规则(拦截到指定的方法后如何管理事务:读写、只读)-->
<tx:advice id="txAdvice" transaction-manager="txm">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
</tx:attributes>
</tx:advice>
<!--Aop配置:建立切入点表达式与事务通知规则的关系-->
<aop:config>
<!--切入点表达式-->
<aop:pointcut id="pt" expression="execution(* com.server.impl.ServerImpl.insert(..))"/>
<!--建立关联-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
<!--结束Spring声明式事务控制的配置-->
</beans>
测试
import com.po.Account;
import com.server.Server;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(value = SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:beans.xml")
public class App {
@Autowired
private Server server;
@Test
public void test() {
System.out.println(server);
Account account = new Account();
account.setName("晓红");
account.setMoney(999.99);
server.insert(account);
}
}
当业务不报错时,会添加两条插入信息。
运行结果:
但业务报错时,不会添加信息。
运行结果:
基于xml和注解的声明式事务配置【掌握】
改造账户dao实现类,使用注解进行配置
package com.dao.impl;
import com.dao.AccountDao;
import com.po.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository("dao")
public class AccountDaoImpl implements AccountDao {
@Autowired
@Qualifier("jdbcTamplate")
private JdbcTemplate jdbcTemplate;
@Override
public void insert(Account account) {
String sql = "INSERT INTO account values(null,?,?)";
jdbcTemplate.update(sql, account.getName(), account.getMoney());
}
}
改造账户service实现类,使用注解进行配置
通过**@Transactional注解,配置service实现类事务**【重点】
package com.server.impl;
import com.dao.AccountDao;
import com.po.Account;
import com.server.Server;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service("server")
@Transactional(transactionManager = "txm")
public class ServerImpl implements Server {
@Autowired
@Qualifier("dao")
private AccountDao accountDao;
@Override
public void insert(Account account) {
accountDao.insert(account);
// int i = 1 / 0;
accountDao.insert(account);
}
}
配置bean.xml
开启spring对注解事务支持【关键步骤】,不需要配置事务通知规则。
<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--设置组件扫描-->
<context:component-scan base-package="com"/>
<!--扫描配置文件-->
<context:property-placeholder location="jdbc.properties"/>
<!--开始注入AccountDaoImpl依赖-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="jdbcTamplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"/>
</bean>
<bean id="dao" class="com.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTamplate"/>
</bean>
<!--结束注入AccountDaoImpl依赖-->
<!--开始注入ServerImpl依赖-->
<bean id="sever" class="com.server.impl.ServerImpl">
<property name="accountDao" ref="dao"/>
</bean>
<!--结束注入ServerImpl依赖-->
<!--开始Spring声明式事务控制的配置-->
<!-- 配置事务管理器(事务切面类)-->
<bean id="txm" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入连接池-->
<constructor-arg ref="dataSource"/>
</bean>
<!--结束Spring声明式事务控制的配置-->
<!--开启spring对注解事务的支持-->
<tx:annotation-driven transaction-manager="txm"/>
</beans>
测试
不报错时:
报错时:
关于@Transactional注解特别说明【重点】
-
@Transactional注解和xml中属性含义一致。该注解可以出现在接口上、类上、方法上。
-
出现在接口上,表明该接口的所有实现类都有事务支持。
-
出现在类上,表明类中所有方法都有事务支持。
-
出现在方法上,表明该方法有事务支持。
-
三者同时出现,优先级为:方法>类>接口。
纯注解的声明式事务配置【了解】
编写配置类
说明:分模块进行编写配置类。分别有持久层对象配置类、主配置类。
编写持久层对象配置类
package com.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
@PropertySource("classpath:jdbc.properties")
public class SqlConfig {
@Value("${jdbc.driverClassName}")
private String className;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String name;
@Value("${jdbc.password}")
private String password;
//编写DruidDataSource
@Bean
public DruidDataSource getDateSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(className);
druidDataSource.setUrl(url);
druidDataSource.setUsername(name);
druidDataSource.setPassword(password);
return druidDataSource;
}
//编写JdbcTemplate
@Bean("jdbcTamplate")
public JdbcTemplate getJdbcTemplate(DruidDataSource dataSource){
return new JdbcTemplate(dataSource);
}
//编写事务管理配置类
@Bean("tx")
public DataSourceTransactionManager getTXmanger(DruidDataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
}
编写主配置类
package com.config;
import org.springframework.context.annotation.*;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@ComponentScan(basePackages = {"com"})
@EnableAspectJAutoProxy
@EnableTransactionManagement
@Import(SqlConfig.class)
public class SpringConfig {
}
测试
报错时:
不报错时:
spring编程式事务控制【了解】
编程式事务管理API介绍
PlatformTransactionManager
它是spring事务管理的接口,参考声明式事务中的介绍。
TransactionDefinition
它是事务定义接口,参考声明式事务中的介绍。
TransactionTemplate
它是事务管理模版类,继承DefaultTransactionDefinition类。用于简化事务管理,事务管理由模板类定义,主要是通过TransactionCallback回调接口或TransactionCallbackWithoutResult回调接口指定,通过调用模板类的execute()方法来自动实现事务管理。
TransactionCallback
通过实现该接口的“T doInTransaction(TransactionStatus status)”方法来定义需要事务管理的操作代码。适合于有返回值的目标方法。
TransactionCallbackWithoutResult
实现TransactionCallback接口,提供“void doInTransactionWithoutResult(TransactionStatus status)”。适合于不需要返回值的目标方法。
编程式事务管理代码模版(测试类)
import com.config.SpringConfig;
import com.dao.AccountDao;
import com.po.Account;
import com.server.Server;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
@RunWith(value = SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class App {
@Autowired
@Qualifier("dao")
private AccountDao accountDao;
@Autowired
private Server server;
@Autowired
private TransactionTemplate transactionTemplate;
//适合于有返回值的目标方法
@Test
public void test() {
Account account = new Account();
account.setName("晓红");
account.setMoney(999.99);
accountDao.insert(account);
TransactionCallback<Object> callback = new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus transactionStatus) {
server.insert(account);
return null;
}
};
//接受返回的值,为上面的null
Object execute = transactionTemplate.execute(callback);
}
//适合于无返回值的目标方法
@Test
public void test2() {
Account account = new Account();
account.setName("晓红");
account.setMoney(999.99);
accountDao.insert(account);
TransactionCallbackWithoutResult withoutResult = new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
server.insert(account);
}
};
transactionTemplate.execute(withoutResult);
}
}
编程式事务案例
配置SqlConfig.java
说明:增加事务管理模版TransactionTemplate配置。
package com.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
@PropertySource("classpath:jdbc.properties")
public class SqlConfig {
@Value("${jdbc.driverClassName}")
private String className;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String name;
@Value("${jdbc.password}")
private String password;
@Bean
public DruidDataSource getDateSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(className);
druidDataSource.setUrl(url);
druidDataSource.setUsername(name);
druidDataSource.setPassword(password);
return druidDataSource;
}
@Bean("jdbcTamplate")
public JdbcTemplate getJdbcTemplate(DruidDataSource dataSource){
return new JdbcTemplate(dataSource);
}
@Bean("tx")
public DataSourceTransactionManager getTXmanger(DruidDataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
//增加事务管理模版TransactionTemplate配置
@Bean
public TransactionTemplate getTransactionTemplate(DataSourceTransactionManager dataSourceTransactionManager){
return new TransactionTemplate(dataSourceTransactionManager);
}
}
配置SqlConfig.java
package com.config;
import org.springframework.context.annotation.*;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@ComponentScan(basePackages = {"com"})
@EnableAspectJAutoProxy
@EnableTransactionManagement
@Import(SqlConfig.class)
public class SpringConfig {
}
改造账户Dao实现类
package com.dao.impl;
import com.dao.AccountDao;
import com.po.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository("dao")
public class AccountDaoImpl implements AccountDao {
@Autowired
@Qualifier("jdbcTamplate")
private JdbcTemplate jdbcTemplate;
@Override
public void insert(Account account) {
String sql = "INSERT INTO account values(null,?,?)";
jdbcTemplate.update(sql, account.getName(), account.getMoney());
}
}
改造账户service实现类
package com.server.impl;
import com.dao.AccountDao;
import com.po.Account;
import com.server.Server;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ServerImpl implements Server {
@Autowired
@Qualifier("dao")
private AccountDao accountDao;
@Override
public void insert(Account account) {
accountDao.insert(account);
int i = 1 / 0;
accountDao.insert(account);
}
}
测试
报错时:
不报错时: