spring 复习整理四(仗剑走天涯) spring 框架的事务抽象 三个关键类 TransactionManager org.springframework.transaction.PlatformTransactionManager
接口用于为不同平台提供统一抽象的事务管理器,重要
org.springframework.transaction.ReactiveTransactionManager
接口用于响应式事务管理,这个不重要
1 2 3 4 5 6 7 8 9 public interface PlatformTransactionManager extends TransactionManager { TransactionStatus getTransaction (@Nullable TransactionDefinition definition) throws TransactionException ; void commit (TransactionStatus status) throws TransactionException ; void rollback (TransactionStatus status) throws TransactionException ; }
任何PlatformTransactionManager接口实现类的方法抛出的TransactionException是未检查的 它继承了java.lang.RuntimeException的类
TransactionDefinition TransactionDefinition
接口指定了当前事务的相关配置
Propagation
:通常情况下,事务范围内的所有代码都在该事务中运行。 但是,如果事务方法在 已经存在事务的上下文中运行,则可以指定事务的传播行为
Isolation
:该事务与其他事务的工作隔离的程度。 例如,这个事务可以看到其他事务未提交的写入吗 隔离级
Timeout
:该事务在超时并被底层事务基础设施自动回滚之前运行多长时间
只读状态
:当代码读取但不修改数据时,可以使用只读事务。 在某些情况下,如使用Hibernate时,只读事务可能是一种有用的优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public interface TransactionDefinition { int PROPAGATION_REQUIRED = 0 ; int PROPAGATION_SUPPORTS = 1 ; int PROPAGATION_MANDATORY = 2 ; int PROPAGATION_REQUIRES_NEW = 3 ; int PROPAGATION_NOT_SUPPORTED = 4 ; int PROPAGATION_NEVER = 5 ; int PROPAGATION_NESTED = 6 ; int ISOLATION_DEFAULT = -1 ; int ISOLATION_READ_UNCOMMITTED = 1 ; int ISOLATION_READ_COMMITTED = 2 ; int ISOLATION_REPEATABLE_READ = 4 ; int ISOLATION_SERIALIZABLE = 8 ; int TIMEOUT_DEFAULT = -1 ; default int getPropagationBehavior () { return PROPAGATION_REQUIRED; } default int getIsolationLevel () { return ISOLATION_DEFAULT; } default int getTimeout () { return TIMEOUT_DEFAULT; } default boolean isReadOnly () { return false ; } @Nullable default String getName () { return null ; } static TransactionDefinition withDefaults () { return StaticTransactionDefinition.INSTANCE; }
TransactionStatus TransactionStatus
接口为事务代码提供了一种简单的方法来控制事务执行和查询事务状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public interface TransactionStatus extends TransactionExecution , SavepointManager , Flushable { @Override boolean isNewTransaction () ; boolean hasSavepoint () ; @Override void setRollbackOnly () ; @Override boolean isRollbackOnly () ; void flush () ; @Override boolean isCompleted () ; }
编程式事务管理 使用 TransactionManager 1 2 3 4 driverClass =com.mysql.cj.jdbc.Driver url =jdbc:mysql://localhost:3306/db_mybatis?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true user =root password =xiaobo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 <?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:p ="http://www.springframework.org/schema/p" xmlns:context ="http://www.springframework.org/schema/context" xmlns:aop ="http://www.springframework.org/schema/aop" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd" > <context:property-placeholder location ="jdbc.properties" /> <context:component-scan base-package ="com.dream.xiaobo.service" /> <bean id ="transactionManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name ="dataSource" ref ="druidDataSource" /> </bean > <bean id ="jdbcTemplate" class ="org.springframework.jdbc.core.JdbcTemplate" > <property name ="dataSource" ref ="druidDataSource" /> </bean > <bean id ="druidDataSource" class ="com.alibaba.druid.pool.DruidDataSource" > <property name ="driverClassName" value ="${driverClass}" /> <property name ="url" value ="${url}" /> <property name ="username" value ="${user}" /> <property name ="password" value ="${password}" /> </bean > </beans >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 @Service public class AccountServiceImpl implements AccountService { @Autowired private PlatformTransactionManager transactionManager; @Autowired private JdbcTemplate jdbcTemplate; @Override public void transfer (String from, String to, Integer money) { TransactionDefinition definition = new DefaultTransactionDefinition(); TransactionStatus transaction = transactionManager.getTransaction(definition); try { String fromSql = "UPDATE account SET money = money - ? WHERE name = ?" ; int i = 1 / 0 ; String toSql = "UPDATE account SET money = money + ? WHERE name = ?" ; jdbcTemplate.update(fromSql,new Object[]{ money, from }); jdbcTemplate.update(toSql,new Object[]{ money, to }); }catch (RuntimeException exception){ exception.printStackTrace(); transactionManager.rollback(transaction); } transactionManager.commit(transaction); } }
使用TransactionTemplate 1 2 3 4 5 6 <bean id ="transactionManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name ="dataSource" ref ="druidDataSource" /> </bean > <bean id ="transactionTemplate" class ="org.springframework.transaction.support.TransactionTemplate" > <property name ="transactionManager" ref ="transactionManager" /> </bean >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @Service public class AccountServiceImpl2 implements AccountService { @Autowired private JdbcTemplate jdbcTemplate; @Autowired private TransactionTemplate transactionTemplate; @Override public void transfer (String from, String to, Integer money) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult (TransactionStatus status) { String fromSql = "UPDATE account SET money = money - ? WHERE name = ?" ; int i = 1 / 0 ; String toSql = "UPDATE account SET money = money + ? WHERE name = ?" ; jdbcTemplate.update(fromSql,new Object[]{ money, from }); jdbcTemplate.update(toSql,new Object[]{ money, to }); } }); } }
new TransactionCallback<Object>() {}
带返回值
new TransactionCallbackWithoutResult(){}
不带返回值
声明式事务管理 Spring Framework的TransactionInterceptor为命令式和响应式编程模型提供了事务管理 拦截器通过检查方法返回类型来检测所需的事务管理风格 事务管理风格会影响需要哪个事务管理器 命令式事务需要PlatformTransactionManager 而响应式事务使用 ReactiveTransactionManager 实现
@Transactional 通常用于PlatformTransactionManager 管理的 线程绑定事务 将事务暴露给当前执行线程中的所有数据访问操作
1 2 3 4 5 6 7 8 9 10 11 12 <tx:advice id ="interceptor" transaction-manager ="transactionManager" > <tx:attributes > <tx:method name ="get*" read-only ="true" /> <tx:method name ="*" /> </tx:attributes > </tx:advice > <aop:config > <aop:pointcut id ="point" expression ="execution(public * com.dream.xiaobo.service..*(..))" /> <aop:advisor advice-ref ="interceptor" pointcut-ref ="point" /> </aop:config >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class TxAdvice implements MethodInterceptor { private PlatformTransactionManager transactionManager; public void setTransactionManager (PlatformTransactionManager transactionManager) { this .transactionManager = transactionManager; } @Override public Object invoke (MethodInvocation methodInvocation) throws Throwable { TransactionDefinition definition = new DefaultTransactionDefinition(); TransactionStatus transaction = transactionManager.getTransaction(definition); Object invoke = null ; try { invoke = methodInvocation.getMethod().invoke( methodInvocation.getThis(), methodInvocation.getArguments() ); }catch (RuntimeException exception){ exception.printStackTrace(); transactionManager.rollback(transaction); throw new RuntimeException("出现异常了" ); } transactionManager.commit(transaction); return invoke; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public void transfer2 (String from, String to, Integer money) { String fromSql = "UPDATE account SET money = money - ? WHERE name = ?" ; int i = 1 / 0 ; String toSql = "UPDATE account SET money = money + ? WHERE name = ?" ; jdbcTemplate.update(fromSql,new Object[]{ money, from }); jdbcTemplate.update(toSql,new Object[]{ money, to }); }
事务回滚 在其默认配置中,Spring框架的事务基础结构代码只在运行时、未检查的异常情况下标记事务进行回滚。 也就是说,当抛出的异常是 RuntimeException 的实例或子类时 (默认情况下,Error 实例也会导致回滚) 事务方法抛出的已检查异常不会导致默认配置的回滚
指定回滚规则 1 2 3 4 5 6 <tx:advice id ="txAdvice" transaction-manager ="txManager" > <tx:attributes > <tx:method name ="get*" read-only ="true" rollback-for ="NoProductInStockException" /> <tx:method name ="*" /> </tx:attributes > </tx:advice >
指定无回滚规则 1 2 3 4 5 6 <tx:advice id ="txAdvice" > <tx:attributes > <tx:method name ="updateStock" no-rollback-for ="InstrumentNotFoundException" /> <tx:method name ="*" /> </tx:attributes > </tx:advice >
默认配置
属性介绍 嵌套在tx:advice/ 和
tx:attributes/ 标签中的tx:method/ 标签的各种属性如下
属性
Required?
默认值
描述
name
Yes
要与事务属性相关联的方法名
通配符()字符可用于将相同的事务属性设置与许多方法相关联(例如,’ get ‘、’ handle* ‘、’ on*Event ‘等等)
propagation
No
REQUIRED
事务传播行为
isolation
No
DEFAULT 事务隔离级别
仅适用于’ REQUIRED ‘或’ REQUIRES_NEW ‘的传播设置
timeout
No
-1 事务超时(秒)
仅适用于传播’ REQUIRED ‘或’ REQUIRES_NEW ‘
read-only
No
false 读写事务与只读事务
只适用于’ REQUIRED ‘或’ REQUIRES_NEW ‘
rollback-for
No
触发回滚的“Exception”实例的逗号分隔列表例如,“com.foo.MyBusinessException, ServletException”
no-rollback-for
No
不触发回滚的“Exception”实例的逗号分隔列表例如,“com.foo.MyBusinessException, ServletException”
@Transactional @Transactional
注解是元数据,它指定接口、类或方法必须具有事务性语义
1 @EnableTransactionManagement
1 <tx:annotation-driven transaction-manager ="txManager" />
如果要连接的TransactionManager 的bean名称为 TransactionManager 则可以省略 tx:annotation-driven 标记中的 transaction-manager 属性
事务传播 事务的传播通常发生在【一个service层中的方法】调用【其他service层中的方法】,虽然我们并不推荐这么做,但是的确会存在一个【大的业务】包含多个【小业务】,大业务和小业务都可独立运行的场景
传播行为
含义
PROPAGATION_REQUIRED
表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务
PROPAGATION_SUPPORTS
表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行
PROPAGATION_MANDATORY
表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
PROPAGATION_REQUIRED_NEW
表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NOT_SUPPORTED
表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NEVER
表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常
PROPAGATION_NESTED
表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。
隔离级别
含义
ISOLATION_DEFAULT
使用后端数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED
最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
ISOLATION_READ_COMMITTED
允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
ISOLATION_REPEATABLE_READ
对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
ISOLATION_SERIALIZABLE
最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的
spring简单整合mybatis
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 <dependencies > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13.2</version > <scope > test</scope > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.2.18.RELEASE</version > </dependency > <dependency > <groupId > ch.qos.logback</groupId > <artifactId > logback-classic</artifactId > <version > 1.2.10</version > </dependency > <dependency > <groupId > javax.annotation</groupId > <artifactId > javax.annotation-api</artifactId > <version > 1.3.2</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > druid</artifactId > <version > 1.2.8</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.9.6</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.11</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-jdbc</artifactId > <version > 5.3.19</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.5</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis-spring</artifactId > <version > 2.0.2</version > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <version > 1.18.16</version > </dependency > </dependencies >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 <?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:p ="http://www.springframework.org/schema/p" xmlns:context ="http://www.springframework.org/schema/context" xmlns:aop ="http://www.springframework.org/schema/aop" xmlns:tx ="http://www.springframework.org/schema/tx" xmlns:mybatis ="http://mybatis.org/schema/mybatis-spring" xsi:schemaLocation ="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd http://mybatis.org/schema/mybatis-spring https://mybatis.org/schema/mybatis-spring.xsd " > <context:property-placeholder location ="jdbc.properties" /> <context:component-scan base-package ="com.dream.xiaobo" /> <mybatis:scan base-package ="com.dream.xiaobo.dao" /> <bean id ="sqlSessionFactory" class ="org.mybatis.spring.SqlSessionFactoryBean" > <property name ="dataSource" ref ="druidDataSource" /> <property name ="configLocation" value ="classpath:mybatis-config.xml" /> <property name ="mapperLocations" value ="classpath*:mapper/**/*.xml" /> </bean > <bean id ="transactionManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name ="dataSource" ref ="druidDataSource" /> </bean > <bean id ="druidDataSource" class ="com.alibaba.druid.pool.DruidDataSource" > <property name ="driverClassName" value ="${driverClass}" /> <property name ="url" value ="${url}" /> <property name ="username" value ="${user}" /> <property name ="password" value ="${password}" /> </bean > </beans >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration > <settings > <setting name ="lazyLoadingEnabled" value ="true" /> <setting name ="aggressiveLazyLoading" value ="false" /> <setting name ="logImpl" value ="SLF4J" /> <setting name ="logPrefix" value ="mybatis.sql." /> <setting name ="mapUnderscoreToCamelCase" value ="true" /> </settings > <typeAliases > <package name ="com.dream.xiaobo.entity" /> </typeAliases > </configuration >
1 2 3 4 driverClass =com.mysql.cj.jdbc.Driver url =jdbc:mysql://localhost:3306/db_mybatis?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8 user =root password =xiaobo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.dream.xiaobo.dao.UserMapper" > <resultMap id ="userMap" type ="com.dream.xiaobo.entity.User" > <id column ="id" property ="id" /> <result column ="username" property ="username" /> <result column ="password" property ="password" /> </resultMap > <sql id ="sql" > id,username,password </sql > <select id ="selectUser" resultType ="com.dream.xiaobo.entity.User" > SELECT <include refid ="sql" /> FROM `user` </select > </mapper >
你知道的越多 你不知道的越多 嘿 我是小博 带你一起看我目之所及的世界……