Spring JDBC与事务管理6:Spring声明式事务一:声明式事务配置;

本文深入探讨Spring的声明式事务管理,它通过AOP实现,避免了编程式事务的繁琐。核心配置包括在pom.xml中引入aspectj,配置TransactionManager,添加tx和aop命名空间,以及事务通知和切点定义。声明式事务让程序员在编写业务代码时无需考虑事务控制,简化了开发流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

说明:

(1)在【Spring JDBC与事务管理5:Spring编程式事务;】已知,编程式事务虽然易于理解,但是由于程序员水平问题或疏忽问题,可能会出现忘记编写事务控制代码的情况;为此,声明式事务应运而生;

(2)这一点要明确:声明式事务不是新技术,而是【Spring AOP面向切面编程】一个典型的应用场景而已;

(3)【声明式事务】把事务控制的工作,转移到了applicationContext.xml配置文件中;这样以后,在编写业务代码的时候,程序员就不需要再设置事务了;这种策略,既安全又高效;

目录

一:声明式事务简介 

二:声明式事务配置过程

三:初始代码:创建项目s02;(这儿不是本篇博客的核心,快速浏览就行。)

(1)pom.xml:

(2)Employee实体类:

(3)applicationContext.xml配置文件:

(4)EmployeeDao类:

(5)EmployeeService类:

四:需求阐述

五:【声明式事务】:配置过程;(核心!)

1.在pom.xml中引入aspectj;

2.在applicationContext.xml中配置TransactionManager事务管理器对象;(第一步)

3.在applicationContext.xml中:引入【tx】命名空间和约束;【aop】命名空间和约束;

4.在applicationContext.xml中:事务通知配置:决定哪些方法使用事务,哪些方法不使用事务;(核心!)(第二步;第三步)

5.实际测试;

(1)首先,有异常的情况: 

(2)然后,没有异常的情况:

六:【声明式事务】总结;扩展分析;现存问题分析;

1.【声明式事务】summary;

2.扩展分析;

(1)通配符映射:匹配多个方法

(2)如果某类方法不需要事务控制,如何设置; 

(3)【其他选项 *】:设置【未被匹配到的方法】;

(4)【声明式事务】:在某种程度上也有助于程序的规范;

3.现存问题分析:事务传播方式还未了解;


一:声明式事务简介 

(1)声明式事务不是新技术;而是,【Spring AOP面向切面编程】一个典型的应用场景而已;

(2)利用【Spring AOP】中的环绕通知,可以轻松解决这个需求。即,【声明式事务】就是通过【Spring AOP的环绕通知】,在不修改源代码的情况下,完成程序的扩展,实现事务的控制;

(3)关于【Spring AOP 环绕通知】可以快速参考下【Spring AOP面向切面编程7:AOP相关概念五:AOP通知之:Around Advice 环绕通知;】 的内容;


二:声明式事务配置过程

因为,【声明式事务】是通过【Spring AOP的环绕通知】来实现的;所以,【声明式事务】的整个配置过程,都是在applicationContext.xml配置文件中完成的,不需要修改源代码;

(1)首先,需要在applicationContext.xml中配置TransactionManager对象。

         前面已经知道,TransactionManager对象的作用是提交或回滚事务;【编程式事务】和【声明式事务】都需要TransactionManager事务管理器对象;

(2)然后,给不同的方法,配置事务通知和事务属性;

         在实际开发中,有的方法(比如新增、修改、删除方法)需要事务控制,有的方法(比如查询方法)不需要事务控制。需要根据不同的情况,进行不同的配置;

(3)最后,为事务绑定【划定范围的切点】;

         这儿需要利用【AOP】的切点表达式来划定一个范围;只有在这个范围内的,且满足(2)中的条件的方法,才会开启事务控制;


三:初始代码:创建项目s02;(这儿不是本篇博客的核心,快速浏览就行。)

s02的内容和前面几篇博客的代码基本一致,只是去除了编程式事务的有关代码而已;

(1)pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.imooc.spring</groupId>
    <artifactId>jdbc</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>

        <!--logback日志组件,Spring框架默认集成-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>

    </dependencies>

</project>

说明:

(1)没什么好说的,目前在pom.xml中,引入了【Spring Context模块】,【Spring JDBC模块】,【MySQL JDBC驱动】,【junit测试依赖】,【Spring Test模块】,【logback日志依赖】这几个;

(2)Employee实体类:

package com.imooc.spring.jdbc.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 +
                '}';
    }
}

说明:

(0)Employee实体类很简单,没什么好说的;而且,在前面几篇博客中已经介绍过了;

(1)因为Employee实体类的作用,就是用来承载和存储数据库employee表的查询内容的;所以Employee类的属性,最好严格按照【驼峰命名规则】比照着employee表的字段名去写;

(2)然后,按常规写get和set方法;然后,为了方便观察Employee对象的内容,又重写了toString()方法;

(3)applicationContext.xml配置文件:

<?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">
    <!-- 数据源 -->
    <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/springjdbctest?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="12345"/>
    </bean>
    <!--JdbcTemplate提供数据CRUD的API-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="employeeDao" class="com.imooc.spring.jdbc.dao.EmployeeDao">
        <!--为Dao注入JdbcTemplate对象-->
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>

    <bean id="employeeService" class="com.imooc.spring.jdbc.service.EmployeeService">
        <property name="employeeDao" ref="employeeDao"/>
    </bean>

</beans>

说明:

(1)目前只是引入了【默认命名空间】,【context命名空间】; 

(2)这个applicationContext.xml很简单,没什么好说的,就是基本的【Spring JDBC】配置;

(4)EmployeeDao类:

package com.imooc.spring.jdbc.dao;

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

import java.util.List;
import java.util.Map;

public class EmployeeDao {
    private JdbcTemplate jdbcTemplate;

    public Employee findById(Integer eno){
        String sql = "select * from employee where eno = ?";
        //查询单条数据
        Employee employee = jdbcTemplate.queryForObject(sql, new Object[]{eno}, new BeanPropertyRowMapper<Employee>(Employee.class));
        return employee;
    }

    public List<Employee> findByDname(String dname){
        String sql = "select * from employee where dname = ?";
        //查询复合数据
        List<Employee> list = jdbcTemplate.query(sql, new Object[]{dname}, new BeanPropertyRowMapper<Employee>(Employee.class));
        return list;
    }

    public List<Map<String, Object>> findMapByDname(String dname){
        String sql = "select eno as empno , salary as s from employee where dname = ?";
        //将查询结果作为Map进行封装
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql, new Object[]{dname});
        return maps;
    }

    public void insert(Employee employee){
        String sql = "insert into employee(eno,ename,salary,dname,hiredate) values(?,?,?,?,?)";
        //利用update方法实现数据写入操作
        jdbcTemplate.update(sql,new Object[]{
           employee.getEno() , employee.getEname(),employee.getSalary(),employee.getDname() , employee.getHiredate()
        });
    }

    public int update(Employee employee){
        String sql = "UPDATE employee SET ename = ?, salary = ?, dname = ?, hiredate = ? WHERE eno = ?";
        int count = jdbcTemplate.update(sql, new Object[]{employee.getEname(), employee.getSalary(), employee.getDname(), employee.getHiredate(), employee.getEno()});
        return count;
    }

    public int delete(Integer eno){
        String sql = "delete from employee where eno = ?";
        return jdbcTemplate.update(sql, new Object[]{eno});
    }


    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

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

说明:

(1)因为EmployeeDao这个Dao类需要直接操作数据库,所以增加了JdbcTemplate属性;自然再applicationContext.xml中也在EmployeeDao对象中注入了JdbcTemplate对象; 

(2)EmployeeDao中都是定义了一些查询、新增、删除、修改方法;而且,在前面几篇博客中已经介绍过了;

(5)EmployeeService类:

package com.imooc.spring.jdbc.service;

import com.imooc.spring.jdbc.dao.EmployeeDao;
import com.imooc.spring.jdbc.entity.Employee;
import java.util.Date;

public class EmployeeService {
    private EmployeeDao employeeDao;

    public void batchImport() {
        for (int i = 1; i <= 10; i++) {
            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;
    }

}

说明:

(1)啰嗦一下吧:因为Service类需要调用Dao类中的方法;所以EmployeeService类中增加了EmployeeDao属性,并且在IoC容器中完成了对象注入;

(2)在EmployeeService中也增加了batchImport()这个业务方法; 然后,我们没有使用【编程式事务控制中的,那些事务控制代码;去控制事务】;


四:需求阐述

已知:EmployeeService中的batchImport()业务方法,是批量更新的操作;在实际开发中,这显然是需要进行事务控制的。

要求:在不修改原始代码的情况下,对系统重要的Service类,配置【声明式事务】;从而,让程序员在编写代码的过程中,不用再考虑事务的问题;(因为,我们已经通过【声明式事务】完成了事务控制的工作;所以,程序员在编写业务代码的时候,就不用再考虑编写事务控制的代码;)


五:【声明式事务】:配置过程;(核心!)

1.在pom.xml中引入aspectj;

2.在applicationContext.xml中配置TransactionManager事务管理器对象;(第一步)

说明:

(1)【编程式事务】和【声明式事务】都需要TransactionManager事务管理器对象;

(2)在【Spring JDBC与事务管理5:Spring编程式事务;】中已经介绍过配置TransactionManager事务管理器对象了;

3.在applicationContext.xml中:引入【tx】命名空间和约束;【aop】命名空间和约束;

说明:

(1)这些命名空间的内容其实很简单,不需要死记硬背;而且,在Spring官网也都有的;

4.在applicationContext.xml中:事务通知配置:决定哪些方法使用事务,哪些方法不使用事务;(核心!)(第二步;第三步)

(1)第二步是:通过<tx:advice>,配置哪些开启事务,以及事务的属性是什么;

(2)第三步是:通过<aop:config>,来划定一个范围;只有在这个范围内的,且满足第二步条件的方法,才会开启事务;

5.实际测试;

(1)首先,有异常的情况: 

……………………………………………………

(2)然后,没有异常的情况:


六:【声明式事务】总结;扩展分析;现存问题分析;

1.【声明式事务】summary;

(1)【声明式事务】就是在不修改源代码的情况下,实现事务的控制;

(2)【声明式事务】控制的规则是:如果方法执行成功,则提交事务;如果遇到运行时异常,则回滚事务;

(3)【声明式事务】是【Spring AOP】的一种典型应用;

2.扩展分析;

(1)<tx:method>:通配符映射:匹配多个方法

……………………………………………………

(2)如果某类方法不需要事务控制,如何设置; 

…………………………………………………… 

(3)【其他选项 *】:设置【未被匹配到的方法】;

……………………………………………………

(4)【声明式事务】:在某种程度上也有助于程序的规范;

 【声明式事务】让程序员从繁琐的事务控制中摆脱出来,即我们不用再在代码中编写事务控制代码,而是由程序根据自己的执行情况,自己决定提交事务还是回滚事务。

3.现存问题分析:事务传播方式还未了解;

现存问题:【propagation="NOT_SUPPORTED" 】,事务传播行为是什么,然后【NOT_SUPPORTED】和【REQUIRED】区别又是什么。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值