SpringBoot中使用MyBatis+Druid+Swagger
创建一个基本的SpringBoot项目,然后引入mybatis-spring-boot-starter和数据库连接驱动(我这里使用的是mysql)、阿里巴巴druid-spring-boot-starter以及swagger依赖
1.引入所需依赖
注意(坑): 引入mybatis-spring-boot-starter要和SpringBoot版本对应,具体可参考官方文档:http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/,起初我使用的是2点几的版本,启动一直失败,后面SpringBoot使用1.5.9.RELEASE版本,mybatis-spring-boot-starter使用的是2.1.4版本,并且需要添加插件(下面会介绍),方可正常使用。
SpringBoot版本是:1.5.9.RELEASE
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
所需依赖
Druid和MyBatis为Spring Boot项目提供了对应的starter:
在pom中引入:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<!--阿里巴巴Druid数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.6</version>
</dependency>
<!--MySql数据库连接驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
<!-- swagger 和swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
</dependency>
添加插件
在bulid->plugins下添加插件
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
2.Druid数据源配置
Spring Boot的数据源配置的默认类型是org.apache.tomcat.jdbc.pool.Datasource,为了使用Druid连接池,需要在application.yml下配置(附参考):
spring:
datasource:
druid:
# 数据库访问配置, 使用druid数据源
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/permission?serverTimezone=UTC
username: root
password: root
# 连接池配置
initial-size: 5
min-idle: 5
max-active: 20
# 连接等待超时时间
max-wait: 30000
# 配置检测可以关闭的空闲连接间隔时间
time-between-eviction-runs-millis: 60000
# 配置连接在池中的最小生存时间
min-evictable-idle-time-millis: 300000
validation-query: select '1' from dual
test-while-idle: true
test-on-borrow: false
test-on-return: false
# 打开PSCache,并且指定每个连接上PSCache的大小
pool-prepared-statements: true
max-open-prepared-statements: 20
max-pool-prepared-statement-per-connection-size: 20
# 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙
filters: stat,wall
# Spring监控AOP切入点,如x.y.z.service.*,配置多个英文逗号分隔
aop-patterns: com.springboot.servie.*
# WebStatFilter配置
web-stat-filter:
enabled: true
# 添加过滤规则
url-pattern: /*
# 忽略过滤的格式
exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
# StatViewServlet配置
stat-view-servlet:
enabled: true
# 访问路径为/druid时,跳转到StatViewServlet
url-pattern: /druid/*
# 是否能够重置数据
reset-enable: false
# 需要账号密码才能访问控制台
login-username: druid
login-password: 123456
# IP白名单
# allow: 127.0.0.1
# IP黑名单(共同存在时,deny优先于allow)
# deny: 192.168.1.218
# 配置StatFilter
filter:
stat:
log-slow-sql: true
其他配置可参考官方文档:
https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
Druid可以做为数据库连接池,也可以作为Druid监控功能。
配置完成,运行项目,访问地址:http://localhost:8080/web/druid/login.html
输入账号密码,即可看到运行sql的Druid监控信息:
3.使用Swagger
通过swagger可以方便管理、查看接口文档,并进行在线调试的插件
1)RESTful风格
描述 | 传统请求 | 方法 | RESTful请求 | 方法 |
---|---|---|---|---|
查询 | /user/query?name=zhang | GET | /user?name=zhang | GET |
详情 | /user/getInfo?id=1 | GET | /user/1 | GET |
创建 | /user/create?name=mrbird | POST | /user | POST |
修改 | /user/update?name=mrbird&id=1 | POST | /user/1 | PUT |
删除 | /user/delete?id=1 | GET | /user/1 | DELETE |
RESTful只是一种风格,并不是一种强制性的标准。 |
a.restFul是使用rest风格来代替传统的接口风格
b.rest风格主要是使用四个动词,GET 查询 POST更新|添加 PUT添加|更新 DELETE 删除
c.PUT和DELETE 请求
请求地址地址为:http://localhost:8080/web/user/deleteUser/1
后台地址为:http://localhost:8080/web/user/deleteUser/{id}
参数前要用@PathVariable 注解接收
请求地址为对象时,前台要传json字符串,并且定义 contentType:“application/json”,后台接收参数前要使用 @RequestBody接收,能将json字符串转换为json对象
2)配置SwaggerConfig
使用JavaConfig方式配置SwaggerConfig
package com.xxx.springboot.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;
import springfox.documentation.service.Contact;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket productApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(buildApiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.xxx.springboot.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo buildApiInfo() {
return new ApiInfoBuilder()
.title("系统RESTful API文档")
.description("mybatis接口文档规整")
.contact(new Contact("czg", "xxx", "zhigaochengit@163.com"))
.version("1.0")
.build();
}
}
在配置类中添加@EnableSwagger2注解来启用Swagger2,apis()定义了扫描的包路径
Swagger常用注解:
@Api(description = "用户相关接口文档") 用于类,每个controller上的注解,表示这个controller主要的业务
@ApiModel 作用在实体类上
@ApiModelProperty(value = "错误码描述", name = "错误码描述") 用于字段,类型每个属性的作用
@ApiOperation(alue = "用户首页", notes = "访问页面") 用于方法 每个方法的注解,表示这个方法的业务功能是什么
@ApiImplicitParam() 用于方法 表示单独的请求参数
@ApiImplicitParams() 用于方法,包含多个 @ApiImplicitParam
@ApiParam(name = "userName",value = "用户昵称",required = true)方法参数注解
3) Swagger中paramType用法
paramType:表示参数放在哪个地方
header-->请求参数的获取:@RequestHeader(代码中接收注解)
query-->请求参数的获取:@RequestParam(代码中接收注解)
path(用于restful接口)-->请求参数的获取:@PathVariable(代码中接收注解)
body-->请求参数的获取:@RequestBody(代码中接收注解)
form(不常用)
4)编写API接口
@ApiImplicitParam(name = "id", value = "用户id", required = true, dataType = "int", paramType = "query")
@ApiOperation(value = "查询用户",notes="根据用户id查询用户")
//@GetMapping("/query")
@RequestMapping(value = "/queryUser",method = RequestMethod.GET)
@ResponseBody
public User queryUser(Integer id){
return userService.query(id);
}
@ApiOperation(value = "添加用户",notes="添加新用户")
@RequestMapping(value = "addUser",method = RequestMethod.POST)
@ResponseBody
public String addUser(){
User user = new User("kangkang", "123456", 3, 0);
Integer result =userService.addUser(user);
return result==1?"添加用户成功":"添加用户失败";
}
@ApiOperation(value = "修改用户",notes="修改用户")
@RequestMapping(value = "updateUser",method = RequestMethod.PUT)
@ResponseBody
public String updateUser(){
User user = new User();
user.setId(2);
user.setName("Loui");
Integer result = userService.updateUser(user);
return result==1?"修改用户成功":"修改用户失败";
}
@ApiOperation(value = "删除用户",notes="删除用户")
@ApiImplicitParam(name = "id", value = "用户id", required = true, dataType = "int", paramType = "query")
@RequestMapping(value = "deleteUser",method = RequestMethod.GET)
@ResponseBody
public String deleteUser(Integer id){
Integer result = userService.deleteUser(id);
return result==1?"删除用户成功":"删除用户失败";
}
注意:@ApiImplicitParam中 dataType要用基本类型,不要使用包装类
对于不需要生成API的方法或者类,只需要在上面添加@ApiIgnore注解即可。
5)测试
启动项目,访问http://localhost:8080/web/swagger-ui.html 即可看到Swagger给我们生成的API页面:
点击queryUser接口,输入参数点击“Try it out”,Swagger会用curl命令发送请求,并且返回响应信息,如下所示:
4.使用Mybatis
1)基础配置
使用的数据库表:
创建对应的实体:
在SpringBoot启动类添加MyBatis包扫描:
@SpringBootApplication
@MapperScan("com.xxx.springboot.mapper")
public class SpringbootMybatisApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMybatisApplication.class, args);
}
}
2)使用注解方式实现基本的CRUD操作
public interface UserMapper {
@Select("select * from sys_user where id =#{id}")
@Results(id="query",value = {
@Result(property = "id",column = "id"),
@Result(property = "name",column = "name"),
@Result(property = "password",column = "password"),
@Result(property = "deptId",column = "dept_id"),
@Result(property = "delFlag",column = "del_flag")
})
User query(Integer id);
@Insert("insert into sys_user(name,password,dept_id,del_flag) values (#{name},#{password},#{deptId},#{delFlag})")
@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
Integer addUser(User user);
@Update("update sys_user set name = #{name} where id = #{id}")
Integer updateUser(User user);
@Delete("delete from sys_user where id = #{id}")
Integer deleteUser(Integer id);
}
@Results映射置数据库字段与实体类字体不一致问题,
@Options设置主键自增,
简单的CRUD只需要使用@Insert、@Update、@Delete、@Select这4个注解即可。
3)使用xml方式实现基本的CRUD操作
使用xml方式需要在application.yml中进行一些配置:
主要是:mapper.java和mapper.xml的映射和类型别名配置
mybatis:
# type-aliases扫描路径
# type-aliases-package:
# mapper xml实现扫描路径
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.xxx.springboot.entity
property:
order: BEFORE
编写Mapper.java接口
public interface UserMapper {
User query(Integer id);
Integer addUser(User user);
Integer updateUser(User user);
Integer deleteUser(Integer id);
}
编写Mapper.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.xxx.springboot.mapper.UserMapper">
<!--结果集映射-->
<resultMap id="userMap" type="User">
<id column="id" property="id"></id>
<result column="name" property="name"></result>
<result column="password" property="password"></result>
<result column="dept_id" property="deptId"></result>
<result column="del_flag" property="delFlag"></result>
</resultMap>
<!--查询-->
<select id="query" resultMap="userMap" parameterType="integer">
select * from sys_user where id =#{id}
</select>
<!--添加用户-->
<insert id="addUser" parameterType="User" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into sys_user(name,password,dept_id,del_flag) values (#{name},#{password},#{deptId},#{delFlag})
</insert>
<!--更新用户-->
<update id="updateUser" parameterType="User">
update sys_user set name = #{name} where id = #{id}
</update>
<!--删除用户-->
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from sys_user where id = #{id}
</delete>
</mapper>
项目目录结构:
4)测试
创建好service层和controller层,使用我们的swagger进行测试:
5)MyBatis动态sql
动态 SQL 是 MyBatis 的强大特性之一,借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
if
使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如:
<select id="queryByName" resultMap="userMap" parameterType="string">
select * from sys_user where del_flag = 0
<if test="name!=null and !''.equals(name.trim())">
<!--and name like #{name}-->
and name like concat('%',#{name},'%')
</if>
</select>
模糊查询时使用mysql函数concat拼接,可以防止使用$拼接符产生sql注入问题
choose (when, otherwise)
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句
<select id="queryByName" resultMap="userMap" parameterType="string">
select * from sys_user where del_flag = 0
<choose>
<when test="name!=null and !''.equals(str.trim())">
and name like concat('%',#{name},'%')
</when>
<otherwise>
and name = "admin"
</otherwise>
</choose>
</select>
foreach
foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。
foreach元素的属性主要有 item,index,collection,open,separator,close。
item 表示集合中每一个元素进行迭代时的别名,
index 指定一个名字,用于表示在迭代过程中,每次迭代到的位置,
open 表示该语句以什么开始,
separator 表示在每次进行迭代之间以什么符号作为分隔符,
close 表示以什么结束。
在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况 下,该属性的值是不一样的,主要有以下3种情况:
1. 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
2. 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
3. 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,collection的属性值为map的key
例如传入的的参数类型List:
public List<User> queryByNameAll(){
ArrayList<String> list = new ArrayList<>();
list.add("Iverson");
list.add("Iverson6");
return userService.queryByNameAll(list);
}
<select id="queryByNameAll" resultMap="userMap" parameterType="java.util.List">
select * from sys_user where del_flag = 0 and name in
<foreach collection="list" open="(" separator="," close=")" item="item" index="index">
#{item}
</foreach>
</select>
例如传入的参数类型Map:
public List<User> queryByNameAll3(){
HashMap map = new HashMap<>();
ArrayList<String> list = new ArrayList<>();
list.add("Iverson");
list.add("Iverson6");
map.put("ids",list);
map.put("delFlag",0);
return userService.queryByNameAll3(map);
}
<select id="queryByNameAll3" resultMap="userMap" parameterType="java.util.Map">
select * from sys_user where del_flag = #{delFlag} and name in
<foreach collection="ids" open="(" separator="," close=")" item="item" index="index">
#{item}
</foreach>
</select>
foreach具体例子也可参考该文章:https://www.cnblogs.com/fangyu19900812/p/6046209.html
6)一对一查询
这里我们以用户和部门为例,假设一个用户只能拥有一个部门。
User实体
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("用户实体类")
public class User {
@ApiModelProperty("主键id")
private Integer id;
@ApiModelProperty("用户名")
private String name;
@ApiModelProperty("用户密码")
private String password;
@ApiModelProperty("用户部门id")
private Integer deptId;
@ApiModelProperty("删除标识")
private Integer delFlag;
//一个人属于一个部门
private Dept dept;
}
Dept实体
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("部门实体类")
public class Dept {
private Integer id;
private String name;
private Integer parentId;
private Integer delFlag;
}
mapper.xml
<!--一对一查询-->
<resultMap id="one" type="User">
<result property="id" column="id"></result>
<result property="name" column="name"></result>
<result property="password" column="password"></result>
<result property="deptId" column="dept_id"></result>
<result property="delFlag" column="del_flag"></result>
<association property="dept">
<result column="did" property="id"></result>
<result column="dname" property="name"></result>
<result column="parent_id" property="parentId"></result>
<result column="del_flags" property="delFlag"></result>
</association>
</resultMap>
<select id="getUserOne" parameterType="integer" resultMap="one">
select u.*,d.id did,d.name dname ,d.parent_id,d.del_flag del_flags from
sys_user u left join sys_dept d on u.dept_id = d.id
where u.id = #{id}
</select>
编写好controller接口,使用swagger测试:
7)一对多查询
这里我们以用户和手机号为例,假设一个用户拥有多个手机号。
User实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("用户实体类")
public class User {
@ApiModelProperty("主键id")
private Integer id;
@ApiModelProperty("用户名")
private String name;
@ApiModelProperty("用户密码")
private String password;
@ApiModelProperty("用户部门id")
private Integer deptId;
@ApiModelProperty("删除标识")
private Integer delFlag;
//一个人有多个电话号码
private List<Tels> tels;
//一个人属于一个部门
// private Dept dept;
}
Tels实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("电话号码实体类")
public class Tels {
private Integer id;
private String tellphone;
private Integer delFlag;
private Integer userId;
}
mapper.xml
<!--一对多查询-->
<resultMap id="many" type="User">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
<result property="password" column="password"></result>
<result property="deptId" column="dept_id"></result>
<result property="delFlag" column="del_flag"></result>
<collection property="tels" ofType="Tels">
<id property="id" column="tid"></id>
<result property="tellphone" column="tellphone"></result>
<result property="delFlag" column="del_flags"></result>
<result property="userId" column="user_id"></result>
</collection>
</resultMap>
<select id="getUserMany" parameterType="integer" resultMap="many">
SELECT
u.*, t.id tid,t.tellphone,t.del_flag del_flags,t.user_id
FROM
sys_user u
LEFT JOIN sys_tels t ON u.id = t.user_id
WHERE
u.id = #{id}
</select>
注意:
a) 这里两个表都有相同的字段,要起别名区分,否则查询数据会错乱。
b) collection 中一定要加 ofType指定类型,否则会报错。
### Error querying database. Cause: java.lang.NullPointerException
编写好controller接口,使用swagger测试:
8)多对多查询
这里我们以用户和角色为例,假设一个用户拥有多个角色,同时一个角色有多个用户
User实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("用户实体类")
public class User {
@ApiModelProperty("主键id")
private Integer id;
@ApiModelProperty("用户名")
private String name;
@ApiModelProperty("用户密码")
private String password;
@ApiModelProperty("用户部门id")
private Integer deptId;
@ApiModelProperty("删除标识")
private Integer delFlag;
//一个人有多个角色
private List<Role> roles;
Role实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("角色实体类")
public class Role {
private Integer id;
private String name;
private Integer delFlag;
private List<User> users;
}
mapper.xml
<!--多对多查询-->
<resultMap id="manys" type="User">
<result property="id" column="id"></result>
<result property="name" column="name"></result>
<result property="password" column="password"></result>
<result property="deptId" column="dept_id"></result>
<result property="delFlag" column="del_flag"></result>
<collection property="roles" ofType="Role">
<result property="id" column="rid"></result>
<result property="delFlag" column="del_flags"></result>
<result property="name" column="rname"></result>
</collection>
</resultMap>
<select id="getUserManys" parameterType="integer" resultMap="manys">
SELECT
u.*, r.id rid ,r.del_flag del_flags,r.`name` rname
FROM
sys_user u
LEFT JOIN sys_user_role ur ON u.id = ur.user_id
JOIN sys_role r ON ur.role_id = r.id
WHERE
u.id = #{id}
</select>
编写好controller接口,使用swagger测试:
9)SpringBoot+MyBatis配置log4j日志输出
在pom.xml添加log4j依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--排除web自带的logging依赖-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--引入log4j starter-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
<version>1.3.8.RELEASE</version>
</dependency>
在application.yml中配置mybatis输出日志:
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
org.apache.ibatis.logging.stdout.StdOutImpl:这个是可以打印sql、参数、查询结果的
org.apache.ibatis.logging.log4j.Log4jImpl:这个不打印查询结果
这个yml配置和在mybatis.xml配置作用相同
<configuration>
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
</configuration>
在src/main/resource下创建log4j.properties文件
#控制日志级别,在哪里输出Output pattern : date [thread] priority category - message FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7
log4j.rootLogger=info,ServerDailyRollingFile,stdout
log4j.appender.ServerDailyRollingFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.ServerDailyRollingFile.DatePattern='.'yyyy-MM-dd
log4j.appender.ServerDailyRollingFile.File=${catalina.base}/log/springboot/info.log #日志的保存位置
log4j.appender.ServerDailyRollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.ServerDailyRollingFile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p [%c] - %m%n
log4j.appender.ServerDailyRollingFile.Append=true
#控制台 显示的的方式为控制台普通方式
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#日志输出的格式
log4j.appender.stdout.layout.ConversionPattern=%d %-5p [%c{5}] - %m%n
#打印sql部分
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Connection = DEBUG
log4j.logger.java.sql.Statement = DEBUG
log4j.logger.java.sql.PreparedStatement = DEBUG
log4j.logger.java.sql.ResultSet = DEBUG
#配置logger扫描的包路径 这样才会打印sql
log4j.logger.com.xxx.springboot.mapper=DEBUG
测试效果:
附参考地址:https://blog.csdn.net/qq_15006743/article/details/82464914