欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-04-14 15:03:11
前言 spring 的 jdbc templet 是 spring 对 jdbc 使用的一个基本的封装。他主要是帮助程序员实现了数据库连接的管理,其余的使用方式和直接...

前言

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 数据源的大部分配置如下图。每个配置代表的含义可以自行查询一下

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

程序开发

用户数据库实体

根据需求,对应的用户数据实体有两个属性,一个是 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 上,大家也可以通过

总结

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