Spring Boot中使用JDBC Templet的方法教程

网友投稿 290 2023-02-13


Spring Boot中使用JDBC Templet的方法教程

前言

Spring 的 JDBC Templet 是 Spring 对 JDBC 使用的一个基本的封装。他主要是帮助程序员实现了数据库连接的管理,其余的使用方式和直接使用 JDBC 没有什么大的区别。

业务需求

JDBC 的使用大家都比较熟悉了。这里主要为了演示在 SpringBoot 中使用 Spring JDBC Templet 的步骤,所以我们就设计一个简单的需求。一个用户对象的 CURD 的操作。对象有两个属性,一个属性是id,一个属性是名称。存储在 mysql 的 auth_user 表里面。

新建项目和增加依赖

在 Intellij IDEA 里面新建一个空的 SpringBoot 项目。具体步骤参考

Intellij IDEA创建spring-boot项目的图文教程。根据本样例的需求,我们要添加下面三个依赖

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-jdbc

mysql

mysql-connector-java

6.0.6

因为要发布 Http Rest 的服务,所以添加 spring-boot-starter-web 依赖,这里我们要使用 JDBC Tempet 方法来访问数据库,所以添加了 spring-boot-starter-jdbc 依赖,要访问 MySQL 数据库,所以添加了 MySQL 最新版本的 JDBC 驱动程序。

准备数据库环境

假定在 linux 操作系统上已经安装了 MySQL 5.7。以下操作都是在操作系统的命令行中,通过 root 用户登录到 MySQL 的命令行客户端中执行的。

建库建表

create database springboot_jdbc;

create table auth_user (uuid bigint not null,name varchar(32), primary key (uuid)) default charset=utf8mb4;

设定用户权限

grant all privileges on springboot_jdbc.* to 'springboot'@'%' identified by 'springboot';

flush privileges;

配置数据源(连接池)

SpringBoot 的数据源是自动配置的。在 SpringBoot 2.0 中,有几种数据源配置可选,他们按照 HikariCP -> Tomcat pooling -> Commons DBCP2 优先顺序来选择最后实际使用哪个数据源。

在项目加入 spring-boot-starter-jdbc 依赖的时候,就已经包括了 HikariCP 数据源的依赖,所以这里自动配置 HikariCP 连接池数据源。

在 appplications.properties 中增加如下的配置

#通用数据源配置

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.datasource.url=jdbc:mysql://10.110.2.5:3306/spring-boot-jdbc?charset=utf8mb4&useSSL=false

spring.datasource.username=springboot

spring.datasource.password=springboot

# Hikari 数据源专用配置

spring.datasource.hikari.maximum-pool-size=20

spring.datasource.hikari.minimum-idle=5

其中 Hikari 数据源的大部分配置如下图。每个配置代表的含义可以自行查询一下

程序开发

用户数据库实体

根据需求,对应的用户数据实体有两个属性,一个是 id ,一个是 name 。这是一个纯 POJO 对象。

package com.yanggaochao.springboot.learn.springbootjdbclearn.domain.dao;

/**

* 用户实体对象

*

* @author 杨高超

* @since 2018-03-09

*/

public class UserDO {

private Long id;

private String name;

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;

}

}

通用的 Http Rest 返回对象

通常在 Http Rest 接口中,我们不仅想直接返回业务对象的内容,还要返回一些通用的信息,例如接口调用的结果,调用失败的时候返回的自定义文本消息等。那么我们就需要建立两个通用的 rest 返回对象,除了返回通用的接口调用结果和文本消息,一个包括一个单独的业务内容,一个包含一个持有多个业务内容的集合。具体定义如下

单独业务内容返回对象

package com.yanggaochao.springboot.learn.springbootjdbclearn.domain.bo;

/**

* 单个对象返回结果

*

* @author 杨高超

* @since 2018-03-09

*/

public class RestItemResult {

private String result;

private String message;

private T item;

public String getResult() {

return result;

}

public void setResult(String result) {

this.result = result;

}

public String getMessage() {

return message;

}

public void setMessage(String message) {

this.message = message;

}

public T getItem() {

return item;

}

public void setItem(T item) {

this.item = item;

}

}

集合业务内容返回对象

package com.yanggaochao.springboot.learn.springbootjdbclearn.domain.bo;

import java.util.Collection;

/**

* 集合对象返回结果

*

* @author 杨高超

* @since 2018-03-09

*/

public class RestCollectionResult {

private String result;

private String message;

private Collection items;

public String getResult() {

return result;

}

public void setResult(String result) {

this.result = result;

}

public String getMessage() {

return message;

}

public void setMessage(String message) {

this.message = message;

}

public Collection getItems() {

return items;

}

public void setItems(Collection items) {

this.items = items;

}

}

数据持久层开发

用户数据持久层接口定义

package com.yanggaochao.springboot.learn.springbootjdbclearn.dao;

import com.yanggaochao.springboot.learn.springbootjdbclearn.domain.dao.UserDO;

import java.util.List;

/**

* 用户数据层接口

*

* @author 杨高超

* @since 2018-03-09

*/

public interface UserDao {

/**

* 向数据库中保存一个新用户

*

* @param user 要保存的用户对象

* @return 是否增肌成功

*/

Boolean add(UserDO user);

/**

* 更新数据库中的一个用户

*

* @param user 要更新的用户对象

* @return 是否更新成功

*/

Boolean update(UserDO user);

/**

* 删除一个指定的用户

*

* @param id 要删除的用户的标识

* @return 是否删除成功

*/

boolean delete(Long id);

/**

* 精确查询一个指定的用户

*

* @param id 要查询的用户的标识

* @return 如果能够查询到,返回用户信息,否则返回 null

*/

UserDO locate(Long id);

/**

* 通过名称模糊查询用户

*

* @param name 要模糊查询的名称

* @return 查询到的用户列表

*/

List matchName(String name);

}

用户数据持久层实现

package com.yanggaochao.springboot.learn.springbootjdbclearn.dao.impl;

import com.yanggaochao.springboot.learn.springbootjdbclearn.dao.UserDao;

import com.yanggaochao.springboot.learn.springbootjdbclearn.domain.dao.UserDO;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.jdbc.core.JdbcTemplate;

import org.springframework.jdbc.support.rowset.SqlRowSet;

import org.springframework.stereotype.Repository;

import java.util.ArrayList;

import java.util.List;

/**

* 用户对象数据库访问实现类

*

* @author 杨高超

* @since 2018-03-09

*/

@Repository

public class UserDaoJDBCTempletImpl implements UserDao {

private final JdbcTemplate jdbcTemplate;

@Autowired

public UserDaoJDBCTempletImpl(JdbcTemplate jdbcTemplate) {

this.jdbcTemplate = jdbcTemplate;

}

@Override

public Boolean add(UserDO user) {

String sql = "INSERT INTO AUTH_USER(UUID,NAME) VALUES(?,?)";

return jdbcTemplate.update(sql, user.getId(), user.getName()) > 0;

}

@Override

public Boolean update(UserDO user) {

String sql = "UPDATE AUTH_USER SET NAME = ? WHERE UUID = ?";

return jdbcTemplate.update(sql, user.getName(), user.getId()) > 0;

}

@Override

public boolean delete(Long id) {

String sql = "DELETE FROM AUTH_USER WHERE UUID = ?";

return jdbcTemplate.update(sql, id) > 0;

}

@Override

public UserDO locate(Long id) {

String sql = "SELECT * FROM AUTH_USER WHERE UUID=?";

SqlRowSet rs = jdbcTemplate.queryForRowSet(sql, id);

if (rs.next()) {

return generateEntity(rs);

}

return null;

}

@Override

public List matchName(String name) {

String sql = "SELECT * FROM AUTH_USER WHERE NAME LIKE ?";

SqlRowSet rs = jdbcTemplate.queryForRowSet(sql, "%" + name + "%");

List users = new ArrayList<>();

while (rs.next()) {

users.add(generateEntity(rs));

}

return users;

}

private UserDO generateEntity(SqlRowSet rs) {

UserDO weChatPay = new UserDO();

weChatPay.setId(rs.getLong("UUID"));

weChatPay.setName(rs.getString("NAME"));

return weChatPay;

}

}

这里首先用一个注解 @Repository 表示这是一个数据持久层的类,SpringBoot 将自动将这个类实例化。然后在构造函数上增加一个 @Autowired ,SpringBoot 在实例化这个类的时候,会自动将 JDBCTemplet 实例注入到这个类里面。这里 JDBCTemplet 实例是  SpringBoot 根据 applications.properties 中数据源相关的配置自动配置出来的。按照 SpringBoot 自动配置数据源的算法,这里将会配置的数据源是 HikariCP。

剩下的则和普通的 Spring JDBCTemplet 开发一样,通过程序员手动在对象和数据库 SQL 之间进行转换,实现了用户的增加、修改、删除、模糊匹配、精确查询等功能。

数据业务层开发

数据业务层接口定义

package com.yanggaochao.springboot.learn.springbootjdbclearn.service;

import com.yanggaochao.springboot.learn.springbootjdbclearn.domain.dao.UserDO;

import java.util.List;

/**

* 用户服务层接口

*

* @author 杨高超

* @since 2018-03-09

*/

public interface UserService {

UserDO add(UserDO user);

UserDO update(UserDO user);

boolean delete(Long id);

UserDO locate(Long id);

List matchName(String name);

}

数据业务层实现

package com.yanggaochao.springboot.learn.springbootjdbclearn.service.impl;

import com.yanggaochao.springboot.learn.springbootjdbclearn.dao.UserDao;

import com.yanggaochao.springboot.learn.springbootjdbclearn.domain.dao.UserDO;

import com.yanggaochao.springboot.learn.springbootjdbclearn.service.UserService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import java.util.Date;

import java.util.List;

/**

* 用户业务层实现类

*

* @author 杨高超

* @since 2018-03-09

*/

@Service

public class UserServiceImpl implements UserService {

private final UserDao userDao;

@Autowired

public UserServiceImpl(UserDao userDao) {

this.userDao = userDao;

}

@Override

public UserDO add(UserDO user) {

user.setId(new Date().getTime());

if (userDao.add(user)) {

return user;

}

return null;

}

@Override

public UserDO update(UserDO user) {

if (userDao.update(user)) {

return locate(user.getId());

}

return null;

}

@Override

public boolean delete(Long id) {

return userDao.delete(id);

}

@Override

public UserDO locate(Long id) {

return userDao.locate(id);

}

@Override

public List matchName(String name) {

return userDao.matchName(name);

}

}

这里通过一个 @Service 注解声明这个实现类是一个业务层的类。持久层的 UserDao 通过 @Autowired 让 SpringBoot 实例化这个业务层类的时候,自动将对应的持久层类注入到这个业务类中。

这里在增加用户对象的时候,给用户设定标识的时候,简单的用了一个当前时间的毫秒数作为标识。实际开发的过程中,这个地方需要用一个保证全局唯一的机制来保证这个标识不能重复。

对外服务层开发

package com.yanggaochao.springboot.learn.springbootjdbclearn.web;

import com.yanggaochao.springboot.learn.springbootjdbclearn.domain.bo.RestCollectionResult;

import com.yanggaochao.springboot.learn.springbootjdbclearn.domain.bo.RestItemResult;

import com.yanggaochao.springboot.learn.springbootjdbclearn.domain.dao.UserDO;

import com.yanggaochao.springboot.learn.springbootjdbclearn.service.UserService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.*;

import java.util.List;

/**

* 用户 Http Rest 接口

*

* @author 杨高超

* @since 2018-03-09

*/

@RestController

@RequestMapping("api/v1/user")

public class UserApi {

@Autowired

private UserService userService;

@RequestMapping(value = "/add", method = RequestMethod.POST)

public RestItemResult add(@RequestBody UserDO user) {

RestItemResult result = new RestItemResult<>();

user = userService.add(user);

if (user != null) {

result.setItem(user);

result.setResult("success");

} else {

result.setMessage("新增用户失败");

result.setResult("failure");

}

return result;

}

@RequestMapping(value = "/update", method = RequestMethod.POST)

public RestItemResult update(@RequestBody UserDO user) {

RestItemResult result = new RestItemResult<>();

user = userService.update(user);

if (user != null) {

result.setItem(user);

result.setResult("success");

} else {

result.setMessage("修改用户失败");

result.setResult("failure");

}

return result;

}

@RequestMapping(value = "/delete/{uuid}", method = RequestMethod.GET)

public RestItemResult delete(@PathVariable Long uuid) {

RestItemResult result = new RestItemResult<>();

if (userService.delete(uuid)) {

result.setResult("success");

} else {

result.setMessage("删除用户失败");

result.setResult("failure");

}

return result;

}

@RequestMapping(value = "/locate/{uuid}", method = RequestMethod.GET)

public RestItemResult locate(@PathVariable Long uuid) {

RestItemResult result = new RestItemResult<>();

UserDO user = userService.locate(uuid);

if (user != null) {

result.setItem(user);

result.setResult("success");

} else {

result.setMessage("查询用户失败");

result.setResult("failure");

}

return result;

}

@RequestMapping(value = "/match/{name}", method = RequestMethod.GET)

public RestCollectionResult match(@PathVariable String name) {

RestCollectionResult result = new RestCollectionResult<>();

List users = userService.matchName(name);

result.setItems(users);

result.setResult("success");

return result;

}

}

这里 @RestController 用来声明这是一个 Http Rest 接口类。通过类上的 @RequestMapping 和方法上的 @RequestMapping组合形成每个接口的调用路由。方法上的 @RequestMapping 中的 method 属性声明了 http 调用的方法。 @RequestBody 注解自动将 post 数据中的 json 对象转成 POJO 对象。@PathVariable 将 http url 路径中的数据自动转换成为服务方法的参数。

Http Rest 接口测试

测试通过 Apache commons的 HttpClient 来调用 Http Rest 服务。

Http Resst 调用辅助类

package com.yanggaochao.springboot.learn.springbootjdbclearn;

import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;

import org.apache.commons.httpclient.HttpClient;

import org.apache.commons.httpclient.methods.GetMethod;

import org.apache.commons.httpclient.methods.PostMethod;

import org.apache.commons.httpclient.methods.StringRequestEntity;

import org.apache.commons.httpclient.params.HttpMethodParams;

import java.io.BufferedReader;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.Reader;

import java.util.Map;

/**

* @author 杨高超

* @since 2018-03-09

*/

public class HttpClientHelper {

/**

* 用 get 方法发起一个http请求

*

* @param url 要访问的 http 的 url

* @return 访问 http 后得到的回应文本

*/

public String httpGetRequest(String url, Map headers) {

try {

HttpClient httpclient = new HttpClient();

GetMethod method = new GetMethod(url);

method.setRequestHeader("Content-Type", "application/json; charset=utf-8");

method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,

new DefaultHttpMethodRetryHandler(3, false));

if (headers != null) {

for (String key : headers.keySet()) {

method.setRequestHeader(key, headers.get(key));

}

}

int statusCode = httpclient.executeMethod(method);

if (statusCode == 200) {

return parseInputStream(method.getResponseBodyAsStream());

} else {

System.out.println(url + " status = " + statusCode);

}

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

/**

* 用 post 方法发起一个 http 请求

*

* @param url 要访问的 http 的 url

* @param data post 请求中的 data 数据

* @return 访问 http 后得到的回应文本

*/

public String httpPostRequest(String url, String data, Map headers) {

try {

HttpClient httpclient = new HttpClient();

PostMethod method = new PostMethod(url);

method.setRequestHeader("Content-Type",

"application/json;charset=UTF-8");

method.setRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.131 Safari/537.36");

if (headers != null) {

for (String key : headers.keySet()) {

method.setRequestHeader(key, headers.get(key));

}

}

method.setRequestEntity(new StringRequestEntity(data, "json", "utf-8"));

int statusCode = httpclient.executeMethod(method);

if (statusCode == 200) {

return parseInputStream(method.getResponseBodyAsStream());

} else {

System.out.println(url + " status = " + statusCode + parseInputStream(method.getResponseBodyAsStream()));

}

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

/**

* 从 java.io.Reader 中解析文本数据

*

* @param rd java.io.Reader 对象

* @throws Exception 发生错误时抛出异常

*/

private String parseReader(Reader rd) throws Exception {

BufferedReader brd = new BufferedReader(rd);

String line;

StringBuilder respongseContext = new StringBuilder();

while ((line = brd.readLine()) != null) {

respongseContext.append(line).append("\n");

}

//rd.close();

if (respongseContext.length() > 0) {

respongseContext.deleteCharAt(respongseContext.length() - 1);

}

return respongseContext.toString();

}

/**

* 从输入流中解析文本数据

*

* @param is 输入流

* @throws Exception 发生错误时抛出异常

*/

private String parseInputStream(InputStream is) throws Exception {

return parseReader(new BufferedReader(new InputStreamReader(is)));

}

}

这里主要是实现了用 GET 和 POST 方法调用 Http Rest 服务的方法。

测试用例

采用 JUnit 来执行测试用例。为了实现测试,我们额外增加了下面的 maven 依赖

commons-httpclient

commons-httpclient

3.1

test

org.codehaus.jettison

jettison

1.3.3

test

package com.yanggaochao.springboot.learn.springbootjdbclearn;

import org.codehaus.jettison.json.JSONObject;

import org.junit.After;

import org.junit.Before;

import org.junit.Test;

import java.net.URLEncoder;

import java.util.ArrayList;

import java.util.List;

/**

* Description:

*

* @author 杨高超

* @since 2018-03-09

*/

public class UserApiTest {

private String userAddUrl = "http://localhost:3030/security/api/v1/user/add";

private String userLocateUrl = "http://localhost:3030/security/api/v1/user/locate/";

private String userDeleteUrl = "http://localhost:3030/security/api/v1/user/delete/";

private String userUpdateUrl = "http://localhost:3030/security/api/v1/user/update";

private String userMatchUrl = "http://localhost:3030/security/api/v1/user/match/";

JSONObject addUser = new JSONObject();

Long addUserId = null;

List userIds = new ArrayList<>();

@Before

public void before() throws Exception {

addUser.put("name", "美羊羊");

JSONObject addResultJson = new JSONObject(new HttpClientHelper().httpPostRequest(userAddUrl, addUser.toString(), null));

assert ("success".equals(addResultJson.getString("result")));

addUserId = addResultJson.getJSONObject("item").getLong("id");

JSONObject user = new JSONObject();

user.put("name", "喜羊羊");

addResultJson = new JSONObject(new HttpClientHelper().httpPostRequest(userAddUrl, user.toString(), null));

assert ("success".equals(addResultJson.getString("result")));

userIds.add(addResultJson.getJSONObject("item").getLong("id"));

user.put("name", "灰太狼");

addResultJson = new JSONObject(new HttpClientHelper().httpPostRequest(userAddUrl, user.toString(), null));

assert ("success".equals(addResultJson.getString("result")));

userIds.add(addResultJson.getJSONObject("item").getLong("id"));

}

@Test

public void testUpdateUser() throws Exception {

JSONObject user = new JSONObject();

user.put("name", "霉羊羊");

user.put("id", addUserId);

new HttpClientHelper().httpPostRequest(userUpdateUrl, user.toString(), null);

JSONObject locateResultJson = new JSONObject(new HttpClientHelper().httpGetRequest(userLocateUrl + addUserId, null));

assert (user.getString("name").equals(locateResultJson.getJSONObject("item").getString("name")));

}

@Test

public void testMatchUser() throws Exception {

JSONObject matchResultJson = new JSONObject(new HttpClientHelper().httpGetRequest(userMatchUrl + URLEncoder.encode("羊","UTF-8"), null));

assert (matchResultJson.has("items") && matchResultJson.getJSONArray("items").length() == 2);

matchResultJson = new JSONObject(new HttpClientHelper().httpGetRequest(userMatchUrl + URLEncoder.encode("狼","UTF-8"), null));

assert (matchResultJson.has("items") && matchResultJson.getJSONArray("items").length() == 1);

}

@After

public void after() throws Exception {

if (addUserId != null) {

JSONObject deleteResultJson = new JSONObject(new HttpClientHelper().httpGetRequest(userDeleteUrl + addUserId, null));

assert ("success".equals(deleteResultJson.getString("result")));

}

for (Long userId : userIds) {

JSONObject deleteResultJson = new JSONObject(new HttpClientHelper().httpGetRequest(userDeleteUrl + userId, null));

assert ("success".equals(deleteResultJson.getString("result")));

}

}

}

这里在 @Test 声明了两个测试用例,一个测试了用户修改功能,一个测试了用户模糊查询功能。  @Before 声明了在执行每个测试用例之前要做的准备工作。这里首先往数据库中插入三条数据,同时也测试了数据的增加功能、精确查询的功能。@After 声明了执行每个测试用例后的清理工作。这里主要是将之前插入的数据给删除了。这里同步测试了用户删除的功能。

后记

这里就展示了一个完整的 SpringBoot 使用 JDBC Templet 的完整样例。如果有在 Spring 下使用 JDBC Templet 的经历,那么在 Spring 里面主要是减少了很多配置的工作。

本文涉及的代码已经上传到 github 上,大家也可以通过本地下载

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:浏览器接口测试工具(web端接口测试工具)
下一篇:在vue中使用css modules替代scroped的方法
相关文章

 发表评论

暂时没有评论,来抢沙发吧~