当前位置: 首页 > article >正文

spring揭秘20-spring事务02-编程式事务与声明式事务管理

文章目录

  • 【README】
  • 【1】编程式事务管理
    • 【1.1】使用PlatformTransactionManager进行编程式事务管理
    • 【1.2】使用TransactionTemplate进行编程式事务管理
    • 【1.3】基于Savepoint的嵌套事务
  • 【2】声明式事务管理
    • 【2.1】基于xml的声明式事务
      • 【2.1.1】使用ProxyFactory(ProxyFactoryBean)+TransactionInterceptor
      • 【2.1.2】使用TransactionProxyFactoryBean管理事务
      • 【2.1.3】使用BeanNameAutoProxyCreator自动代理织入管理事务
      • 【2.1.4】使用xsd的声明事务配置
    • 【2.2】基于注解的声明式事务
      • 【2.2.1】使用@Transactional注解管理事务

【README】

本文总结自《spring揭秘》,作者王福强,非常棒的一本书,墙裂推荐;

事务管理有2种实现方式,包括编程式事务管理, 声明式事务管理;



【1】编程式事务管理

1)spring的编程式事务管理有2种方式:

  • 使用 PlatformTransactionManager ;
  • 使用 TransactionTemplate;


【1.1】使用PlatformTransactionManager进行编程式事务管理

【BusiPlatformTransactionManager】

public class BusiPlatformTransactionManager {
    private PlatformTransactionManager jdbcTransactionManager;
    private JdbcTemplate jdbcTemplate;
    private DefaultTransactionDefinition defaultTransactionDefinition;

    public BusiPlatformTransactionManager() {
        DataSource dataSource = DataSourceUtils.getDataSource();
        this.jdbcTransactionManager = new JdbcTransactionManager(dataSource);
        this.jdbcTemplate = new JdbcTemplate(dataSource);
        this.defaultTransactionDefinition = new DefaultTransactionDefinition();
        this.defaultTransactionDefinition.setTimeout(20);
    }
    public void update(Long id, String newName) {
        // 获取连接
        TransactionStatus txStatus = jdbcTransactionManager.getTransaction(defaultTransactionDefinition);
        try {
            jdbcTemplate.update("update user_tbl set name=? where id=?", new PreparedStatementSetter() { // jdbcTemplate操作数据
                @Override
                public void setValues(PreparedStatement ps) throws SQLException {
                    ps.setString(1, newName);
                    ps.setLong(2, id);
                }
            });
            jdbcTransactionManager.commit(txStatus); // 提交事务
        } catch (Exception e) {
            jdbcTransactionManager.rollback(txStatus); // 回滚事务
            e.printStackTrace(); // 仅演示,不要这么做
        }
    }
}

【代码解说】

1)使用PlatformTransactionManager管理事务太过底层;可以借助模版方法模式与回调接口对PlatformTransactionManager事务管理逻辑进行封装,即使用TransactionTemplate进行编程式事务管理;



【1.2】使用TransactionTemplate进行编程式事务管理

1)TransactionTemplate封装思路:

  • 对PlatformTransactionManager的事务操作以及异常处理进行模板化封装;
  • TransactionTemplate有2个回调接口:
    • TransactionCallback :返回执行结果;
    • Consumer : 不返回执行结果;

2)事务处理期间(如执行sql):

  • 若没有任何问题,TransactionTemplate最终会提交事务;
  • 若回调接口执行逻辑(TransactionCallback或Consumer)有问题,则需用回滚事务;

3)回滚事务有2种处理方式

  • 抛出unchecked exception, TransactionTemplate会自动回滚事务;如果事务操作抛出checked exception,那么可以将其转译为unchecked exception或者通过TransactionStatus将事务标记为 rollBackOnly;
  • 使用回调接口中TransactionStatus将事务标记为 rollBackOnly。TransactionTemplate在提交事务时,如果检测到rollBackOnly=true,则把提交事务修改为回滚事务;

【BusiTransactionTemplate】

public class BusiTransactionTemplate {

    private JdbcTemplate jdbcTemplate;
    private TransactionTemplate transactionTemplate;

    public BusiTransactionTemplate() {
        DataSource dataSource = DataSourceUtils.getDataSource();
        // 创建jdbc模版
        this.jdbcTemplate = new JdbcTemplate(dataSource);
        // 创建事务模版
        JdbcTransactionManager jdbcTransactionManager = new JdbcTransactionManager(dataSource);
        DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
        defaultTransactionDefinition.setTimeout(20);
        this.transactionTemplate = new TransactionTemplate(jdbcTransactionManager, defaultTransactionDefinition);
    }

    public void update(Long id, String name) {
        Object result = transactionTemplate.execute(new TransactionCallback<Object>() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                try {
                    return jdbcTemplate.update("update user_tbl set name=? where id=?", name, id);
                } catch (Exception e) {
                    status.setRollbackOnly();
                }
                return -1;
            }
        });
    }

    public void updateWithoutResult(Long id, String name) {
        transactionTemplate.executeWithoutResult(new Consumer<TransactionStatus>() {
            @Override
            public void accept(TransactionStatus transactionStatus) {
                try {
                    jdbcTemplate.update("update user_tbl set name=? where id=?", name, id);
                } catch (Exception e) {
                    transactionStatus.setRollbackOnly(); // 设置 rollbackOnly=true,以便TransactionManager回滚事务
                }
            }
        });
    }
}


【1.3】基于Savepoint的嵌套事务

1)savepoint,保存点: 作用在于事务打桩,以便回滚到某个保存点而不是只能回滚到事务起点;

2) 如带有保存点的事务操作如下(我们可以选择回滚到保存点1,保存点2,而不是只能回滚到事务起点):

  • 开启事务;
  • 调用method1()
  • 创建保存点1
  • 调用method2()
  • 创建保存点2
  • 调用method3()

3)业务场景:银行转账。 账户A向账户B转账,优先向其主卡转账(firstCard),若转账失败,则降级向其副卡转账(secondCard);

【BusiNestedTransactionBasedSavepointMain】

public class BusiNestedTransactionBasedSavepointMain {
    public static void main(String[] args) {
        BusiNestedTransactionBasedSavepoint busiNestedTransactionBasedSavepoint
                = new BusiNestedTransactionBasedSavepoint();
        BankCardDto srcCard = BankCardDto.newBankCardDto(1000L, "1000", new BigDecimal(50), "");
        BankCardDto firstCard = BankCardDto.newBankCardDto(1001L, "1001", new BigDecimal(150), "");
        BankCardDto secondCard = BankCardDto.newBankCardDto(1002L, "1002", new BigDecimal(100), "");
        busiNestedTransactionBasedSavepoint.update(srcCard, firstCard, secondCard, new BigDecimal("50"));
    }
}

【BusiNestedTransactionBasedSavepoint】基于Savepoint创建嵌套事务

public class BusiNestedTransactionBasedSavepoint {
    private JdbcTemplate jdbcTemplate;
    private TransactionTemplate transactionTemplate;

    public BusiNestedTransactionBasedSavepoint() {
        DataSource dataSource = DataSourceUtils.getDataSource();
        // 创建jdbc模版
        this.jdbcTemplate = new JdbcTemplate(dataSource);
        // 创建事务模版
        JdbcTransactionManager jdbcTransactionManager = new JdbcTransactionManager(dataSource);
        DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
        defaultTransactionDefinition.setTimeout(20);
        this.transactionTemplate = new TransactionTemplate(jdbcTransactionManager, defaultTransactionDefinition);
    }

    public void update(BankCardDto srcCard, BankCardDto firstCard, BankCardDto secondCard, BigDecimal transferMoney) {
        transactionTemplate.executeWithoutResult(new Consumer<TransactionStatus>() {
            @Override
            public void accept(TransactionStatus txStatus) {
                try {
                    Object savepoint1 = txStatus.createSavepoint();// 创建保存点
                    try {
                        transfer(srcCard, firstCard, transferMoney); // 转账到主卡
                        throw new RuntimeException("mock异常"); // 故意抛出异常
                    } catch (Exception e1) {
                        e1.printStackTrace(); // 仅演示,不要这么做
                        txStatus.rollbackToSavepoint(savepoint1); // 回滚到保存点
                        transfer(srcCard, secondCard, transferMoney); // 转账到副卡
                    } finally {
                        txStatus.releaseSavepoint(savepoint1); // 释放保存点
                    }
                } catch (Exception e2) {
                    System.out.println("抛出异常");
                    e2.printStackTrace(); // 仅演示,不要这么做
                    txStatus.setRollbackOnly(); // 重置rollbackOnly=true,jdbcTransactionManager事务管理器会回滚事务,而不是提交
                }
            }
        });
    }

    private void transfer(BankCardDto srcCard, BankCardDto targetCard, BigDecimal transferMoney) {
        srcCard.setBalance(srcCard.getBalance().subtract(transferMoney));
        targetCard.setBalance(targetCard.getBalance().add(transferMoney));
        jdbcTemplate.update("update bank_card_tbl set balance=? where id=?", srcCard.getBalance(), srcCard.getId());
        jdbcTemplate.update("update bank_card_tbl set balance=? where id=?", targetCard.getBalance(), targetCard.getId());
    }
}

【补充】

想要达到Savepoint保存点的效果,推荐使用结合 PROPAGATION_NESTED传播行为的声明式事务管理方式;



【2】声明式事务管理

1)编程式事务管理的缺点:事务管理代码与业务逻辑代码相互混杂,紧耦合;

  • 解决方法:基于aop思想,把事务管理代码与业务逻辑代码解耦开;即事务管理代码作为横切逻辑(切面)织入到作为目标对象的业务逻辑代码中
  • 声明式事务:通过xml或注解织入事务管理代码切面的事务管理方式称为声明式事务;


【2.1】基于xml的声明式事务

1)spring提供了4种方式在ioc容器配置文件中指定事务元数据;

  • 使用ProxyFactory(ProxyFactoryBean) + TransactionInterceptor
  • 使用 TransactionProxyFactoryBean
  • 使用BeanNameAutoProxyCreator
  • 使用基于xsd风格的xml的声明事务配置

2)事务元数据包括传播行为,隔离级别,超时时间,是否只读事务等;



【2.1.1】使用ProxyFactory(ProxyFactoryBean)+TransactionInterceptor

1)xml元数据驱动的声明式事务,本质上说,是为 TransactionInterceptor提供需要的TransactionDefinition(包括事务隔离级别,传播行为,超时时间,是否为只读); 而事务管理代码作为切面织入到业务代码,就是一个aop配置的过程;

  • 织入器使用ProxyFactory或ProxyFactoryBean;
  • TransactionInterceptor接口的实现类封装事务管理代码,它是一个横切逻辑(如环绕通知);

【DeclareTransactionBasedFactoryBeanMain】

public class DeclareTransactionBasedFactoryBeanMain {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext container =
                new ClassPathXmlApplicationContext("chapter20/factorybean/DeclareTxBasedProxyFactory.xml");
        IBankCardService bankCardServiceProxy = container.getBean("bankCardServiceProxy", IBankCardService.class);
        // 查询
//        System.out.println(bankCardServiceProxy.queryById(1001L));

        // 新增
        List<BankCardDto> bankCardDtos = List.of(
                BankCardDto.newBankCardDto(1004L, "1004", new BigDecimal("1004"), "备注1004")
                , BankCardDto.newBankCardDto(1005L, "1005", new BigDecimal("1005"), "备注1005"));
        bankCardServiceProxy.saveBankCard(bankCardDtos);
    }
}

【DeclareTxBasedProxyFactory.xml】spring ioc容器xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="locations">
            <value>jdbc-springdiscover.properties</value>
        </property>
    </bean>
    <!--注册数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="url" value="${jdbc.url}" />
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>
    <!--注册jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!--注册事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!--注册事务拦截器或横切逻辑-->
    <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
        <property name="transactionManager" ref="transactionManager" />
        <property name="transactionAttributes">
            <props>
                <prop key="queryBy*">PROPAGATION_SUPPORTS,readOnly,timeout_20</prop>
                <prop key="saveBankCard">PROPAGATION_REQUIRED</prop>
                <prop key="updateBankCard">PROPAGATION_REQUIRED</prop>
                <prop key="deleteBankCard">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean>
    <!--注册目标对象(业务逻辑服务)-->
    <bean id="bankCardServiceImpl" class="com.tom.springnote.chapter20.declaretx.service.BankCardServiceImpl">
        <constructor-arg name="jdbcTemplate" ref="jdbcTemplate" />
    </bean>
    <!--注册factoryBean,生成织入横切逻辑后的代理对象-->
    <bean id="bankCardServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="targetName" value="bankCardServiceImpl" />
        <property name="proxyInterfaces" value="com.tom.springnote.chapter20.declaretx.service.IBankCardService" />
        <property name="interceptorNames">
            <list>
                <value>transactionInterceptor</value>
            </list>
        </property>
    </bean>
</beans>

【IBankCardService】业务逻辑服务接口

public interface IBankCardService {

    BankCardDto queryById(Long id);

    BankCardDto queryByNo(String cardNo);

    void saveBankCard(List<BankCardDto> bankCardDto);

    void updateBankCard(BankCardDto bankCardDto);

    void deleteBankCard(BankCardDto bankCardDto);
}

【BankCardServiceImpl】业务逻辑服务实现类

public class BankCardServiceImpl implements IBankCardService {

    private JdbcTemplate jdbcTemplate;

    public BankCardServiceImpl(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public BankCardDto queryById(Long id) {
        String sql = "select id, card_no, balance, remark from bank_card_tbl where id = ?";
        return jdbcTemplate.queryForObject(sql, new RowMapper<>() {
            @Override
            public BankCardDto mapRow(ResultSet rs, int rowNum) throws SQLException {
                BankCardDto bankCardDto = new BankCardDto();
                bankCardDto.setId(rs.getLong(1));
                bankCardDto.setCardNo(rs.getString(2));
                bankCardDto.setBalance(rs.getBigDecimal(3));
                bankCardDto.setRemark(rs.getString(4));
                return bankCardDto;
            }
        }, id);
    }

    @Override
    public BankCardDto queryByNo(String cardNo) {
        return null;
    }

    @Override
    public void saveBankCard(List<BankCardDto> bankCardDtoList) {
        if (CollectionUtils.isEmpty(bankCardDtoList)) {
            return ;
        }
        String sql = "insert into bank_card_tbl(id, card_no, balance, remark) values(?, ?, ?, ?)";
        jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                BankCardDto bankCardDto = bankCardDtoList.get(i);
                ps.setLong(1, bankCardDto.getId());
                ps.setString(2, bankCardDto.getCardNo());
                ps.setBigDecimal(3, bankCardDto.getBalance());
                ps.setString(4, bankCardDto.getRemark());
            }

            @Override
            public int getBatchSize() {
                return bankCardDtoList.size();
            }
        });
    }

    @Override
    public void updateBankCard(BankCardDto bankCardDto) {

    }

    @Override
    public void deleteBankCard(BankCardDto bankCardDto) {

    }
}

2)为事务拦截器配置事务属性

<!--注册事务拦截器或横切逻辑-->
    <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
        <property name="transactionManager" ref="transactionManager" />
        <!--方式1:通过transactionAttributes为事务拦截器配置事务属性-->
        <property name="transactionAttributes">
            <props>
                <prop key="queryBy*">PROPAGATION_SUPPORTS,readOnly,timeout_20</prop>
                <prop key="saveBankCard">PROPAGATION_REQUIRED</prop>
                <prop key="updateBankCard">PROPAGATION_REQUIRED</prop>
                <prop key="deleteBankCard">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
        <!--方式2:通过transactionAttributeSource为事务拦截器配置事务属性-->
        <property name="transactionAttributeSource">
         <value>
             com.tom.springnote.chapter20.declaretx.service.IBankCardService.queryBy*=PROPAGATION_SUPPORTS,readOnly,timeout_20
             com.tom.springnote.chapter20.declaretx.service.IBankCardService.saveBankCard=PROPAGATION_REQUIRED
             com.tom.springnote.chapter20.declaretx.service.IBankCardService.updateBankCard=PROPAGATION_REQUIRED
             com.tom.springnote.chapter20.declaretx.service.IBankCardService.deleteBankCard=PROPAGATION_REQUIRED
         </value>
        </property>
    </bean>

在这里插入图片描述

方式1:通过transactionAttributes为事务拦截器配置事务属性, 类型为Properties;底层新建NameMatchTransactionAttributeSource封装transactionAttributes ;

【TransactionInterceptor#setTransactionAttributes()】

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
......
}
// TransactionAspectSupport#setTransactionAttributes
public void setTransactionAttributes(Properties transactionAttributes) {
    NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource();
    tas.setProperties(transactionAttributes);
    this.transactionAttributeSource = tas;
}

方式2:通过transactionAttributeSource为事务拦截器配置事务属性,类型为 TransactionAttributeSource ;

  • 通过String类型的值来配置,容器底层将通过TransactionAttributeSourceEditor来转换,再设置给TransactionInterceptor;
  • 以string类型配置事务属性时,其规则由TransactionAttributeSourceEditor定义,如下:PROPAGATION_NAME,[ISOLATION_NAME],[readOnly],[timeout_NNNN],[+Exception1],[-Exception2] 之间通过逗号分割(取值参加TransactionDefinition接口定义);
    • PROPAGATION_NAME必输,其他非必输;取值有 PROPAGATION_REQUIRED, PROPAGATION_SUPPORTS, PROPAGATION_MANDATORY, PROPAGATION_REQUIRES_NEW, PROPAGATION_NOT_SUPPORTED, PROPAGATION_NEVER, PROPAGATION_NESTED
    • ISOLATION_NAME非必输: 包括ISOLATION_DEFAULT, ISOLATION_READ_UNCOMMITTED, ISOLATION_READ_COMMITTED, ISOLATION_REPEATABLE_READ, ISOLATION_SERIALIZABLE
    • readOnly:是否只读; true=只读事务;false=读写事务;
    • timeout_NNNN:以 timeout为前缀,后面加数字;如timeout_20 表示限定超时时间为20秒;
    • [+Exception1]:表示业务方法抛出该异常也要提交事务;
    • [-Exception2]:表示业务方法抛出该异常回滚事务;
    • 此外:未受编译器检查异常,默认情况下会自动回滚;通过自定义回滚异常规则针对的是受编译器检查异常;


【2.1.2】使用TransactionProxyFactoryBean管理事务

1)TransactionProxyFactoryBean: 是面向事务管理的ProxyFactoryBean,内部声明并直接管理TransactionInterceptor

  • 即: 使用TransactionProxyFactoryBean,TransactionProxyFactoryBean内部创建了TransactionInterceptor实例,xml配置文件就无需注册TransactionInterceptor实例,以简化xml配置;

【DeclareTransactionBasedTxProxyFactoryBeanMain】

public class DeclareTransactionBasedTxProxyFactoryBeanMain {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext container =
                new ClassPathXmlApplicationContext("chapter20/factorybean/DeclareTxBasedTransactionProxyFactoryBean.xml");
        IBankCardService bankCardServiceProxy = container.getBean("bankCardServiceProxy", IBankCardService.class);
        // 查询
        System.out.println(bankCardServiceProxy.queryById(1001L));
    }
}

【DeclareTxBasedTransactionProxyFactoryBean.xml】

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="locations">
            <value>jdbc-springdiscover.properties</value>
        </property>
    </bean>
    
    <!--注册数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="url" value="${jdbc.url}" />
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>
    
    <!--注册jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>
    
    <!--注册事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!--注册目标对象(业务逻辑服务)-->
    <bean id="bankCardServiceImpl" class="com.tom.springnote.chapter20.declaretx.service.BankCardServiceImpl">
        <constructor-arg name="jdbcTemplate" ref="jdbcTemplate" />
    </bean>

    <!--注册factoryBean,生成织入横切逻辑后的代理对象-->
    <bean id="bankCardServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="target" ref="bankCardServiceImpl" />
        <property name="proxyInterfaces" value="com.tom.springnote.chapter20.declaretx.service.IBankCardService" />
        <property name="transactionManager" ref="transactionManager" />
        <!--方式1:通过transactionAttributes为事务拦截器配置事务属性-->
        <property name="transactionAttributes">
            <props>
                <prop key="queryBy*">PROPAGATION_SUPPORTS,readOnly,timeout_20</prop>
                <prop key="saveBankCard">PROPAGATION_REQUIRED</prop>
                <prop key="updateBankCard">PROPAGATION_REQUIRED</prop>
                <prop key="deleteBankCard">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean>
</beans>


【2.1.3】使用BeanNameAutoProxyCreator自动代理织入管理事务

1) 织入器使用BeanNameAutoProxyCreator自动织入器;用BeanNameAutoProxyCreator织入器装配事务管理逻辑,然后把目标bean添加到BeanNameAutoProxyCreator的BeanNames属性中;

【DeclareTxBasedBeanNameAutoProxyCreatorMain】

public class DeclareTxBasedBeanNameAutoProxyCreatorMain {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext container =
                new ClassPathXmlApplicationContext("chapter20/factorybean/DeclareTxBasedBeanNameAutoProxyCreator.xml");
        IBankCardService bankCardServiceProxy = container.getBean("bankCardServiceImpl", IBankCardService.class);
        // 查询
        System.out.println(bankCardServiceProxy.queryById(1001L));
    }
}

【DeclareTxBasedBeanNameAutoProxyCreator.xml】

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="locations">
            <value>jdbc-springdiscover.properties</value>
        </property>
    </bean>
    <!--注册数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="url" value="${jdbc.url}" />
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>
    <!--注册jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!--注册事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!--注册事务拦截器或横切逻辑-->
    <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
        <property name="transactionManager" ref="transactionManager" />
        <!--方式2:通过transactionAttributeSource为事务拦截器配置事务属性-->
        <property name="transactionAttributeSource">
            <value>
               com.tom.springnote.chapter20.declaretx.service.IBankCardService.queryBy*=PROPAGATION_SUPPORTS,readOnly,timeout_20
                com.tom.springnote.chapter20.declaretx.service.IBankCardService.saveBankCard=PROPAGATION_REQUIRED
                com.tom.springnote.chapter20.declaretx.service.IBankCardService.updateBankCard=PROPAGATION_REQUIRED
                com.tom.springnote.chapter20.declaretx.service.IBankCardService.deleteBankCard=PROPAGATION_REQUIRED
            </value>
        </property>
    </bean>
    <!--注册目标对象(业务逻辑服务)-->
    <bean id="bankCardServiceImpl" class="com.tom.springnote.chapter20.declaretx.service.BankCardServiceImpl">
        <constructor-arg name="jdbcTemplate" ref="jdbcTemplate" />
    </bean>
    <!--注册 BeanNameAutoProxyCreator ,自动织入事务管理切面-->
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <property name="interceptorNames">
            <list>
                <value>transactionInterceptor</value>
            </list>
        </property>
        <property name="beanNames">
            <list>
                <idref bean="bankCardServiceImpl" />
            </list>
        </property>
    </bean>
</beans>


【2.1.4】使用xsd的声明事务配置

1)spring提供了基于xml schema的配置方式,专门为事务管理提供了单独的命名空间(tx)用于简化配置;

【DeclareTxBasedXmlSchemaMain】

public class DeclareTxBasedXmlSchemaMain {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext container =
                new ClassPathXmlApplicationContext("chapter20/factorybean/DeclareTxBasedXmlSchema.xml");
        IBankCardService bankCardServiceProxy = container.getBean("bankCardServiceImpl", IBankCardService.class);
        // 查询
        System.out.println(bankCardServiceProxy.queryById(1001L));

        // 新增
        List<BankCardDto> bankCardDtos = List.of(
                BankCardDto.newBankCardDto(1006L, "1006", new BigDecimal("1006"), "备注1006")
                , BankCardDto.newBankCardDto(1007L, "1007", new BigDecimal("1007"), "备注1007"));
        bankCardServiceProxy.saveBankCard(bankCardDtos);
    }
}

【DeclareTxBasedXmlSchema.xml】

<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="locations">
            <value>jdbc-springdiscover.properties</value>
        </property>
    </bean>
    <!--注册数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="url" value="${jdbc.url}" />
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>
    <!--注册jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!--注册事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!--注册目标对象(业务逻辑服务)-->
    <bean id="bankCardServiceImpl" class="com.tom.springnote.chapter20.declaretx.service.BankCardServiceImpl">
        <constructor-arg name="jdbcTemplate" ref="jdbcTemplate" />
    </bean>

    <!-- 配置切面  -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="queryBy*" propagation="SUPPORTS" read-only="true" timeout="20" />
            <tx:method name="saveBankCard" propagation="REQUIRED" />
            <tx:method name="updateBankCard" propagation="REQUIRED"/>
            <tx:method name="deleteBankCard" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    <!-- 装配切面 -->
    <aop:config>
        <aop:pointcut id="txPointcut" expression="execution(* com.tom.springnote.chapter20.declaretx.service.IBankCardService.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
    </aop:config>

</beans>

【代码解说】

tx:advice元素:专门声明事务advice的元素;底层使用TransactionInterceptor拦截器封装切面;

tx:method元素:用于配置TransactionDefinition信息,只有name属性必输;

在这里插入图片描述



【2.2】基于注解的声明式事务

1)使用注解的声明式事务管理原理:通过注解为方法或类配置事务元数据(如传播行为,隔离级别等); 在业务方法执行期间,通过反射读取标注在该业务方法上的注解所配置的事务元数据;根据元数据,创建TransactionDefinition,TransactionManager对象,为管理事务提供支持;

1)spring定义了注解 Transactional: 用于标注业务方法所对应的事务元数据信息; 通过Transactional, 可以指定与 tx:method元素几乎相同的信息

【Transactional注解】 org.springframework.transaction.annotation.Transactional

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Reflective
public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";

    String[] label() default {};

    Propagation propagation() default Propagation.REQUIRED;

    Isolation isolation() default Isolation.DEFAULT;

    int timeout() default -1;

    String timeoutString() default "";

    boolean readOnly() default false;

    Class<? extends Throwable>[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};
}

关于Transactional注解:

  • Transactional注解可以标注类,也可以标注方法,且方法上Transactional注解配置信息会覆盖类上Transactional注解配置信息;


【2.2.1】使用@Transactional注解管理事务

【DeclareTxBasedAnnotationMain】

public class DeclareTxBasedAnnotationMain {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext container =
                new ClassPathXmlApplicationContext("chapter20/factorybean/DeclareTxBasedAnnotation.xml");
        IBankCardService bankCardServiceProxy = container.getBean("bankCardServiceImpl", IBankCardService.class);
        // 查询
        System.out.println(bankCardServiceProxy.queryById(1001L));

        // 新增
        List<BankCardDto> bankCardDtos = List.of(
                BankCardDto.newBankCardDto(1008L, "1008", new BigDecimal("1008"), "备注1008")
                , BankCardDto.newBankCardDto(1009L, "1009", new BigDecimal("1009"), "备注1009"));
        bankCardServiceProxy.saveBankCard(bankCardDtos);

        Transactional a;
    }
}
<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="locations">
            <value>jdbc-springdiscover.properties</value>
        </property>
    </bean>
    <!--注册数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="url" value="${jdbc.url}" />
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>
    <!--注册jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!--注册事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!--注册目标对象(业务逻辑服务)-->
    <bean id="bankCardServiceImpl" class="com.tom.springnote.chapter20.declaretx.service.BankCardServiceImpl">
        <constructor-arg name="jdbcTemplate" ref="jdbcTemplate" />
    </bean>

    <!-- 通过注解驱动的声明式事务管理 -->
    <tx:annotation-driven transaction-manager="transactionManager" />

</beans>

http://www.kler.cn/news/303519.html

相关文章:

  • 如何在Oracle中实现数据的加密
  • 深度学习-12-多模态Multimodality人工智能
  • React与Vue的对比
  • 秋招突击——算法练习——9/4——73-矩阵置零、54-螺旋矩阵、48-旋转图像、240-搜索二维矩阵II
  • vue原理分析(十四)研究new Vue()中的 initProvide
  • 局域网windows下使用Git
  • c#如何读取Modbus中Slave和Poll的值
  • vue之 package.json和package-lock.json
  • 【机器学习】线性动态系统的基本概念以及卡尔曼滤波器的概念和应用方式
  • c#引用同一命名空间下的其他类
  • 提权——Linux
  • Sequential的使用和搭建实战
  • js 深入理解生成器
  • 实时分析都靠它→揭秘YashanDB列式存储引擎的技术实现
  • 力扣第560题 和为k的子数组
  • 解锁编程潜力,从掌握GitHub开始
  • 突发!OpenAI发布最强模型o1:博士物理92.8分,IOI金牌水平
  • 高职人工智能训练师边缘计算实训室解决方案
  • 产品规划文档
  • PHP一键寄送尽在掌中快递寄件小程序
  • 设计模式篇--抽象工厂模式
  • Vue - 详细介绍vue-qr在线生成二维码组件(Vue2 Vue3)
  • 为 WebSocket 配置 Nginx 反向代理来支持 Uvicorn 的最佳实践
  • 动手学习RAG: moka-ai/m3e 模型微调deepspeed与对比学习
  • 苍穹外卖随记(一)
  • YOLOV8实现小目标检测
  • Qt自动打开文件夹并高亮文件
  • CI/CD持续集成和持续部署以及相关软件的使用
  • Docker日志管理之Filebeat+ELK日志管理
  • (不用互三)解密AI创作:提升Prompt提示词的提问技巧