Spring JDBC与事务管理

2022-11-22 08:26:52 浏览数 (1)

Spring JDBC与事务管理

  • 一.Spring JDBC
    • 1.Spring JDBC简介
    • 2.Spring JDBC的使用步骤
    • 3.Spring JDBC的配置过程
    • 4. jdbcTemplate的数据查询的方法
    • 5. jdbcTemplate的数据写入的方法
  • 二.Spring编程式事务
  • 三.Spring声明式事务
  • 四.事务传播行为
  • 五.注解配置声明式事务

本节,开始学习Spring JDBC模块与事务管理。说起JDBC我们一定不陌生,JDBC可以让我们使用Java程序去访问数据库。那作为Spring生态体系,肯定对JDBC也有良好的支持。所以这一篇博客,着重讲解Spring JDBC 与事务管理。

一.Spring JDBC

1.Spring JDBC简介

Spring JDBC 是Spring框架用于处理关系型数据库的模块。 Spring JDBC对JDBC的API进行封装,极大地简化了开发的工作量。 jdbcTemplate是Spring JDBC核心类,提供数据CRUD(增删改查)方法。

这时候,你可能会抱有疑惑,为什么有Mybatis了,还需要Spring JDBC呢?

其实这两者面向的对象是不一样的,Mybatis作为一个ORM框架(Object Relational Mapping),它封装的程度较高,适合于中小企业进行软件的敏捷开发。让程序员可以快速地完成与数据库的交互工作。但是,我们学过Mybatis的都知道,这里面就涉及到一系列的例如XML的配置以及各种各样的操作细节,实际上封装的程度还是比较高的。封装的程度高,就意味着我们执行效率较低。但是Spring JDBC只是对原始的JDBC的API简单封装。对于一线的互联网大厂,无论是数据量,还是用户的并发量都是非常高的。这时侯,如果使用Mybatis,可能由于微小的性能上的差距,就会导致整体应用变慢。因此,作为一线大厂,他们很少使用市面上的成熟框架。更多的时候,是使用像Spring JDBC这样的轻量级的封装框架。在这个基础上,结合自己企业的特点进行二次的封装。可以说,Spring JDBC的执行效率比起Mybatis是要高的。也因为有了Spring 底层的IoC容器的存在,也不至于让程序像原生JDBC那样难以管理。Spring JDBC是介于ORM框架和原生JDBC之间的一个折中的选择。

那说到这里,Spring JDBC该如何使用呢?主要有以下的3个步骤。 1

2.Spring JDBC的使用步骤

Maven工程引入spring-jdbc这个依赖 在applicationContext.xml中配置DataSource数据源。DataSource数据源是用于指向到底我们要连接哪种数据库的哪台服务器以及它的用户名和密码是什么等。 在Dao注入jdbcTemplate对象,实现数据CRUD。 1

3.Spring JDBC的配置过程

下面通过代码案例来讲解如何在Spring JDBC模块下,配置jdbcTemplate对象,同时实现增删改查的操作。这个案例需要用到一个建库脚本,我把它上传到CSDN上了,大家可以免费去下载。https://download.csdn.net/download/weixin_50216991/46916802

如果不想下载,建库脚本如下:

代码语言:javascript复制
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for employee
-- ----------------------------
DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee`  (
  `eno` int(11) NOT NULL,
  `ename` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `salary` float(10, 2) NOT NULL,
  `dname` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `hiredate` date NULL DEFAULT NULL,
  PRIMARY KEY (`eno`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of employee
-- ----------------------------
INSERT INTO `employee` VALUES (3308, '张三', 6000.00, '研发部', '2011-05-08');
INSERT INTO `employee` VALUES (3420, '李四', 8700.00, '研发部', '2006-11-11');
INSERT INTO `employee` VALUES (3610, '王五', 4550.00, '市场部', '2009-10-01');

SET FOREIGN_KEY_CHECKS = 1;

1.打开Idea,创建全新的Maven工程,创建好数据库表。

然后引入依赖,如下:

代码语言:javascript复制
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.13</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.13</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.27</version>
        </dependency>

    </dependencies>

2.在resources目录下面创建applicationContext.xml文件,填入schema约束。

代码语言:javascript复制
<?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:context="http://www.springframework.org/schema/context"
       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">
    
</beans>

3.在applicationContext.xml进行Spring JDBC的底层数据源的配置

代码语言:javascript复制
<?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:context="http://www.springframework.org/schema/context"
       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">

    <!--进行Spring JDBC的底层配置(数据源的配置)-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/demo?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai&amp;allowPublicKeyRetrieval=true"/>
        <property name="username" value="root"/>
        <property name="password" value="zc20020106"/>
    </bean>

</beans>

3.配置jdbcTemplate , 给Spring JDBC绑定相应的数据源。

代码语言:javascript复制
<?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:context="http://www.springframework.org/schema/context"
       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">

    <!--进行Spring JDBC的底层配置(数据源的配置)-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/demo?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai&amp;allowPublicKeyRetrieval=true"/>
        <property name="username" value="root"/>
        <property name="password" value="zc20020106"/>
    </bean>

    <!--配置jdbcTemplate-->
    <!--这个beanId最好固定叫jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--给Spring JDBC绑定相应的数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
</beans>

4.回到工程目录,在java目录下新增com.haiexijun.entity包,然后在里面创建Employee员工实体类。作为员工实体,它自然要和数据表中的字段依依对应。下面编写一下Employee类的代码。在里面重写toString()方法利于调试。

代码语言:javascript复制
package com.haiexijun.entity;

import java.util.Date;

public class Employee {
    private Integer eno;
    private String ename;
    private Float salary;
    private String dname;
    private Date hiredate;

    public Integer getEno() {
        return eno;
    }

    public void setEno(Integer eno) {
        this.eno = eno;
    }

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public Float getSalary() {
        return salary;
    }

    public void setSalary(Float salary) {
        this.salary = salary;
    }

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }

    public Date getHiredate() {
        return hiredate;
    }

    public void setHiredate(Date hiredate) {
        this.hiredate = hiredate;
    }

    @Override
    public String toString() {
        return "Employee{ " "eno=" eno " , " "ename=" ename " , " "salary=" salary " , " "dname=" dname " , " "hiredate=" hiredate " }";
    }
}

5.创建com.haiexijun.dao包,在里面创建EmployeeDao类。作为EmployeeDao,它在进行数据操作时,依赖于jdbcTemplate。因为刚才已经在IoC容器里面进行配置了jdbcTemplate的配置,所以在容器启动以后,自动的会对jdbcTemplate进行初始化,在后期使用时,只需要在这个对象采用配置的方式注入到dao的jdbcTemplate属性中就可以了。

代码语言:javascript复制
package com.haiexijun.dao;

import com.haiexijun.entity.Employee;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

public class EmployeeDao {
    
    private JdbcTemplate jdbcTemplate;

    //通过id来查找员工
    public Employee findById(Integer eno){
        //定义sql语句
        String sql="select * from employee where eno = ?";
        //进行指定的查询,将唯一返回的数据转换为对应的对象
        //参数1表示要执行的sql语句
        //参数2传入一个Object数组,里面的参数是要传入的?的参数
        //参数3表示把查询结果转换为的相应对象的类型。BeanPropertyRowMapper表示将Bean的属性与每一行的列进行对应类型匹配,来获取实体类型
        Employee employee= jdbcTemplate.queryForObject(sql,new Object[]{eno},new BeanPropertyRowMapper<Employee>(Employee.class));
        return employee;
    }

    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
}

6.编写好EmployeeDao后,需要在applicationContext.xml中对它进行配置

代码语言:javascript复制
<?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:context="http://www.springframework.org/schema/context"
       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">

    <!--进行Spring JDBC的底层配置(数据源的配置)-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/demo?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai&amp;allowPublicKeyRetrieval=true"/>
        <property name="username" value="root"/>
        <property name="password" value="zc20020106"/>
    </bean>

    <!--配置jdbcTemplate-->
    <!--这个beanId最好固定叫jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--给Spring JDBC绑定相应的数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <!--配置EmployeeDao-->
    <bean id="employeeDao" class="com.haiexijun.dao.EmployeeDao">
        <!--为Dao注入jdbcTemplate对象-->
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>

</beans>

到这里,算是配置完了,下面来测试一下: 创建一个入口类SrpingApplication,编写如下的方法:

代码语言:javascript复制
import com.haiexijun.dao.EmployeeDao;
import com.haiexijun.entity.Employee;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringApplication {
    public static void main(String[] args) {
        ApplicationContext context=new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        EmployeeDao employeeDao=context.getBean("employeeDao",EmployeeDao.class);
        Employee employee= employeeDao.findById(3308);
        System.out.println(employee);
    }
}

运行结果如下:

到这里,我们就完成了Sping JDBC的一个基本的配置过程。 1

4. jdbcTemplate的数据查询的方法

本小节,讲解一下jdbcTemplate的数据查询方法。为了学习便利,在当前工程中增加Junit单元测试框架,以及spring-test的依赖。如下:

代码语言:javascript复制
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.13</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>

然后在Test目录下的Java目录下创建jdbcTemplateTestor测试用例类,然后进行配置:

代码语言:javascript复制
import com.haiexijun.dao.EmployeeDao;
import com.haiexijun.entity.Employee;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class jdbcTemplateTestor {

    @Resource
    private EmployeeDao employeeDao;

}

查询多条记录, 在EmployeeDao里面添加如下方法:

代码语言:javascript复制
//查询多条记录
    public List<Employee> findByDname(String dname){
        String sql="select * from employee where dname=?";
        //query方法用于查询多条数据
        List<Employee> employees= jdbcTemplate.query(sql,new Object[]{dname},new BeanPropertyRowMapper<Employee>(Employee.class));
        return employees;
    }

然后到jdbcTemplateTestor里面创建一个新的测试用例:

代码语言:javascript复制
@Test
    public void testFindByDname(){
        List<Employee> employees=employeeDao.findByDname("研发部");
        System.out.println(employees);
    }

运行后,打印出张三和李四。

关于查询方法,我们还有一个特例,我们之前查询的都是所有的字段(*),并将这些字段名按照实体类的属性的对照关系依依进行转换,但是在实际运行过程中,有很多字段是没有与之对应的实体属性的。下面来模拟一下: 在EmployeeDao里面创建如下的方法:

代码语言:javascript复制
    //将查询结果封装的Map为List
    public List<Map<String,Object>> findMapByDname(String dname){
        String sql="select eno as empno ,salary as s from employee where dname=?";
        //把查询结果保存为list集合,每一个元素都是map,每一个Map对应一条记录
        List<Map<String,Object>> list= jdbcTemplate.queryForList(sql,new Object[]{dname});
        return list;
    }

添加一个测试用例:

代码语言:javascript复制
    @Test
    public void testFindMapByDname(){
        List<Map<String,Object>> maps=employeeDao.findMapByDname("研发部");
        System.out.println(maps);
    }

运行结果:

1

5. jdbcTemplate的数据写入的方法

这一节同样是以代码案例的方式来演示增删改查的操作。

新增数据: 下面先来演示增加数据的操作,在EmployeeDao下添加如下的方法: 数据的写入用 jdbcTemplate的update方法。

代码语言:javascript复制
    //数据的新增插入操作
    public void insert(Employee employee){
        String sql="insert into employee(eno,ename,salary,dname,hiredate)values(?,?,?,?,?)";
        jdbcTemplate.update(sql,new Object[]{employee.getEno(),employee.getEname(),employee.getSalary(),employee.getDname(),employee.getHiredate()});
        
    }

下面,编写测试用例来测试一下:

代码语言:javascript复制
    @Test
    public void testInsert(){
        Employee employee=new Employee();
        employee.setEno(8888);
        employee.setEname("赵六");
        employee.setSalary(6666f);
        employee.setDname("研发部");
        employee.setHiredate(new Date());
        employeeDao.insert(employee);
    }

运行后,数据成功插入:

数据更新: 在EmployeeDao下添加如下代码用于数据更新:

代码语言:javascript复制
    //用于数据更新
    public void update(Employee employee){
        String sql="update employee set ename=?,salary=?,dname=?,hiredate=? where eno=?";
        jdbcTemplate.update(sql,new Object[]{employee.getEname(),employee.getSalary(),employee.getDname(),employee.getHiredate(),employee.getEno()});
    }

编写测试方法:

代码语言:javascript复制
    @Test
    public void testUpdate(){
        Employee employee=new Employee();
        employee.setEno(8888);
        employee.setEname("赵六");
        employee.setSalary(8888f);
        employee.setDname("研发部");
        employee.setHiredate(new Date());
        employeeDao.update(employee);

    }

测试结果如下:

下面演示一下删除操作: 在EmployeeDao中添加如下方法:

代码语言:javascript复制
    //数据删除
    public void delete(Integer eno){
        String sql="delete from employee where eno = ?";
        jdbcTemplate.update(sql,new Object[]{eno});
    }

这里就不作测试了。

但是一涉及到写操作就要想到事务。下面就来学习。

二.Spring编程式事务

从本节开始,我们来了解一下Spring JDBC中如何进行事务管理。

编程式事务:编程式事务,就是指通过代码手动提交回滚的事务控制方法。Spring JDBC通过TransactionManager事务管理器实现事务控制。事务管理器提供commit/rollback方法进行事务提交与回滚。

下面通过实际的代码,来通过事务管理器来提交和回滚事务。

下面要把10名新员工导入到employee表中。对于这导入的10名新员工,我有一个小要求,要么全部导入成功,要么全部导入失败,什么都不做。 下面新创建一个service包,像上面批量导入的操作是属于业务逻辑中的方法。然后在里面创建EmployeeService类。

代码语言:javascript复制
package com.haiexijun.service;

import com.haiexijun.dao.EmployeeDao;
import com.haiexijun.entity.Employee;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.Date;

public class EmployeeService {
    private EmployeeDao employeeDao;
    private DataSourceTransactionManager transactionManager;

    //批量插入10名员工
    public void batchImport(){
        //定义了事务默认的标准配置
        TransactionDefinition definition=new DefaultTransactionDefinition();
        TransactionStatus status= transactionManager.getTransaction(definition);
        try {
            //我们用for循环来模拟一下
            for (int i = 1; i < 11; i  ) {
                Employee employee = new Employee();
                employee.setEno(8000   i);
                employee.setEname("员工"   i);
                employee.setSalary(4000f);
                employee.setDname("市场部");
                employee.setHiredate(new Date());
                employeeDao.insert(employee);
            }
            //提交事务
            transactionManager.commit(status);
        }catch (Exception e){
            //回滚事务
            transactionManager.rollback(status);
            throw e;
        }
    }

    public EmployeeDao getEmployeeDao() {
        return employeeDao;
    }

    public void setEmployeeDao(EmployeeDao employeeDao) {
        this.employeeDao = employeeDao;
    }

    public DataSourceTransactionManager getTransactionManager() {
        return transactionManager;
    }

    public void setTransactionManager(DataSourceTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }
}

还要在applicationContext.xml中对EmployeeService与事务管理器事务管理器进行配置:

代码语言:javascript复制
    <!--配置EmployeeService-->
    <bean id="employeeService" class="com.haiexijun.service.EmployeeService">
        <property name="employeeDao" ref="employeeDao"/>
        <property name="transactionManager" ref="transactionManager"/>
    </bean>

    <!--编程式事务的配置(配置事务管理器)-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--绑定数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

这样就算配置好了。

三.Spring声明式事务

声明式事务是指在不修改源代码的情况下通过配置的形式自动实现事务控制,声明式事务的本质就是AOP环绕通知当目标方法执行成功时,自动提交事务。当目标方法抛出运行时异常时,自动回滚事务。

声明式事务的整个配置过程都是在applicationContext.xml这个配置文件里面来完成的,无需修改源代码。

配置过程: 1.配置TransactionManager事务管理器。 2.配置事务通知与事务属性。有的方法需要使用事务,而有的则不需要使用事务,比如查询事务。我么要根据不同的情况不同配置。 3.为事务通知绑定PointCut切点。PointCut切点用于说明到底是在系统的哪些类的哪些方法上来应用通知呢?PointCut相当于限定了执行范围。

下面还是通过案例来演示声明式事务。还是回到之前的项目,把之前配置编程式事务的代码和配置给删掉,保留原始的批量新增用户的代码。

上面提到过,声明式事务基于AOP的,那就要导入Spring AOP的相关的aspectjweaer依赖。

代码语言:javascript复制
        <!--aspectjweaer是Spring AOP的底层依赖-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.8.RC2</version>
        </dependency

然后打开applicationContext这个文件,进行声明式事务的配置: 无论是编程式事务还是声明式事务,都要配置transactionManager事务管理器。我们还要添加一段xml命名空间:**xmlns:ts=“http://www.springframework.org/schema/tx”**和真实schema文件的位置:http://www.springframework.org/schema/tx https://www.springframework.org/schema/context/spring-tx.xsd tx这个命名空间专用于事务控制。

除了tx以外,我们还要增加aop这个命名空间。xmlns:aop="http://www.springframework.org/schema/aop"和spring aop的schema的真实地址http://www.springframework.org/schema/aop https://www.springframework.org/schema/context/spring-aop.xsd

下面是注解,bean,ioc,aop,tx事务管理的约束配置:

代码语言:javascript复制
<?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:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

</beans>

下面开始事务通知的配置,决定哪些方法使用事务,哪些方法不使用事务:

代码语言:javascript复制
    <bean id="employeeService" class="com.haiexijun.service.EmployeeService">
        <property name="employeeDao" ref="employeeDao"/>
    </bean>

    <!--声明式事务的配置-->

    <!--1.事务管理器的配置用于 |创建事务、提交事务、回滚事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--2.事务通知配置,决定哪些方法使用事务,哪些方法不使用事务-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--用于设置声明式事务的属性-->
        <tx:attributes>
            <!--说明哪些方法要使用事务,哪些方法不使用事务-->
            <!--name设置要实用事务的方法名,propagation设置事务的传播行为-->
            <!--百分之九十九的情况下propagation都是使用REQUIRED这个参数-->
            <tx:method name="batchImport" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!--配置通知作用在哪些类上(作用范围)-->
    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(public * com.haiexijun.service..*Service.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
    </aop:config>

下面对batchImport方法进行改造,制造一个运行时异常。看运行结果:

代码语言:javascript复制
package com.haiexijun.service;

import com.haiexijun.dao.EmployeeDao;
import com.haiexijun.entity.Employee;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.Date;

public class EmployeeService {
    private EmployeeDao employeeDao;


    //批量插入10名员工
    public void batchImport(){
            for (int i = 1; i < 11; i  ) {
                if (i==3){
                    throw new RuntimeException("意料之外的异常");
                }
                Employee employee = new Employee();
                employee.setEno(8000   i);
                employee.setEname("员工"   i);
                employee.setSalary(4000f);
                employee.setDname("市场部");
                employee.setHiredate(new Date());
                employeeDao.insert(employee);
            }
    }

    public EmployeeDao getEmployeeDao() {
        return employeeDao;
    }

    public void setEmployeeDao(EmployeeDao employeeDao) {
        this.employeeDao = employeeDao;
    }

}

下面把异常去掉,运行看结果:

这时候又引发了一个新的问题,作为EmployeeService的方法中只有一个batchImport方法,但是在未来EmployeeService中会有各种各样的方法,难道我们要定义成百上千个<tx:method name=“batchImport” propagation=“REQUIRED”/>来配置哪些方法使用事务吗?其实大可不必,其实作为method的配置他允许进进行通配符映射。如上面的batchImport可以写成batch*,也是一样的效果。那如果对于查询不需要事务,该这么去配置呢?其实也很简单。配置:<tx: method name=“find*” propagation=“NOT_SUPPORTED” read-only=“true”>就可以了。

四.事务传播行为

事务传播行为在我们日常开发中,使用的比较少。

事务传播行为是指多个拥有事务的方法在嵌套调用时的事务控制方式。

五.注解配置声明式事务

把applicationContext.xml之前的关于声明式事务的配置删除掉,就是如下部分:

代码语言:javascript复制
    <!--2.事务通知配置,决定哪些方法使用事务,哪些方法不使用事务-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="batchImport" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!--配置通知作用在哪些类上(作用范围)-->
    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(public * com.haiexijun.service..*Service.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
    </aop:config>

然后换成注解形式配置声明式事务: 添加如下的代码,就可以了:

代码语言:javascript复制
<tx:annotation-driven transaction-manager="transactionManager"/>

然后就可以用注解进行配置了。配置声明式事务我们只需要用到@Transactional这一个注解就行了:

代码语言:javascript复制
import java.util.Date;
//声明式事务的核心注解
//放在类上,表示将声明式事务配置于当前类的所有方法中,默认事务传播为REQUIRED,也可以配置propagation属性
//也可以写在方法中,表示只对方法生效。
//如:@Transactional((propagation = Propagation.REQUIRED))等
@Transactional
public class EmployeeService {

·······················

}

0 人点赞