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

news2024/12/27 14:47:06

文章目录

  • 【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.coloradmin.cn/o/2116493.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【基础篇】应届毕业生必备:机器学习面试题指南【1】

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发…

【软件工程】软件开发模型

三、瀑布模型 四、几种软件开发模型的主要特点 题目 判断题 选择题 小结

房贷计算及比较

本博客主要介绍&#xff1a; 1. 等额本金计算公式 2. 等额本息计算公式 3. 对比两种还款方式 4. 本示例:贷款金额为35万&#xff0c; 期限12年&#xff0c;年利率4.9% 等额本金计算 import matplotlib.pyplot as plt import matplotlib matplotlib.rcParams[font.sans-s…

day1 QT

作业 #include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//设置窗口大小this->resize(1025,533);//固定窗口大小this->setFixedSize(1025,533);//设置窗口背景色,设置弧度//this->setStyleSheet("background-image:url(E:/…

JS_数据类型

一、JS的数据类型 数值类型 数值类型统一为 number,不区分整数和浮点数 字符串类型 字符串类型为 string 和JAVA中的String相似,JS中不严格区分单双引号,都可以用于表示字符串 布尔类型 布尔类型为boolean 和Java中的boolean相似,但是在JS的if语句中,非空字符串会被转换为…

SuperMap iManger 单个镜像更新流程

1. 下载镜像 docker pull registry.cn-beijing.aliyuncs.com/supermap/common-dashboard-api:11.1.1-240802-amd64 label 没区分架构&#xff0c;在 x64 环境 pull arm64 镜像 通过 --platformarm64 参数可以实现 docker pull mariadb:10.5.26 --platformarm64 # 指定拉取ar…

【重学 MySQL】十九、位运算符的使用

【重学 MySQL】十九、位运算符的使用 示例检查权限添加权限移除权限 在 MySQL 中&#xff0c;位运算符允许你直接在整数类型的列或表达式上进行位级操作。这些操作对于处理那些需要在二进制表示上进行直接修改或比较的场景特别有用&#xff0c;比如权限管理、状态标记等。 &…

20240909 每日AI必读资讯

重磅&#xff01;TIME揭榜2024全球AI 100人&#xff1a;奥特曼、黄仁勋、姚期智、王小川等上榜 - TIME正式揭晓了第二届100位最具影响力AI人物名单&#xff01;「领导者」榜单中&#xff0c;有我们耳熟能详的OpenAI CEO Sam Altman、英伟达CEO黄仁勋。而字节跳动联合创始人兼C…

数据结构(邓俊辉)学习笔记】排序 7——希尔排序:Shell 序列 + 输入敏感性

文章目录 1.邮资问题2. 定理K3.逆序对 1.邮资问题 此前曾经讲到希尔排序在对矩阵逐列排序时所使用的算法本身未必需要十分高效&#xff0c;而更重要的是应该具有输入敏感的特性&#xff0c;因此我们更倾向于使用插入排序。那么背后的具体原因又当如何解释呢&#xff1f;这里的…

ubuntu16.04 vim使用中文出现乱编文档处理

问题现象 vim 编译文件时出现乱码问题 解决方法 1. 中文语言包安装: apt-get install language-pack-zh-hans 2. 配置环境变量:echo "export LC_ALLzh_CN.UTF-8" >>/etc/bash.bashrc 3. 修改当前环境的字符集 /etc/default/locale cat /etc/default/locale…

国内外网络安全政策动态(2024年8月)

▶︎ 1.《关于进一步加强智能网联汽车准入、召回及软件在线升级管理的通知》公开征求意见 8月1日&#xff0c;工业和信息化部装备工业一司联合市场监管总局质量发展局组织编制了《关于进一步加强智能网联汽车准入、召回及软件在线升级管理的通知&#xff08;征求意见稿&#…

事务的原理

1. 什么是事务 事务就是一个包含多个步骤的事情&#xff0c;这些步骤要么都做好&#xff0c;要么都别做。 2. ACID 事务都跟ACID相关&#xff0c;注意这里说的是“相关”&#xff0c;而不是一定都“满足”。全都严格满足&#xff0c;是“刚性事务”&#xff0c;部分满足或一…

240908-Python代码实现6种与DBGPT-Knowledge-API的交互方式

A. Chat模式 # import asyncio # from dbgpt.core import ModelRequest # from dbgpt.model.proxy import OllamaLLMClient# clientOllamaLLMClient()# print(asyncio.run(client.generate(ModelRequest._build("qwen2:1.5b", "你是谁&#xff1f;"))))imp…

Debug-027-el-tooltip组件的使用及注意事项

前言&#xff1a; 这两天&#xff0c;碰到这个饿了么的el-tooltip比较多。这个组件使用起来也挺简单的&#xff0c;常用于展示鼠标 hover 时的提示信息。但是有一些小点需要注意。这里不再机械化的介绍文档&#xff0c;不熟悉的话可以先看一下&#xff1a; https://element-pl…

这个隐藏功能,90%的人还不知道!可一键直达40+AI应用!含Kimi、腾讯元宝、秘塔AI等常用AI工具

大家好&#xff0c;我是程序员X小鹿&#xff0c;前互联网大厂程序员&#xff0c;自由职业2年&#xff0c;也一名 AIGC 爱好者&#xff0c;持续分享更多前沿的「AI 工具」和「AI副业玩法」&#xff0c;欢迎一起交流~ 又是被粉丝感动的一天。 昨天一位读者加到我&#xff0c;是一…

基于Java+SpringBoot+Vue+MySQL的美容美发管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 基于SpringBootVue的美容美发管理系统【附源码文档】、前后…

音频创作无界限:全球热门剪辑软件深度评测

如果是一个音乐爱好者肯定会有过想要录制自己音乐作品的想法吧。这个操作放在早些年可能有些难度&#xff0c;但是现在是科技告诉发展的时代&#xff0c;互联网上有不少在线音频剪辑工具可以供我们选择。这次我们就一起来探讨有什么适合音频剪辑的工具。 1.福昕音频剪辑 链接…

3.C_数据结构_栈

概述 什么是栈&#xff1a; 栈又称堆栈&#xff0c;是限定在一段进行插入和删除操作的线性表。具有后进先出(LIFO)的特点。 相关名词&#xff1a; 栈顶&#xff1a;允许操作的一端栈底&#xff1a;不允许操作的一端空栈&#xff1a;没有元素的栈 栈的作用&#xff1a; 可…

【机器人工具箱Robotics Toolbox开发笔记(一)】Matlab机器人工具箱简介

MATLAB是一款被广泛应用于科学计算和工程领域的专业软件。它的全称为Matrix Laboratory&#xff08;矩阵实验室&#xff09;&#xff0c;因为其最基本的数据类型就是矢量与矩阵&#xff0c;所以在处理数学和科学问题时非常方便&#xff0c;可用于线性代数计算、图形和动态仿真的…

系统架构的演进:同步通讯到异步通讯的过渡

系统架构的演进&#xff1a;同步通讯到异步通讯的过渡 一 . 同步通讯 VS 异步通讯1.1 同步调用方案① 耦合度高② 性能下降③ 资源浪费④ 级联失败 1.2 异步调用方案① 同步解耦② 性能提升 , 吞吐量增加③ 服务没有强依赖 , 不必考虑级联失败问题④ 流量削峰 1.3 小结 二 . 三…