Spring-借助Spring使用JDBC访问关系数据库

目标

构建一个应用程序,使用 Spring 的 JdbcTemplate 来访问存储在关系数据库中的数据。

Spring initializr 配置

https://start.spring.io
添加 JDBC API 和 H2 Database 依赖。

创建 Customer 对象

一个简单的数据访问逻辑,你需要管理用户的姓和名。为了在应用层表示这些数据,创建 Customer 类,代码如下 src/main/java/com/example/relationaldataaccess/Customer.java :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.example.relationaldataaccess;

public class Customer {
private long id;
private String firstName, lastName;

public Customer(long id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}

@Override
public String toString() {
return String.format(
"Customer[id=%d, firstName='%s', lastName='%s']",
id, firstName, lastName);
}

// getters & setters omitted for brevity
}

存储和取回数据

Spring 提供了一个模版类 JdbcTemplate 来简化 SQL 关系数据库和 JDBC 的交互。

大多数 JDBC 代码都陷入资源获取、连接管理、异常处理和一般错误检查中,这与代码的预期目标完全无关。JdbcTemplate 让你无需再去关心这些和你的业务无关的任务。你只需关注你手头的任务即可。下面的代码 src/main/java/com/example/relationaldataaccess/RelationalDataAccessApplication.java 展示了使用 JDBC 存储和取回数据的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.example.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 log = LoggerFactory.getLogger(RelationalDataAccessApplication.class);

public static void main(String args[]) {
SpringApplication.run(RelationalDataAccessApplication.class, args);
}

@Autowired
JdbcTemplate jdbcTemplate;

@Override
public void run(String... strings) throws Exception {

log.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", "Josh 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 -> log.info(String.format("Inserting customer record for %s %s", name[0], name[1])));

// Uses JdbcTemplate's batchUpdate operation to bulk load data
jdbcTemplate.batchUpdate("INSERT INTO customers(first_name, last_name) VALUES (?,?)", splitUpNames);

log.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 -> log.info(customer.toString()));
}
}

Spring Boot 支持 H2(一个内存关系数据库引擎)并自动创建一个连接。因为使用了 spring-jdbc,Spring Boot 自动创建 JdbcTemplate,@Autowired JdbcTemplate 域会自动加载并使其可用。

Application 类 实现了 Spring Boot’s 的 CommandLineRunner 接口,意味着 run() 方法会在 application 上下文加载后运行。

首先,使用 JdbcTemplate 的 execute 方法执行一些 DDL 语句。

其次,将一个字符列表用 Java 8 的 streams 按 firstname/lastname 对分割成 Java 数组,列表中每一项生成一个数组,最后集合成一个对象数组列表。

单个插入语句,使用 insert 即可,多个插入语句,最好使用 batchUpdate
使用 ? 来作为占位绑定变量到 SQL 语句,而不直接使用字符串拼接,可以避免 SQL 注入攻击。

最后,使用 query 方法在表中搜索匹配的记录。再次使用 ? 来占位,调用时传递实际的值到其中。最后一个参数时 Java 8 的 lambda 语句,用来转换每个结果行为一个新的 Customer 对象。

Java 8 lambda 对单个方法接口的映射很不错,如 Spring 的 RowMapper。如果使用的是 Java 7 或者更早版本,可以用一个匿名接口实现来实现。

构建一个可执行的 JAR

使用 Gradle

  • 直接运行 ./gradlew bootRun
  • 构建 JAR 文件 ./gradlew build

使用 Maven

  • 直接运行 ./mvnw spring-boot:run
  • 构建 JAR 文件 ./mvnw clean package

运行 JAR

java -jar target/gs-relational-data-access-0.1.0.jar

输出如下:

1
2
3
4
5
6
7
8
9
10
2022-12-02 21:15:50.835  INFO 78029 --- [           main] c.e.r.RelationalDataAccessApplication    : Creating tables
2022-12-02 21:15:50.836 INFO 78029 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2022-12-02 21:15:50.944 INFO 78029 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2022-12-02 21:15:50.958 INFO 78029 --- [ main] c.e.r.RelationalDataAccessApplication : Inserting customer record for John Woo
2022-12-02 21:15:50.958 INFO 78029 --- [ main] c.e.r.RelationalDataAccessApplication : Inserting customer record for Jeff Dean
2022-12-02 21:15:50.958 INFO 78029 --- [ main] c.e.r.RelationalDataAccessApplication : Inserting customer record for Josh Bloch
2022-12-02 21:15:50.958 INFO 78029 --- [ main] c.e.r.RelationalDataAccessApplication : Inserting customer record for Josh Long
2022-12-02 21:15:50.998 INFO 78029 --- [ main] c.e.r.RelationalDataAccessApplication : Querying for customer records where first_name = 'Josh':
2022-12-02 21:15:51.010 INFO 78029 --- [ main] c.e.r.RelationalDataAccessApplication : Customer[id=3, firstName='Josh', lastName='Bloch']
2022-12-02 21:15:51.010 INFO 78029 --- [ main] c.e.r.RelationalDataAccessApplication : Customer[id=4, firstName='Josh', lastName='Long']
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022 qusong
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信