Spring事务一网打尽

编程入门 行业动态 更新时间:2024-10-21 11:40:15

Spring<a href=https://www.elefans.com/category/jswz/34/1770772.html style=事务一网打尽"/>

Spring事务一网打尽

Spring事务一网打尽

    • 什么是事务
    • 首先说一个坑
    • Spring 中的事务
      • 两种用法
      • 三大基础设施
      • 编程性事务
        • TransactionManager 实现编程性事务
        • TransactionTemplate 实现编程性事务
      • 声明式事务
        • XML配置声明式事务
        • 注解配置声明式事务
        • 注解+XML混合配置声明式事务

什么是事务


这里要额外补充一点:只有保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。也就是说 A、I、D 是手段,C 是目的!

首先说一个坑

在单元测试方法里面不能用事务注解,不然,代码增删改永远不会生效

Spring 中的事务

两种用法

三大基础设施

  • PlatformTransactionManager
    PlatformTransactionManager 就像以前学的 JDBC,它就是一个接口,一个规范

    public interface PlatformTransactionManager extends TransactionManager {TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;void commit(TransactionStatus status) throws TransactionException;void rollback(TransactionStatus status) throws TransactionException;
    }
    
  • TransactionDefinition
    TransactionDefinition 主要定义的一些事务的属性,看源码就清楚了

  • TransactionStatus
    你可以理解为事务本身,当然也可以说是事务状态

编程性事务

TransactionManager 实现编程性事务
  • 导入必须的依赖
        <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.20</version></dependency><!--jdbc事务相关的代码--><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.30</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.25</version></dependency>
    
  • 全部代码如下
    @Service
    public class UserServie {@Autowiredprivate JdbcTemplate jdbcTemplate;@Autowiredprivate PlatformTransactionManager transactionManager;@Autowiredprivate TransactionTemplate transactionTemplate;public void transfer() {// 定义默认的事务属性DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();// 获取 transactionStatusTransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);try {jdbcTemplate.update("update user set money = ? where username = ?;", 33, "hok");// 提交事务transactionManagermit(transactionStatus);} catch (DataAccessException e) {e.printStackTrace();transactionManager.rollback(transactionStatus);}}
    
    public class App {public static void main(String[] args) {ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");UserServie userServie = ctx.getBean(UserServie.class);userServie.transfer();}
    }
    
    <?xml version="1.0" encoding="UTF-8"?><beans xmlns=""xmlns:xsi=""xmlns:context=""xmlns:tx=""xmlns:aop=""xsi:schemaLocation=" .xsd .xsd .xsd .xsd"><!--配置包扫描--><context:component-scan base-package="com.lhg.springtx" /><!-- 配置数据源 --><bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource" ><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://120.26.161.184:3306/wxpay" /><property name="username" value="root"/><property name="password" value="root"/></bean><!--配置事务管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><bean  id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"><property name="transactionManager" ref="transactionManager" /></bean><!--JdbcTemplate是 Spring 对 JDBC 的封装,用于操作数据库及事务的--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" ><property name="dataSource" ref="dataSource" /></bean>
    </beans>
    
  • 运行结果如下,可以看到数据正确修改
  • 如果转账过程中出现了问题会怎么样?

    如下图可以看到数据库更改并没有生效,事务回滚成功
    TransactionTemplate 实现编程性事务
    把 transfer 方法改成如下,实地验证会发现有同样的效果
    @Service
    public class UserServie {@Autowiredprivate JdbcTemplate jdbcTemplate;@Autowiredprivate PlatformTransactionManager transactionManager;@Autowiredprivate TransactionTemplate transactionTemplate;public void transfer() {transactionTemplate.execute(new TransactionCallbackWithoutResult() {@Overrideprotected void doInTransactionWithoutResult(TransactionStatus status) {try {jdbcTemplate.update("update user set money = ? where username = ?;", 888, "hok");int i = 1/0;} catch (DataAccessException e) {status.setRollbackOnly();throw new RuntimeException(e);}}});}
    }
    

从上面可以看到,编程性事务对业务方法侵入性太强了,实际项目开发一般不会去用

声明式事务

XML配置声明式事务

首先在上面的基础上再加个依赖

<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.7</version>
</dependency>

applicationContext.xml配置

<?xml version="1.0" encoding="UTF-8"?><beans xmlns=""xmlns:xsi=""xmlns:context=""xmlns:tx=""xmlns:aop=""xsi:schemaLocation=" .xsd .xsd .xsd .xsd"><!--配置包扫描--><context:component-scan base-package="com.lhg.springtx" /><!-- 配置数据源 --><bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource" ><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://120.26.161.184:3306/wxpay" /><property name="username" value="root"/><property name="password" value="root"/></bean><!--配置事务管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><bean  id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"><property name="transactionManager" ref="transactionManager" /></bean><!--JdbcTemplate是 Spring 对 JDBC 的封装,用于操作数据库及事务的--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" ><property name="dataSource" ref="dataSource" /></bean><!-- XML 配置事务分为三个步骤:1、配置事务管理器2、配置事务通知3、配置AOP--><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><!--配置事务的属性:isolation:用于指定事务的隔离级别,默认值是DEFAULT,表示使用数据库的默认隔离级别propagation:用于指定事务的传播行为,默认值是REQUERD,表示一定会有事务,增删改的选择,查询方法可以使用SUPPORTSread-only:用于指定事务是否只读,只有查询方法才能设置为true,默认值是false,表示读写rollback-for:用于指定一个异常,当该异常产生时,事务回滚,产生其它异常时事务不回滚,没有默认值,表示任何异常都回滚no-rollback-for:用于指定一个异常,当该异常产生时事务不回滚,产生其它异常时事务回滚,没有默认值,表示任何异常都回滚--><tx:method name="add*"  propagation="REQUIRED" read-only="false"/><!--通用匹配--><tx:method name="find*" propagation="SUPPORTS" read-only="true" /><!--匹配以find开头的方法,优先级更高--><tx:method name="transfer*" /></tx:attributes></tx:advice><aop:config><!--配置切入点表达式--><aop:pointcut id="pt" expression="execution(* com.lhg.springtx.UserServie.*(..))"/><!--建立切入点表达式与事务通知的对应关系--><aop:advisor advice-ref="txAdvice" pointcut-ref="pt" /></aop:config>
</beans>

业务逻辑代码改成如下,实际运行发现已正确修改

@Service
public class UserServie {@Autowiredprivate JdbcTemplate jdbcTemplate;public void transfer() {jdbcTemplate.update("update user set money = ? where username = ?;", 666, "hok");}
}

当然如果执行过程中出异常了,事务是能够正确回滚的,这里就不再截图了

public void transfer() {jdbcTemplate.update("update user set money = ? where username = ?;", 999, "hok");int i = 1/0;
}
注解配置声明式事务

依赖和上面一样,无需更改,然后定义一个 Java配置类

@Configuration
@ComponentScan(basePackages = "com.lhg.springtx")
@EnableTransactionManagement
public class JavaConfig {@Beanpublic DataSource dataSource(){DriverManagerDataSource dataSource = new DriverManagerDataSource();dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");dataSource.setUrl("jdbc:mysql://120.26.161.184:3306/wxpay?serverTimezone=Asia/Shanghai");dataSource.setUsername("root");dataSource.setPassword("root");return dataSource;}@Beanpublic PlatformTransactionManager transactionManager() {return new DataSourceTransactionManager(dataSource());}@Beanpublic JdbcTemplate jdbcTemplate() {return new JdbcTemplate(dataSource());}
}

业务层代码如下,需要在哪个方法上加事务就在哪个方法上加个@Transactional注解

@Service
public class UserServie {@Autowiredprivate JdbcTemplate jdbcTemplate;@Transactionalpublic void transfer() {jdbcTemplate.update("update user set money = ? where username = ?;", 222, "hok");int i = 1/0;}
}

启动执行

public class App {public static void main(String[] args) {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);UserServie userServie = ctx.getBean(UserServie.class);userServie.transfer();}
}

运行后会发现事务依然能够生效,这里就不一一截图了

注解+XML混合配置声明式事务

说白了就是想把这一坨干掉,换成<tx:annotation-driven />

来看看 xml 配置

<?xml version="1.0" encoding="UTF-8"?><beans xmlns=""xmlns:xsi=""xmlns:context=""xmlns:tx=""xmlns:aop=""xsi:schemaLocation=" .xsd .xsd .xsd .xsd"><!--配置包扫描--><context:component-scan base-package="com.lhg.springtx" /><!-- 配置数据源 --><bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource" ><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://120.26.161.184:3306/wxpay" /><property name="username" value="root"/><property name="password" value="root"/></bean><!--配置事务管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><bean  id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"><property name="transactionManager" ref="transactionManager" /></bean><!--JdbcTemplate是 Spring 对 JDBC 的封装,用于操作数据库及事务的--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" ><property name="dataSource" ref="dataSource" /></bean><!--事务注解支持--><tx:annotation-driven />
</beans>

业务代码如下

@Service
public class UserServie {@Autowiredprivate JdbcTemplate jdbcTemplate;@Transactionalpublic void transfer() {jdbcTemplate.update("update user set money = ? where username = ?;", 333, "hok");int i = 1/0;}
}

启动运行

public class App {public static void main(String[] args) {ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");UserServie userServie = ctx.getBean(UserServie.class);userServie.transfer();}
}

当然最终效果还是一样,事务正常

注解+XML混合配置需要根据实际项目需要,不一定是像上面这样…

更多推荐

Spring事务一网打尽

本文发布于:2023-11-15 09:06:57,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1597189.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:事务   Spring

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!