Transaction事务
-
Spring Transaction 示例#
1 Transaction sample#
1.1 配置文件#
<?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" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 用来替换${jdbc.username} 的值 -->
<context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder>
<!-- 创建数据源对象 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
</bean>
<!-- 引入jdbctemplate 对象,用来进行db操作 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 业务逻辑处理类 -->
<bean id="bookService" class="com.gientech.tx.xml.service.BookService">
<property name="bookDao" ref="bookDao"></property>
</bean>
<bean id="bookDao" class="com.gientech.tx.xml.dao.BookDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- 当前myAdvice类应用到txPoint expression所指代的类/方法上 -->
<aop:config>
<!-- 匹配com.gientech.tx.xml类下面的所有类,所有方法。 -->
<aop:pointcut id="txPoint" expression="execution(* com.gientech.tx.xml.*.*.*(..))"/>
<aop:advisor advice-ref="myAdvice" pointcut-ref="txPoint"></aop:advisor>
</aop:config>
<!-- -->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="checkout" propagation="REQUIRED" />
<tx:method name="updateStock" propagation="REQUIRES_NEW" />
</tx:attributes>
</tx:advice>
</beans>
1.2 db配置文件#
jdbc.username=root
jdbc.password=123456
jdbc.url=jdbc:mysql://localhost:3306/gientechtest?useSSL=false
jdbc.driverClassName=com.mysql.jdbc.Driver
1.3 service#
package com.gientech.tx.xml.service;
import com.gientech.tx.xml.dao.BookDao;
public class BookService {
BookDao bookDao;
public BookDao getBookDao() {
return bookDao;
}
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
/**
* 结账:传入哪个用户买了哪本书
* @param username
* @param id
*/
public void checkout(String username,int id){
try {
bookDao.updateStock(id);
} catch (Exception e) {
e.printStackTrace();
}
// for (int i = 1 ;i>=0 ;i--)
// System.out.println(10/i);
// int price = bookDao.getPrice(id);
// bookDao.updateBalance(username,price);
}
}
1.4 dao#
package com.gientech.tx.xml.dao;
import org.springframework.jdbc.core.JdbcTemplate;
public class BookDao {
JdbcTemplate jdbcTemplate;
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
* 减去某个用户的余额
* @param userName
* @param price
*/
public void updateBalance(String userName,int price){
String sql = "update account set balance=balance-? where username=?";
jdbcTemplate.update(sql,price,userName);
}
/**
* 按照图书的id来获取图书的价格
* @param id
* @return
*/
public int getPrice(int id){
String sql = "select price from book where id=?";
return jdbcTemplate.queryForObject(sql,Integer.class,id);
}
/**
* 减库存,减去某本书的库存
* @param id
*/
public void updateStock(int id){
String sql = "update book_stock set stock='stock-1' where id=?";
jdbcTemplate.update(sql,id);
// for (int i = 1 ;i>=0 ;i--)
// System.out.println(10/i);
}
}
1.5 启动文件#
public class TxTest {
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"d:\\code");
ApplicationContext context = new ClassPathXmlApplicationContext("tx.xml");
BookService bookService = context.getBean("bookService", BookService.class);
bookService.checkout("zhangsan",1);
System.out.println();
}
}
2 Transaction 过程解析#
2.1 加载XML 定义信息。#
在obtainFreshBeanFactory()方法中,加载xml配置文件的属性值到当前工厂中,在parseCustomElement(ele, null) 中,
ConfigBeanDefinitionParser.parseAdvisor方法解析Advisor标签时,创建类对象,名称spring会按照一定规则进行创建,如图所示
2.1.2 解析<tx: 标签#
<tx开始的标签是在delegate.parseCustomElement(ele)方法中进行解析,在BeanDefinitionParserDelegate.parseCustomElement方法中,第一步根据命名空间获取NamespaceHandler,在spring-tx\src\main\resources\META-INF\spring.handlers文件中进行具体的配置,
2.2 调用invokeBeanFactoryPostProcessors(beanFactory)#
此时,加载的xml配置文件属性值并未填充为实际值,调用后,会填充为真实值。下图为替换之前的值
2.3 注册BeanPostProcessor#
registerBeanPostProcessors(beanFactory) 作用是注册BeanPostProcessor,此时会创建动态代理自动创建器AspectJAwareAdvisorAutoProxyCreator,在执行前,一级缓存中没有AspectJAwareAdvisorAutoProxyCreator对象,执行完成后AspectJAwareAdvisorAutoProxyCreator对象生成。执行前如下图。
执行完成后AspectJAwareAdvisorAutoProxyCreator对象生成,一级缓存截图如下。
2.4 初始化Advisor#
创建DefaultBeanFactoryPointcutAdvisor对象,但未创建对应属性对象,内存截图如下:
advice-ref="myAdvice" 对应的是adviceBeanName,是RuntimeBeanNameReference 对象,名称创建完成,pointcut-ref="txPoint" 对应的是pointcut,对象创建完成截图如下:
在此例中,bookservice,bookdao也是需要被动态代理的。动态代理对象创建完成之后截图如下:
注解式事务
1 @EnableTransactionManagement 注解中引入了@Import注解,在 @Import注解中,TransactionManagementConfigurationSelector.class 类中注入了AutoProxyRegistrar.class, ProxyTransactionManagementConfiguration.class
代码示例:
package com.gientech.tx.annotation.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.gientech.tx.annotation.dao.BookDao;
import com.gientech.tx.annotation.service.BookService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration
@PropertySource("classpath:dbconfig.properties")
@EnableTransactionManagement
public class TransactionConfig {
@Value("${jdbc.driverClassName}")
private String driverClassname;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource data = new DruidDataSource();
data.setDriverClassName(driverClassname);
data.setUrl(url);
data.setUsername(username);
data.setPassword(password);
return data;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
@Bean
public BookDao bookDao(){return new BookDao();}
@Bean
public BookService bookService(){
bookDao();
return new BookService();
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
package com.gientech.tx.annotation.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
public class BookDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Transactional(propagation = Propagation.SUPPORTS)
public void updateBalance(String userName, int price){
String sql = "update account set balance=balance-? where username=?";
jdbcTemplate.update(sql,price,userName);
}
@Transactional(propagation = Propagation.REQUIRED)
public int getPrice(int id){
String sql = "select price from book where id=?";
return jdbcTemplate.queryForObject(sql,Integer.class,id);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateStock(int id){
String sql = "update book_stock set stock='stock-1' where id=?";
jdbcTemplate.update(sql,id);
// for (int i = 1 ;i>=0 ;i--)
// System.out.println(10/i);
}
}
package com.gientech.tx.annotation.service;
import com.gientech.tx.annotation.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
public class BookService {
@Autowired
private BookDao bookDao;
@Transactional(propagation = Propagation.REQUIRED)
public void checkout(String username,int id){
bookDao.updateStock(id);
int price = bookDao.getPrice(id);
System.out.println(price);
bookDao.updateBalance(username,price);
}
}
package com.gientech.tx.annotation;
import com.gientech.tx.annotation.config.TransactionConfig;
import com.gientech.tx.annotation.service.BookService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TxAnnotationTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(TransactionConfig.class);
applicationContext.refresh();
BookService bean = applicationContext.getBean(BookService.class);
bean.checkout("yuwen", 1);
}
}
当程序执行到applicationContext.refresh(); 时,transactionConfig 类型为AnnotatedGenericBeanDefinition, 截图如下:
作者:zgcy123456
出处:https://www.cnblogs.com/zgcy123456/p/18239657
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!