Spring 事务
案例
转账案例
IAccountDao 接口
public interface IAccountDao {
/**
* 根据名称查询账户实体
* @param name
* @return
*/
Account findByName(String name);
/**
*
* @param account
*/
void update(Account account);
}
AccountDaoImpl 实现类
@Repository
public class AccountDaoImpl implements IAccountDao {
@Autowired
private QueryRunner queryRunner;
@Autowired
private TxManager tx;
public Account findByName(String name) {
try {
return queryRunner.query(tx.getConnection(), "SELECT * FROM tb_account WHERE name = ?", new BeanHandler<Account>(Account.class), name);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public void update(Account account) {
try {
queryRunner.update(tx.getConnection(), "UPDATE tb_account set balance=? WHERE name=?", account.getBalance(), account.getName());
} catch (SQLException e) {
e.printStackTrace();
}
}
}
IAccountService接口
public interface IAccountService {
/**
* 转账操作
*
* @param sourceAccountName 来源账户名
* @param targetAccountName 目标账户名
* @param balance 金额
*/
void transfer(String sourceAccountName,
String targetAccountName,
Float balance);
/**
* 根据名称查询账户信息
*
* @param name
* @return
*/
Account findByName(String name);
}
AccountServiceImpl实现类
@Service
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;
@Autowired
private TxManager tx;
public void transfer(String sourceAccountName, String targetAccountName, Float balance) {
try {
//开启事务
tx.begin();
//查询
Account sourceAccount = accountDao.findByName(sourceAccountName);
Account targetAccount = accountDao.findByName(targetAccountName);
//余额变动
sourceAccount.setBalance(sourceAccount.getBalance() - balance);
targetAccount.setBalance(targetAccount.getBalance() + balance);
//更新
accountDao.update(sourceAccount);
int i = 1 / 0;
accountDao.update(targetAccount);
//没有报错
tx.commit();
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
} finally {
//事务关闭
tx.close();
}
}
public Account findByName(String name) {
return accountDao.findByName(name);
}
}
自定义事务管理器 使用ThreadLocal
@Component
public class TxManager {
@Autowired
private DataSource dataSource;
//本地存放connection集合 ThreadLocal
private ThreadLocal<Connection> th = new ThreadLocal<Connection>();
/**
* 获取conneciton对象
*
* @return
*/
public Connection getConnection() {
try {
Connection connection = th.get();
if (connection == null) {
connection = dataSource.getConnection();
//存储connection
th.set(connection);
}
return connection;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
/**
* 开启事务
*/
public void begin() {
try {
getConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 提交事务
*/
public void commit() {
try {
getConnection().commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 事务回滚
*/
public void rollback() {
try {
getConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 关闭事务
*/
public void close() {
try {
getConnection().close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
配置类
- SpringConfig.java
//扫描注解
@ComponentScan("com.itheima")
//随着ioc容器加载
@Configuration
@Import(DBConfig.class)
public class SpringConfig {
}
- DBConfig.java
@Configuration
@PropertySource("db.properties")
public class DBConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public QueryRunner queryRunner(DataSource dataSource) {
return new QueryRunner(dataSource);
}
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
测试类
//指定单元测试运行器
@RunWith(SpringJUnit4ClassRunner.class)
//指定配置文件
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {
@Autowired
private IAccountService service;
@Test
public void testAccountService() {
//转账操作
service.transfer("test1", "test2", 5f);
}
}
使用动态代理优化
JDK代理
使用Java提供的动态代理,InvocationHandler来对代理对象进行增强
//指定单元测试运行器
@RunWith(SpringJUnit4ClassRunner.class)
//指定配置文件
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {
@Autowired
private IAccountService service;
@Autowired
private TxManager tx;
@Test
public void testAccountService() {
InvocationHandler invocationHandler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object object = null;
try {
//开启事务
tx.begin();
//调用原来目标对象方法
object = method.invoke(service, args);
tx.commit();
} catch (Exception e) {
tx.rollback();
e.printStackTrace();
} finally {
tx.close();
}
return object;
}
};
//创建代理对象
IAccountService accountService = (IAccountService) Proxy.newProxyInstance(service.getClass().getClassLoader(),
service.getClass().getInterfaces(),
invocationHandler);
//代理对象工作
accountService.transfer("test2", "test3", 100f);
//转账操作
// service.transfer("test1", "test2", 5f);
}
}
cglib动态代理
使用cglib创建代理对象
public void testAccountService() {
InvocationHandler invocationHandler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object object = null;
try {
//开启事务
tx.begin();
//调用原来目标对象方法
object = method.invoke(service, args);
tx.commit();
} catch (Exception e) {
tx.rollback();
e.printStackTrace();
} finally {
tx.close();
}
return object;
}
};
//创建代理对象
/* IAccountService accountService = (IAccountService) Proxy.newProxyInstance(service.getClass().getClassLoader(),
service.getClass().getInterfaces(),
invocationHandler);
//代理对象工作
accountService.transfer("test2", "test3", 100f);
//转账操作*/
// service.transfer("test1", "test2", 5f);
//cglib 创建增强器
Enhancer enhancer = new Enhancer();
//设置父类 目标对象对应的类
enhancer.setSuperclass(AccountServiceImpl.class);
enhancer.setCallback(invocationHandler);
service = (AccountServiceImpl) enhancer.create();
service.transfer("test1","test2",100f);
}
jdk和cglib两种代理方式的选择
- 在创建代理实现类时,jdk的速度要高于cglib
- 当被代理有接口的时候,使用jdk动态代理,因为效率较高
- 当被代理类没有接口的时候,使用cglib动态代理,只能选择cglib
AOP
AOP概念
AOP(面向切面编程)是一种思想,它的目的就是在不修改源代码的基础上,对原有功能进行增强
Spring AOP 是对AOP思想的一种实现,Spring底层同时支持jdk和cglib动态代理
Spring会根据被代理的类是否有接口自动选择代理方式:
- 如果有接口,就采用jdk动态代理
- 没有接口,就采用cglib的方式
术语
- 目标对象 被代理类产生的对象
- 连接点 目标对象中的所有方法
- 切入点 目标对象中一部分方法(要进行功能增强的那部分)
- 增强(通知) 具体的功能
- 织入 将增强功能添加在切入点的过程(动作)
- 代理对象 经过织入之后产生的对象(最后工作的对象)
- 切面:切面是一种描述,描述了一件事:一个什么样的增强功能加到了哪个切点的哪个位置上
-
所以说 切面=切点+增强
image.png
-
SpringAOP
- 引入坐标
<!-- spring context-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<!-- 切点表达式解析坐标-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
- 目标对象
public interface AccountDao {
void save(Object o);
//查询所有
List<Object> findAll();
//主键查询
Object findById(Integer id);
}
- 目标对象实现类
public class AccountDaoImpl implements AccountDao {
public void save(Object o) {
System.out.println("save");
}
public List<Object> findAll() {
System.out.println("find All");
return null;
}
public Object findById(Integer id) {
System.out.println("find by id");
return null;
}
}
- 开发增强
public class Logger {
public void beforeMethod() {
System.out.println("方法运行之前");
}
public void afterMethod(){
System.out.println("方法运行之后");
}
}
- 配置切面
切面配置文件
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl"/>
<bean id="logger" class="com.itheima.log.Logger"/>
<!-- 配置切面-->
<aop:config>
<!--
切点表达式:
* 代表忽略返回值类型
com.xxx 代表类型
* 代表一个或多个参数
.. 代表0个或多个参数
-->
<aop:pointcut id="pt" expression="execution(* com.itheima.dao.impl.*.*(..))"/>
<!-- 切面
一个打印日志的增强功能,配置到pt的aop:before上
-->
<aop:aspect ref="logger">
<!-- aop:before 代表添加到pt上 切点为pt-->
<aop:before method="beforeMethod" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
SpringAOP配置详解
切点表达式
<!--
切点表达式:
* 代表忽略返回值类型
com.xxx 代表类型
* 代表一个或多个参数
.. 代表0个或多个参数
-->
<aop:pointcut id="pt" expression="execution(* com.itheima.dao.impl.*.*(..))"/>
四大通知
四大通知描述的就是增强方法在切点方法的什么位置上执行
- 前置通知(before):在切点运行之前执行
- 后置通知(after-returning):在切点正常运行结束之后执行
- 异常通知(after-throwing):在切点发生异常的时候执行
- 最终通知(after)在切点的最终执行
根据四大通知的特性我们可以使用一个结构来使用
try{
前置通知
//方法运行
后置通知
}catch(Exception e){
异常通知
}finally{
最终通知
}
- 方法
public class Logger {
public void beforeMethod() {
System.out.println("方法运行之前");
}
public void afterMethod() {
System.out.println("方法运行之后");
}
public void exceptionMethod() {
System.out.println("方法异常");
}
public void finallyMethod() {
System.out.println("方法执行到最后");
}
}
- 配置
<!-- 配置切点-->
<aop:pointcut id="pt" expression="execution(* com.itheima.dao.impl.*.*(..))"/>
<!-- 切面
一个打印日志的增强功能,配置到pt的aop:before上
-->
<aop:aspect ref="logger">
<!-- aop:before 代表添加到pt上 切点为pt-->
<aop:before method="beforeMethod" pointcut-ref="pt"/>
<aop:after-returning method="afterMethod" pointcut-ref="pt"/>
<aop:after-throwing method="exceptionMethod" pointcut-ref="pt"/>
<aop:after method="finallyMethod" pointcut-ref="pt"/>
</aop:aspect>
环绕通知
环绕通知是一种特殊的通知,允许以编码的形式实现四大通知
- 方法
// 环绕通知
public void arroundMethod(ProceedingJoinPoint pjp) {
try {
System.out.println("方法执行之前");
//切点方法执行
pjp.proceed();
System.out.println("方法执行之后");
} catch (Throwable throwable) {
System.out.println("异常发生");
throwable.printStackTrace();
} finally {
System.out.println("方法运行到最后");
}
}
- 配置
<aop:aspect ref="logger">
<!-- aop:before 代表添加到pt上 切点为pt-->
<!-- <aop:before method="beforeMethod" pointcut-ref="pt"/>
<aop:after-returning method="afterMethod" pointcut-ref="pt"/>
<aop:after-throwing method="exceptionMethod" pointcut-ref="pt"/>
<aop:after method="finallyMethod" pointcut-ref="pt"/>-->
<aop:around method="arroundMethod" pointcut-ref="pt"/>
</aop:aspect>
SpringAOP注解版
- 添加注解扫描
<!-- 注解扫描-->
<context:component-scan base-package="com.itheima"/>
- 激活切面自动代理
<!-- 激活切面自动代理-->
<aop:aspectj-autoproxy/>
- accountDao
@Repository
public class AccountDaoImpl implements AccountDao {
- logger
@Component
public class Logger {
- 切面换成注解
@Component
@Aspect //在增强类上配置一个切面注释
public class Logger {
//@Pointcut 声明切点 execution切点表达式
@Pointcut("execution(* com.itheima.dao.impl.*.*(..))")
public void pt() {
}
@Before("pt()")
public void beforeMethod() {
System.out.println("方法运行之前");
}
@AfterReturning("pt()")
public void afterMethod() {
System.out.println("方法运行之后");
}
@AfterThrowing("pt()")
public void exceptionMethod() {
System.out.println("方法异常");
}
@After("pt()")
public void finallyMethod() {
System.out.println("方法执行到最后");
}
// 环绕通知
@Around("pt()")
public void arroundMethod(ProceedingJoinPoint pjp) {
try {
System.out.println("方法执行之前");
//切点方法执行
pjp.proceed();
System.out.println("方法执行之后");
} catch (Throwable throwable) {
System.out.println("异常发生");
throwable.printStackTrace();
} finally {
System.out.println("方法运行到最后");
}
}
}
Spring的四大通知一旦全部使用注解,会出现BUG所以推荐使用环绕通知
环绕通知
@Around("execution(* com.itheima.dao.impl.*.*(..))")
public void arroundMethod(ProceedingJoinPoint pjp) {
try {
System.out.println("方法执行之前");
//切点方法执行
pjp.proceed();
System.out.println("方法执行之后");
} catch (Throwable throwable) {
System.out.println("异常发生");
throwable.printStackTrace();
} finally {
System.out.println("方法运行到最后");
}
}
- Spring主配置 SpringConfig
@ComponentScan("com.itheima")//相当于<context:componet-scan base-package="com.itheima"
@EnableAspectJAutoProxy//相当于激活自动代理<aop:aspectj-autoproxy/>
public class SpringConfig {
}
- 测试
@Test
public void testAccountDao() {
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
AccountDao accountDao = ac.getBean(AccountDao.class);
accountDao.findAll();
}
使用Spring的AOP完成事务管理
在上面的基础项目上
目标对象serviceImpl
- 准备增强
@Component
public class TxManger {
@Autowired
private DataSource dataSource;
//本地存放connection对象的集合
private ThreadLocal<Connection> th = new ThreadLocal<Connection>();
//获取到connection对象
public Connection getConnection() throws SQLException {
Connection connection = th.get();
if (connection == null) {
connection = dataSource.getConnection();
th.set(connection);
}
return connection;
}
//开启
public void begin() {
try {
getConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
//提交
public void commit() {
try {
getConnection().commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
//回滚
public void rollback() {
try {
getConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
//关闭
public void close() {
try {
getConnection().close();
th.remove();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
xml版本
- 配置切点、切面
<aop:config>
<!-- 配置切点-->
<aop:pointcut id="pt" expression="execution(* com.itheima.dao.impl.*.*(..))"/>
<!-- 配置切面 切面就是一种功能描述 描述了什么样的一个增强功能加到了哪个切点上-->
<aop:aspect ref="txManger">
<aop:before method="begin" pointcut-ref="pt"/>
<!-- 配置正常结束-->
<aop:after-returning method="commit" pointcut-ref="pt"/>
<!-- 配置异常抛出-->
<aop:after-throwing method="rollback" pointcut-ref="pt"/>
<!-- 配置最终通知-->
<aop:after method="close" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
- 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountServiceTest {
@Autowired
private AccountService accountService;
@Test
public void testTransfer() {
accountService.transfer("test1",
"test2",
1f);
}
}
- 环绕——增强方法
/**
* 环绕方法
* @param pointcut
*/
public void aroundMethod(ProceedingJoinPoint pointcut) {
try {
//开启事务
getConnection().setAutoCommit(false);
pointcut.proceed();
getConnection().commit();
} catch (Throwable throwable) {
throwable.printStackTrace();
try {
getConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
} finally {
try {
getConnection().close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
- 配置环绕方法
<aop:aspect ref="txManger">
<aop:around method="aroundMethod" pointcut-ref="pt"/>
</aop:aspect>
注解版
- applicaitonContext.xml 加入激活自动代理
<!--注解扫描-->
<context:component-scan base-package="com.itheima"/>
<!-- 激活切面自动代理-->
<aop:aspectj-autoproxy/>
- 增强类注解
/**
* 环绕通知
*
* @param point 切点对象
*/
@Around("execution(* com.itheima.service.impl.*.*(..))")
public void aroundMethod(ProceedingJoinPoint point) {
try {
//开启事务
getConnection().setAutoCommit(false);
//切点方法执行
point.proceed();
//正常执行 提交事务
getConnection().commit();
} catch (Throwable throwable) {
throwable.printStackTrace();
try {
//异常 事务回滚
getConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
} finally {
//最终
try {
getConnection().close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
JdbcTemplate
JdbcTemplate是Spring提供的持久层技术,用于操作数据库,底层封装了JDBC
核心类
JdbcTemplate :用于执行增删改查SQL语句
RowMapper 这是一个接口,主要作用就是将数据库返回的记录封装进实体对象ResultSetHandler
核心方法
- update() 用来执行增删改查语句
- query() queryForObject() 用来执行查询语句
实例代码
//创建一个JdbcTemplate对象,用来执行增删改查, 需要给一个数据源
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
//update方法,用于执行增删改语句
//第一个参数:sql语句 后面的参数:sql语句中的所需要的的值
jdbcTemplate.update("insert into account value(null,?,?)",1,2);
//query或者queryForObject方法,用于执行查询语句
//query 用于查询多条记录,返回一个集合 queryForObject用于查询一条记录,返回一个实体
//第一个参数:sql语句 第二个参数:封装返回值 后面的参数:sql语句中的所需要的的值
jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<Account>(Account.class));
jdbcTemplate.queryForObject("select * from account where aid = ?", new BeanPropertyRowMapper<Account>(Account.class), 1);
案例:
- 创建工程 引入maven坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
- 创建dao接口
public interface AccountDao {
Account findByName(String name);
//更新操作
void update(Account account);
//查询所有
List<Account> findAll();
}
- 创建dao实现类
@Repository
public class AccountDaoImpl implements IAccountDao {
//注入jdbctemplate
@Autowired
private JdbcTemplate jdbcTemplate;
public Account findByName(String name) {
return jdbcTemplate.queryForObject("SELECT * FROM tb_account WHERE name = ?", new BeanPropertyRowMapper<Account>(Account.class), name);
}
public void update(Account account) {
jdbcTemplate.update("UPDATE tb_account SET balance=? WHERE name=?", account.getBalance(), account.getName());
}
public List<Account> findAll() {
return jdbcTemplate.query("SELECT * FROM tb_account", new BeanPropertyRowMapper<Account>(Account.class));
}
}
- 创建Service接口
@Service
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;
public void transfer(String oriAccountName, String tatgetAccountName, Float balance) {
Account sourceAccount = accountDao.findByName(oriAccountName);
Account targetAccount = accountDao.findByName(tatgetAccountName);
//更新钱
sourceAccount.setBalance(sourceAccount.getBalance() - balance);
targetAccount.setBalance(targetAccount.getBalance() + balance);
//更新数据库
accountDao.update(sourceAccount);
accountDao.update(targetAccount);
}
public List<Account> findAll() {
return accountDao.findAll();
}
}
- 加入配置文件
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描注解-->
<context:component-scan base-package="com.itheima"/>
<!-- 注入数据库datasource-->
<!-- 注入jdbctemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="jdbc:mysql:///spring_lsn"/>
<property name="username" value="root"/>
<property name="password" value="xml123xml"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
</beans>
- 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestAccountService {
@Autowired
private IAccountService service;
@Test
public void testAccountService() {
service.findAll().stream().forEach(account -> System.out.println(account));
}
}
Spring中的事务管理
事务管理方式
Spring支持两种事务管理方式:编程式事务和声明式事务
- 编程式事务:将业务代码和业务代码放在一起书写,耦合性高不推荐使用
- 生命是食物就是将事务代码和业务代码隔离开发,然后通过一段配置让他们组装运行,最后达到控制事务的目的
Spring事务管理相关的API
PlatformTransactionManager
这是Spring进行事务管理的一个根接口,我们要使用它的实现类做事务管理
mybatis和jdbcTemplate都可以使用它的一个子类(DataSourceTransactionManager)做事务管理
TransactionDefinition
TransactionDefinition这个API是用来做事务定义
隔离级别
传播行为
事务传播行为指的就是当一个业务方法被另一个业务方法调用时,应该如何进行事务控制
TransactionStatus
TransactionStatus代表的是事务的当前状态
三个API之间的关系
PlatformTransactionManager通过读取TransactionDefinition中定义事务信息参数来管理事务
管理之后会产生一些列的TransactionStatus
声明式事务
思路:使用DataSourceTransactionManager作为事务管理器,需要向事务管理器传递一些参数:事务隔离级别、事务传播行为、事务超时、事务是否只读
xml版
- 准备目标对象[自己开发的业务类]
@Service
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;
public void transfer(String oriAccountName, String tatgetAccountName, Float balance) {
Account sourceAccount = accountDao.findByName(oriAccountName);
Account targetAccount = accountDao.findByName(tatgetAccountName);
//更新钱
sourceAccount.setBalance(sourceAccount.getBalance() - balance);
targetAccount.setBalance(targetAccount.getBalance() + balance);
//更新数据库
accountDao.update(sourceAccount);
accountDao.update(targetAccount);
}
public List<Account> findAll() {
return accountDao.findAll();
}
}
- 配置事务管理器
<!--向事务管理传递参数的地方-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--
name 必传参数,它的作用是在切点中再次匹配, 然后控制行为
isolation="DEFAULT" 设置事务隔离级别
propagation="REQUIRED" 设置事务传播行为
read-only="false" 设置事务是否只读
timeout="-1" 事务超时时长
no-rollback-for="" 指定哪些异常不回滚
rollback-for="" 指定哪些异常回滚
-->
<!-- <tx:method name="find*" read-only="true" />
<tx:method name="get*" read-only="true" />
<tx:method name="select*" read-only="true" />
<tx:method name="update*" />
<tx:method name="delete*" />
<tx:method name="save*" />-->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
- 测试
@Test
public void testAccountService() {
// service.findAll().stream().forEach(account -> System.out.println(account));
service.transfer("test1",
"test2",
100f);
}
注解版本
- 删除配置文件中的所有aop和tx开头
- 添加事务注解驱动
<tx:annotation-driven transaction-manager="transactionManager"/>
- 使用注解做声明式事务
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,//默认隔离级别
timeout = -1,
readOnly = false
)
public void transfer(String oriAccountName, String tatgetAccountName, Float balance) {
Account sourceAccount = accountDao.findByName(oriAccountName);
Account targetAccount = accountDao.findByName(tatgetAccountName);
//更新钱
sourceAccount.setBalance(sourceAccount.getBalance() - balance);
targetAccount.setBalance(targetAccount.getBalance() + balance);
//更新数据库
accountDao.update(sourceAccount);
accountDao.update(targetAccount);
}
- 事务优化
默认配置
@Service
@Transactional
public class AccountServiceImpl implements IAccountService {
- 纯注解配置
@Configuration
@ComponentScan("com.itheima")
@EnableTransactionManagement
public class SpringConfig {
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername("root");
dataSource.setPassword("xml123xml");
dataSource.setUrl("jdbc:mysql:///spring_lsn");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
return jdbcTemplate;
}
@Bean
public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) {
DataSourceTransactionManager manager = new DataSourceTransactionManager(dataSource);
return manager;
}
}