spring5 JdbcTemplate 事务

本文详细介绍了如何在Spring中使用编程式和声明式事务管理,涉及事务概念、银行转账案例、事务操作过程、编程式事务配置、Spring事务API及注解驱动和XML配置的比较,以及事务参数配置。通过实例演示了如何确保事务一致性并处理异常情况。
摘要由CSDN通过智能技术生成

1、事务概念

https://blog.csdn.net/chengqingshihuishui/article/details/111806766

2、事务操作

3、引出事务

需求:

银行转账
* lucy 转账100元 给mary
* lucy少100,mary多100

3.1 创建数据表,添加记录

用SQLyog直接创建好数据表

3.2 编写spring配置文件,和上例一样的。bean1.xml下写配置。

(1)配置druid数据池

(2)jdbcTemplate对象中注入dataSource数据池

(3)开启组件注解扫描,扫描目录为com.atguigu

<!-- 数据库连接池 --> 
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> 
  <property name="url" value="jdbc:mysql:///user_db" /> 
  <property name="username" value="root" /> 
  <property name="password" value="root" /> 
  <property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>


<!-- 配置JdbcTemplate对象,注入DataSource --> 
<!-- JdbcTemplate对象 --> 
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 
  <!--注入dataSource--> 
  <property name="dataSource" ref="dataSource"></property> 
</bean>

<!-- 开启组件扫描 --> 
<context:component-scan base-package="com.atguigu"></context:component-scan>

3.3 在com.atguigu.spring5文件夹下创建service文件夹,在该包下com.atguigu.spring5.service创建UserService类

package com.atguigu.spring5.service;

@Service
public class UserService{

  //将创建好的userDao对象注入
  @Autowired
  private UserDao userDao;
}

3.4 在com.atguigu.spring5文件夹下创建dao文件夹,创建接口UserDao 和 该接口实现类UserDaoImpl

创建接口

package com.atguigu.spring5.dao;

public interface UserDao{
}

创建实现类

package com.atguigu.spring5.dao;

//创建UserDao对象 userDao
@Repository
public class UserDaoImpl implements UserDao{

  //在该类注入jdbcTemplate对象
  @Autowired
  private JdbcTemplate jdbcTemplate;
}

3.5 在dao创建两个方法:多钱和少钱的方法,在实现类中实现lucy 转账100元 给mary,lucy少100,mary多100

package com.atguigu.spring5.dao;

public interface UserDao{
  //多钱
  public void addMoney();
  //少钱
  public void reduceMoney();
}

进入实现类中,实现转账方法

package com.atguigu.spring5.dao;

//创建UserDao对象 userDao
@Repository
public class UserDaoImpl implements UserDao{

  //在该类注入jdbcTemplate对象
  @Autowired
  private JdbcTemplate jdbcTemplate;
  
  //lucy转账100给mary 
  //少钱方法 
  @Override 
  public void reduceMoney() { 
    String sql = "update t_account set money=money-? where username=?"; 
    jdbcTemplate.update(sql,100,"lucy"); 
  } 
  //多钱 
  @Override 
  public void addMoney() { 
    String sql = "update t_account set money=money+? where username=?"; 
    jdbcTemplate.update(sql,100,"mary"); 
  }
}

再进入UserService写方法,accountMoney是lucy少100,mary多一百的集合

package com.atguigu.spring5.service;

@Service
public class UserService{

  //将创建好的userDao对象注入
  @Autowired
  private UserDao userDao;
  
  //转账的方法 
  public void accountMoney() { 
    //lucy少100 
    userDao.reduceMoney(); 
    //mary多100 
    userDao.addMoney(); }
}

在com.atguigu.spring5文件夹下,创建test文件夹,再该包下创建测试类TestBook

package com.atguigu.spring5.test;

public class TestBook{
  @Test
  public void testAccount(){
    ApplicationContext context=new ClassPathXmlApplicationContext("bean1.xml");
    UserService userService=context.getBean("userService",UserService.class);
    userService.accountMoney();
  }
}

以上代码,某个环节一旦出现异常,就会破坏事务一致性原则。因此需要引出事务进行操作

4、事务操作过程

以上步骤只是方便说明事务的过程,实际已经不用了,以上步骤是编程式事务管理,非常不方便。

5、spring事务管理介绍

5.1 简介

(1)事务添加到JavaEE三层结构(web、Service、dao)里面的Service层(业务逻辑层)

(2)Spring进行管理操作有两种方式:编程式事务管理 和 声明式事务管理(使用)

(3)声明式事务管理又分为基于注解方式(使用) 和 基于xml配置文件方式

(4)Spring进行声明式事务管理,底层使用AOP原理

5.2 Spring事务管理API

事务管理器 PlatformTransactionManager接口,目录结构如下:

该接口都写好了子接口和实现类(根据不同的框架有不同的实现类)

我们jdbcTemplate自然就使用DateSourceTransactionManager实现类

5.3 事务操作演示

5.3.1 注解声明式事务管理

(1)在spring配置文件配置事务管理器

    回到bean1.xml配置文件。用标签创建事务管理器实现类对象

<!-- 数据库连接池 --> 
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> 
  <property name="url" value="jdbc:mysql:///user_db" /> 
  <property name="username" value="root" /> 
  <property name="password" value="root" /> 
  <property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>


<!-- 配置JdbcTemplate对象,注入DataSource --> 
<!-- JdbcTemplate对象 --> 
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 
  <!--注入dataSource--> 
  <property name="dataSource" ref="dataSource"></property> 
</bean>

<!-- 开启组件扫描 --> 
<context:component-scan base-package="com.atguigu"></context:component-scan>

<!--创建事务管理器--> 
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
<!--注入数据源--> 
  <property name="dataSource" ref="dataSource"></property> 
</bean>

(2)使用spring配置文件,扫描开启事务的注解

① 引入名称空间tx

<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">

② 开启事务的注解

<!--开启事务注解-->

<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

说明:

tx:annotation-driven 标签开启注解

transaction-manager属性表示事务管理器对象,说明开启哪个事务管理器的注解

完整的bean.xml

<!-- 数据库连接池 --> 
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> 
  <property name="url" value="jdbc:mysql:///user_db" /> 
  <property name="username" value="root" /> 
  <property name="password" value="root" /> 
  <property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>


<!-- 配置JdbcTemplate对象,注入DataSource --> 
<!-- JdbcTemplate对象 --> 
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 
  <!--注入dataSource--> 
  <property name="dataSource" ref="dataSource"></property> 
</bean>

<!-- 开启组件扫描 --> 
<context:component-scan base-package="com.atguigu"></context:component-scan>

<!--创建事务管理器--> 
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
<!--注入数据源--> 
  <property name="dataSource" ref="dataSource"></property> 
</bean>

<!--开启事务注解--> 
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

(3)在service类上面(或者service类里面方法上面)添加事务注解

① @Transactional,这个注解添加到类上面,也可以添加方法上面

② 如果把这个注解添加类上面,这个类里面所有的方法都添加事务

③ 如果把这个注解添加方法上面,为这个方法添加事务

package com.atguigu.spring5.service;

@Service
@Transactional //整个类的所有方法,都开启事务
public class UserService{

  //将创建好的userDao对象注入
  @Autowired
  private UserDao userDao;
  
  //转账的方法 
  public void accountMoney() { 
    //lucy少100 
    userDao.reduceMoney(); 
    //mary多100 
    userDao.addMoney(); }
}

(4)编写测试文件(就是原来的测试文件)

package com.atguigu.spring5.test;

public class TestBook{
  @Test
  public void testAccount(){
    ApplicationContext context=new ClassPathXmlApplicationContext("bean1.xml");
    UserService userService=context.getBean("userService",UserService.class);
    userService.accountMoney();
  }
}

此时,就算出现异常,也会自动回滚了。(原来没有加事务注解之前,出异常就凉凉)

5.3.2 声明式事务管理的参数配置(即@Transactional注解的属性值配置)

说明:在service类上面添加注解@Transactional,在这个注解里面可以配置事务相关参数(主要讲解以下标记红色的参数)

(1)propagation():事务传播行为

事务传播行为:多个事务方法之间,存在相互调用,这个过程就是传播行为

事务方法:对数据库表的数据进行变化的操作(比如增删改,查不是事务方法)

如图:add事务方法调用update事务方法,就是传播行为

最常用的两个行为就是REQUIRED 和 REQUIRED NEW(必须掌握,其他了解即可)

注解属性说明
REQUIRED(默认行为)如果add方法本身有事务,调用update方法之后,update使用当前add方法里面的事务。如果add方法本身没有事务,调用update方法之后,创建新事务
REQUIRED_NEW使用add方法调用update方法,如果add无论是否有事务,都创建新的事务

 

@Transaction不写,默认使用propagation=Propagation.REQUIRED

package com.atguigu.spring5.service;

@Service
@Transactional(propagation=Propagation.REQUIRED) //不写属性,默认也是这个
public class UserService{

}

(2)ioslation:事务的隔离级别

复习mysql事务隔离性:

① 事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题

② 有三个读问题:脏读、不可重复读、虚(幻)读

③ 脏读(绝对不能忍,会导致严重问题):一个未提交事务读取到另一个未提交事务的数据

④ 不可重复读(也不算问题,可以忍,所以mysql默认是不解决这个):一个未提交事务读取到另一提交事务修改数据

⑤ 虚读:一个未提交事务读取到另一提交事务添加数据(跟不可重复读一样,可以忍的)

解决办法:设置事务隔离级别

 语法:

@Transactional注解的isolation=属性值

默认值:isolation=Isolation.REPEATABLE_READ

@Service
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.REPEATABLE_READ)
public class UserService{
}

(4)timeout:超时时间

① 事务需要在一定时间内进行提交,如果不提交进行回滚
② 默认值是 -1 ,设置时间以秒单位进行计算

语法:

@Transactional(timeout=-1)

(5)readOnly:是否只读

① 读:查询操作,写:添加修改删除操作
② readOnly默认值false,表示可以查询,可以添加修改删除操作
③ 设置readOnly值是true,设置成true之后,只能查询

@Transactional(readOnly=false)

(6)rollbackFor:回滚

    设置出现哪些异常进行事务回滚

(7)noRollbackFor:不回滚

    设置出现哪些异常不进行事务回滚

5.4 XML声明式事务管理(基本不用,了解即可)

一定要参考AOP https://blog.csdn.net/chengqingshihuishui/article/details/112528712

Step1 新建一个bean2.xml配置文件

<!-- 数据库连接池 --> 
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> 
  <property name="url" value="jdbc:mysql:///user_db" /> 
  <property name="username" value="root" /> 
  <property name="password" value="root" /> 
  <property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>


<!-- 配置JdbcTemplate对象,注入DataSource --> 
<!-- JdbcTemplate对象 --> 
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 
  <!--注入dataSource--> 
  <property name="dataSource" ref="dataSource"></property> 
</bean>


<!--step1 创建事务管理器--> 
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
<!--注入数据源--> 
  <property name="dataSource" ref="dataSource"></property> 
</bean>

<!--2 配置通知-->

<tx:advice id="txadvice"> 
  <!--配置事务参数--> 
  <tx:attributes> 
    <!--指定哪种规则的方法上面添加事务--> 
    <tx:method name="accountMoney" propagation="REQUIRED"/> 
    <!--<tx:method name="account*"/>--> 
  </tx:attributes>
</tx:advice> 

<!--3 配置切入点和切面--> 
<aop:config> 
  <!--配置切入点--> 
  <aop:pointcut id="pt" expression="execution(* com.atguigu.spring5.service.UserService.*(..))"/> 
  <!--配置切面--> 
  <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/> 
</aop:config>

Step2 编写测试文件

package com.atguigu.spring5.test;

public class TestBook{
  @Test
  public void testAccount(){
    ApplicationContext context=new ClassPathXmlApplicationContext("bean2.xml");
    UserService userService=context.getBean("userService",UserService.class);
    userService.accountMoney();
  }
}

5.5 完全注解式声明事务管理

在com.atguigu.spring5下新建一个config文件夹(包com.atguigu.spring5.config),在该文件夹下创建TxConfig类,用于替代xml配置文件(将xml下所有的配置,都用这个配置类来写)

package com.atguigu.spring5.config;

@Configuration 
//配置类 
@ComponentScan(basePackages = "com.atguigu") 
//组件扫描 
@EnableTransactionManagement 
//开启事务 
public class TxConfig { 
  //创建数据库连接池 
  @Bean 
  public DruidDataSource getDruidDataSource() { 
    DruidDataSource dataSource = new DruidDataSource(); 
    dataSource.setDriverClassName("com.mysql.jdbc.Driver"); 
    dataSource.setUrl("jdbc:mysql:///user_db");
    dataSource.setUsername("root");
    dataSource.setPassword("root"); 
    return dataSource; 
    } 
  //创建JdbcTemplate对象
  @Bean 
  public JdbcTemplate getJdbcTemplate(DataSource dataSource) { 
    //到ioc容器中根据类型找到
    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; 
  } 
}

编写测试类

package com.atguigu.spring5.test;

public class TestBook{
  @Test
  public void testAccount(){
    ApplicationContext context=new AnnotationConfigApplicationContext(TxConfig.class);
    UserService userService=context.getBean("userService",UserService.class);
    userService.accountMoney();
  }
}

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值