图源:简书 (jianshu.com)
上篇文章从零开始 Spring Boot 2:处理请求 - 魔芋红茶’s blog (icexmoon.cn)介绍了如何接收和处理HTTP请求,这篇文章将介绍如何连接数据库,并通过数据库来存储和读取数据。
在介绍使用Spring Boot框架使用数据库前,需要先有一个数据库才行,这里推荐使用MySQL。其安装方式是多种多样的,我是通过XAMPP安装的,相关内容可以阅读PHP开发环境(XAMPP+XDebug+VSCode)搭建 - 魔芋红茶’s blog (icexmoon.cn)。
还推荐安装一个数据库客户端,这样操作和查看数据库更方便,对于MySQL,我一直使用的是SQLyog。可以通过下边的连接下载安装一个:
下面示例会使用一个user
表,这里给出DDL语句:
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`name` varchar(10) NOT NULL COMMENT '姓名',
`age` int(11) NOT NULL COMMENT '年龄',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1
数据库名称我使用的是test
。
下面我们将一步步在上篇文章那个简单Web应用的基础上添加上对数据库的支持。
可以从learn_spring_boot获取上篇文章的Web应用示例代码。
首先我们需要在Maven配置中添加对JDBC和MySQL的支持。其中JDBC是一个在数据库驱动之上的抽象层,Java可以通过JDBC来统一地访问各种类型的数据库。因此无论是使用何种数据库,都应当添加对JDBC的支持。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
要连接数据库,还需要在Spring Boot
的配置文件application.properties
中添加数据库IP、帐号密码等相关信息:
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
我这里的密码是空,因为本地测试库没有设置密码。
为了让代码更灵活,这里我们需要在实体层和Controller
之间创建一个“中间层”Service
,用于对Controller
提供基础的数据读写功能。
一些结构简单的语言,如PHP,其实会将
Service
层的代码写入Model
层。
下面是UserService
的完整代码:
package cn.icexmoon.my_first_app.service;
import cn.icexmoon.my_first_app.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Service;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
@Service
public class UserService {
@Autowired
JdbcTemplate jdbcTemplate;
public long addUser(User user){
String sql = "INSERT INTO `user` (`name`, `age`) VALUES (?, ?)";
PreparedStatementCreator preparedStatementCreator = new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
PreparedStatement ps = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps.setString(1,user.getName());
ps.setInt(2, user.getAge());
return ps;
}
};
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(preparedStatementCreator, keyHolder);
return keyHolder.getKey().longValue();
}
public void deleteUser(long id) {
jdbcTemplate.update("delete from user where id=?", id);
}
public List<User> getUsers() {
List<User> users = jdbcTemplate.query("SELECT `id`,`name`,`age` FROM USER", new BeanPropertyRowMapper<>(User.class));
return users;
}
public User getUser(long id) {
String sql = String.format("select `id`,`name`,`age` from user where id=%d",id);
User user;
try{
user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class));
}
catch (EmptyResultDataAccessException e){
return null;
}
return user;
}
public void updateUser(User user){
String sql = String.format("UPDATE `user` SET`name`='%s',`age`='%d' WHERE id=%d",user.getName(),user.getAge(),user.getId());
jdbcTemplate.update(sql);
}
}
Spring Boot的Service层类使用@Service
注解。这里使用了一个基础的JDBC连接类JdbcTemplate
,它可以提供基础的数据库操作。
Spring Boot最优秀的地方在于,我们不需要编写数据库连接和断开的相关处理代码,只需要为JdbcTemplate
添加一个@Autowired
注解,框架就会对其正确初始化,并在需要时连接数据库,在不需要时断开数据库。
JdbcTemplate
提供这么几种基本方法对数据库进行操作:
JdbcTemplate.update
,执行数据变更SQL,如update
、delete
等。JdbcTemplate.query
,执行数据查询SQL,即select
。JdbcTemplate.exec
,执行DDL语句,如create table
。具体的实现这里就不细说了,比较简单,看示例即可。值得一说的有:
PreparedStatementCreator
匿名类,并用它返回一个可以处理SQL语句的PreparedStatement
对象。最后再使用一个KeyHolder
对象来保存生成的自增主键值。queryForObject
可以查询单条数据,并以实体类的方式返回,但在查询不到结果时会抛出一个EmptyResultDataAccessException
异常,这里对其进行捕获,并返回null
。剩余的工作相对简单,只要在UserController
中去除内存中Map
及相关代码,然后改为通过UserService
获取数据并返回即可:
package cn.icexmoon.my_first_app.controller;
import cn.icexmoon.my_first_app.model.Result;
import cn.icexmoon.my_first_app.model.User;
import cn.icexmoon.my_first_app.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
UserService userService;
@PostMapping("")
public String addUser(@RequestBody User user) {
long newId = userService.addUser(user);
Result result = new Result();
result.setData(newId);
return result.toString();
}
@DeleteMapping("/{id}")
public Result deleteUser(@PathVariable long id) {
Result result = new Result();
User user = userService.getUser(id);
if (user == null) {
result.setSuccess(false);
result.setMsg("no this person.");
return result;
}
userService.deleteUser(id);
result.setMsg("delete success.");
return result;
}
@PutMapping("/{id}")
public Result updateUser(@PathVariable long id, @RequestBody User user) {
Result result = new Result();
User u = userService.getUser(id);
if (u == null) {
result.setSuccess(false);
result.setMsg("no this person.");
return result;
}
user.setId(id);
userService.updateUser(user);
return result;
}
@GetMapping("/{id}")
public Result getUser(@PathVariable long id) {
Result result = new Result();
User user = userService.getUser(id);
if (user == null) {
result.setSuccess(false);
result.setMsg("no this person.");
return result;
}
result.setData(user);
return result;
}
@GetMapping("")
public String getUsers() {
Result result = new Result();
result.setData(userService.getUsers());
return result.toString();
}
}
这里同样以@Autowired
注解的方式引入UserService
,并不需要我们自行对其初始化。
好了,现在再试试通过HTTP客户端模拟访问,即使应用重启数据也依然存在。
谢谢阅读。
本篇文章的最终代码可以从learn_spring_boot(github.com)获取。