Java后端爱上SpringBoot 第一节:一个简单的增删改查
PS:我的是后端开发工程师,本片博客是后端做的符合restful api的增删改查接口
SpringBoot的魅力
SpringBoot大大的 简化了后端开发,用过SpringMVC的朋友都知道,大量的Spring配置文件烦的一笔,本文讲述了Spring Boot的应用和一些后端开发的东西。说实话,这样的简单开发过程会慢慢的被代码生成工具和AI写代码所替代,大量的程序员将要失业啦!
一些链接
本人用的SpringBoot 2.1.1版本,官网文档先贴上:
链接: Spring Boot 2.1.1 官网文档.
本文标签
- SpringBoot
- Spring-Data-Jpa
- Swagger
- MySql
开发工具
有的人喜欢用IDEA有些人喜欢用Eclipse,关于工具的选择和自身环境有关,比如:公司的人都在用Eclipse,你用IDEA,别人的项目拿过来会有坑,你的项目拿过去别人不能用。我的使用的是Eclipse+SpringBoot插件。
插件链接奉上: Spring Boot Eclipse插件(Spring Tool Suite).
Begin Hello World
废话不说,装好JDK,Eclipse,Spring插件以后,配置Eclipse的JDK,配置maven 3.5.4,开始我们的SpringBoot HelloWorld
把这些东西都加上,后面会用到:
maven现在开始导入Spring Boot,我的使用的是阿里源
在maven配置里面把maven默认的源改为如下:
E:\apache-maven-3.5.4\conf\settings.xml
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
OK,下载完之后,启动工程以前,有几个坑要说一下:
- 将application.properties转换成application.yml,因为application.properties的默认编码为ISO-8859-1,之后有中文的时候,用@Value注释的中文一直是乱码,只有改成yml文件才会显示中文,至于其他解决application.properties乱码的方法,我的试了好几种,都不管用,所以改成了application.yml文件。
- 连接Mysql的驱动改为8.0版本,因为我的的机器上装的是MySql8.0,用之前老的驱动不能连接。为什么用8.0的Mysql,因为Mysql官网发布,8.0的Mysql的性能比5.7的Mysql高了两倍,还有其他好东西,有兴趣的道友可自行查找Mysql相关资料。
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version><!--$NO-MVN-MAN-VER$ -->
</dependency>
- 配置连接Mysql连接
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/spring?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&allowPublicKeyRetrieval=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
username: hyn
password: hyn1234
url驱动说明:characterEncoding=UTF-8 指定字符集。
useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai 指定时区,不然插入数据库时间和当前时间差8个小时。
还要更改Mysql8.0密码验证方式,这个大家在网上一搜就有,这里就不说了。
com.mysql.cj.jdbc.Driver 这个东西是Mysql8.0的新驱动
好了,坑填完了,启动一下:
用了3秒启动起来了。这比SpringMvc启动快了很多,这还是SpringBoot没有优化启动的结果。
写一个用户的增删改查
第一个Controller,基本每个系统都有用户,那我的就先写一个用户的增删改查。
包的目录接口献上:
解释一哈:
//系统配置包
com.hyn.spring.config
//前端控制器
com.hyn.spring.controller
//实体
com.hyn.spring.domain
//数据访问层
com.hyn.spring.repository
//业务接口
com.hyn.spring.service
//业务实现
com.hyn.spring.service.impl
//视图包
com..hyn.spring.vo
实体
不说了贴代码,省略get set:
package com.hyn.spring.domain;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.UpdateTimestamp;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
*
* @Title: SysUser.java
* @Package com.hyn.spring.domain
* @Description: TODO
* @author hyn
* @date 2018年12月9日 下午1:38:53
* @version V1.0
*/
@Entity
@Table(name = "sys_user")
@EntityListeners(AuditingEntityListener.class)
public class SysUser implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
@Id
@GenericGenerator(name = "idGenerator", strategy = "uuid") // 这个是hibernate的注解/生成32位UUID
@GeneratedValue(generator = "idGenerator")
/**
* 用户ID
*/
@Column(columnDefinition="varchar(36) not null comment '用户ID' ")
String id;
/**
*
* 用户状态
*/
@Column(columnDefinition = "enum('true','false') default 'true' not null comment '用户状态' ")
String status;
/**
* 邮箱
*/
@Column(columnDefinition = "varchar(20) not null default '' comment '用户邮箱'" )
private String email;
/**
* 登录用户名
*/
@Column(columnDefinition = "varchar(20) not null unique default '' comment '登录用户名'" )
private String loginName;
/**
* 用户名
*/
@Column(columnDefinition = "varchar(10) not null default '' comment '用户名称'" )
private String userName;
/**
* 密码
*/
@Column(columnDefinition = "varchar(100) not null default '888888' comment '登录密码'" )
private String password;
/**
* 电话
*/
@Column(columnDefinition = "varchar(11) not null unique default '' comment '手机号码'" )
private String phone;
/**
* 更新时间
*/
@Temporal(TemporalType.TIMESTAMP)
@Column(columnDefinition = "timestamp default current_timestamp on update current_timestamp comment '更新时间'")
@UpdateTimestamp
private Date moditime;
//省略 get set
}
在application.yml文件中加入Spring-data-jpa配置:
spring:
###JPA配置
jpa:
properties:
hibernate:
hbm2ddl:
auto: update
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
show-sql: true
generate-ddl: true
database: mysql
这里再启动一下工程,使用spring-data-jpa自动生成表的功能,至于spring-data-jpa到底是什么,干啥的,怎么用,有兴趣的道友可以提前看下,以后我的的博客会有介绍。
控制台会打印出建表语句,:
不放心的话去数据库看看:
表结构如下:
表建好了,接下来做一下数据访问层。
数据访问层
package com.hyn.spring.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import com.hyn.spring.domain.SysUser;
public interface ISysUserRepository extends JpaRepository<SysUser, String>,JpaSpecificationExecutor<SysUser>{
}
你没有看错,就这个,数据访问层就完了,完全可以实现增删改查。
视图层
接下来定义和前端交互的数据格式,新建一个SysUserVO类,但是作为一个合格的后端开发者,我的定义的数据格式还要每个都和前端的道友说一遍吗?NO,那怎么办?使用Swagger来书写我的和前端道友交互的接口文档。
附上swagger使用的几篇文章:
Spring Boot中使用Swagger2构建强大的RESTful API文档
SpringBoot 使用Swagger2打造在线接口文档
maven引入swagge:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.8.0</version>
</dependency>
Swagger配置:
package com.hyn.spring.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.common.base.Predicates;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
*
* @Title: SwaggerConfigurer.java
* @Package com.hyn.spring.config
* @Description: TODO
* @author hyn
* @date 2018年12月9日 下午3:00:05
* @version V1.0
*/
@Configuration
@EnableSwagger2
public class SwaggerConfigurer {
@Bean
public Docket createRestApi() {
//错误路径不监控
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.any())
.paths(Predicates.not(PathSelectors.regex("/error.*")))
.build()
.apiInfo(buildApiInf());
}
private ApiInfo buildApiInf() {
return new ApiInfoBuilder().title("spring-first接口文档").description("api文档")
.version("0.0.1").build();
}
}
新建的SysUserVO如下:
package com.hyn.spring.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel(value="用户")
public class SysUserVO {
@ApiModelProperty(value = "id", name = "id")
String id;
/**
*
* 用户状态
*/
@ApiModelProperty(value = "用户状态", name = "status")
String status;
/**
* 邮箱
*/
@ApiModelProperty(value = "邮箱", name = "email")
private String email;
/**
* 登录用户名
*/
@ApiModelProperty(value = "登录用户名", name = "loginName")
private String loginName;
/**
* 用户名
*/
@ApiModelProperty(value = "用户名", name = "userName")
private String userName;
/**
* 电话
*/
@ApiModelProperty(value = "电话", name = "phone")
private String phone;
//同样省略get set
业务层
首先是定义需要实现的接口,接下来是实现。
业务接口
package com.hyn.spring.service;
import org.springframework.data.domain.Page;
import com.hyn.spring.vo.SysUserVO;
public interface ISysUserService {
/**
* 增加一个用户
* @return
*/
String save(SysUserVO sysUserVO);
/**
* 删除一个用户
* @return
*/
String delete(String id);
/**
* 更新一个用户
* @return
*/
String update(SysUserVO sysUserVO);
/**
* 分页查询用户
* @param userName用户名
* @param pageable 分页查询条件
* @return
*/
Page<SysUserVO> getSysUser(String userName,Pageable pageable);
}
业务实现
package com.hyn.spring.service.impl;
import java.util.LinkedList;
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.StringUtils;
import com.hyn.spring.domain.SysUser;
import com.hyn.spring.repository.ISysUserRepository;
import com.hyn.spring.service.ISysUserService;
import com.hyn.spring.utils.IPageResponse;
import com.hyn.spring.vo.SysUserVO;
@Service
public class ISysUserServiceImpl implements ISysUserService {
@Autowired
ISysUserRepository iSysUserRepository;
@Override
public String save(SysUserVO sysUserVO) {
SysUser sysUser = new SysUser();
sysUser.setEmail(sysUserVO.getEmail());
sysUser.setLoginName(sysUserVO.getLoginName());
sysUser.setPassword(sysUserVO.getPassword());
sysUser.setPhone(sysUserVO.getPhone());
sysUser.setStatus(sysUserVO.getStatus());
sysUser.setUserName(sysUserVO.getUserName());
iSysUserRepository.save(sysUser);
return "success";
}
@Override
public String delete(String id) {
iSysUserRepository.deleteById(id);
return "success";
}
@Override
public String update(SysUserVO sysUserVO) {
SysUser sysUser = new SysUser();
sysUser.setEmail(sysUserVO.getEmail());
sysUser.setLoginName(sysUserVO.getLoginName());
sysUser.setPassword(sysUserVO.getPassword());
sysUser.setPhone(sysUserVO.getPhone());
sysUser.setStatus(sysUserVO.getStatus());
sysUser.setUserName(sysUserVO.getUserName());
sysUser.setId(sysUserVO.getId());
iSysUserRepository.saveAndFlush(sysUser);
return "success";
}
@Override
public Page<SysUserVO> getSysUser(String userName, Pageable pageable) {
Page<SysUser> page = iSysUserRepository.findAll(this.getWhereClause(userName), pageable);
List<SysUserVO> voList = new LinkedList<>();
for (SysUser sysUser : page.getContent()) {
SysUserVO sysUserVO = new SysUserVO();
sysUserVO.setEmail(sysUser.getEmail());
sysUserVO.setId(sysUser.getId());
sysUserVO.setLoginName(sysUser.getLoginName());
sysUserVO.setPassword(sysUser.getPassword());
sysUserVO.setPhone(sysUser.getPhone());
sysUserVO.setStatus(sysUser.getStatus());
sysUserVO.setUserName(sysUser.getUserName());
voList.add(sysUserVO);
}
Page<SysUserVO> voPage = new IPageResponse<>(voList, pageable, page.getTotalElements());
return voPage;
}
/**
* 构造断言
*
* @param userName
* @return
*/
private Specification<SysUser> getWhereClause(String userName) {
return new Specification<SysUser>() {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public Predicate toPredicate(Root<SysUser> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
Predicate predicate = criteriaBuilder.conjunction();
if (StringUtils.isEmpty(userName)) {
predicate.getExpressions()
.add(criteriaBuilder.notLike(root.<String>get("userName"), "%" + userName + "%"));
}
return predicate;
}
};
}
}
这里有个坑要注意:因为我们返回给前端的分页数据是从0开始的,但是前端需要是从1开始,所有我们要重写JPA的PageImpl,代码如下:
package com.hyn.spring.utils;
import java.util.List;
import org.springframework.data.domain.AbstractPageRequest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
/**
*
* @Title: IPageResponse.java
* @Package com.hyn.spring.utils
* @Description: TODO
* @author hyn
* @date 2018年12月9日 下午2:48:16
* @version V1.0
*/
public class IPageResponse<T> extends PageImpl<T> implements Page<T> {
/**
*
*/
private static final long serialVersionUID = 1L;
public IPageResponse(List<T> content, Pageable pageable, long total) {
super(content, pageable, total);
}
/**
* 重写当前页,将当前页加1返回前台,spring data jpa起始页0加1后返回前台
* @return
*/
@Override
public int getNumber() {
return super.getNumber() + 1;
}
@Override
public Pageable getPageable() {
return null;
}
public static Page<?> getResponsePage(Page<?> page, AbstractPageRequest pageRequest) {
if (page == null || page.getContent() ==null || page.getContent().size()==0) {
return null;
} else {
return new IPageResponse<>(page.getContent(), pageRequest, page.getTotalElements());
}
}
}
控制层
给前端发布的restful api接口。
package com.hyn.spring.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.hyn.spring.service.ISysUserService;
import com.hyn.spring.utils.IPageRequest;
import com.hyn.spring.vo.SysUserVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
@Api(value = "user", tags = "系统用户模块")
@Controller(value = "user")
@RequestMapping(value = "/user")
public class SysUserController {
@Autowired
ISysUserService iSysUserService;
/**
*
* @param sysUserVO
* @return
*/
@ApiOperation(value = "新增用户接口")
@RequestMapping(value = "/", method = RequestMethod.POST)
@ResponseBody
public String saveUser(@ApiParam(value = "用戶信息", required = true) @RequestBody SysUserVO sysUserVO) {
String resultCode = iSysUserService.save(sysUserVO);
return resultCode;
}
/**
*
* @param id
* @return
*/
@ApiOperation(value = "删除用户接口")
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
@ResponseBody
public String delete(@ApiParam(value = "用户ID", required = true) @PathVariable(name = "id") String id) {
String resultCode = iSysUserService.delete(id);
return resultCode;
}
/**
*
* @param sessionSysUser
* @return IResult
*/
@ApiOperation(value = "更新用户信息")
@RequestMapping(value = "/", method = RequestMethod.PUT)
@ResponseBody
public String update(@ApiParam(value = "用戶信息", required = true) @RequestBody SysUserVO sysUserVO) {
String resultCode = iSysUserService.update(sysUserVO);
return resultCode;
}
/**
*
* 查询用户 分页查询
*
*/
@ApiOperation(value = "查询用户信息")
@RequestMapping(value = "/", method = RequestMethod.GET)
@ResponseBody
public Page<SysUserVO> getSysUser(
@ApiParam(value = "用户名称", required = false) @RequestParam(name = "userName", required = false) String userName,
@ApiParam(value = "排序方式", required = false) @RequestParam(name = "sort", required = false) String sort,
@ApiParam(value = "排序字段", required = false) @RequestParam(name = "sidx", required = false) String sidx,
@ApiParam(value = "页码", required = false) @RequestParam(name = "page", required = false) Integer page,
@ApiParam(value = "页大小", required = false) @RequestParam(name = "size", required = false) Integer size) {
IPageRequest iPageRequest = new IPageRequest(sort, sidx, page, size);
Page<SysUserVO> sysUsers = iSysUserService.getSysUser(userName, iPageRequest.getRequestPage());
return sysUsers;
}
}
这里需要自定义一个请求封装类IPageRequest,用来接收前端的分页参数:
package com.hyn.spring.utils;
import org.springframework.data.domain.AbstractPageRequest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.util.StringUtils;
/**
*
* @Title: IPageRequest.java
* @Package com.hyn.spring.utils
* @Description: TODO
* @author hyn
* @date 2018年12月9日 下午3:19:09
* @version V1.0
*/
public class IPageRequest {
/**
* 排序方式
*/
String sort;
/**
* 排序字段
*/
String sidx;
/**
* 当前頁面
*/
Integer page=0;
/**
* 页大小
*/
Integer size=0;
public String getSort() {
return sort;
}
public void setSort(String sort) {
this.sort = sort;
}
public String getSidx() {
return sidx;
}
public void setSidx(String sidx) {
this.sidx = sidx;
}
public Integer getPage() {
return page;
}
public void setPage(Integer page) {
this.page = page;
}
public Integer getSize() {
return size;
}
public void setSize(Integer size) {
this.size = size;
}
public IPageRequest(String sort, String sidx, Integer page, Integer size) {
super();
this.sort = sort;
this.sidx = sidx;
this.page = page;
this.size = size;
}
public IPageRequest() {
super();
}
@Override
public String toString() {
return "IPageRequest [sort=" + sort + ", sidx=" + sidx + ", page=" + page + ", size=" + size + "]";
}
public AbstractPageRequest getRequestPage() {
Sort mysort = null;
if (StringUtils.isEmpty(this.sidx)) {
this.sidx="moditime";
}
if (StringUtils.isEmpty(this.sort)) {
mysort = new Sort(Sort.Direction.DESC, this.sidx);
} else {
if (Sort.Direction.DESC.name().equals(this.sort.toUpperCase())) {
mysort = new Sort(Sort.Direction.DESC, this.sidx);
} else {
mysort = new Sort(Sort.Direction.ASC, this.sidx);
}
}
int pagTmp = 0;
int sizeTmp = 0;
if (this.page == null || this.page < 1) {
pagTmp = 0;
} else {
pagTmp = this.page - 1;
}
if (this.size==null || this.size <= 0) {
sizeTmp = 15;
}else
{
sizeTmp = this.size;
}
AbstractPageRequest pageable =null;
if(mysort!=null)
{
pageable=PageRequest.of(pagTmp, sizeTmp, mysort);
}
return pageable;
}
}
测试
好了,所有的代码都贴完了,测一下:
给项目加个path,给application.yml添加:
server:
servlet:
context-path: /api/
启动项目,输入:http://127.0.0.1:8080/api/swagger-ui.html
新增用户
查询用户
修改
删除
好了,有些累,有问题可留言!