事务分为声明式和编程式两种:
声明式事务:声明式事务是指通过注解的形式对事务的各种特性进行控制和管理。
编码式(编程式)事务:指的是通过编码的方式实现事务的声明。
##创建tx数据库
drop database if exists `tx`;
CREATE database `tx`;
##切换tx数据库
USE `tx`;
##删除用户表
DROP TABLE IF EXISTS `user`;
##创建用户表
CREATE TABLE `user` (
`id` int primary key auto_increment,
`username` varchar(50) NOT NULL,
`money` int(11) DEFAULT NULL
);
##插入数据
insert into `user`(`username`,`money`) values ('张三',1000),('李四',1000);
##删除图书表
drop table if exists `book`;
##创建图书表
create table `book`(
`id` int primary key auto_increment,
`name` varchar(500) not null,
`stock` int
);
##插入数据
insert into book(`name`,`stock`) values('java编程思想',100),('C++编程思想',100);
##查看数据
select * from book;
select * from user;
JavaBean对象
public class Book {
private Integer id;
private String name;
private int stock;
public class User {
private Integer id;
private String username;
private int money;
Dao们
@Repository
public class BookDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public int updateBook() {
String sql = "update book set name = '**我被修改了!**' where id = 1";
return jdbcTemplate.update(sql);
}
}
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public int updateUser() {
String sql = "update user set username = '**我被修改了**' where id = 1";
return jdbcTemplate.update(sql);
}
}
Service代码
@Service
public class TransactionService {
@Autowired
private UserDao userDao;
@Autowired
private BookDao bookDao;
public void updateTwoTable() {
userDao.updateUser();
bookDao.updateBook();
}
}
实验1:测试service服务层的默认事务
@ContextConfiguration(locations = "classpath:application.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringTest {
@Autowired
private DataSource dataSource;
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private TransactionService transactionService;
@Test
public void testDataSource() throws Exception {
System.out.println(dataSource.getConnection());
}
@Test
public void testJdbcTempalte() throws Exception {
System.out.println( jdbcTemplate );
}
@Test
public void testTransaction() throws Exception {
transactionService.updateTwoTable();
}
}
public void updateTwoTable() {
userDao.updateUser();
int i = 12 / 0;
bookDao.updateBook();
}
结果:
但结果却是一张表更新了,另一种表未更新就发生异常,导致两张表不能同步,经典案例就是银行的转账,这边转过去了,对方没有收到。这里就要用到事务来处理了,要是在同一个事务中有一处出错,整个事务都回滚,从而阻止这种非一致性的更新操作。
PlatformTransactionManager接口,提供所有事务操作规范。我们使用DataSource数据库连接池,使用的事务管理器是DataSourceTransactionManager类
注:和Javaweb的Filter、jdk动态代理、aop、Utilsjdbc的原理一样分三层结构处理业务
实验2:测试Spring的声明式事务
先导入AOP包,因为Spring的底层事务使用到了aop功能
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
配置Spring的事务需要的切面类DataSourceTransactionManager
在Spring的配置文件中加入tx名称空间
在Spring的配置文件中
还需要在Service的事务方法中添加@Transactioaln注解
@Transactional
public void updateTwoTable() {
userDao.updateUser();
int i = 12 / 0;
bookDao.updateBook();
}
实验3:noRollbackFor和noRollbackForClassName测试不回滚的异常
/**
* noRollbackFor=ArithmeticException.class 表示当接收到数学异常之后。不回滚
* noRollbackForClassName="java.lang.ArithmeticException" 表示当接收到指定字符串表示的全类名的异常的时候,不回滚事务
*/
@Transactional(noRollbackFor=ArithmeticException.class)
public void updateTwoTable() {
userDao.updateUser();
int i = 12 / 0;
bookDao.updateBook();
}
Book:
User表:
注:spring默认回滚的是运行时异常RuntimeException和RuntimeException的子异常
实验5:rollbackFor和rollbackForClassName回滚的异常
Spring默认回滚的是RuntimeException,运行时异常或运行时异常的子异常
/**
* spring默认回滚的是运行时异常RuntimeException和RuntimeException的子异常
* rollbackFor=FileNotFoundException.class 表示FileNotFoundException也会回滚
* rollbackForClassName="java.io.FileNotFoundException" 表示当出现配置字符串所表示的全类名的异常的时候。也会回滚事务
* @throws FileNotFoundException
*
*/
@Transactional(rollbackFor=FileNotFoundException.class)
public void updateTwoTable() throws FileNotFoundException {
userDao.updateUser();
int i = 0;
if (i == 0) {//java.io.FileNotFoundException
throw new FileNotFoundException("sadf");
}
bookDao.updateBook();
}
实验4:测试readOnly只读属性
/**
* readOnly 如果值为true。表示只支持查询操作。不支持写操作
*
如果设置为false,支持全部
*/
@Transactional(readOnly=true)
public void updateTwoTable() throws FileNotFoundException {
userDao.updateUser();
bookDao.updateBook();
}
timeout=3表示操作不能超过3秒,否在就包错
@Transactional(timeout=3)
public void updateTwoTable() throws FileNotFoundException {
userDao.updateUser();
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
bookDao.updateBook();
}
什么是事务的传播行为:
当事务方法(XxxService())被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
事务的传播行为可以由传播属性指定。Spring定义了7种类传播行为。
事务的传播特性,有以下几种类型:(常用的就前两种,所以下面进行这两种的测试)required(默认的)
因为事务方法是被其他事务方法调用所以,添加几个事务方法进行测试
UserService
BookService
TransactionService
@Transactional(propagation = Propagation.REQUIRED)
public void multiUpdate() {
@Transactional(propagation = Propagation.REQUIRED)
public void updateBook() {
@Transactional(propagation=Propagation.REQUIRED)
public void updateUser() {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void multiUpdate()
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateBook()
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateUser()
@Transactional(propagation = Propagation.REQUIRED)
public void multiUpdate()
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateBook()
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateUser()
@Transactional(propagation = Propagation.REQUIRED)
public void multiUpdate()
@Transactional(propagation = Propagation.REQUIRED)
public void updateBook()
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateUser()
@Transactional(propagation = Propagation.REQUIRED)
public void multiUpdate()
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateBook()
@Transactional(propagation = Propagation.REQUIRED)
public void updateUser()
去掉。所有@Transactional的注解。
当然注解有的功能,xml也可以实现
总结:使用jdbcTemplate或者是事务管理器 属性都要引用数据源,因为都是在数据库连接的基础上操作的。
总结:注解方式和配置文件方式的事务
(1)、无论哪种方式,默认都是propagation="REQUIRED";方式
(2)、无论哪种方式,默认值都是:transaction-manager="transactionManager"
(3)、无论哪种方式,都要有事务管理器---管理事务的切面类-DataSourceTransactionManager
advice - advisor :建议 ,顾问
配置文件情况下:一、(为什么用aop.jar包?因为事务的底层是用aop的三层结构实现的,学习aop的主要作用就是处理事务)
二、还有事务的特性配置
注解方法情况下:在Spring的配置文件中
这里的transaction-manager="transactionManager"一般如果切面类的id值是transactionManager,这里就可以省略了,阴谋在事务的注解驱动里,默认是这个值的。但如果切面类的管理器的id不是这个值,这里就一定要加上对应的值了。
Spring的核心包
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
aop包
spring-aop-4.0.0.RELEASE.jar(注解,包扫描)
spring-aspects-4.0.0.RELEASE.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
JDBC-ORM包
spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar(事务管理)
Spring的web整合包
spring-web-4.0.0.RELEASE.jar
测试包
spring-test-4.0.0.RELEASE.jar
整合Spring和Web容器分两个步骤:
1、导入spring-web-4.0.0.RELEASE.jar
2、在web.xml配置文件中配置org.springframework.web.context.ContextLoaderListener(直接提示导入,无需记忆)监听器监听ServletContext的初始化
3、在web.xml配置文件中配置contextConfigLocation上下文参数。配置Spring配置文件的位置,以用于初始化Spring容器
contextConfigLocation
classpath:applicationContext.xml
org.springframework.web.context.ContextLoaderListener
方法一(官方推荐):
WebApplicationContextUtils.getWebApplicationContext(getServletContext())
方法二(不推荐):
getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);