目录
下面介绍如何通过 XML 的方式实现声明式事务管理,步骤如下。
1、什么事务
(1)事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败.
(2)典型场景:银行转账
*lucy转账100元给mary
*lucy少100,mary多100
2、事务四个特性(ACID)
(1)原子性:过程中要么都成成功,要么都失败
(2)一致性:操作之前和操作之后总量是不变的,比如转账之前有一百,转账后两个人前加起来还是一百
(3)隔离性:在多事务操作时不会产生影响,比如两个人一同操作同一个事物,他们操作之间不会产生影响
(4)持久性:事务提交会真正发生变化
我们知道mvc是三层架构,界面层、业务逻辑层、数据访问层,在事务操作时我们先只看业务逻辑层(Srevice)、数据访问层(Dao)。
创建service,搭建dao,完成对象创建和注入关系。
- @Service
-
- public class UserService {
- // 注入dao
- @Autowired
- private UserDao userdao;
-
- public void accountMoney(){
- // lucy少100
- userdao.reducwMoney();
- // 模拟异常
- int i = 10/0;
-
- // marry多100
- userdao.addMoney();
- }
-
- }
- public interface UserDao {
- // 多钱
- public void addMoney();
- // 少钱
- public void reducwMoney();
- }
- @Repository
- public class UserDaoImpl implements UserDao{
- @Autowired
- private JdbcTemplate jdbcTemplate;
-
- // 多钱:marry进账100
- @Override
- public void addMoney() {
- String sql = "update t_account set moeny = moeny+? where username=?";
- jdbcTemplate.update(sql, 100,"mary");
-
- }
-
- // 少钱,Lucy转账给marry
- @Override
- public void reducwMoney() {
- String sql = "update t_account set moeny=moeny-? where username=?";
- jdbcTemplate.update(sql, 100,"lucy");
-
-
- }
- }
xml配置:(有固定的模板)
- "1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-
- xmlns:context="http://www.springframework.org/schema/context"
- 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/context http://www.springframework.org/schema/context/spring-context.xsd
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
- ">
- <context:component-scan base-package="com.java.spring">context:component-scan>
-
- <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
- <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
- <property name="url" value="jdbc:mysql://localhost:3306/user_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"/>
- <property name="username" value="root"/>
- <property name="password" value="123456"/>
- bean>
- <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
-
- <property name="dataSource" ref="dataSource">property>
- bean>
- <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource">property>
- bean>
-
- beans>
测试
- public class Test1 {
- @Test
- public void testAccount(){
- ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
- UserService userService = context.getBean("userService", UserService.class);
- userService.accountMoney();
- }
无异常结果
有异常结果:Lucy转给Mary100,但由于异常Mary没有得到,导致丢失100.
Spring 实现声明式事务管理主要有 2 种方式:
- "1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
- http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
接下来,我们就需要借助数据源配置,定义相应的事务管理器实现的 Bean,配置内容如下。
- <context:component-scan base-package="com.java.spring">context:component-scan>
-
- <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
- <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
- <property name="url" value="jdbc:mysql://localhost:3306/user_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"/>
- <property name="username" value="root"/>
- <property name="password" value="123456"/>
- bean>
- <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
-
- <property name="dataSource" ref="dataSource">property>
- bean>
- <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource">property>
- bean>
- <tx:advice id="txadvice">
-
- <tx:attributes>
- <tx:method name="accountMoney" propagation="REQUIRED"/>
-
- tx:attributes>
- tx:advice>
- <aop:config>
- <aop:pointcut id="pt" expression="execution(* com.java.spring.service.UserService.*(..))"/>
- <aop:advisor advice-ref="txadvice" pointcut-ref="pt">aop:advisor>
- aop:config>
-
- beans>
添加事务在发生异常二者都既不少钱也不多钱其余的实体类和上面的UserService一样,接口和接口实现类以及测试类也一样。
上面属性标签解释
propagation:事务传播行为
当一个事务方法被另外一个事务方法调用时候,这个事务方法如何进行
T2并不影响T1,如果1掉完方法2出现了问题它并不影响2,2在自己的事务内运行,可以正常提交
当前A没有事务,当他调用B时,是不在事务中运行的
当前A有了事务,调用B时,B在A的事务内运行
ioslation事务隔离级别
(1)事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题。
(2)有三个读问题:脏读、不可重复读、虚(幻)读
事务A读取到60000后,那60000进行操作,但事务B还没有提交就开始回滚,以至于A拿到错数据去操作
解决办法:通过设置隔离性,解决三种问题
1.在spring配置文件中配置事务管理器,开启事务注解。
- "1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
- http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
- <context:component-scan base-package="com.java.spring">context:component-scan>
-
- <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
- <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
- <property name="url" value="jdbc:mysql://localhost:3306/user_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"/>
- <property name="username" value="root"/>
- <property name="password" value="123456"/>
- bean>
- <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
-
- <property name="dataSource" ref="dataSource">property>
- bean>
- <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource">property>
- bean>
- <tx:annotation-driven transaction-manager="dataSourceTransactionManager">tx:annotation-driven>
-
- beans>
2.在Service类(或方法上)上面添加事务注解
-
- @Service
- @Transactional
- public class UserService {
- // 注入dao
- @Autowired
- private UserDao userdao;
-
- public void accountMoney(){
- // lucy少100
- userdao.reducwMoney();
- // 模拟异常
- int i = 10/0;
- // marry多100
- userdao.addMoney();
- }
-
- }
其他类和测试类和第一个类一样,注意配置文件名称更改
发生异常,但该类添加了事务注解,所以Lucy不会少。Mary不会多
第一个参数:事务的传播方式(propagation),第二个隔离性(ioslation)
其他参数
timeout:超时时间
(1) 事务需要在一定时间内进行提交,如果不提交进行回滚
(2) 默认值是-1,设置时间以秒单位进行计算
readonly:是否只读
(1)读:查询操作,写:添加修改删除操作
(2)readonly默认值false,表示可以查询,可以添加修改删除操作。
(3)设置eadonly.值是true,设置成tru之后,只能查询
rolbaskor:回滚
(1)设置出现哪些异常进行事务回滚
noRolbackFor:不回滚
(1)设置出现哪些异常不进行事务回滚
1,创建配置类,使用配置类替代xml配置文件
2、Spring5.0框架自带了通用的日志封装
(1)Spring5已经移除Log4jConfigListener,官方建议使用Log42
(2)Spring5框架整合Log4j2
第一步引入jar包
第二步创建log4j2.xml配置文件
@EnableTransactionManagement//开启事务
- @Configuration//配置类
- @ComponentScan(basePackages = "com.java")//组建扫描
- @EnableTransactionManagement//开启事务
- public class TxConfig {
-
- // 创建数据库连接池
- @Bean
- public DruidDataSource getDruidDataSource(){
- DruidDataSource dataSource = new DruidDataSource();
- dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
- dataSource.setUrl("jdbc:mysql://localhost:3306/user_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai");
- dataSource.setUsername("root");
- dataSource.setPassword("123456");
- return dataSource;
-
- }
-
- // 创建JdbcTempLate对象
- // 应为连接池创建时已经生成了dataSource对象所以这里的参数实在连接池中找的对象,而不是我们要传入的参数
- @Bean
- public JdbcTemplate getJdbcTemplate(DruidDataSource dataSource){
- JdbcTemplate jdbcTemplate = new JdbcTemplate();
- // 注入datasource
- jdbcTemplate.setDataSource(dataSource);
- return jdbcTemplate;
- }
-
-
- // 创建事务管理器
- @Bean
- public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
- DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
- transactionManager.setDataSource((dataSource));
- return transactionManager;
- }
- }
测试类
- public void testAccount2(){
- ApplicationContext context = new AnnotationConfigApplicationContext("TxConfig.class");
- UserService userService = context.getBean("userService", UserService.class);
- userService.accountMoney();
- }
Spring5框架核心容器支持@Nullable注解
(1)@Nullable注解可以使用在方法上面,属性上面,参数上面,表示方法返回可以为空,属性值可以为空,参数值可以为空
(2)注解用在方法上面,方法返回值可以为空
(3)注解使用在方法参数里面,方法