Skip to content

Spring教程 - 10 JdbcTemplate

在 Java 教程中,我们介绍了使用 JDBC 去操作数据库,但是整体而言,操作起来是比较麻烦的。

JdbcTemplate 是 Spring 框架提供的一个核心 JDBC 辅助类,它简化了传统 JDBC 的复杂操作,可以帮我们处理连接获取、语句创建、结果集遍历和资源释放等工作,让我们可以专注于 SQL 查询和业务逻辑。

下面就来介绍 JdbcTemplate的使用。


10.1 准备工作

1 准备数据库

首先准备一下数据库,我们这里就使用 MySQL 数据库了。

首先创建数据库和表:

sql
-- 创建数据库
CREATE DATABASE foooor_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 创建一张用户表
CREATE TABLE tb_user (
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
    username VARCHAR(50) NOT NULL COMMENT '用户名',
    balance INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '余额',
    create_time DATETIME NOT NULL COMMENT '创建时间'
);

数据库准备好了!


2 准备项目环境

  1. 首先创建一个空的 Maven 项目。在 pom.xml 中引入相应的依赖。

这里主要引入 spring-jdbc 的依赖,用来操作数据库。

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.foooor</groupId>
    <artifactId>hello-spring</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>


    <dependencies>
        <!-- 引入spring核心依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.2.9</version>
        </dependency>

        <!-- 引入spring jdbc依赖,用于数据库操作 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>6.2.9</version>
        </dependency>

        <!-- 引入junit5依赖 -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.13.4</version>
        </dependency>

        <!-- 引入spring 对 junit的支持依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>6.2.11</version>
            <scope>test</scope>
        </dependency>

        <!-- 引入logback依赖,日志框架实现 -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.5.18</version>
        </dependency>

        <!-- 引入slf4j依赖,日志框架接口门面 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>2.0.17</version>
        </dependency>

        <!-- 引入mysql驱动依赖,用于连接mysql数据库 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>

        <!-- 引入druid依赖,提供数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.27</version>
        </dependency>
    </dependencies>
</project>
  • 引入后,记得右键 --> Maven--> Reload project

  1. 创建数据库配置文件

为了方便管理,单独为数据库配置信息创建一个配置文件。

在项目的 resources 目录下创建 jdbc.properties 文件,配置数据的连接信息,如下:

properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/foooor_db?useSSL=false&serverTimezone=UTC
jdbc.username=root
jdbc.password=123456

  1. 创建Spring配置类

创建包,并在包下创建配置类,和前面以注解的方式使用 Spring IoC 的操作一样的。

我这里新建了 com.foooor.hellospring.config 包,并在其下创建 SpringConfig.java (名称自定义)

java
package com.foooor.hellospring.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

@Configuration  // 配置类
@ComponentScan("com.foooor.hellospring")  // 开启组件扫描,并指定扫描的包
@PropertySource("classpath:jdbc.properties")  // 加载外部配置文件
public class SpringConfig {

    @Value("${jdbc.driver}")  // 读取从配置文件注入的属性值。
    private String driver;

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    /**
     * 配置 Druid 数据源
     */
    @Bean
    public DataSource dataSource() {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        return ds;
    }

    /**
     * 配置 JdbcTemplate
     * 方法的参数,Spring IoC 容器会自动注入对应的 Bean
     */
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}
  • 首先在类上添加各种注解,包括配置类表示、配置Spring扫描的路径、和外部配置文件的路径;
  • 然后在配中配置 Druid 的数据源,通过 @Bean 注解创建;
  • 在配置类中创建 JdbcTemplateJdbcTemplate 依赖 DataSource ,我们DataSource 作为 jdbcTemplate 方法的参数即可,这样 Spring IoC 容器会自动帮我们注入

  1. 准备数据库实体类

我们查询出数据库的数据,都需要封装为对象进行操作,所以我们一般都需要创建一个 Java 实体类与数据库中的表进行对应。

所以这里创建一个 User.java 实体类,用于对查询到的数据进行封装:

java
package com.foooor.hellospring.pojo;

import java.util.Date;

public class User {

    private String id;
    private String username;
    private Integer balance;
    private Date createTime;

    // ...getters and setter 略

    /**
     * 重写 toString 方法,方便打印输出
     */
    @Override
    public String toString() {
        return "User{" +
                "id='" + id + '\'' +
                ", username='" + username + '\'' +
                ", balance='" + balance + '\'' +
                ", createTime=" + createTime +
                '}';
    }
}
  • 在查询数据的时候,我们会将数据库的每一条数据封装成一个对象。

准备工作做完了,现在演示一下 JdbcTemplate 的使用。

10.2 JdbcTemplate实现CRUD

下面就使用 JdbcTemplate 实现增删改查。

我们直接编写测试方法进行测试。

1 增删改

增删改放在一起,因为它们调用的方法是一样的。

首先看一下新增数据:

java
package com.foooor.hellospring;

import com.foooor.hellospring.config.SpringConfig;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import java.util.Date;

@SpringJUnitConfig(classes = {SpringConfig.class})  // 指定 Spring 配置文件
public class JdbcTest {

    private static final Logger logger = LoggerFactory.getLogger(JdbcTest.class);

    @Autowired
    private JdbcTemplate jdbcTemplate;

    /**
     * 测试插入用户
     */
    @Test
    public void testInsert() {
        // 1.准备sql
        String sql = "insert into tb_user (username, balance, create_time) values (?, ?, ?)";
        // 2.执行更新
        int rows = jdbcTemplate.update(sql, "zhangsan", 100, new Date());
        logger.info("----rows:{}", rows);
    }
}
  • 使用起来很简单,首先准备 sql语句,然后调用 update 方法,传递 sql语句 和 参数即可。
  • 上面用到的 update() 方法第一个参数是 sql 语句,后面的是可变参数,传递任意个,需要和 sql 占位符对应。或者传递一个 Object 数组也可以。

下面是修改和删除,和新增是一样的,只是 sql 不同而已,也是调用 update() 方法:

java
/**
 * 测试更新用户
 */
@Test
public void testUpdate() {
    // 1.准备sql
    String sql = "update tb_user set balance = balance + ? where id = ?";
    // 2.执行更新,金额加100
    int rows = jdbcTemplate.update(sql, 100, 5);
    logger.info("----rows:{}", rows);
}

/**
 * 测试删除用户
 */
@Test
public void testDelete() {
    // 1.准备sql
    String sql = "delete from tb_user where id = ?";
    // 2.执行更新
    int rows = jdbcTemplate.update(sql, 5);
    logger.info("----rows:{}", rows);
}
  • 调用的时候,注意传递的参数需要和占位符对应。

和操作 JDBC 比起来,可以看到使用起来非常简单。


2 查询

单独介绍一下查询,操作有一些不同。

查询方式也有多种:

  • 查询单个用户
  • 查询用户列表
  • 查询单个数据,例如查询条目数或最大值。

先从查询单个用户开始,编写测试查询的方法如下:

java
/**
 * 根据id查询用户
 */
@Test
public void testSelect() {
    // 1.准备sql
    String sql = "select * from tb_user where id = ?";

    // 2.执行查询
    User user = jdbcTemplate.queryForObject(sql, new RowMapper<User>() {
        @Override
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            User user = new User();
            user.setId(rs.getString("id"));
            user.setUsername(rs.getString("username"));
            user.setBalance(rs.getInt("balance"));
            user.setCreateTime(rs.getTimestamp("create_time"));
            return user;
        }
    }, 1);  //查询id为1的用户

    logger.info("----user:{}", user);
}
  • 查询单个用户使用 queryForObject 方法,第一个参数是 sql;第二个参数是一个接口,我们可以创建一个匿名实现类传入,第二个参数主要是获取数据库每条记录的信息,封装成一个对象返回;第三个以及后面的参数是 sql 中占位符对应的参数。
  • 我们只需要将数据库的字段和类属性映射即可。
  • 需要注意,查询不到用户会报错,如果需要可以捕获 EmptyResultDataAccessException (没有找到)和 IncorrectResultSizeDataAccessException(查询到多个) 异常。

上面的写法已经比原生的 JDBC 要简单很多了,Spring 还帮我们封装了自动映射的类 BeanPropertyRowMapper ,使用它可以帮我们自动映射。

举个栗子:

java
/**
 * 根据id查询用户
 */
@Test
public void testSelect2() {
    // 1.准备sql
    String sql = "select * from tb_user where id = ?";

    // 2.执行查询
    User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), 1);

    logger.info("----user:{}", user);
}
  • new BeanPropertyRowMapper<>(User.class) 会帮我们把数据库的字段和 User.class 进行映射。
  • 但是需要注意,数据库字段和类的属性需要遵守下划线转驼峰,例如 create_timecreateTime

是不是非常的简单。


上面是查询单个对象,下面介绍一下列表查询。

和查询单个对象基本是一样的,只是使用的方法不同,使用 query 方法,参数都是一样的,如下:

java
/**
 * 方式一
 * 根据条件查询用户列表
 */
@Test
public void testSelect3() {
    // 1.准备sql
    String sql = "select * from tb_user where username like ?";

    // 2.执行查询
    List<User> userList = jdbcTemplate.query(sql, new RowMapper<User>() {
        @Override
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            User user = new User();
            user.setId(rs.getString("id"));
            user.setUsername(rs.getString("username"));
            user.setBalance(rs.getInt("balance"));
            user.setCreateTime(rs.getTimestamp("create_time"));
            return user;
        }
    }, "li%");  // 查询li开头的用户名

    logger.info("----user:{}", userList);
}

/**
 * 方式二
 * 根据条件查询用户列表
 */
@Test
public void testSelect4() {
    // 1.准备sql
    String sql = "select * from tb_user where username like ?";

    // 2.执行查询, 查询li开头的用户名
    List<User> userList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class), "li%");

    logger.info("----user:{}", userList);
}
  • 和查询单个对象基本相同,也可以使用 BeanPropertyRowMapper
  • 如果查询没有条件,可以不传递参数。

查询单个数据,例如查询所有用户的数量:

java
/**
 * 查询指定条件的用户数量
 */
@Test
public void testSelect5() {
    // 1.准备sql
    String sql = "select count(*) from tb_user where username like ?";

    // 2.执行查询
    Integer count = jdbcTemplate.queryForObject(sql, Integer.class, "%li%");

    logger.info("----用户数量:{}", count);
}
  • 上面查询的是名称中包含 li 的用户数量,如果没有条件,可以不传递参数。
  • 第二个参数是返回结果的类型,我们查询的是条目数量,可以传递 Integer.classLong.class

使用 Spring JdbcTemplate 比使用基础的 JDBC 操作起来要方便很多,操作简单,好评。

内容未完......