Bootstrap

Spring 核心技术解析【纯干货版】- IX:Spring 数据访问模块 Spring-Jdbc 模块精讲

在现代企业级应用中,数据访问层的稳定性和高效性至关重要。为了简化和优化数据库操作,Spring Framework 提供了 Spring-JDBC 模块,旨在通过高度封装的 JDBC 操作,简化开发者的编码负担,减少冗余代码,同时提升系统的健壮性和可维护性。Spring-JDBC 模块的核心组成之一,JdbcTemplate,是开发者在数据库交互中最常用的工具,它不仅大幅度减少了样板代码,还自动处理了事务和异常管理。本文将深入探讨 Spring-JDBC 模块的基本功能、核心组件以及事务管理机制,帮助开发者更好地理解和使用该模块,从而提升数据访问的效率与安全性。



1、Spring-Jdbc 模块介绍

1.1、Spring-Jdbc 模块概述

Spring JDBC 模块,是一个提供了对 JDBC 访问的高度抽象的模块,它简化了使用 JDBC 进行数据库操作的过程。

Spring JDBC 模块,它包含了一个 JdbcTemplate 类,该类封装了诸如查询、更新、事务处理等常用操作,使得编写数据库交互代码变得更加简洁且不易出错。JdbcTemplate 还能自动处理资源管理和异常翻译,提高了代码的健壮性。

1.2、Spring-Jdbc 模块依赖

Spring-Jdbc 模块的依赖有三个,分别是 Spring-Beans 模块、Spring-Core 模块和 Spring-Tx 模块。

其中 Spring Beans 模块是对 Spring Bean 进行定义,实现 IOC 基础功能的模块。Spring-Core 是 Spring 中的基础模块,它提供了框架运行所必需的核心功能。而 Spring Tx 模块,是 Spring 中处理事务管理的模块。

1.3、Spring-Jdbc 模块作用

Spring-JDBC 的主要作用:

  1. 简化 JDBC 操作:Spring-JDBC 提供了 JdbcTemplate 类,封装了 JDBC 的核心操作(如连接管理、SQL 执行、结果集处理等),开发者只需关注 SQL 语句和业务逻辑,无需手动处理资源管理(如关闭连接、结果集等)。
  2. 减少样板代码:传统的 JDBC 代码需要手动处理 ConnectionStatementResultSet 等资源的创建和释放,容易出错且代码冗长。Spring-JDBC 通过模板方法模式,自动处理这些资源,减少了重复代码。
  3. 异常处理:Spring-JDBC 将 JDBC 的 SQLException 转换为 Spring 的 DataAccessException 异常体系,提供了更清晰的异常层次结构,便于开发者处理数据库操作中的错误。
  4. 事务管理:Spring-JDBC 与 Spring 的事务管理模块无缝集成,支持声明式事务管理(通过注解或 XML 配置),简化了事务控制的实现。
  5. 支持多种数据库操作:除了基本的 CRUD 操作,Spring-JDBC 还支持批量操作、存储过程调用、复杂结果集映射等功能。
  6. 与 ORM 框架集成:Spring-JDBC 可以与其他 ORM 框架(如 Hibernate、MyBatis)结合使用,提供更灵活的数据访问方式。

2、Spring-Jdbc 核心组件

JdbcTemplate 为 Spring JDBC 的核心类,提供数据 CRUD 方法。本节介绍对于 JdbcTemplate 的使用。

2.1、配置文件和依赖

使用 Maven,确保在 pom.xml 中正确引入了相关的 Spring JDBC 和数据库连接池的依赖。例如:

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.39</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.39</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>
    </dependencies>
2.2、配置 Spring 容器

首先,确保我们的 Spring 配置文件配置正确,并且已经引入了 Spring 的上下文和 JDBC 配置。例如:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 配置数据源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/yourdb"/>
        <property name="username" value="yourusername"/>
        <property name="password" value="yourpassword"/>
    </bean>

    <!-- 配置JdbcTemplate -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 配置EmpDao -->
    <bean id="empDao" class="com.example.EmpDao">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>
</beans>
2.3、启动 Spring 容器

在 Spring 项目中,通常使用 ClassPathXmlApplicationContext 来加载配置文件并启动容器。我们可以在 main 方法中使用如下代码启动 Spring 容器:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        // 加载 Spring 配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-bean.xml");
        
        // 获取 EmpDao Bean 并调用方法
        EmpDao empDao = (EmpDao) context.getBean("empDao");
        
        // 调用 EmpDao 中的方法
        empDao.addEmployee("John Doe", 30);
        empDao.deleteEmployee(5);
    }
}
2.4、使用 EmpDao 类中的方法

确保我们的 EmpDao 类已经正确配置了增删改查的方法,并且使用了 JdbcTemplate。例如:

import org.springframework.jdbc.core.JdbcTemplate;

public class EmpDao {
    private JdbcTemplate jdbcTemplate;

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

    public void addEmployee(String name, int age) {
        String sql = "INSERT INTO employee (name, age) VALUES (?, ?)";
        jdbcTemplate.update(sql, name, age);
    }

    public void deleteEmployee(int id) {
        String sql = "DELETE FROM employee WHERE id = ?";
        jdbcTemplate.update(sql, id);
    }
}

3、Spring-Jdbc 事务管理

Spring JDBC 提供了对事务管理的支持,使得数据库操作的事务管理更加简洁和统一。通过 Spring 的事务管理,我们可以在操作数据库时保证事务的一致性和原子性,避免数据的不一致性。

Spring 提供了两种事务管理机制:

  1. 编程式事务管理:通过代码显式地控制事务的开始、提交、回滚。
  2. 声明式事务管理:通过配置和注解(@Transactional)来实现自动的事务管理,Spring 会自动控制事务的开始、提交、回滚。
3.1、Spring JDBC 事务管理概述

Spring 提供了 DataSourceTransactionManager 来管理 JDBC 事务,它实现了 PlatformTransactionManager 接口,Spring 会通过该类来控制事务的生命周期。

  • 事务的状态:通常有 begin(开始)、commit(提交)、rollback(回滚)。
  • 传播行为:事务的传播方式,决定了一个方法被调用时,当前事务的状态。
3.2、使用 @Transactional 进行声明式事务管理

最常用的方式是通过 @Transactional 注解实现声明式事务管理。Spring 会在方法执行前开启事务,执行完毕后提交事务。如果出现异常,事务会回滚。

例子:使用 @Transactional 注解

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Transactional;

public class EmpDao {
    private JdbcTemplate jdbcTemplate;

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

    // 使用 @Transactional 注解,声明式事务管理
    @Transactional
    public void addEmployeeAndDepartment(String empName, int empAge, String deptName) {
        String addEmployeeSql = "INSERT INTO employee (name, age) VALUES (?, ?)";
        jdbcTemplate.update(addEmployeeSql, empName, empAge);

        String addDepartmentSql = "INSERT INTO department (name) VALUES (?)";
        jdbcTemplate.update(addDepartmentSql, deptName);

        // 模拟一个错误,事务应该回滚
        if (empAge < 0) {
            throw new RuntimeException("Invalid age");
        }
    }
}

在上面的例子中:

  • @Transactional 注解表示在 addEmployeeAndDepartment 方法执行时,Spring 会自动管理事务。
  • 如果方法正常执行,事务会提交。
  • 如果方法抛出异常,Spring 会自动回滚事务。

配置支持事务管理:

为了让 Spring 管理事务,我们需要在配置文件中启用事务管理器,并且确保 Spring 容器扫描事务管理相关注解。

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 配置事务管理器 -->
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/yourdb"/>
        <property name="username" value="yourusername"/>
        <property name="password" value="yourpassword"/>
    </bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 开启事务管理 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
3.3、编程式事务管理

如果不想使用声明式事务,可以使用编程式事务管理。在这种方式中,我们需要显式地使用 PlatformTransactionManager 来控制事务。

例子:编程式事务管理

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

public class EmpDao {
    private JdbcTemplate jdbcTemplate;
    private PlatformTransactionManager transactionManager;

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

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

    public void addEmployeeAndDepartment(String empName, int empAge, String deptName) {
        // 创建事务定义
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = transactionManager.getTransaction(def);

        try {
            String addEmployeeSql = "INSERT INTO employee (name, age) VALUES (?, ?)";
            jdbcTemplate.update(addEmployeeSql, empName, empAge);

            String addDepartmentSql = "INSERT INTO department (name) VALUES (?)";
            jdbcTemplate.update(addDepartmentSql, deptName);

            // 模拟一个错误,事务应该回滚
            if (empAge < 0) {
                throw new RuntimeException("Invalid age");
            }

            // 提交事务
            transactionManager.commit(status);
        } catch (Exception e) {
            // 回滚事务
            transactionManager.rollback(status);
            throw e; // 抛出异常
        }
    }
}

在编程式事务管理中,我们需要手动创建事务定义(DefaultTransactionDefinition),并使用 PlatformTransactionManager 来开始、提交或回滚事务。

3.4、事务传播行为

事务的传播行为决定了事务在多个方法调用之间如何传播。常见的传播行为有:

  • PROPAGATION_REQUIRED:如果当前没有事务,则新建一个事务;如果已有事务,则加入当前事务(默认行为)。
  • PROPAGATION_REQUIRES_NEW:无论当前是否有事务,都会新建一个事务。
  • PROPAGATION_NESTED:支持事务嵌套。如果当前事务存在,则会在当前事务内开启一个子事务。

我们可以通过设置 @Transactional 注解的 propagation 属性来控制传播行为,例如:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addEmployeeAndDepartmentWithNewTransaction(String empName, int empAge, String deptName) {
    // 新建事务,独立于外部事务
}

X、后记

通过本文的讲解,我们深入了解了 Spring-JDBC 模块如何通过 JdbcTemplate 类,简化 JDBC 操作,并自动处理数据库连接、事务管理和异常翻译等繁琐任务。Spring-JDBC 不仅减轻了开发者的工作量,还能显著提高代码的可读性和可维护性。无论是常见的增删改查操作,还是复杂的事务控制,Spring-JDBC 都提供了极其简洁和灵活的解决方案。掌握这些核心技术,将为开发者带来更高效、可靠的数据库交互体验。希望大家能将本文中的知识点应用到实际开发中,不断优化和提升自己的技术水平。

;