本文介绍 Spring Boot 2 使用 JdbcTemplate 操作数据库的方法。
目录
-
JdbcTemplate
简介 - 开发环境
- 基础示例
- 总结
JdbcTemplate
简介
JdbcTemplate
是 Spring JDBC 包中的核心类,处理资源的创建和释放,并帮助处理常见错误,如忘记关闭连接。JdbcTemplate
执行 JDBC 核心流程中的基础任务,如创建和执行 Statement
。
开发环境
- Oracle JDK 1.8.0_201
- Apache Maven 3.6.0
- IntelliJ IDEA (Version 2018.3.3)
- MySQL 5.6.38
基础示例
- 创建存储用户信息的数据表。
CREATE TABLE `user` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT ,
`name` varchar(16) CHARACTER SET utf8 NOT NULL COMMENT '姓名' ,
`age` int(3) UNSIGNED NOT NULL COMMENT '年龄' ,
`email` varchar(32) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '电子邮箱' ,
PRIMARY KEY (`id`)
)
DEFAULT CHARACTER SET=utf8
COMMENT='用户信息';
创建 Spring Boot 工程,参考:IntelliJ IDEA 创建 Spring Boot 工程。
在生成的
pom
文件中添加以下依赖:
-
spring-boot-starter-jdbc
:Spring JDBC 支持 -
mysql-connector-java
:MySQL 数据库驱动 -
junit
:单元测试
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.4.RELEASE
tutorial.spring.boot
spring-boot-jdbc
0.0.1-SNAPSHOT
spring-boot-jdbc
Spring Boot JdbcTemplate
1.8
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-jdbc
mysql
mysql-connector-java
5.1.47
junit
junit
4.12
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
- 在
application.yml
中添加数据源配置。
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&useSSL=true
username: root
password: 123456
- 编写映射数据表的领域模型类。
package tutorial.spring.boot.jdbc.domain;
public class User {
/**
* 数据库表 user 列名
*/
public static class ColumnConstant {
public static final String ID = "id";
public static final String NAME = "name";
public static final String AGE = "age";
public static final String EMAIL = "EMAIL";
}
private Long id;
private String name;
private Integer age;
private String email;
public User(String name, Integer age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
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 Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
User user = (User) o;
if (name != null ? !name.equals(user.name) : user.name != null) {
return false;
}
if (age != null ? !age.equals(user.age) : user.age != null) {
return false;
}
return email != null ? email.equals(user.email) : user.email == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (age != null ? age.hashCode() : 0);
result = 31 * result + (email != null ? email.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
'}';
}
}
- 编写 DAO(Data Access Object)层接口。
package tutorial.spring.boot.jdbc.dao;
import tutorial.spring.boot.jdbc.domain.User;
import java.sql.SQLException;
import java.util.List;
public interface IUserDao {
/**
* 查询用户总数
*
* @return 记录总数
*/
int count() throws SQLException;
/**
* 根据 ID 删除用户
*
* @param id 用户ID,对应主键
* @return 删除记录数目
*/
int delete(long id);
/**
* 新增用户
*
* @param user 封装用户信息的User对象
* @return 插入记录主键
*/
long insert(User user) throws SQLException;
/**
* 批量新增用户
*
* @return 新增记录总数
*/
int insertBatch(List users);
/**
* 根据 ID 查询用户
*
* @param id 用户ID,对应主键
* @return 查询到的用户记录,如无对应记录则返回 null
*/
User select(long id);
/**
* 查询全部用户
*
* @return 封装用户信息的User对象列表
*/
List selectAll();
/**
* 更新用户
*
* @param user 封装用户信息的User对象
* @return 更新记录数
*/
int update(User user);
}
- 编写 DAO(Data Access Object)层实现。
package tutorial.spring.boot.jdbc.dao.impl;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
import tutorial.spring.boot.jdbc.dao.IUserDao;
import tutorial.spring.boot.jdbc.domain.User;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@Repository
public class UserDaoImpl implements IUserDao {
private final JdbcOperations jdbcOperations;
public UserDaoImpl(JdbcOperations jdbcOperations) {
this.jdbcOperations = jdbcOperations;
}
@Override
public int count() throws SQLException {
String sql = "SELECT COUNT(*) FROM user";
Integer count = jdbcOperations.queryForObject(sql, Integer.class);
if (Objects.isNull(count)) {
throw new SQLException();
}
return count;
}
@Override
public int delete(long id) {
String sql = "DELETE FROM user WHERE id=?";
return jdbcOperations.update(sql, id);
}
@Override
public long insert(User user) throws SQLException {
if (Objects.isNull(user)) {
throw new IllegalArgumentException("Param[user] is null!");
}
final String sql = "INSERT INTO user ("
+ User.ColumnConstant.NAME + ", "
+ User.ColumnConstant.AGE + ", "
+ User.ColumnConstant.EMAIL + ") VALUES (?, ?, ?)";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcOperations.update(connection -> {
PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
preparedStatement.setString(1, Objects.isNull(user.getName()) ? "Anonymous User" : user.getName());
preparedStatement.setInt(2, Objects.isNull(user.getAge()) ? -1 : user.getAge());
preparedStatement.setString(3, Objects.isNull(user.getEmail()) ? "" : user.getEmail());
return preparedStatement;
}, keyHolder);
if (Objects.isNull(keyHolder.getKey())) {
throw new SQLException();
}
return keyHolder.getKey().longValue();
}
@Override
public int insertBatch(List users) {
List
- 编写单元测试。
package tutorial.spring.boot.jdbc.dao;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import tutorial.spring.boot.jdbc.domain.User;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class UserDaoTest {
@Autowired
private IUserDao userDao;
@Test
public void testNotNull() {
Assert.assertNotNull(userDao);
}
@Test
public void testCount() throws SQLException {
int count = userDao.count();
Assert.assertTrue(count >= 0);
userDao.insert(new User("Tom", 32, "[email protected]"));
userDao.insert(new User("Lily", 16, "[email protected]"));
userDao.insert(new User("James", 28, "[email protected]"));
Assert.assertEquals(count + 3, userDao.count());
}
@Test
public void testDelete() throws SQLException {
long id = userDao.insert(new User("Harry", 12, "[email protected]"));
Assert.assertEquals(1, userDao.delete(id));
}
@Test
public void testInsert() throws SQLException {
int count = userDao.count();
User user = new User("Jack", 18, "[email protected]");
long id = userDao.insert(user);
Assert.assertTrue(id > 0);
Assert.assertEquals(count + 1, userDao.count());
User result = userDao.select(id);
Assert.assertEquals(user, result);
}
@Test
public void testInsertBatch() {
List users = Arrays.asList(new User("Tom", 32, "[email protected]"),
new User("Lily", 16, "[email protected]"),
new User("James", 28, "[email protected]"),
new User("Jack", 18, "[email protected]"));
Assert.assertEquals(4, userDao.insertBatch(users));
List allUsers = userDao.selectAll();
users.forEach(user -> Assert.assertTrue(allUsers.contains(user)));
}
@Test
public void testSelect() throws SQLException {
User user = new User("Lily", 16, "[email protected]");
long id = userDao.insert(user);
User result = userDao.select(id);
Assert.assertNotNull(result);
Assert.assertEquals(user, result);
}
@Test
public void testSelectAll() throws SQLException {
int count = userDao.count();
List users = Arrays.asList(new User("Tom", 32, "[email protected]"),
new User("Lily", 16, "[email protected]"),
new User("James", 28, "[email protected]"),
new User("Jack", 18, "[email protected]"),
new User("Harry", 12, "[email protected]"));
Assert.assertEquals(5, userDao.insertBatch(users));
List allUsers = userDao.selectAll();
Assert.assertTrue(allUsers.size() > 0);
Assert.assertEquals(count + 5, userDao.count());
users.forEach(user -> Assert.assertTrue(allUsers.contains(user)));
}
@Test
public void testUpdate() throws SQLException {
User user = new User("Jack", 18, "[email protected]");
long id = userDao.insert(user);
user.setId(id);
User result1 = userDao.select(id);
Assert.assertEquals(user, result1);
user.setName("Changed");
user.setAge(19);
user.setEmail("[email protected]");
Assert.assertEquals(1, userDao.update(user));
User result2 = userDao.select(id);
Assert.assertNotEquals(result1, result2);
Assert.assertEquals(user, result2);
}
}
执行过程略。
总结
- 虽然本文标题说明介绍
JdbcTemplate
,但是代码中注入的是JdbcOperations
对象,JdbcOperations
是JdbcTemplate
实现的接口,满足接口编程原则; -
org.springfamework.jdbc.datasource.embedded
包支持使用 Java 数据库引擎创建嵌入式数据库,如HSQL
,H2
,Derby
,本文演示更贴近于实际生产环境的MySQL
; - Spring Boot 默认支持
HikariDataSource
数据源,也支持很多其它第三方数据源(后续介绍)