Spring事务
一,事务管理
1,事务基础概念回顾
⚫ 事务指数据库中多个操作合并在一起形成的操作序列
-
事务的作用
- 当数据库操作序列中个别操作失败时,提供一种方式使数据库状态恢复到正常状态**(A)**,保障数据库
即使在异常状态下仍能保持数据一致性(C)(要么操作前状态,要么操作后状态)。
- 当出现并发访问数据库时,在多个访问间进行相互隔离,防止并发访问操作结果互相干扰**(I)**。
⚫ 事务特征(ACID)
◆ 原子性(Atomicity)指事务是一个不可分割的整体,其中的操作要么全执行或全不执行
◆ 一致性(Consistency)事务前后数据的完整性必须保持一致
◆ 隔离性(Isolation)事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的
事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离
◆ 持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性
的,接下来即使数据库发生故障也不应该对其有任何影响
-
事务隔离级
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mIj1tpUH-1595069873805)(E:\每日讲课笔记\Spring\img\1595034932927.png)]
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ye6HFz1H-1595069873808)(E:\每日讲课笔记\Spring\img\1595035017600.png)]
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yAu4cdVg-1595069873810)(E:\每日讲课笔记\Spring\img\1595035050743.png)]
2,Spring核心事务对象介绍
-
⚫ J2EE开发使用分层设计的思想进行,对于简单的业务层转调数据层的单一操作,事务开启在业务层或者
数据层并无太大差别,当业务中包含多个数据层的调用时,需要在业务层开启事务,对数据层中多个操
作进行组合并归属于同一个事务进行处理
⚫ Spring为业务层提供了整套的事务解决方案
◆ PlatformTransactionManager (平台事务管理器)
◆ TransactionDefinition (事务定义)
◆ TransactionStatus(事务状态)
——————————————————————————————————————
-
PlatformTransactionManager (平台事务管理器)
⚫ 平台事务管理器实现类
◆ DataSourceTransactionManager 适用于Spring JDBC或MyBatis
◆ HibernateTransactionManager 适用于Hibernate3.0及以上版本
◆ JpaTransactionManager 适用于JPA
◆ JdoTransactionManager 适用于JDO
◆ JtaTransactionManager 适用于JTA
⚫ JPA(Java Persistence API)Java EE 标准之一,为POJO提供持久化标准规范,并规范了持久化开发的统一API,符合 JPA规范的开发可以在不同的JPA框架下运行
⚫ JDO(Java Data Object )是Java对象持久化规范,用于存取某种数据库中的对象,并提供标准化API。与JDBC相比, JDBC仅针对关系数据库进行操作,JDO可以扩展到关系数据库、文件、XML、对象数据库(ODBMS)等,可移植性更强
⚫ JTA(Java Transaction API)Java EE 标准之一,允许应用程序执行分布式事务处理。与JDBC相比,JDBC事务则被限定 在一个单一的数据库连接,而一个JTA事务可以有多个参与者,比如JDBC连接、JDO 都可以参与到一个JTA事务中
————————————————————————————
-
PlatformTransactionManager
-
此接口定义了事务的基本操作
-
获取事务
TransactionStatus getTransaction(TransactionDefinition definition)
-
提交事务
void commit(TransactionStatus status)
-
回滚事务
void rollback(TransactionStatus status)
-
-
TransactionDefinition
-
⚫ 此接口定义了事务的基本信息
-
获取事务定义名称
String getName()
-
获取事务的读写属性
boolean isReadOnly()
-
获取事务隔离级别
int getIsolationLevel()
-
获取事务超时时间
int getTimeout()
-
获取事务传播行为特征
int getPropagationBehavior()
-
-
-
TransactionStatus
-
此接口定义了事务在执行过程中某个时间点上的状态信息及对应的状态操作
-
获取事务是否处于新开启事务状态
boolean isNewTransaction()
-
获取事务是否处于已完成状态
boolean isCompleted()
-
获取事务是否处于回滚状态
boolean isRollbackOnly()
-
刷新事务状态
void flush()
-
获取事务是否具有回滚存储点
boolean hasSavepoint()
-
设置事务处于回滚状态
void setRollbackOnly()
-
3,案例环境介绍
-
事务控制方式 (下面就具体讲这三种方法怎么实现事务管理)
⚫ 编程式
⚫ 声明式(XML)
⚫ 声明式(注解)
-
案例介绍
⚫ 银行转账业务说明
银行转账操作中,涉及从A账户到B账户的资金转移操作。数据层仅提供单条数据的基础操作,未
设计多账户间的业务操作。
-
案例环境(基于Spring、Mybatis整合)
-
业务层接口提供转账操作
/** * 转账操作 * @param outName 出账用户名 * @param inName 入账用户名 * @param money 转账金额 */ public void transfer(String outName,String inName,Double money);
-
业务层实现提供转账操作
public void transfer(String outName,String inName,Double money){ accountDao.inMoney(outName,money); accountDao.outMoney(inName,money); }
-
数据层提供对应的入账与出账操作
<update id="inMoney"> update account set money = money + #{money} where name = #{name} </update> <update id="outMoney"> update account set money = money - #{money} where name = #{name} </update>
-
4,编程式事务
-
⚫ 使用spring提供的专用于mybatis的事务管理器在业务层硬编码进行事务管理
⚫ 业务层要注入dataSource对象
-
public void transfer(String outName,String inName,Double money){ //创建事务管理器 DataSourceTransactionManager dstm = new DataSourceTransactionManager(); //为事务管理器设置与数据层相同的数据源 dstm.setDataSource(dataSource); //创建事务定义对象 TransactionDefinition td = new DefaultTransactionDefinition(); //创建事务状态对象,用于控制事务执行 TransactionStatus ts = dstm.getTransaction(td); accountDao.inMoney(outName,money); int i = 1/0; //模拟业务层事务过程中出现错误 accountDao.outMoney(inName,money); //提交事务 dstm.commit(ts); }
-
<!--applicationContext.xml配置--> <context:property-placeholder location="classpath:*.properties"/> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> <!--<property name="dataSource" ref="dataSource"/>--> </bean> <bean class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="typeAliasesPackage" value="com.itheima.domain"/> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.itheima.dao"/> </bean>
5,AOP改造编程式事务
-
⚫ 将业务层的事务处理功能抽取出来制作成AOP通知,利用环绕通知运行期动态织入
-
制作通知类
package com.itheima.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; import javax.sql.DataSource; public class TxAdvice { //注入dataSource,下面传参 private DataSource dataSource; public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } public Object transactionManager(ProceedingJoinPoint pjp) throws Throwable { //开启事务 PlatformTransactionManager ptm = new DataSourceTransactionManager(dataSource); //事务定义 TransactionDefinition td = new DefaultTransactionDefinition(); //事务状态 TransactionStatus ts = ptm.getTransaction(td); Object ret = pjp.proceed(pjp.getArgs()); //提交事务 ptm.commit(ts); return ret; } }
-
配置applicationContext.xml
-
<!--配置AOP通知类,并注入dataSource--> <bean id="txAdvice" class="com.itheima.aop.TxAdvice"> <property name="dataSource" ref="dataSource"/> </bean> <!--上面开启aop,然后在这配置aop,使用环绕通知将通知类织入到原始业务对象执行过程中--> <aop:config> <!--保证表达式能拦截到业务层的方法,就可以进行aop增强--> <aop:pointcut id="pt" expression="execution(* *..transfer(..))"/> <aop:aspect ref="txAdvice"> <aop:around method="transactionManager" pointcut-ref="pt"/> </aop:aspect> </aop:config>
6,声明式事务(xml格式)(TX命名空间管理事务)
-
<!--第三种xml配置事务,以后要管理事务只需要配置这个xml文件,其他不用变,记得上面开启tx命名空间###########################################--> <!--配置事务管理器,并且配数据源--> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--使用tx命名空间配置事务专属通知类--> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="*" read-only="false"/> <tx:method name="find*" read-only="true"/> </tx:attributes> </tx:advice> <!-- 使用aop:advisor在AOP配置中引用事务专属通知类--> <aop:config> <aop:pointcut id="pt" expression="execution(* com.itheima.service.*Service.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/> </aop:config>
-
选择的定义:
-
你可能顶这很多压力做出了这个选择,比如。担负了一家人的希望,朋友已就业,父母朋友的不理解,和自己的朋友圈的脱节,仿佛在以前的圈子里消失隐匿了,比如你的王者好友战队啥的,但既然你选择了暂时放弃舒适圈来实现自己另一种野心(别人羡慕的职业,自己能说出去的工作环境,找到好工作后当初不理解的人说了一句,牛批)。选择没有对错,所以我希望你不要后悔自己的选择,你要做的是悄悄变得优秀,然后惊艳所有人,
-
最后反正大家一定会找到好工作,不,是高薪工作。所以祝大家身体健康,不管是为了他还是那个他,还是自己,毕竟活下去才能才能享受人生嘛。做一个所谓的低俗的有钱人
7,tx配置详解
-
aop:advice与aop:advisor区别
⚫ aop:advice配置的通知类可以是普通java对象,不实现接口,也不使用继承关系
⚫ aop:advisor配置的通知类必须实现通知接口
◆ MethodBeforeAdvice
◆ AfterReturningAdvice
◆ ThrowsAdvice
◆ ……
-
⚫ 名称:tx:advice
⚫ 类型:标签
⚫ 归属:beans标签
⚫ 作用:专用于声明事务通知
⚫ 格式:
<beans> <tx:advice id="txAdvice" transaction-manager="txManager"> </tx:advice> </beans>
⚫ 基本属性:
◆ id :用于配置aop时指定通知器的id
◆ transaction-manager :指定事务管理器bean
-
⚫ 名称:tx:attributes
⚫ 类型:标签
⚫ 归属:tx:advice标签
⚫ 作用:定义通知属性
⚫ 格式:
<tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> </tx:attributes> </tx:advice>
-
⚫ 名称:tx:method
⚫ 类型:标签
⚫ 归属:tx:attribute标签
⚫ 作用:设置具体的事务属性
⚫ 格式:
<tx:attributes> <tx:method name="*" read-only="false" /> <tx:method name="get*" read-only="true" /> </tx:attributes>
⚫ 说明:
通常事务属性会配置多个,包含1个读写的全事务属性,1个只读的查询类事务属性
-
tx:method属性
-
<tx:method name="*" 待添加事务的方法名表达式(支持*号通配符),例如get* 、* 、…… read-only="false" 设置事务的读写属性,true为只读,false为读写 timeout="-1" 设置事务超时时长,单位秒 isolation="DEFAULT" 设置事务隔离级别,该隔离级设定是基于Spring的设定,非数据库端 no-rollback-for="" 设置事务中不回滚的异常,多个异常间使用,分割 rollback-for="" 设置事务中必回滚的异常,多个异常间使用,分割 propagation="REQUIRED" 设置事务的传播行为 />
-
8,事务传播行为
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HpEgXvGe-1595069873813)(E:\每日讲课笔记\Spring\img\1595068668023.png)]
-
REQUIREW(需要),SUPPORTS, 支持,MANDATORY, 必须要有事务
9,声明式事务(注解)
-
开启注解驱动支持
-
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--开启事务注解驱动,并指定对应的事务管理器--> <tx:annotation-driven transaction-manager="txManager"/>
-
在需要的方法或者接口上添加@Transacational注解即可
-
@Transactional( readOnly = false, timeout = -1, isolation = Isolation.DEFAULT, rollbackFor = {ArithmeticException.class, IOException.class}, noRollbackFor = {}, propagation = Propagation.REQUIRES_NEW )
10,声明式事务(注解驱动)
-
使用@EnableTransactionManagerment去开启注解支持,写在主配置类上
-
利用注解形式配置TransactionManager(可以在jdbcConfig中写,顺便可以传同一个dataSource)
-
利用@Transactional可以标注在需要事务的方法上
-
@Configuration @ComponentScan("com.itheima") @PropertySource("classpath:jdbc.properties") @Import({JDBCConfig.class,MyBatisConfig.class,TransactionManagerConfig.class}) @EnableTransactionManagement public class SpringConfig { }
-
public class TransactionManagerConfig { @Bean public PlatformTransactionManager getTransactionManager(@Autowired DataSource dataSource){ return new DataSourceTransactionManager(dataSource); } }
二,模板对象
1,JdbcTemplate
-
整合
@Bean("dataSource") public DataSource getDataSource(){ DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(driver); ds.setUrl(url); ds.setUsername(userName); ds.setPassword(password); return ds; } //注册JdbcTemplate模块对象bean @Bean("jdbcTemplate") public JdbcTemplate getJdbcTemplate(@Autowired DataSource dataSource){ return new JdbcTemplate(dataSource); }
2,redis环境准备
3,redisTemplate
-
整合
-
RedisConfig
-
package com.itheima.config; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.PropertySource; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.jedis.JedisClientConfiguration; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @PropertySource("redis.properties") public class RedisConfig { @Value("${redis.host}") private String hostName; @Value("${redis.port}") private Integer port; // @Value("${redis.password}") // private String password; @Value("${redis.maxActive}") private Integer maxActive; @Value("${redis.minIdle}") private Integer minIdle; @Value("${redis.maxIdle}") private Integer maxIdle; @Value("${redis.maxWait}") private Integer maxWait; @Bean //配置RedisTemplate public RedisTemplate createRedisTemplate(RedisConnectionFactory redisConnectionFactory){ //1.创建对象 RedisTemplate redisTemplate = new RedisTemplate(); //2.设置连接工厂 redisTemplate.setConnectionFactory(redisConnectionFactory); //3.设置redis生成的key的序列化器,对key编码进行处理 RedisSerializer stringSerializer = new StringRedisSerializer(); redisTemplate.setKeySerializer(stringSerializer); redisTemplate.setHashKeySerializer(stringSerializer); //4.返回 return redisTemplate; } @Bean //配置Redis连接工厂 public RedisConnectionFactory createRedisConnectionFactory(RedisStandaloneConfiguration redisStandaloneConfiguration,GenericObjectPoolConfig genericObjectPoolConfig){ //1.创建配置构建器,它是基于池的思想管理Jedis连接的 JedisClientConfiguration.JedisPoolingClientConfigurationBuilder builder = (JedisClientConfiguration.JedisPoolingClientConfigurationBuilder)JedisClientConfiguration.builder(); //2.设置池的配置信息对象 builder.poolConfig(genericObjectPoolConfig); //3.创建Jedis连接工厂 JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration,builder.build()); //4.返回 return jedisConnectionFactory; } @Bean //配置spring提供的Redis连接池信息 public GenericObjectPoolConfig createGenericObjectPoolConfig(){ //1.创建Jedis连接池的配置对象 GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig(); //2.设置连接池信息 genericObjectPoolConfig.setMaxTotal(maxActive); genericObjectPoolConfig.setMinIdle(minIdle); genericObjectPoolConfig.setMaxIdle(maxIdle); genericObjectPoolConfig.setMaxWaitMillis(maxWait); //3.返回 return genericObjectPoolConfig; } @Bean //配置Redis标准连接配置对象 public RedisStandaloneConfiguration createRedisStandaloneConfiguration(){ //1.创建Redis服务器配置信息对象 RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); //2.设置Redis服务器地址,端口和密码(如果有密码的话) redisStandaloneConfiguration.setHostName(hostName); redisStandaloneConfiguration.setPort(port); // redisStandaloneConfiguration.setPassword(RedisPassword.of(password)); //3.返回 return redisStandaloneConfiguration; } }
-
redisTemplate.opsForValue() //String redisTemplate.opsForHash() //Hash redisTemplate.opsForList() //list redisTemplate.opsForSet() //set
4,事务底层原理解析——设计模式
s服务器配置信息对象
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
//2.设置Redis服务器地址,端口和密码(如果有密码的话)
redisStandaloneConfiguration.setHostName(hostName);
redisStandaloneConfiguration.setPort(port);
// redisStandaloneConfiguration.setPassword(RedisPassword.of(password));
//3.返回
return redisStandaloneConfiguration;
}
}
- ```pro
redisTemplate.opsForValue() //String
redisTemplate.opsForHash() //Hash
redisTemplate.opsForList() //list
redisTemplate.opsForSet() //set