# Spring `JdbcTemplate`教程

> 原文： [http://zetcode.com/db/jdbctemplate/](http://zetcode.com/db/jdbctemplate/)

Spring `JdbcTemplate`教程展示了如何使用 Spring 的`JdbcTemplate`处理数据。 我们是 MySQL 数据库。 我们创建使用`JdbcTemplate`的经典 Spring 和 Spring Boot 应用。 ZetCode 拥有用于 MySQL Java 的完整电子书，其中包含扩展的`JdbcTemplate`章节： [MySQL Java 编程电子书](/ebooks/mysqljava/)。

[Tweet](https://twitter.com/share) 

## 目录



1.  [在 MySQL 中创建数据库](#mysql)
2.  [Maven 依赖项](#maven)
3.  [`queryForObject()`方法](#qfo)
4.  [`RowMapper`](#rm)
5.  [`BeanPropertyRowMapper`](#bprm)
6.  [`queryForList()`方法](#qfl)
7.  [使用命名参数](#named)
8.  [使用`JdbcTemplate`的经典 Spring](#cla) 
9.  [使用`JdbcTemplate`的 Spring Boot](#boot) 



## Spring

Spring 是流行的 Java 应用框架。 `JdbcTemplate`是用于简化 JDBC 编程的工具。 它处理乏味且容易出错的底层细节，例如处理事务，清理资源以及正确处理异常。 `JdbcTemplate`包含在 Spring 的`spring-jdbc`模块中。 Spring Boot 是 Spring 的解决方案，用于创建独立的，生产级的基于 Spring 的应用。

MySQL 是一个开源关系数据库管理系统。 它是最受欢迎的数据库之一。 它通常在 Web 应用中使用。

## MySQL 创建数据库

我们使用`mysql`监视器创建一个新的`testdb`数据库。

`cars_mysql.sql`

```java
DROP TABLE IF EXISTS cars;
CREATE TABLE cars(id INT PRIMARY KEY AUTO_INCREMENT,
                  name VARCHAR(255), price INTEGER) ENGINE=InnoDB;

INSERT INTO cars(name, price) VALUES('Audi', 52642);
INSERT INTO cars(name, price) VALUES('Mercedes', 57127);
INSERT INTO cars(name, price) VALUES('Skoda', 9000);
INSERT INTO cars(name, price) VALUES('Volvo', 29000);
INSERT INTO cars(name, price) VALUES('Bentley', 350000);
INSERT INTO cars(name, price) VALUES('Citroen', 21000);
INSERT INTO cars(name, price) VALUES('Hummer', 41400);
INSERT INTO cars(name, price) VALUES('Volkswagen', 21600);

```

这是在 MySQL 中创建`cars`表的 SQL。

要创建数据库和表，我们使用`mysql`监视工具。

```java
$ sudo service mysql start

```

MySQL 用`sudo service mysql start`命令启动。

```java
$ mysql -u user7 -p

```

我们使用`mysql`监视器连接到数据库。

```java
mysql> CREATE DATABASE testdb;
Query OK, 1 row affected (0.02 sec)

```

使用`CREATE DATABASE`语句创建一个新数据库。

```java
mysql> USE testdb;
mysql> SOURCE cars_mysql.sql

```

使用`source`命令，加载并执行`cars_mysql.sql`文件。

```java
mysql> SELECT * FROM cars;
+----+------------+--------+
| id | name       | price  |
+----+------------+--------+
|  1 | Audi       |  52642 |
|  2 | Mercedes   |  57127 |
|  3 | Skoda      |   9000 |
|  4 | Volvo      |  29000 |
|  5 | Bentley    | 350000 |
|  6 | Citroen    |  21000 |
|  7 | Hummer     |  41400 |
|  8 | Volkswagen |  21600 |
+----+------------+--------+
8 rows in set (0.00 sec)

```

我们验证数据。

## Maven 依赖

对于我们的应用，我们需要下载数据库驱动程序和 Spring 模块。 我们使用 Maven 做到这一点。

```java
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.1.3.RELEASE</version>
</dependency>

```

这将下载`spring-jdbc`模块。

```java
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>

```

这是 MySQL 驱动程序的 Maven 依赖关系。

## `queryForObject()`方法

`queryForObject()`方法执行 SQL 查询并返回结果对象。 结果类型在参数中指定。

com/zetcode/SpringDBQueryObjectEx.java

```java
package com.zetcode;

import java.sql.SQLException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;

public class SpringDBQueryObjectEx {

    public static void main(String[] args) throws SQLException {

        var ds = new SimpleDriverDataSource();
        ds.setDriver(new com.mysql.jdbc.Driver());
        ds.setUrl("jdbc:mysql://localhost:3306/testdb");
        ds.setUsername("user7");
        ds.setPassword("s$cret");

        var sql = "SELECT COUNT(*) FROM cars";

        var jtm = new JdbcTemplate(ds);
        int numOfCars = jtm.queryForObject(sql, Integer.class);

        System.out.format("There are %d cars in the table", numOfCars);
    }
}

```

在示例中，我们使用`queryForObject()`方法获取`cars`表中的汽车数量。

```java
var sql = "SELECT COUNT(*) FROM cars";

```

该 SQL 返回`cars`表中的行数。

```java
int numOfCars = jtm.queryForObject(sql, Integer.class);

```

`queryForObject()`方法的第二个参数指定结果的类型； 在我们的案例中是`Integer`。

## 行映射器

`RowMapper`逐行映射结果集的行。 此接口的实现执行将每一行映射到结果对象的实际工作。

com/zetcode/model/Car.java

```java
package com.zetcode.model;

import java.util.Objects;

public class Car {

    private Long id;
    private String name;
    private int price;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Car car = (Car) o;
        return price == car.price &&
                Objects.equals(id, car.id) &&
                Objects.equals(name, car.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, price);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Car{");
        sb.append("id=").append(id);
        sb.append(", name='").append(name).append('\'');
        sb.append(", price=").append(price);
        sb.append('}');
        return sb.toString();
    }
}

```

我们有一个`Car` bean。 它具有`id`，`name`和`price`属性。

com/zetcode/SpringDBRowMapper.java

```java
package com.zetcode;

import java.sql.ResultSet;
import java.sql.SQLException;

import com.zetcode.model.Car;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;

public class SpringDBRowMapper {

    public static void main(String[] args) throws SQLException {

        var ds = new SimpleDriverDataSource();
        ds.setDriver(new com.mysql.jdbc.Driver());
        ds.setUrl("jdbc:mysql://localhost:3306/testdb");
        ds.setUsername("user7");
        ds.setPassword("s$cret");

        var rm = (RowMapper<Car>) (ResultSet result, int rowNum) -> {

            var car = new Car();

            car.setId(result.getLong("id"));
            car.setName(result.getString("name"));
            car.setPrice(result.getInt("price"));

            return car;
        };

        var sql = "SELECT * FROM cars WHERE id=?";
        Long id = 1L;

        var jtm = new JdbcTemplate(ds);
        var car = (Car) jtm.queryForObject(sql, new Object[]{id}, rm);

        System.out.println(car);
    }
}

```

在示例中，我们使用`RowMapper`将结果集的行映射到`Car`对象。

```java
var rm = (RowMapper<Car>) (ResultSet result, int rowNum) -> {

    var car = new Car();
    car.setId(result.getLong("id"));
    car.setName(result.getString("name"));
    car.setPrice(result.getInt("price"));

    return car;
};

```

这是结果集行到`Car`对象的映射。

```java
var car = (Car) jtm.queryForObject(sql, new Object[] {id}, rm);

```

`RowMapper`的实例作为第三个参数传递到`queryForObject()`。

## `BeanPropertyRowMapper`

`BeanPropertyRowMapper`是一种`RowMapper`实现，可将行转换为指定映射目标类的新实例。 映射的目标类必须是顶级类，并且必须具有默认或无参数构造函数。 结果集元数据中的列名与相应属性的公共设置器匹配。

com/zetcode/model/Car.java

```java
package com.zetcode.model;

public class Car {

    private Long id;
    private String name;
    private int price;

    // getters and setters etc.
}

```

这是我们将`cars`表行映射到的`Car` bean。

com/zetcode/SpringBeanPropertyRowMapper.java

```java
package com.zetcode;

import com.zetcode.model.Car;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;

import java.sql.SQLException;

public class SpringBeanPropertyRowMapper {

    public static void main(String[] args) throws SQLException {

        var dataSource = new SimpleDriverDataSource();

        dataSource.setDriver(new com.mysql.jdbc.Driver());
        dataSource.setUrl("jdbc:mysql://localhost:3306/testdb");
        dataSource.setUsername("user7");
        dataSource.setPassword("s$cret");

        var sql = "SELECT * FROM cars WHERE id=?";
        Long id = 1L;

        var jtm = new JdbcTemplate(dataSource);

        var car = (Car) jtm.queryForObject(sql, new Object[]{id},
                 BeanPropertyRowMapper.newInstance(Car.class));

        System.out.println(car);
    }
}

```

该示例连接到`testdb`数据库，并从`cars`表中检索一辆汽车。

```java
var sql = "SELECT * FROM cars WHERE id=?";

```

该 SQL 语句从数据库中选择汽车对象。

```java
var jtm = new JdbcTemplate(dataSource);

```

`JdbcTemplate`已创建； 它以数据源为参数。

```java
var car = (Car) jtm.queryForObject(sql, new Object[]{id},
    BeanPropertyRowMapper.newInstance(Car.class));

```

使用`queryForObject()`方法，我们查询一个对象。 我们提供了 SQL 语句，参数和行映射器。 `BeanPropertyRowMapper`将一行转换为`Car`目标类的新实例。

```java
System.out.println(car);

```

取回的汽车被打印到终端。

```java
Car{id=1, name='Audi', price=52642}

```

应用从`cars`表中打印第一行。

## `queryForList()`方法

`queryForList()`方法执行对结果列表的查询。 在以下示例中，我们从`cars`表中检索所有汽车。

```java
pom.xml
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │               SpringDBQueryForList.java
│   └───resources
│           db.properties
└───test
    └───java

```

这是项目结构。

我们将数据源属性放入`db.properties`文件。 最好将资源与 Java 文件分开。

resources/db.properties

```java
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/testdb
jdbc.username=user7
jdbc.password=s$cret

```

这些是 MySQL 数据库的属性。

com/zetcode/SpringDBQueryForList.java

```java
package com.zetcode;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Driver;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public class SpringDBQueryForList {

    public static void main(String[] args) throws IOException,
            ClassNotFoundException {

        var prop = new Properties();
        prop.load(new FileInputStream("src/main/resources/db.properties"));

        var ds = new SimpleDriverDataSource();

        ds.setDriverClass(((Class<Driver>) Class.forName(prop.getProperty("jdbc.driver"))));
        ds.setUrl(prop.getProperty("jdbc.url"));
        ds.setUsername(prop.getProperty("jdbc.username"));
        ds.setPassword(prop.getProperty("jdbc.password"));

        var sql = "SELECT * FROM cars";

        var jtm = new JdbcTemplate(ds);
        var rows = (List<Map<String, Object>>) jtm.queryForList(sql);

        rows.forEach(System.out::println);
    }
}

```

该示例连接到 MySQL `testdb`数据库，并从`cars`表中检索所有行。

```java
var prop = new Properties();
prop.load(new FileInputStream("src/main/resources/db.properties"));

```

数据源属性是从`db.properties`文件加载的。

```java
var ds = new SimpleDriverDataSource();

ds.setDriverClass(((Class<Driver>) Class.forName(prop.getProperty("jdbc.driver"))));
ds.setUrl(prop.getProperty("jdbc.url"));
ds.setUsername(prop.getProperty("jdbc.username"));
ds.setPassword(prop.getProperty("jdbc.password"));

```

我们用属性填充`SimpleDriverDataSource's`属性。

```java
var jtm = new JdbcTemplate(ds);
var rows = (List<Map<String, Object>>) jtm.queryForList(sql);

```

`JdbcTemplate`的`queryForList()`方法返回表中的行列表。

```java
rows.forEach(System.out::println);

```

我们遍历列表并将数据打印到终端。

```java
{id=1, name=Audi, price=52642}
{id=2, name=Mercedes, price=57127}
{id=3, name=Skoda, price=9000}
{id=4, name=Volvo, price=29000}
{id=5, name=Bentley, price=350000}
{id=6, name=Citroen, price=21000}
{id=7, name=Hummer, price=41400}
{id=8, name=Volkswagen, price=21600}

```

这是示例的输出。

## 使用命名参数

`NamedParameterJdbcTemplate`是具有一组基本 JDBC 操作的模板类，允许使用命名参数而不是传统的`?`占位符。

resources/db.properties

```java
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/testdb
jdbc.username=user7
jdbc.password=s$cret

```

这些是 MySQL 数据库的属性。

com/zetcode/model/Car.java

```java
package com.zetcode.model;

public class Car {

    private Long id;
    private String name;
    private int price;

    // getters and setters etc.
}

```

这是`Car` bean。

com/zetcode/SpringDBNamedParameters.java

```java
package com.zetcode;

import com.zetcode.model.Car;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Driver;
import java.util.Properties;

public class SpringDBNamedParameters {

    public static void main(String[] args) throws IOException,
            ClassNotFoundException {

        var prop = new Properties();
        prop.load(new FileInputStream("src/main/resources/db.properties"));

        var ds = new SimpleDriverDataSource();
        ds.setDriverClass(((Class<Driver>) Class.forName(prop.getProperty("jdbc.driver"))));
        ds.setUrl(prop.getProperty("jdbc.url"));
        ds.setUsername(prop.getProperty("jdbc.username"));
        ds.setPassword(prop.getProperty("jdbc.password"));

        var sql = "SELECT * FROM cars WHERE name LIKE :name";
        var carName = "Volvo";

        var jtm = new NamedParameterJdbcTemplate(ds);

        var namedParams = new MapSqlParameterSource("name", carName);
        var car = (Car) jtm.queryForObject(sql, namedParams,
                BeanPropertyRowMapper.newInstance(Car.class));

        System.out.println(car);
    }
}

```

该示例查找汽车名称； 其 SQL 代码使用命名参数。

```java
var sql = "SELECT * FROM cars WHERE Name LIKE :name";

```

SQL 具有`:name`令牌，这是一个命名参数。

```java
var jtm = new NamedParameterJdbcTemplate(ds);

```

`NamedParameterJdbcTemplate`用于命名参数。

```java
var namedParams = new MapSqlParameterSource("name", carName);

```

`MapSqlParameterSource`用于将参数值的简单映射传递给`NamedParameterJdbcTemplate`类的方法。

```java
var car = (Car) jtm.queryForObject(sql, namedParams,
        BeanPropertyRowMapper.newInstance(Car.class));

```

命名的参数作为第二个参数传递给`queryForObject()`方法。

```java
Car{id=4, name='Volvo', price=29000}

```

这是示例的输出。

## 使用`JdbcTemplate`的经典 Spring 示例

在下面的示例中，我们创建一个经典的命令行 Spring 应用，该应用使用`JdbcTemplate`连接到数据库并发出 SQL 语句。

```java
pom.xml
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           │   ClassicSpringJdbcTemplate.java
│   │           ├───config
│   │           │       DBConfig.java
│   │           ├───model
│   │           │       Car.java
│   │           └───service
│   │                   CarService.java
│   │                   ICarService.java
│   └───resources
│           db.properties
└───test
    └───java

```

这是项目结构。

`pom.xml`

```java
<?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.zetcode</groupId>
    <artifactId>classicspringex</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>

        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <spring-version>5.1.3.RELEASE</spring-version>

    </properties>

    <dependencies>

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

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring-version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring-version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring-version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring-version}</version>
        </dependency>

    </dependencies>

</project>

```

Maven `pom.xml`文件包含 MySQL 驱动程序，核心 Spring 库和 JdbcTemplate 的依赖项。

com/zetcode/model/Car.java

```java
package com.zetcode.model;

public class Car {

    private Long id;
    private String name;
    private int price;

    // getters and setters etc.
}

```

这是`Car` bean。

resources/db.properties

```java
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/testdb
jdbc.username=user7
jdbc.password=s$cret

```

这些是数据库属性。

com/zetcode/config/DBConfig.java

```java
package com.zetcode.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import javax.sql.DataSource;

@Configuration
@PropertySource(value = "classpath:db.properties", ignoreResourceNotFound = true)
public class DBConfig {

    @Autowired
    private Environment env;

    @Bean
    public DataSource dataSource() {

        var dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("jdbc.driver"));
        dataSource.setUrl(env.getProperty("jdbc.url"));
        dataSource.setUsername(env.getProperty("jdbc.username"));
        dataSource.setPassword(env.getProperty("jdbc.password"));

        return dataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate() {

        var template = new JdbcTemplate();
        template.setDataSource(dataSource());

        return template;
    }
}

```

`DBConfig`生成两个 bean：`dataSource`和`jdbcTemplate`。 从`db.properties`中读取数据库属性。

com/zetcode/service/ICarService.java

```java
package com.zetcode.service;

import com.zetcode.model.Car;

import java.util.List;

public interface ICarService {

    Car findById(Long id);
    List<Car> all();
}

```

`ICarService`定义了两种签约方法：`findById()`和`all()`。

com/zetcode/service/CarService.java

```java
package com.zetcode.service;

import com.zetcode.model.Car;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class CarService implements ICarService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public List<Car> all() {

        return jdbcTemplate.query("SELECT * FROM cars",
                BeanPropertyRowMapper.newInstance(Car.class));
    }

    public Car findById(Long id) {

        var sql = "SELECT * FROM cars WHERE id=?";

        return jdbcTemplate.queryForObject(sql, new Object[]{id},
                BeanPropertyRowMapper.newInstance(Car.class));
    }
}

```

`CarService`具有与`JdbcTemplate`一起使用的代码。

```java
@Autowired
private JdbcTemplate jdbcTemplate;

```

Spring 允许使用`@Autowired`注入依赖项。 使用字段注入，我们添加了`DBConfig`中生成的`jdbcTemplate` bean。

com/zetcode/ClassicSpringJdbcTemplate.java

```java
package com.zetcode;

import com.zetcode.model.Car;
import com.zetcode.service.ICarService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan("com.zetcode")
public class ClassicSpringJdbcTemplate {

    public static void main(String[] args) {

        var ctx = new AnnotationConfigApplicationContext(ClassicSpringJdbcTemplate.class);
        var app = ctx.getBean(ClassicSpringJdbcTemplate.class);

        app.run();

        ctx.close();
    }

    @Autowired
    private ICarService carService;

    private void run() {

        System.out.println("Fetching a car with Id 3");
        Long id = 3L;
        var car = (Car) carService.findById(id);
        System.out.println(car);

        System.out.println("Fetching all cars");
        var cars = carService.all();
        cars.forEach(System.out::println);
    }
}

```

该示例使用`JdbcTemplate`从表中检索特定行和所有行。

```java
var ctx = new AnnotationConfigApplicationContext(ClassicSpringJdbcTemplate.class);

```

`AnnotationConfigApplicationContext`允许创建带有特定注释的 Spring bean，例如`@Service`或`@Configuration`。

```java
@Autowired
private CarService carService;

```

数据库功能委托给`CarService`，并随同`@Autowired`一起注入。

```java
private void run() {

    System.out.println("Fetching a car with Id 3");
    Long id = 3L;
    var car = (Car) carService.findById(id);
    System.out.println(car);

    System.out.println("Fetching all cars");
    var cars = carService.all();
    cars.forEach(System.out::println);
}

```

我们调用服务方法来获取特定行并获取所有行。

```java
Fetching a car with Id 3
Car{id=3, name='Skoda', price=9000}
Fetching all cars
Car{id=1, name='Audi', price=52642}
Car{id=2, name='Mercedes', price=57127}
Car{id=3, name='Skoda', price=9000}
Car{id=4, name='Volvo', price=29000}
Car{id=5, name='Bentley', price=350000}
Car{id=6, name='Citroen', price=21000}
Car{id=7, name='Hummer', price=41400}
Car{id=8, name='Volkswagen', price=21600}

```

这是输出。

## 使用`JdbcTemplate`的 Spring Boot 示例

在这个例子中，我们创建一个命令行 Spring Boot 应用，它将使用`JdbcTemplate`连接到数据库。 我们将有两个数据源：一个用于 Derby，一个用于 MySQL。 该项目可在作者的 [Github](https://github.com/janbodnar/Spring-Boot-JdbcTemplate) 页面上找到。

```java
pom.xml
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           │   Application.java
│   │           │   MyRunner.java
│   │           ├───config
│   │           │       AppConfig.java
│   │           ├───model
│   │           │       Car.java
│   │           ├───repository
│   │           │       CarRepository.java
│   │           │       ICarRepository.java
│   │           └───service
│   │                   CarService.java
│   │                   ICarService.java
│   └───resources
│           application.properties
└───test
    └───java

```

这是项目结构。

`pom.xml`

```java
<?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.zetcode</groupId>
    <artifactId>springbootjdbctemplate</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

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

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
    </parent>

    <dependencies>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

```

`pom.xml`文件包含 Spring Boot 和 MySQL 的依赖项。

resources/application.properties

```java
mysql.datasource.driverClassName=com.mysql.jdbc.Driver
mysql.datasource.jdbcUrl=jdbc:mysql://localhost:3306/testdb
mysql.datasource.username=user7
mysql.datasource.password=s$cret

```

在`application.properties`文件中，我们定义 MySQL 数据源。

com/zetcode/config/AppConfig.java

```java
package com.zetcode.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

@Configuration
public class AppConfig {

    @Bean
    @ConfigurationProperties(prefix = "mysql.datasource")
    public DataSource dataSource() {
        return DataSourceBuilder.create().build();
    }
}

```

在`AppConfig`中，我们使用`DataSourceBuilder`创建一个 MySQL 数据源。

com/zetcode/model/Car.java

```java
package com.zetcode.model;

public class Car {

    private Long id;
    private String name;
    private int price;

    // getters and setters etc.
}

```

这是`Car` bean。

com/zetcode/repository/ICarRepository.java

```java
package com.zetcode.repository;

import com.zetcode.model.Car;

import java.util.List;

public interface ICarRepository {

    void saveCar(Car car);
    Car findCarByName(String name);
    List<Car> findAll();
}

```

`ICarRepository`包含以下方法：保存新车，以其名称取回汽车并取回所有汽车。

com/zetcode/repository/CarRepository.java

```java
package com.zetcode.repository;

import com.zetcode.model.Car;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public class CarRepository implements ICarRepository {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void saveCar(Car car) {

        var sql = "INSERT INTO cars(name, price) VALUES (?, ?)";
        Object[] params = new Object[] {car.getName(), car.getPrice()};

        jdbcTemplate.update(sql, params);
    }

    @Override
    public Car findCarByName(String name) {

        var sql = "SELECT * FROM cars WHERE name = ?";
        Object[] param = new Object[] {name};

        return jdbcTemplate.queryForObject(sql, param,
                BeanPropertyRowMapper.newInstance(Car.class));
    }

    @Override
    public List<Car> findAll() {

        var sql = "SELECT * FROM cars";

        return jdbcTemplate.query(sql, BeanPropertyRowMapper.newInstance(Car.class));
    }
}

```

`CarRepository`包含`ICarRepository`合同的实现。 这是与`JdbcTemplate`一起使用的层。

com/zetcode/service/ICarService.java

```java
package com.zetcode.service;

import com.zetcode.model.Car;

import java.util.List;

public interface ICarService {

    Car findByName(String name);
    List<Car> findAll();
}

```

我们有两种合同服务方式。

com/zetcode/service/CarService.java

```java
package com.zetcode.service;

import com.zetcode.model.Car;
import com.zetcode.repository.ICarRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class CarService implements ICarService {

    @Autowired
    private ICarRepository carRepository;

    public Car findByName(String name) {

        return carRepository.findCarByName(name);
    }

    public List<Car> findAll() {

        return carRepository.findAll();
    }
}

```

`CarService`包含`ICarService`合同的实现。 服务方法委托给`ICarRepository`。

com/zetcode/MyRunner.java

```java
package com.zetcode;

import com.zetcode.model.Car;
import com.zetcode.service.ICarService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.stereotype.Component;

@Component
public class MyRunner implements CommandLineRunner {

    @Autowired
    private ICarService carService;

    @Override
    public void run(String... args) {

        try {
            var car = carService.findByName("Citroen");
            System.out.println(car);

        } catch (EmptyResultDataAccessException e) {
            System.out.println("Car was not found");
        }

        var cars = carService.findAll();

        for (Car car: cars) {
            System.out.println(car);
        }
    }
}

```

Spring Boot 命令行应用必须实现`CommandLineRunner`接口。 我们将要执行的代码放入`run()`方法中。

```java
@Autowired
private ICarService carService;

```

Spring 注入`carService` bean。

```java
try {
    var car = carService.findByName("Citroen");
    System.out.println(car);

} catch (EmptyResultDataAccessException e) {
    System.out.println("Car was not found");
}

```

我们试图找到一辆名为雪铁龙的汽车。 如果没有这样的汽车，Spring 会抛出`EmptyResultDataAccessException`异常。

```java
var cars = carService.findAll();

for (Car car: cars) {
    System.out.println(car);
}

```

我们使用`findAll()`方法从数据库中检索所有汽车。 数据被打印到控制台。

com/zetcode/Application.java

```java
package com.zetcode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {

        SpringApplication.run(Application.class, args);
    }
}

```

这是 Spring Boot 应用的入口。

在本教程中，我们介绍了 Spring 的`JdbcTemplate`模块。 我们创建了一个使用`JdbcTemplate`的 Spring Boot 应用。 ZetCode 具有以下相关教程： [Java 教程](/lang/java/)， [Spring `JdbcTemplate`教程](/articles/springjdbctemplate/)， [Spring `EmbeddedDatabaseBuilder`教程](/spring/embeddeddatabasebuilder/)， [EclipseLink 教程](/java/eclipselink/)， [Hibernate Derby 教程](/db/hibernatederby/)， [Servlet FreeMarker `JdbcTemplate`教程](/articles/servletfreemarker/)， [MySQL Java 教程](/db/mysqljava/)和 [PostgreSQL Java 教程](/db/postgresqljavatutorial/)。