spring(4)_JdbcDAOSupport/Spring的事务管理机制/声明式事务管理案例

1.基于JdbcTemplate实现DAO(CURD)

案例:
1.创建一个表book:
2.创建Book类,类中的属性用来对应book表的字段

public class Book {
	private Integer id;
	private String name;
	private Double price;
	......
}

3.编写Dao类
DAO实现CURD,只需要继承Spring提供 JdbcDAOSupport支持类 !
我们可以分析一波JdbcDAOSupport的源码,他内置了一个jdbcTemplate
spring(4)_JdbcDAOSupport/Spring的事务管理机制/声明式事务管理案例_第1张图片
spring(4)_JdbcDAOSupport/Spring的事务管理机制/声明式事务管理案例_第2张图片
而且只要注入datasource,就有了jdbcTemplate,相当于也注入了jdbcTemplate
编写Dao继承JdbcDaoSupport

//图书操作的dao层
//JdbcDaoSupport简化JdbcTemplate的代码开发。
public class BookDao extends JdbcDaoSupport {
	
	//注入jdbctempate
//	private JdbcTemplate jdbcTemplate;
//	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
//		this.jdbcTemplate = jdbcTemplate;
//	}

	//保存图书
	public void save(Book book){
		String sql="insert into book values(null,?,?)";
		//调用jdbctemplate
//		jdbcTemplate.update(sql, book.getName(),book.getPrice());
		super.getJdbcTemplate().update(sql, book.getName(),book.getPrice());
	}
}

配置spring核心配置文件,注入jdbcTemplate到BookDao:



	
	



	
	
	
	

BookDao类继承JdbcDaoSupport,也就继承了setDataSource()方法,所以可以用

1.1 实现增加、删除、修改功能

通过jdbcTemplate提供 update一个方法就可以
1.创建BookDao类

//图书操作的dao层
//JdbcDaoSupport简化JdbcTemplate的代码开发。
public class BookDao extends JdbcDaoSupport {
	
	//注入jdbctempate
//	private JdbcTemplate jdbcTemplate;
//	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
//		this.jdbcTemplate = jdbcTemplate;
//	}

	//保存图书
	public void save(Book book){
		String sql="insert into book values(null,?,?)";
		//调用jdbctemplate
//		jdbcTemplate.update(sql, book.getName(),book.getPrice());
		super.getJdbcTemplate().update(sql, book.getName(),book.getPrice());
	}
	
	//更新
	public void update(Book book){
		String sql="update book set name =? ,price =? where id =?";
		super.getJdbcTemplate().update(sql, book.getName(),book.getPrice(),book.getId());
	}
	
	//删除
	public void delete(Book book){
		super.getJdbcTemplate().update("delete from book where id =?", book.getId());
	}
}

增加、删除、修改功能都是通过update方法

1.2 查询

查询单个对象
在这里插入图片描述
查询数量
在这里插入图片描述
编写BookDao类:
查询集合
在这里插入图片描述
手动装配对象和自动装配对象

//根据id查询
public Book findById(Integer id){
	String sql ="select * from book where id = ?";
	return super.getJdbcTemplate().queryForObject(sql,BeanPropertyRowMapper.newInstance(Book.class), id);
}

//查询所有

public List findAll( ){
	String sql ="select * from book";
	return super.getJdbcTemplate().query(sql,BeanPropertyRowMapper.newInstance(Book.class));
}

//条件查询: 根据图书名称模糊查询信息
public List findByNameLike(String name ){
	String sql ="select * from book where name like ?";
	return super.getJdbcTemplate().query(sql,BeanPropertyRowMapper.newInstance(Book.class),"%"+name+"%");
}

BeanPropertyRowMapper.newInstance(Book.class),就跟我们之前使用的BeanHandler一样,是来讲结果封装成对象的,之前Object和List是通过两个不同的Handler(BeanHandler或者BeanListHandler)

其中

  • queryForObject:泛型方法,返回一个对象
  • queryList:只支持Integer.class String.class 这种单数据类型的,自己定义的Bean不支持。
  • query:泛型方法,返回list集合

2.Spring的事务管理机制

Spring事务管理高层抽象主要包括3个接口,Spring的事务主要是由他们共同完成的:

三个核心接口:

  • PlatformTransactionManager:事务管理器—主要用于平台相关事务的管理
  • TransactionDefinition: 事务定义信息(隔离、传播、超时、只读)—通过配置如何进行事务管理。
  • TransactionStatus:事务具体运行状态—事务管理过程中,每个时间点事务的状态信息。

2.1 PlatformTransactionManager事务管理器

该接口提供三个方法:

  • commit:提交事务
  • rollback:回滚事务
  • getTransaction:获取事务状态

spring为不同的持久化框架提供了不同的PlatformTransactionManager接口实现:

事务 说明
org.springframework.jdbc.datasource.DataSourceTransactionManager 使用Spring JDBC或iBatis 进行持久化数据时使用
org.springframework.orm.hibernate5.HibernateTransactionManager 使用Hibernate5.0版本进行持久化数据时使用
org.springframework.orm.jpa.JpaTransactionManager 使用JPA进行持久化时使用
org.springframework.jdo.JdoTransactionManager 当持久化机制是Jdo时使用
org.springframework.transaction.jta.JtaTransactionManager 使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用
  • DataSourceTransactionManager针对JdbcTemplate、MyBatis 事务控制,使用Connection(连接)进行事务控制 :
    __开启事务 connection.setAutoCommit(false);
    __提交事务 connection.commit();
    __回滚事务 connection.rollback();
  • HibernateTransactionManager针对Hibernate框架进行事务管理,使用Session的Transaction相关操作进行事务控制:
    __开启事务 session.beginTransaction();
    __提交事务 session.getTransaction().commit();
    __回滚事务 session.getTransaction().rollback();

事务管理器的选择?
用户根据选择和使用的持久层技术,来选择对应的事务管理器。


2.2 TransactionDefinition事务定义信息

该接口主要提供的方法:

  • getIsolationLevel:隔离级别获取
  • getPropagationBehavior:传播行为获取
  • getTimeout:获取超时时间(事务的有效期)
  • isReadOnly 是否只读(保存、更新、删除—对数据进行操作-变成可读写的,查询-设置这个属性为true,只能读不能写),事务管理器能够根据这个返回值进行优化。

这些事务的定义信息,都可以在配置文件中配置和定制。

其中比较重要的是:事务的隔离级别和事务的传播行为

1.事务的隔离级别IsolationLevel

隔离级别 含义
DEFAULT 使用后端数据库默认的隔离级别(spring中的的选择项)
READ_UNCOMMITED 允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读
READ_COMMITTED 允许在并发事务已经提交后读取。可防止脏读,但幻读和 不可重复读仍可发生
REPEATABLE_READ 对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生。
SERIALIZABLE 完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的。
  • 脏读:一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的
  • 不可重复读:在同一个事务中,多次读取同一组数据返回的结果不同,换句话说,后续读取可以读取到另一个事务已提交的更新数据.相反,"可重复读"在同一个事务中多次读取数据时,能够保证所读数据的一致性,也就是说,后续读取不能读取到另一个事务已提交的更新数据
  • 幻读:一个事务读取了几行记录后,另一个事务插入了一些记录,幻读就发生了,再后来的查询中,第一个事务就会发现有写原来没有的数据

事务四大特性 ACID —隔离性引发问题 ---- 解决事务的隔离问题 隔离级别
Mysql 默认隔离级别 REPEATABLE_READ
Oracle 默认隔离级别 READ_COMMITTED

MySQL事务隔离级别以及验证

2.事务的传播行为PropagationBehavior
什么是事务的传播行为? 有什么作用?
事务传播行为用于解决两个被事务管理的方法互相调用问题
spring(4)_JdbcDAOSupport/Spring的事务管理机制/声明式事务管理案例_第3张图片
业务层两个方法面临的事务问题:

  • 有些时候需要处于同一个事务(删除用户删除完成之后,需要同时删除用户对应的订单,需要事务回滚,例如商场工作人员删除订单业务)
  • 有些时候不能在同一个事务(取款是一个事务操作,打印凭条是一个事务操作,例如ATM取款业务)

事务的传播行为的7种类型:

事务传播行为类型 说明
PROPAGATION_REQUIRED 支持当前事务,如果不存在 就新建一个
PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务
PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常
PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务
PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务
PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异常
PROPAGATION_NESTED 如果当前事务存在,则嵌套事务执行,只对DataSourceTransactionManager 起效

PROPAGATION_REQUIRED 默认,表示多个方法调用时,所有事务合并为同一个事务(默认)
前三个传播方法效果一样
PROPAGATION_REQUIRES_NEW 每个方法都有独立的事务
后三种传播方法效果一样

案例: 有A和B两个方法,都在各自的方法中开启了事务
//开启事务
A
//提交事务

//开启事务
B
//提交事务

主要分为三大类:

  • PROPAGATION_REQUIRED(默认值)、PROPAGATION_SUPPORTS、PROPAGATION_MANDATORY
    支持当前事务,A调用B时,如果A的事务存在,B和A处于同一个事务
    事务默认传播行为 REQUIRED。最常用的。
  • PROPAGATION_REQUIRES_NEW、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER
    不会支持原来的事务 ,A调用B, 如果A事务存在, B肯定不会和A处于同一个事务。
    常用的事务传播行为:PROPAGATION_REQUIRES_NEW
  • PROPAGATION_NESTED
    嵌套事务 ,只对DataSourceTransactionManager有效 ,底层使用JDBC的SavePoint机制,允许在同一个事务设置保存点,回滚保存点

【面试题】REQUIRED、RE NESTED QUIRES_NEW、区分
REQUIRED:只有一个事务(默认,推荐)
REQUIRES_NEW:存在两个事务 ,如果事务存在,挂起事务,重新又开启了一个新的事务
NESTED 嵌套事务,事务可以设置保存点,回滚到保存点 ,选择提交或者回滚


2.3 TransactionStatus 事务状态

事务运行过程中,每个时间点 事务状态信息 !

flush(),给hibernate使用,底层发出sql的
hasSavepoint():判断是否有保留点
isCompleted():判断事务是否结束
isNewTransaction():判断当前事务是否是新开的一个事务。
isRollbackOnly():判断事务是否只能回滚
setRollbackOnly():设置事务是否回滚

事务的结束:必须通过commit 确认事务提交, rollback 作用标记为回滚。
数据库操作中,如果只是回滚,后面不操作,数据库在关闭连接的时候,自动发出了commit。

try {
		操作
	} catch (){
		rollback
	} finally {
		commit 
}

【三个事务超级接口对象之间的关系】
1)首先用户管理事务,需要先配置TransactionManager(事务管理器)进行事务管理
2)然后根据TransactionDefinition(事务定义信息),通过TransactionManager(事务管理器)进行事务管理;
3)最后事务运行过程中,每个时刻都可以通过获取TransactionStatus(事务状态)来了解事务的运行状态。

2.4 Spring事务管理两种方式

Spring 支持两种方式事务管理

  • 一:编程式的事务管理
    通过TransactionTemplate手动管理事务
    在实际应用中很少使用,原因是要修改原来的代码,加入事务管理代码

  • 二:使用XML或注解配置声明式事务
    Spring的声明式事务是通过AOP实现的(环绕通知)
    代开发中经常使用(码侵入性最小)–推荐使用!


3.声明式事务管理案例-转账(xml、注解)

3.1 编写转账案例,引出事务管理问题

需求:账号转账,Tom账号取出1000元,存放到Jack账号上
脚本:

第一步:创建表t_account
CREATE TABLE `t_account` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(20) NOT NULL,
  `money` DOUBLE DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

第二步:插入测试数据:
INSERT INTO `t_account` VALUES (1, 'Tom', 1000);
INSERT INTO `t_account` VALUES (2, 'Jack', 1100);
INSERT INTO `t_account` VALUES (3, 'Rose', 1200);

1.创建dao接口并实现

//接口
public interface IAccountDao {
	//(存入)转入
	public void in(String name,Double money);
	
	//(取出)转出
	public void out(String name,Double money);
}

//实现类
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao{

	@Override
	public void in(String name, Double money) {
		String sql="update t_account set money = money+ ? where name = ?";
		super.getJdbcTemplate().update(sql, money,name);
	}

	@Override
	public void out(String name, Double money) {
		String sql="update t_account set money = money- ? where name = ?";
		super.getJdbcTemplate().update(sql, money,name);
	}

}

2.service层,创建IAccountService接口,编写转账的业务代码:

public interface IAccountService {
	void transfer(String outName,String inName,Double money);
}

public class AccountServiceImpl implements IAccountService{
	
	private IAccountDao accountDao;
	//通过在xml中注入属性
	public void setAccountDao(IAccountDao accountDao) {
		this.accountDao = accountDao;
	}
	
	@Override
	public void transfer(String outName, String inName, Double money) {
		//取出钱
		accountDao.out(outName, money);
		
		//存入钱
		accountDao.in(inName, money);
	}

}

配置applicationContext.xml:


 	
 
 
 
 
 	
 

使用SpringTest进行测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:applicationContext.xml"})
public class SpringTest {
	//注入测试的service
	@Autowired
	private IAccountService accountService;
	
	//需求:账号转账,Tom账号取出1000元,存放到Jack账号上
	@Test
	public void testTransfer(){
		accountService.transfer("Tom", "Jack", 1000d);
		System.out.println("转账成功!");
	}

}

但是发现问题:当我们在转账的过程中,如果发生了错误,就会导致一个账户取出了钱,而另一个账户却没有收到钱,这钱就不明不白失踪了
spring(4)_JdbcDAOSupport/Spring的事务管理机制/声明式事务管理案例_第4张图片

事务管理问题:在Service层没有事务的情况下,如果出现异常,则会转账不成功,数据异常。
扩展:如果不配置事务,那么每一个数据库的操作都是单独的一个事务。

3.2 XML配置方式添加事务管理(tx、aop元素)

操作思路:
1.确定目标:需要对AccountService 的transfer方法,配置切入点
2.需要Advice(环绕通知),方法前开启事务,方法后提交关闭事务
3.配置切面和切入点

第一步:导入aop相关的包(4个),引入约束名称空间(aop和tx 的名称空间)


spring(4)_JdbcDAOSupport/Spring的事务管理机制/声明式事务管理案例_第5张图片
配置Advice通知:
Spring为简化事务的配置,提供了来配置事务管理,也可以理解为该标签是spring为你实现好了的事务的通知增强方案。
第一步:导入jar包:
spring(4)_JdbcDAOSupport/Spring的事务管理机制/声明式事务管理案例_第6张图片
其中:

com.springsource.org.aopalliance-1.0.0.jar:aop切面编程
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar:注解开发切面
spring-aop-4.2.4.RELEASE.jar:aop切面编程
spring-aspects-4.2.4.RELEASE.jar:注解开发切面
spring-tx-4.2.4.RELEASE.jar:事务处理

配置spring容器,applicationContext.xml文件



    
	 
	
	 
	
	
		
		
		
		
	

	
	
	
		
		
	
    
	
		
		
			
			
			
			
			
			
			
			
		
	
	
	
		
		
		
		
	
	
	
	
		
		
	
	
	
		
		
	
	

  • 配置transactionManager:平台事务管理器,底层操作的还是数据库,所以需要注入数据源
  • 配置tx:advice:事务通知,在事务通知中通过来配置管理策略,包括对哪些方法进行管理
  • 配置事务切入点和切面aop:config:告诉我们对哪些类进行事务管理

之后我们再执行transfer方法,结果如下,表面事务管理奏效了
spring(4)_JdbcDAOSupport/Spring的事务管理机制/声明式事务管理案例_第7张图片
解读
表示:某个切面中的transfer方法开启了事务,事务的隔离级别是默认,传播行为是REQUIRED

我们之前要进行事务管理的前提是,两个方法的con必须一致,但是事务管理器是需要注入datasource(注入),所以在transfer中,只要是通过这个datasource获取的con,都能作用到,transfer执行ok,就commit,中间只要有个报错,就全部rollback

试验其他传播行为:

1.修改expression="bean(account*),此时切面是accountDao和accountService

	
	


2.需要在tx:advice中配置需要被事务管理的方法

	
	
		
		
		
	

注意点:

  • 我们在中的配置,只设置了哪些类需要被事务管理,但是我们还需要在
    中设置具体哪些方法,这样方法才会被事务管理
  • 在transfer方法中,执行到int i = 1/0时就报异常了,所以其实in()方法并没有执行,我们需要关注的是transfer和out方法的传播行为,此处我们将out设置为不知道当前事务,所以out方法有自己的事务,只要执行out方法没有保存,那么就会commit,所以最终的结果如下:
    spring(4)_JdbcDAOSupport/Spring的事务管理机制/声明式事务管理案例_第8张图片
  • 当我们不设置out方法传播行为时,默认PROPAGATION_REQUIRED(之前当前事务),所以即使transfer方法设置为PROPAGATION_REQUIRES_NEW,out方法和transfer还是在同一个事务中


所以结果依旧是一起提交或回滚
spring(4)_JdbcDAOSupport/Spring的事务管理机制/声明式事务管理案例_第9张图片

  • 声明式事务处理对运行时异常有效,任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚

3.3 注解配置方式添加事务管理 @Transactional

步骤:
1.在需要管理事务的方法或者类上面 添加@Transactional 注解
2.配置注解驱动事务管理(事务管理注解生效的作用)(需要配置对特定持久层框架使用的事务管理器)

1.配置:applicationContext-tx.xml
哪些要写在xml中

  • 1.开启注解扫描
  • 2.配置数据源(dataSource),这个一般是写在xml中
  • 3.定义事务管理器



    
	
	
	
	
		
		
		
		
		
	
	
	
	
	
		
		
	
	
	
	
	
	
	
	

2.确定目标(bean的方法):

@Repository("accountDao")
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao{
	
	/*
	 * @Autowired原理是这样的,首先根据类型去spring中找对应的bean,作用在属性上时,会隐式生成setter方法注入
	 * @Autowired的另一个用处是可以作用于传参,原理一样
	 */
	@Autowired
	public void setSuperDataSource(DataSource dataSource){
		//调用父类的方法
		super.setDataSource(dataSource);		
	}
	
	@Override
	public void in(String name, Double money) {
		String sql="update t_account set money = money+ ? where name = ?";
		super.getJdbcTemplate().update(sql, money,name);
	}

	@Override
	public void out(String name, Double money) {
		String sql="update t_account set money = money- ? where name = ?";
		super.getJdbcTemplate().update(sql, money,name);
	}

}

@Service("accountService")
@Transactional //会对该类中,所有共有的方法,自动加上事务
public class AccountServiceImpl implements IAccountService{
	
	@Autowired
	private IAccountDao accountDao;
	
	public void setAccountDao(IAccountDao accountDao) {
		this.accountDao = accountDao;
	}
	
	@Override
	public void transfer(String outName, String inName, Double money) {
		//取出钱
		accountDao.out(outName, money);
		int i = 1/0;
		//存入钱
		accountDao.in(inName, money);
	}
	
	@Transactional(readOnly=true)//使用局部覆盖全局的
	public void findAccount(){
		System.out.println("查询帐号的信息了");
	}

}

注意:使用@Transactional注解,需要在核心文件中开启注解驱动
spring(4)_JdbcDAOSupport/Spring的事务管理机制/声明式事务管理案例_第10张图片

【扩展1】
如果 @Transactional 标注在 Class 上面, 那么将会对这个 Class 里面所有的 public 方法都包装事务方法。等同于该类的每个公有方法都放上了@Transactional。
如果某方法需要单独的事务定义,则需要在方法上加@Transactional来覆盖类上的标注声明。记住:方法级别的事务覆盖类级别的事务

//掌握操作的业务层
/**
 * @Service("accountService")
 * 相当于spring容器中定义:
 */
@Service("accountService")
@Transactional()//会对该类中,所有的共有的方法,自动加上事务--全局的设置,默认是可写
public class AccountServiceImpl implements IAccountService{
	
	//注入dao
	@Autowired
	private IAccountDao accountDao;

	//转账操作的业务逻辑
	@Transactional(readOnly=false)//在方法上添加事务
	public void transfer(String outName,String inName,Double money){
		
		//调用dao层
		//先取出
		accountDao.out(outName, money);
		int d = 1/0;
		//再转入
		accountDao.in(inName, money);

	}
	
	@Transactional(readOnly=true)//使用局部覆盖全局的
	public void findAccount(){
		System.out.println("查询帐号的信息了");
	}

}

3.4 小结-xml和注解的选择

XML配置方式和注解配置方式 进行事务管理 哪种用的多?
XML方式,集中式维护,统一放置到applicationContext.xml文件中,缺点在于配置文件中的内容太多。
使用@Transactional注解进行事务管理,配置太分散,使用XML进行事务管理,属性集中配置,便于管理和维护
spring(4)_JdbcDAOSupport/Spring的事务管理机制/声明式事务管理案例_第11张图片
注意:以后的service的方法名字的命名,必须是上面规则,否则,不能被spring事务管理。!!!!
即以save开头的方法,update开头的方法,delete开头的方法,表示增删改的操作,故事务为可写
以find开头的方法,表示查询,故事务为只读
(1)xml方式小结
spring(4)_JdbcDAOSupport/Spring的事务管理机制/声明式事务管理案例_第12张图片
(2)注解方式小结
spring(4)_JdbcDAOSupport/Spring的事务管理机制/声明式事务管理案例_第13张图片`

4.SSH的整合(Spring整合Hibernate、Struts2整合Spring、延迟加载问题)xml方式

你可能感兴趣的:(Spring)