springboot+mybatis+mysql (用户登录实例)
- 需求
- 登录
- 数据结构
- 用户表
- 报文结构
- 创建项目
- pom.xml
- 配置文件
- application.yml
- application-pro.yml
- RestfulUserApplication类中添加到层的扫描路径
- 添加实体类
- 用户实体(User)
- 请求包实体类(ReqPack)
- 响应包实体类(RespPack)
- 添加统一异常类UnifiedException
- 添加统一异常处理类UnifiedExceptionHandle
- 添加获取当前行号方法(getCurLineInfo)
- 添加Dao层
- 添加mapper文件
- 添加service层
- 添加controller层
- 测试
- 代码地址
需求
- 数据库中存在用户名、密码
- 前端发送登录请求,返回登录结果(成功则返回用户信息)
数据结构
CREATE TABLE `user` (
`id` varchar(32) NOT NULL COMMENT '用户编号(登录账号)',
`password` varchar(32) NOT NULL COMMENT '密码',
`name` varchar(128) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';
报文结构
请求报文
{
"version": "1.0",
"data": {
"id": "lhc",
"pwd": "123456"
}
}
响应报文
成功报文:
{
"code": 0,
"desc": "成功",
"data": {
"id": "lhc",
"pwd": "",
"name": "令狐冲"
}
}
用户名或密码不存在:
{
"code": 1,
"desc": "用户名密码不可为空",
"data": null
}
用户名密码错误:
{
"code": 2,
"desc": "用户名或密码错误",
"data": null
}
创建项目
- 添加druid(数据库连接池)依赖
- 添加阿里fastjson依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ssy</groupId>
<artifactId>restful-user</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>restful-user</name>
<description>Demo restful-user api</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.32</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置文件
server:
port: 9999
spring:
profiles:
active: pro
servlet:
multipart:
max-file-size: 30MB
max-request-size: 30MB
devtools:
restart:
enabled: true
mybatis:
mapper-locations: classpath:mapper/*.xml #注意:一定要对应mapper映射xml文件的所在路径
type-aliases-package: com.catail.restfuluser.bean # 注意:对应实体类的路径
logging:
level:
root: error
com.ssy: info
com.catail.restfuluser.dao: debug
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
filters: stat #监控统计拦截的filters
driver-class-name: com.mysql.cj.jdbc.Driver
#基本属性
url: jdbc:mysql://xxx.xx.xx.xx:3306/restfil-user?useAffectedRows=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=UTC
username: root
password: xxxxxx
initial-size: 1 #初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
min-idle: 5 #最小连接池数量
max-active: 50 #最大连接池数量
max-wait: 60000 #获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降
time-between-eviction-runs-millis: 60000 #间隔多久进行一次检测,检测需要关闭的空闲连接
min-evictable-idle-time-millis: 30000 #配置一个连接在池中最小生存的时间,单位是毫秒
validation-query: SELECT 'x' #用来检测连接是否有效的sql,要求是一个查询语句。
test-while-idle: true #建议配置为true,不影响性能,并且保证安全性。
test-on-borrow: false #申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
test-on-return: false #归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
#打开PSCache,并指定每个连接上PSCache的大小。oracle设为true,mysql设为false。分库分表较多推荐设置为false
pool-prepared-statements: false #是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
max-pool-prepared-statement-per-connection-size: 20
RestfulUserApplication类中添加到层的扫描路径
添加实体类
注意:添加包路径要与配置文件中一致
package com.ssy.restfuluser.bean;
public class User {
private String id;
private String pwd;//此处成员变量名称与数据库表中不同,方便查看mybatis resultmap的作用
private String name;
//getter and setter.....
}
package com.ssy.restfuluser.bean;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ReqPack {
private static final Logger logger = LoggerFactory.getLogger(ReqPack.class);
private String version;
private JSONObject data;
// getter and setter....
}
package com.ssy.restfuluser.bean;
public class RespPack<T> {
private int code;
private String desc;
private T data;
// getter and setter .......
//*******************************************************************
public static RespPack respPackSuc(Object data) {
RespPack respPack = new RespPack();
respPack.setCode(0);
respPack.setDesc("成功");
respPack.setData(data);
return respPack;
}
public static RespPack respPackSuc() {
return respPackSuc(null);
}
public static RespPack respPackFail(int code, String desc, Object data) {
RespPack respPack = new RespPack();
respPack.setCode(code);
respPack.setDesc(desc);
respPack.setData(data);
return respPack;
}
public static RespPack respPackFail(int code, String desc) {
return respPackFail(code, desc, null);
}
}
添加统一异常类UnifiedException
package com.ssy.restfuluser.exception;
public class UnifiedException extends RuntimeException {
private int code;
private String msgDetail;
private Object data;
public UnifiedException(int code, String message) {
super(message);
this.code = code;
}
public UnifiedException(int code, String message, Object obj) {
super(message);
this.code = code;
this.data = obj;
}
// getter and setter....
public static UnifiedException create(int code, String msg, String lineInfo) {
UnifiedException unifiedException = new UnifiedException(code, msg);
unifiedException.setMsgDetail(lineInfo + msg);
return unifiedException;
}
public static UnifiedException create(int code, String msg, String lineInfo, Object obj) {
UnifiedException unifiedException = new UnifiedException(code, msg, obj);
unifiedException.setMsgDetail(lineInfo + msg);
return unifiedException;
}
}
添加统一异常处理类UnifiedExceptionHandle
package com.ssy.restfuluser.exception;
import com.ssy.restfuluser.bean.RespPack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
public class UnifiedExceptionHandle {
private static final Logger logger = LoggerFactory.getLogger(UnifiedExceptionHandle.class);
@ExceptionHandler(value = Exception.class)
@ResponseBody
public RespPack hanle(Exception e) {
if (e instanceof UnifiedException) {
UnifiedException exception = (UnifiedException) e;
logger.error(((UnifiedException) e).getMsgDetail());
return RespPack.respPackFail(exception.getCode(), exception.getMessage(), exception.getData());
}
logger.error("系统异常:" + e);
return RespPack.respPackFail(99, e + "");
}
}
添加获取当前行号方法(getCurLineInfo)
package utils;
public class SysUtil {
public static String getCurLineInfo() {
int originStackIndex = 2;
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
StackTraceElement element = stackTrace[originStackIndex];
String retStr = "【文件】" + element.getFileName() + "【类】" + element.getClassName() +
"【方法】" + element.getMethodName() + "【行号】" + element.getLineNumber() + "\n";
return retStr;
}
}
添加Dao层
注意:添加包路径要与RestfulUserApplication类中dao的扫描路径一致
package com.ssy.restfuluser.dao;
import com.ssy.restfuluser.bean.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface UserDao {
User getUserbyToken(@Param("id") String id, @Param("pwd") String pwd);
}
添加mapper文件
注意:mapper文件的路径要与配置文件中相对应
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.ssy.restfuluser.dao.UserDao">
<resultMap id="user" type="com.ssy.restfuluser.bean.User">
<result property="id" column="id"/>
<result property="pwd" column="password"/>
<result property="name" column="name"/>
</resultMap>
<select id="getUserbyToken" resultMap="user">
select * from user
<where>
<if test="id != null and id.length() > 0">user.id= #{id}</if>
<if test="pwd != null and pwd.length() > 0">and user.password=#{pwd}</if>
</where>
</select>
</mapper>
添加service层
此处service接口和实现不做分离了
UserService
package com.ssy.restfuluser.service;
import com.ssy.restfuluser.bean.User;
import com.ssy.restfuluser.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
UserDao userDao;
User getUserbyToken(String id, String pwd) {
if (id == null || id.length() <= 0 || pwd == null || pwd.length() <= 0)
return null;
return userDao.getUserbyToken(id, pwd);
}
}
添加controller层
UserController
package com.ssy.restfuluser.controller;
import com.ssy.restfuluser.bean.ReqPack;
import com.ssy.restfuluser.bean.RespPack;
import com.ssy.restfuluser.bean.User;
import com.ssy.restfuluser.exception.UnifiedException;
import com.ssy.restfuluser.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import utils.SysUtil;
@RestController
@RequestMapping(value = "/user")
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
@Autowired
UserService userService;
@PostMapping("/login")
Object login(@RequestBody ReqPack reqPack) {
String id;
String pwd;
try {
id = reqPack.getData().getString("id");
pwd = reqPack.getData().getString("pwd");
} catch (Exception e) {
throw UnifiedException.create(1, "参数异常", SysUtil.getCurLineInfo());
}
if (id == null || id.length() <= 0 || pwd == null || pwd.length() <= 0) {
throw UnifiedException.create(1, "用户名密码不可为空", SysUtil.getCurLineInfo());
}
//业务
User user = userService.getUserbyToken(id, pwd);
if (user == null) {
throw UnifiedException.create(2, "用户名或密码错误", SysUtil.getCurLineInfo());
}
user.setPwd("");
return RespPack.respPackSuc(user);
}
}
测试
使用postman工具进行测试