Loading

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会按照一定规则进行创建,如图所示
解析Advisor标签

2.1.2 解析<tx: 标签

<tx开始的标签是在delegate.parseCustomElement(ele)方法中进行解析,在BeanDefinitionParserDelegate.parseCustomElement方法中,第一步根据命名空间获取NamespaceHandler,在spring-tx\src\main\resources\META-INF\spring.handlers文件中进行具体的配置,

xml配置文件加载完成之后,容器中的对象如图,
xml文件加载完成

2.2 调用invokeBeanFactoryPostProcessors(beanFactory)

此时,加载的xml配置文件属性值并未填充为实际值,调用后,会填充为真实值。下图为替换之前的值
xml属性填充之前

XML文件中变量值替换完成之后如图:
xml变量填充后

2.3 注册BeanPostProcessor

registerBeanPostProcessors(beanFactory) 作用是注册BeanPostProcessor,此时会创建动态代理自动创建器AspectJAwareAdvisorAutoProxyCreator,在执行前,一级缓存中没有AspectJAwareAdvisorAutoProxyCreator对象,执行完成后AspectJAwareAdvisorAutoProxyCreator对象生成。执行前如下图。
创建动态代理自动创建器之前

执行完成后AspectJAwareAdvisorAutoProxyCreator对象生成,一级缓存截图如下。
创建动态代理自动创建器之后

2.4 初始化Advisor

创建DefaultBeanFactoryPointcutAdvisor对象,但未创建对应属性对象,内存截图如下:
未赋值之前

advice-ref="myAdvice" 对应的是adviceBeanName,是RuntimeBeanNameReference 对象,名称创建完成,pointcut-ref="txPoint" 对应的是pointcut,对象创建完成截图如下:
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, 截图如下:
注解事务

posted @ 2024-06-22 22:03  zgcy123456  阅读(10)  评论(0编辑  收藏  举报