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>