本章带你用Spring Boot实现访问关系型数据库。
本文目标
用Spring Boot构建一个应用,使用JdbcTemplate 去访问储存在一个关系型数据库里面的数据。
你需要
- 15分钟左右
- IntelliJ IDEA
- JDK 1.8+
- Maven 3.2+
用Spring Initializr生成项目代码
对于所有的Spring应用,你都可以使用Spring Initializr生成基本的项目代码。Initializr提供了一个快速的方式去引入所有你需要的依赖,并且为你做了很多设置。当前例子需要JDBC API和H2 Database依赖。具体设置如下图:
如上图所示,我们选择了Maven作为编译工具。你也可以选择Gradle来进行编译。然后我们分别把Group和Artifact设置为“com.hansy”和“relational-data-access”。
生成的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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.hansy</groupId>
<artifactId>relational-data-access</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>relational-data-access</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
创建一个Customer对象
我们将要实现的数据存取逻辑就是管理客户的名字和姓氏。为了在应用层表示这些数据,定义一个Customer类,代码如下:
package com.hansy.relationaldataaccess;
public class Customer {
private long id;
private String firstName;
private String lastName;
public Customer(long id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public String toString() {
return "Customer{" +
"id=" + id +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
}
保存和检索数据
Spring提供了一个模板类JdbcTemplate,它把SQL关系数据库和JDBC的调用变得很容易。大部分的JDBC代码都在处理 “资源获取”、“连接管理”、“异常处理”和“一般错误的检查”,这些跟代码原本想要实现的效果没有直接关系的操作。但是JdbcTemplate可以把这些原本繁琐的处理都接管了。你只需要专注于手边的任务。下面的代码将展示怎么通过JDBC来保存和检索数据:
package com.hansy.relationaldataaccess;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@SpringBootApplication
public class RelationalDataAccessApplication implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(RelationalDataAccessApplication.class);
@Autowired
private JdbcTemplate jdbcTemplate;
public static void main(String[] args) {
SpringApplication.run(RelationalDataAccessApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
logger.info("Creating tables");
jdbcTemplate.execute("DROP TABLE customers IF EXISTS");
jdbcTemplate.execute("CREATE TABLE customers (" +
" id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))");
// Split up the array of whole names into an array of first/last names
List<Object[]> splitUpNames = Arrays.asList("John Woo", "Jeff Dean", "Josh Bloch", "Jsoh Long")
.stream().map(name -> name.split(" "))
.collect(Collectors.toList());
// Use a Java 8 stream to print out each tuple of the list
splitUpNames.forEach(name -> logger.info(String.format("Inserting customer record for %s %s", name[0], name[1])));
//Use JdbcTemplate's batchUpdate operation to bulk load data
jdbcTemplate.batchUpdate("INSERT INTO customers(first_name, last_name) VALUES (?,?)", splitUpNames);
logger.info("Querying for customer records where first_name = 'Josh':");
jdbcTemplate.query("SELECT id, first_name, last_name FROM customers WHERE first_name = ?", new Object[]{
"Josh"}
, (rs, rowNum) -> new Customer(rs.getLong("id"), rs.getString("first_name"), rs.getString("last_name")))
.forEach(customer -> logger.info(customer.toString()));
}
}
Spring Boot支持H2(一个内存式关系数据库引擎),并且会自动创建一个连接。因为我们使用spring-jdbc,Spring Boot自动创建一个JdbcTemplate。而 @Autowired JdbcTemplate 字段会自动加载JdbcTemplate,然后提供给我们使用。
上面的Application类实现了Spring Boot的CommandLineRunner接口,所以程序上下文加载之后,就会执行run()方法。
run()方法的流程:
- 使用JdbcTemplate的execute方法来执行一些DDL语句。
- 拿到一个字符串列表,接着使用Java 8的Stream API,把字符串列表分解成“名字/姓氏”这样成对的JAVA数组。
- 使用JdbcTemplate的batchUpdate方法插入一些数据到新创建的customers表里面。batchUpdate方法的第一个参数是SQL语句,第二个参数(Object数组)则是SQL语句中“?”所代表的参数的实际值。(单个插入语句的可以使用JdbcTemplate的insert方法,但是多个插入语句最好使用batchUpdate方法)(使用“?”进行参数绑定可以规避SQL注入攻击)
- 使用query方法从customers表中根据条件查询数据。依然使用“?”进行参数绑定。最后一个参数是一个 Java 8的lambda表达式,把每个结果行转换为一个Customer对象。
本文使用了Java 8的lambda表达式对Spring的RowMapper进行了映射。如果你使用Java 7或者更早的版本,你可以使用一个匿名的接口实现类,方法体可以直接使用lambda表达式里面的内容。
运行应用
运行程序,你可以看到如下结果:
2020-07-18 10:58:13.663 INFO 17436 --- [ main] c.h.r.RelationalDataAccessApplication : Creating tables
2020-07-18 10:58:13.675 INFO 17436 --- [ main] c.h.r.RelationalDataAccessApplication : Inserting customer record for John Woo
2020-07-18 10:58:13.675 INFO 17436 --- [ main] c.h.r.RelationalDataAccessApplication : Inserting customer record for Jeff Dean
2020-07-18 10:58:13.675 INFO 17436 --- [ main] c.h.r.RelationalDataAccessApplication : Inserting customer record for Josh Bloch
2020-07-18 10:58:13.675 INFO 17436 --- [ main] c.h.r.RelationalDataAccessApplication : Inserting customer record for Josh Long
2020-07-18 10:58:13.684 INFO 17436 --- [ main] c.h.r.RelationalDataAccessApplication : Querying for customer records where first_name = 'Josh':
2020-07-18 10:58:13.692 INFO 17436 --- [ main] c.h.r.RelationalDataAccessApplication : Customer{id=3, firstName='Josh', lastName='Bloch'}
2020-07-18 10:58:13.693 INFO 17436 --- [ main] c.h.r.RelationalDataAccessApplication : Customer{id=4, firstName='Josh', lastName='Long'}
小结
你已经用Spring Boot开发了一个简单的JDBC客户端。