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项目的图文教程。根据本样例的需求,我们要添加下面三个依赖
<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-jdbc</artifactid> </dependency> <dependency> <groupid>mysql</groupid> <artifactid>mysql-connector-java</artifactid> <version>6.0.6</version> </dependency>
因为要发布 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<t> { 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<t> { private string result; private string message; private collection<t> 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<t> getitems() { return items; } public void setitems(collection<t> 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<userdo> 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<userdo> matchname(string name) { string sql = "select * from auth_user where name like ?"; sqlrowset rs = jdbctemplate.queryforrowset(sql, "%" + name + "%"); list<userdo> 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<userdo> 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<userdo> 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<userdo> add(@requestbody userdo user) { restitemresult<userdo> 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<userdo> update(@requestbody userdo user) { restitemresult<userdo> 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<userdo> delete(@pathvariable long uuid) { restitemresult<userdo> 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<userdo> locate(@pathvariable long uuid) { restitemresult<userdo> 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<userdo> match(@pathvariable string name) { restcollectionresult<userdo> result = new restcollectionresult<>(); list<userdo> 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<string, string> 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<string, string> 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 依赖
<dependency> <groupid>commons-httpclient</groupid> <artifactid>commons-httpclient</artifactid> <version>3.1</version> <scope>test</scope> </dependency> <dependency> <groupid>org.codehaus.jettison</groupid> <artifactid>jettison</artifactid> <version>1.3.3</version> <scope>test</scope> </dependency>
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<long> 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 上,大家也可以通过
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。