Spring 声明式事务 事务管理器 事务的传播行为 详细介绍 初学者必看

声明式事务

一、声明式事务简介

  1. 之前需要通过复杂的编程编写一个事务,现在只需要告诉Spring哪个方法是事务方法,Spring 可以自动进行事务控制

  2. 事务方法:方法内的操作满足原子性等事务的四个特征 (要么都执行,要么都不执行)

  3. Spring中的事务管理器DataSourceTransactionManager可以在目标方法运行时进行事务控制

二、声明式事务使用步骤

  1. 使用c3p0连接数据库

(1) src目录创建c3p0.properties文件

jdbc.username=root
jdbc.password=root
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/tx?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone = GMT
jdbc.driverClass=com.mysql.jdbc.Driver

(2) xml中配置

<context:property-placeholder location="classpath:c3p0.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
   
   <property name="user" value="${jdbc.username}"></property> 
   <property name="password" value="${jdbc.password}"></property>
   <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
   <property name="driverClass" value="${jdbc.driverClass}"></property>
   
</bean>
</beans>

(3) 测试

   @org.junit.Test
   public void test() throws SQLException {
    
    
       ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
       DataSource dataSource = (DataSource)ioc.getBean("dataSource");
       System.out.println(dataSource.getConnection());
       //com.mchange.v2.c3p0.impl.NewProxyConnection@7d68ef40
   }

  1. 使用JdbcTemplate操作数据库

(1) 导包

在这里插入图片描述

(2) 写配置

JdbcTemplate中有一个带参构造器JdbcTemplate(DataSource dataSource);
需要创建JdbcTemplate对象操作数据库,为了不去new 对象,将其注册到容器中

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <!-- 引用容器中已经创建好的使用c3p0连接数据库的dataSource对象 -->
    <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>

(3) 测试

   @org.junit.Test
   public void test2() {
    
    
       //将book表中book_name为book01的书的price修改为50
       ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
       JdbcTemplate jdbcTemplate = (JdbcTemplate)ioc.getBean("jdbcTemplate");
       String sql = "UPDATE book SET price=? WHERE book_name=?";
       int update = jdbcTemplate.update(sql, 50, "book01");
       System.out.println("受影响的行数:" + update);
       //受影响的行数:1
   }

  1. 配置事务管理器(事务切面)

(1) 导包

在这里插入图片描述

(2) xml中编写

在这里插入图片描述

  1. 给事务方法加@Transactional注解

(1) 创建BookDao

@Service
public class BookDao {
    
    
   
   @Autowired
   JdbcTemplate jdbcTemplate;
   
   //修改账户的余额
   public void updateBalance(String username, int price) {
    
    
       String sql = "UPDATE account SET balance=balance-? WHERE username=?";
       jdbcTemplate.update(sql, price, username);
   }
   
   //修改图书的库存
   public void updateStock(int stock, String isbn) {
    
    
       String sql = "UPDATE book_stock SET stock=? WHERE isbn=?";
       jdbcTemplate.update(sql, stock, isbn);
   }
}

(2) 创建BookService

@Service
public class BookService {
    
    
   
   @Autowired
   BookDao bookDao;
   
   //事务方法
   @Transactional
   public void update(String username, int price, String isbn, int stock) {
    
    
       //修改余额
       bookDao.updateBalance(username, price);
       //修改库存
       bookDao.updateStock(stock, isbn);
       int i = 10 / 0; //出现算术异常、事务方法中的两个操作均无法完成(回滚)
   }
}

三、事务细节

以下都是@Transactional注解中的属性

  1. timeout
    超时设置,int类型,事务方法的执行超出指定的时长后若未结束,则终止并回滚,以秒为单位

用法:@Transactional(timeout=3)

  1. readOnly
    设置为只读事务,boolean类型,可以加快查询速度,只有事务方法中是查询操作时可以使用, 若有修改操作,会报错

用法:@Transactional(readOnly=true)

  1. noRollbackFor / rollbackFor
    noRollbackFor:设置某些原本回滚的异常事务可以不回滚
    rollbackFor:设置某些原本不回滚的异常事务让其回滚
    二者都是class[ ]类型

异常分类:
运行时异常:编译时可以不处理,默认都回滚
编译时异常:编译时必须处理(try - catch、throws),默认不回滚

用法:@Transactional(noRollbackFor={NullPointerException.class, xxx.class})

  1. isolation
    设置事务的隔离级别,Isolation类型

用法:

在这里插入图片描述

四、事务传播行为

  1. 简介
    如果有多个事务进行嵌套,也就是事务方法中调用事务方法,若其中一个事务出异常,则嵌 套起来的这些事务哪些随其回滚,哪些不回滚

  2. 使用方法

在这里插入图片描述

  1. 传播行为分类

在这里插入图片描述

如:
		(1) 使用了REQUIRED属性的事务方法如果嵌套在一个大的事务方法中,则二者属于同一个			   事务线上,一个失败,另一个也跟着回滚

		(2) 使用了REQUIRED_NEW属性的事务方法如果嵌套在一个大的事务方法中,则二者的事				务相互独立,一个失败,另一个不受影响(不会回滚)
  1. 图示传播行为

下图中的事务方法MulTx()中调用了两个事务方法checkout()和updatePrice();
讨论两个被嵌套的事务方法使用不同传播行为的属性的情况:

在这里插入图片描述

在这里插入图片描述

  1. 注意点

(1) 被嵌套的事务方法使用@Transactional(timeout=3)等属性不会对整个事务起作用,必须在 嵌套它的大事务中使用这些属性才会对整个事务起作用

(2) 在本类的事务方法中嵌套本类的事务方法,则属于同一个事务

五、使用xml配置的事务控制

  1. 配置事务管理器(事务切面)
	 <!-- 
	 	基于xml配置的事务;依赖tx名称空间和aop名称空间
	 		1)、配置事务管理器(事务切面)
	    	2)、配置出事务方法
			3)、告诉Spring哪些方法是事务方法,事务切面按照给定的切入点表达式去切入事务方法
	  -->
	 
	 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	 	<property name="dataSource" ref="dataSource"></property>
	 </bean>

	 <!-- 事务切面按照给定的切入点表达式去切入事务方法 -->
	 <aop:config>
	 	<!-- 指明要切入哪些方法,但这些方法不一定使用事务 -->
	 	<aop:pointcut expression="execution(* com.atguigu.ser*.*.*(..))" id="txPoint"/>
	 	<!-- advice-ref指向配置事务管理器 -->
	 	<aop:advisor advice-ref="myAdvice" pointcut-ref="txPoint"/>
	 </aop:config>
	 
	 <!--  
	 	  配置事务管理器
	 	 transaction-manager="transactionManager":指定是配置哪个事务管理器;
	 -->
	 <tx:advice id="myAdvice" transaction-manager="transactionManager">
	 		<!--事务属性  -->
	 		<tx:attributes>
	 			<!-- 指明切入点表达式中的哪些方法是事务方法,-->
	 			<tx:method name="*"/> <!-- 所有方法都加事务 -->
	 			<tx:method name="checkout" propagation="REQUIRED" timeout="-1"/>
	 			<tx:method name="get*" read-only="true"/>
	 		</tx:attributes>
	 </tx:advice>

猜你喜欢

转载自blog.csdn.net/weixin_49343190/article/details/109772917