使用事务注意事项
事务是程序运行如果没有错误,会自动提交事物,如果程序运行发生异常,则会自动回滚。
如果使用了try捕获异常时.一定要在catch里面手动回滚。
事务手动回滚代码
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
声明事务实现
概述
管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,
在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,
这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明
(或通过基于@Transactional注解的方式),
便可以将事务规则应用到业务逻辑中。
显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。
声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,
声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。
但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。
<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.learn</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/javassist/javassist -->
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency>
<!-- 引入Spring-AOP等相关Jar -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
</dependencies>
</project>
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 这里表示扫包范围
因为我们是使用注解的,
-->
<context:component-scan base-package="com.learn"></context:component-scan>
<!-- 这里表示开启事务的注解
你如果想要事务的话,你必须开启一个事务注解,
-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy> <!-- 开启事物注解 -->
<!-- 1. 数据源对象: C3P0连接池 -->
<!-- 第一步我们加载C3P0数据源
DBCP和C3P0的区别讲一下,数据库的连接池,
-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/day20"></property>
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
</bean>
<!-- 2. JdbcTemplate工具类实例 -->
<!-- 这里要引用到我的数据源 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 3.配置事务 -->
<bean id="dataSourceTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
package com.learn.service;
public interface UserService {
public void add();
}
package com.learn.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import com.learn.dao.UserDao;
import com.learn.service.UserService;
import com.learn.transaction.TransactionUtils;
/**
* 这就是编程式事务
*
* @author Leon.Sun
*
*/
@Service
public class UserServiceImpl implements UserService {
@Autowired
private TransactionUtils transactionUtils;
@Autowired
private UserDao userDao;
public void add() {
/**
* transactionStatus这样起名还是规范一点
*
*/
TransactionStatus transactionStatus = null;
/**
* 这里一定要try起来
* 事务没问题才会提交
* 有问题就会回滚
*
*/
try {
/**
* 开启事务
*/
transactionStatus = transactionUtils.begin();
userDao.add("test001", 20);
/**
* 这个时候test001会不会提交到数据库里面去
* 肯定不会的
* 因为你的事务还没有提交
* 我怎么可能到数据库里面查到呢
* 是肯定不会到数据库里面去的
* 因为你没有commit我怎么能到数据库里面查的到呢
* 肯定是不会的
* 我们打个断点看一下
* 已经走完了
* 有没有数据你们说
* 是不是没有
* 因为你没有提交到数据库里面去
* 报错他就会走到catch里面去
* 他就会把事务回滚
* 数据库里面是不是没有
*
* 比如我们搞一个不报错的
* 我再运行一遍
* 只要你不提交肯定是查不到的
* 到这里是不是查不到
* 当transactionUtils.commit(transactionStatus)这段代码执行完毕的情况下
* 你这两个数据都是可以查到的
* 是不是提交了
* 提交了之后test001和test002是不是都有了
* 这就是事务的一个基本原理
* 到时候我们结合AOP自己封装一个事务
*
*
*/
System.out.println("开始报错了..........................");
// int i = 1/0;
System.out.println("####################################");
userDao.add("test002", 21);
/**
* 提交事务
* transactionStatus!=null这个是为了安全起见
*/
if(transactionStatus!=null) {
transactionUtils.commit(transactionStatus);
}
} catch (Exception e) {
e.printStackTrace();
/**
* 异常我们最好这样子做
* 如果发生异常的情况下
* 走到这里一定要调用rollback
* 回滚事务
* transactionStatus!=null这个是为了安全起见
*/
if(transactionStatus!=null) {
transactionUtils.rollback(transactionStatus);
}
}
}
}
package com.learn.transaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
/**
* 我们今天讲手写事务
* 我们要他的注解来实现事务
* 注解其实都是封装起来的
* 通过编程事务进行封装的
* 编程事务(需要手动begin 手动回滚 手都提交)
* 在这里我们要封装几个方法
* 你们只要拿到ORM框架的接口就行了
* 我们的数据源接口是哪一个
* 就是dataSourceTransactionManager这个
* 我们要拿到事务数据源
* 然后进行控制事务
*
* 我们要把注入到Spring的容器里面去
* 你们最好不要用单例的
* 否则会产生线程安全问题的
*
*
*
* @author Leon.Sun
*
*/
@Component
public class TransactionUtils {
/**
* 获取事务源
*
* 这里要注入一下@Autowired
*
*/
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
/**
* 这个方法干嘛用的
* 是不是开启事务
*
*
* @return
*/
public TransactionStatus begin() {
/**
* 我们调用一下dataSourceTransactionManager
* 我们先使用默认的传播级别DefaultTransactionAttribute
* 没有学到传播级别
* 不知道他里面怎么去用
* 拿到了事务的一个状态
* 记得要把事务的状态返回回去
* 这里是开启一个事务
* 我要提交事务怎么提交呢
*/
TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
return transaction;
}
/**
* 这叫做提交事务
*
* 把状态传入进来
*
*
* @param transaction
*/
public void commit(TransactionStatus transaction) {
/**
* 提价一个事务状态
*
*/
dataSourceTransactionManager.commit(transaction);
}
/**
* 我们再写一个回滚事务
* @param transaction
*/
public void rollback(TransactionStatus transaction) {
dataSourceTransactionManager.rollback(transaction);
}
}
package com.learn.test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.learn.service.UserService;
public class Test001 {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) applicationContext.getBean("userServiceImpl");
userService.add();
}
}