文章目录
1 摘要
众所周知,Mybatis 是一个优秀的数据持久层框架,有着广泛的应用,其半自动化的特性既是优点也是缺点,优点是性能高,缺点是数据库语句依然需要开发者自行编写,对于一些基础的CRUD操作,将消耗开发者的大量时间和精力。为了减少开发者重复编写基本增删改查(CRUD)操作,国内的技术团队开发并开源了 Mybatis Plus 框架,从此,程序员将更加专注于业务开发,对于简单的增删改查将不需要再手写SQL。Mybatis Plus 是基于 Mybatis 开发,对其做了增强,其根本核心依旧是 Mybatis,Mybatis 的特性也全部支持。本文将介绍基于 SpringBoot 2.3 集成 Mybatis Plus 3.4。
Mybatis Plus 官方文档: https://baomidou.com/guide
2 核心依赖
./demo-mybatis-plus/pom.xml
<!-- Mybatis Plus(include Mybatis) -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
其中 ${mybatis-plus.version}
的版本号为 3.4.0
完整依赖参考 pom.xml
文件
注意事项 : 引入 Mybatis Plus 之后就不需要再引入 Mybatis
的依赖,Mybatis Plus 中已经集成 Mybaits,重复依赖可能导致版本冲突的问题
3 配置信息
3.1 application.yml 配置文件
./demo-mybatis-plus/src/main/resources/application.yml
## config
## server
server:
port: 8450
## spring config
spring:
datasource:
url: "jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=utf8\
&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2b8\
&useSSL=true&allowMultiQueries=true&autoReconnect=true&nullCatalogMeansCurrent=true\
&nullCatalogMeansCurrent=true"
username: root
password: "root"
hikari:
driver-class-name: com.mysql.cj.jdbc.Driver
## mybatis-plus
mybatis-plus:
mapper-locations: classpath*:mybatis/*.xml
type-aliases-package: com.ljq.dmeo.springboot.mybatisplus.model.entity
## mybatis log
logging:
level:
com.ljq.demo.springboot.mybatisplus.dao: debug
注意事项 : 在引入 mybatis-plus-boot-starter
依赖之后,需要使用 Mybatis Plus 对应的配置来取代旧有的 Mybatis 配置,两者的区别仅在于开头的配置(要以 mybatis-plus
开头),如果使用旧有的 Mybatis 配置,将不会生效,从而引起项目运行异常,如无法扫描到对应的 mapper 文件。
3.2 分页配置
Mybatis Plus 自带分页查询工具,但是需要手动配置
./demo-mybatis-plus/src/main/java/com/ljq/demo/springboot/mybatisplus/common/config/MybatisPlusConfig.java
package com.ljq.demo.springboot.mybatisplus.common.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Description: MybatisPlus 配置信息
* @Author: junqiang.lu
* @Date: 2020/9/1
*/
@Configuration
public class MybatisPlusConfig {
/**
* 新的分页插件,一缓和二缓遵循 mybatis 的规则
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
}
注意事项: 从 Mybatis Plus 3.4 开始,将使用新的分页配置,原来的分页配置类 PaginationInterceptor
已经被弃用
更多配置参考 Mybatis Plus 官方文档: https://baomidou.com/guide/interceptor.html
4 演示数据库表
./doc/sql/demo-database-create.sql
/*==============================================================*/
/* DBMS name: MySQL 5.0 */
/* Created on: 2018/9/29 16:10:13 */
/*==============================================================*/
drop database if exists `demo`;
create database `demo` default character set utf8mb4 collate utf8mb4_general_ci;
use `demo`;
drop table if exists user;
/*==============================================================*/
/* Table: user */
/*==============================================================*/
create table user
(
id bigint unsigned not null auto_increment comment 'id 主键',
user_name varchar(30) comment '用户名',
user_passcode varchar(100) comment '登陆密码',
user_email varchar(50) comment '邮箱',
user_insert_time varchar(30) comment '用户注册时间',
user_update_time varchar(30) comment '用户更新时间',
user_status tinyint default 1 comment '用户账号状态,1正常(默认),2禁止登陆',
user_version int unsigned default 1 comment '版本控制字段(默认1)',
user_del tinyint default 0 comment '逻辑删除字段,0正常(默认),1删除',
primary key (id)
)
ENGINE = INNODB DEFAULT
CHARSET = UTF8MB4;
alter table user comment '用户表';
初始化测试数据
./doc/sql/demo-database-test.sql
## test-demo
-- 添加测试用户
INSERT INTO `user`(`user_name`, `user_passcode`, `user_email`, `user_insert_time`, `user_update_time`)
VALUES('tom', MD5('demo12345'), 'tom@example.com', NOW(), NOW()),
('bob', MD5('demo12345'), 'bob@example.com', NOW(), NOW()),
('jack', MD5('demo12345'), 'jack@example.com', NOW(), NOW()),
('lily', MD5('demo12345'), 'lily@example.com', NOW(), NOW()),
('liming', MD5('demo12345'), 'liming@example.com', NOW(), NOW());
5 核心代码
5.1 实体类
./demo-mybatis-plus/src/main/java/com/ljq/demo/springboot/mybatisplus/model/entity/UserEntity.java
package com.ljq.demo.springboot.mybatisplus.model.entity;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 用户表实体类
*
* @author junqiang.lu
* @date 2020-09-01 13:24:08
*/
@Data
@TableName(value = "user", resultMap = "userMap")
@ApiModel(value = "用户表", description = "用户表")
public class UserEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id 主键
**/
@TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty(value = "id 主键", name = "id")
private Long id;
/**
* 用户名
**/
@TableField(value = "user_name")
@ApiModelProperty(value = "用户名", name = "userName")
private String userName;
/**
* 登陆密码
**/
@TableField(value = "user_passcode")
@ApiModelProperty(value = "登陆密码", name = "userPasscode")
private String userPasscode;
/**
* 邮箱
**/
@TableField(value = "user_email")
@ApiModelProperty(value = "邮箱", name = "userEmail")
private String userEmail;
/**
* 用户注册时间
**/
@TableField(value = "user_insert_time")
@ApiModelProperty(value = "用户注册时间", name = "userInsertTime")
private String userInsertTime;
/**
* 用户更新时间
**/
@TableField(value = "user_update_time")
@ApiModelProperty(value = "用户更新时间", name = "userUpdateTime")
private String userUpdateTime;
/**
* 用户账号状态,1正常(默认),2禁止登陆
**/
@TableField(value = "user_status")
@ApiModelProperty(value = "用户账号状态,1正常(默认),2禁止登陆", name = "userStatus")
private Integer userStatus;
/**
* 版本控制字段(默认1)
**/
@TableField(value = "user_version")
@ApiModelProperty(value = "版本控制字段(默认1)", name = "userVersion")
private Integer userVersion;
/**
* 逻辑删除字段,0正常(默认),1删除
**/
@TableLogic(value = "0", delval = "1")
@TableField(value = "user_del")
@ApiModelProperty(value = "逻辑删除字段,0正常(默认),1删除", name = "userDel")
private Integer userDel;
}
这里使用到的Mybatis Plus 注解有:
@TableName
: 用于指定实体类对应的数据库表
@TableId
: 用在主键对应的属性上,可以指定主键对应的数据库字段名,同时可以指定主键的生成策略(默认使用雪花算法生成一个 Long
类型数字)
@TableField
: 用在非主键对应的属性上,可以指定属性对应的数据库字段名,同时也可以设置字段是否为空等属性
@TableLogic
: 用在标记逻辑删除的字段属性上,使用该注解后,Myabtis Plus 所有针对该表的删除都将执行逻辑删除(即更新操作),而不使用该注解,Mybatis Plus 执行的删除操作为物理删除
更多的注解参考 Mybatis Plus 官方文档: https://baomidou.com/guide/annotation.html
5.2 DAO 层
./demo-mybatis-plus/src/main/java/com/ljq/demo/springboot/mybatisplus/dao/UserDao.java
package com.ljq.demo.springboot.mybatisplus.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ljq.demo.springboot.mybatisplus.model.entity.UserEntity;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Component;
/**
* 用户表
*
* @author junqiang.lu
* @date 2020-08-31 14:09:53
*/
@Mapper
@Component("userDao")
public interface UserDao extends BaseMapper<UserEntity> {
}
开发者手写的 DAO 接口需要继承 Mybatis Plus 提供的 com.baomidou.mybatisplus.core.mapper.BaseMapper
接口,同时指定泛型
5.3 请求参数类
增删改查接口对应的请求参数封装类
./demo-mybatis-plus/src/main/java/com/ljq/demo/springboot/mybatisplus/model/param/UserSaveParam.java
./demo-mybatis-plus/src/main/java/com/ljq/demo/springboot/mybatisplus/model/param/UserInfoParam.java
./demo-mybatis-plus/src/main/java/com/ljq/demo/springboot/mybatisplus/model/param/UserListParam.java
./demo-mybatis-plus/src/main/java/com/ljq/demo/springboot/mybatisplus/model/param/UserUpdateParam.java
./demo-mybatis-plus/src/main/java/com/ljq/demo/springboot/mybatisplus/model/param/UserDeleteParam.java
完整代码参考项目 Github 源码: https://github.com/Flying9001/springBootDemo
5.4 Service 层
Service 接口
./demo-mybatis-plus/src/main/java/com/ljq/demo/springboot/mybatisplus/service/UserService.java
package com.ljq.demo.springboot.mybatisplus.service;
import com.ljq.demo.springboot.mybatisplus.common.api.ApiResult;
import com.ljq.demo.springboot.mybatisplus.model.param.*;
/**
* 用户表业务层接口
*
* @author junqiang.lu
* @date 2020-08-31 14:09:53
*/
public interface UserService {
/**
* 保存(单条)
*
* @param userSaveParam
* @return
* @throws Exception
*/
ApiResult save(UserSaveParam userSaveParam) throws Exception;
/**
* 查询详情(单条)
*
* @param userInfoParam
* @return
* @throws Exception
*/
ApiResult info(UserInfoParam userInfoParam) throws Exception;
/**
* 查询列表
*
* @param userListParam
* @return
* @throws Exception
*/
ApiResult list(UserListParam userListParam) throws Exception;
/**
* 更新(单条)
*
* @param userUpdateParam
* @return
* @throws Exception
*/
ApiResult update(UserUpdateParam userUpdateParam) throws Exception;
/**
* 删除(单条)
*
* @param userDeleteParam
* @return
* @throws Exception
*/
ApiResult delete(UserDeleteParam userDeleteParam) throws Exception;
}
Service 实现类
./demo-mybatis-plus/src/main/java/com/ljq/demo/springboot/mybatisplus/service/impl/UserServiceImpl.java
package com.ljq.demo.springboot.mybatisplus.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ljq.demo.springboot.mybatisplus.common.api.ApiResult;
import com.ljq.demo.springboot.mybatisplus.dao.UserDao;
import com.ljq.demo.springboot.mybatisplus.model.entity.UserEntity;
import com.ljq.demo.springboot.mybatisplus.model.param.*;
import com.ljq.demo.springboot.mybatisplus.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.Objects;
/**
* 用户表业务层具体实现类
*
* @author junqiang.lu
* @date 2020-08-31 14:09:53
*/
@Service("userService")
@Transactional(rollbackFor = {Exception.class})
@Slf4j
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
/**
* 保存(单条)
*
* @param userSaveParam
* @return
* @throws Exception
*/
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
public ApiResult save(UserSaveParam userSaveParam) throws Exception {
// 请求参数获取
UserEntity userParam = new UserEntity();
BeanUtil.copyProperties(userSaveParam,userParam,CopyOptions.create()
.setIgnoreNullValue(true).setIgnoreError(true));
// 保存
String nowTime = String.valueOf(System.currentTimeMillis());
userParam.setUserInsertTime(nowTime);
userParam.setUserUpdateTime(nowTime);
userDao.insert(userParam);
return ApiResult.success(userParam.getId());
}
/**
* 查询详情(单条)
*
* @param userInfoParam
* @return
* @throws Exception
*/
@Override
public ApiResult info(UserInfoParam userInfoParam) throws Exception {
UserEntity userDB = userDao.selectById(userInfoParam.getId());
return ApiResult.success(userDB);
}
/**
* 查询列表
*
* @param userListParam
* @return
* @throws Exception
*/
@Override
public ApiResult list(UserListParam userListParam) throws Exception {
LambdaQueryWrapper<UserEntity> userWrapper = new LambdaQueryWrapper<>();
userWrapper.like(Objects.nonNull(userListParam.getUserName()), UserEntity::getUserName,
userListParam.getUserName());
IPage<UserEntity> page = new Page<>(userListParam.getCurrentPage(),userListParam.getPageSize());
userWrapper.orderBy(true, userListParam.isAscFlag(), UserEntity::getId);
page = userDao.selectPage(page, userWrapper);
return ApiResult.success(page);
}
/**
* 更新(单条)
*
* @param userUpdateParam
* @return
* @throws Exception
*/
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
public ApiResult update(UserUpdateParam userUpdateParam) throws Exception {
LambdaQueryWrapper<UserEntity> userWrapper = new LambdaQueryWrapper<>();
userWrapper.eq(true, UserEntity::getId, userUpdateParam.getId());
int countUser = userDao.selectCount(userWrapper);
if (countUser < 1) {
return ApiResult.failure("用户不存在");
}
// 请求参数获取
UserEntity userParam = new UserEntity();
BeanUtil.copyProperties(userUpdateParam, userParam, CopyOptions.create().ignoreNullValue().ignoreError());
userParam.setUserUpdateTime(String.valueOf(System.currentTimeMillis()));
userDao.updateById(userParam);
return ApiResult.success();
}
/**
* 删除(单条)
*
* @param userDeleteParam
* @return
* @throws Exception
*/
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
public ApiResult delete(UserDeleteParam userDeleteParam) throws Exception {
int countUser = userDao.deleteById(userDeleteParam.getId());
if (countUser < 1) {
return ApiResult.failure("用户不存在");
}
return ApiResult.success();
}
}
基础的增删改查在这里都有对应的示例,包括根据条件分页查询
5.5 Controller 层
./demo-mybatis-plus/src/main/java/com/ljq/demo/springboot/mybatisplus/controller/UserController.java
package com.ljq.demo.springboot.mybatisplus.controller;
import com.ljq.demo.springboot.mybatisplus.common.api.ApiResult;
import com.ljq.demo.springboot.mybatisplus.model.param.*;
import com.ljq.demo.springboot.mybatisplus.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
/**
* 用户表
*
* @author junqiang.lu
* @date 2020-08-31 16:39:56
*/
@Slf4j
@RestController
@RequestMapping(value = "/api/mybatis/plus/user")
@Api(value = "用户表控制层", tags = "用户表控制层")
public class UserController {
@Autowired
private UserService userService;
/**
* 保存(单条)
*
* @param userSaveParam
* @return
*/
@PostMapping(value = "/add", produces = {MediaType.APPLICATION_JSON_VALUE})
@ApiOperation(value = "用户表保存(单条)", notes = "用户表保存(单条)")
public ResponseEntity<?> save(@RequestBody UserSaveParam userSaveParam) throws Exception{
ApiResult apiResult = userService.save(userSaveParam);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return new ResponseEntity<>(apiResult, headers, HttpStatus.OK);
}
/**
* 查询详情(单条)
*
* @param userInfoParam
* @return
*/
@GetMapping(value = "/info", produces = {MediaType.APPLICATION_JSON_VALUE})
@ApiOperation(value = "用户表查询详情(单条)", notes = "用户表查询详情(单条)")
public ResponseEntity<?> info(UserInfoParam userInfoParam) throws Exception {
ApiResult apiResult = userService.info(userInfoParam);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return new ResponseEntity<>(apiResult, headers, HttpStatus.OK);
}
/**
* 查询列表
*
* @param userListParam
* @return
*/
@GetMapping(value = "/list", produces = {MediaType.APPLICATION_JSON_VALUE})
@ApiOperation(value = "用户表查询列表", notes = "用户表查询列表")
public ResponseEntity<?> list(UserListParam userListParam) throws Exception {
ApiResult apiResult = userService.list(userListParam);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return new ResponseEntity<>(apiResult, headers, HttpStatus.OK);
}
/**
* 修改(单条)
*
* @param userUpdateParam
* @return
*/
@PutMapping(value = "/update", produces = {MediaType.APPLICATION_JSON_VALUE})
@ApiOperation(value = "用户表修改(单条)", notes = "用户表修改(单条)")
public ResponseEntity<?> update(@RequestBody UserUpdateParam userUpdateParam) throws Exception {
ApiResult apiResult = userService.update(userUpdateParam);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return new ResponseEntity<>(apiResult, headers, HttpStatus.OK);
}
/**
* 删除(单条)
*
* @param userDeleteParam
* @return
*/
@DeleteMapping(value = "/delete", produces = {MediaType.APPLICATION_JSON_VALUE})
@ApiOperation(value = "用户表删除(单条)", notes = "用户表删除(单条)")
public ResponseEntity<?> delete(@RequestBody UserDeleteParam userDeleteParam) throws Exception {
ApiResult apiResult = userService.delete(userDeleteParam);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return new ResponseEntity<>(apiResult, headers, HttpStatus.OK);
}
}
5.6 SpringBoot 启动类
./demo-mybatis-plus/src/main/java/com/ljq/demo/springboot/mybatisplus/DemoMybatisPlusApplication.java
package com.ljq.demo.springboot.mybatisplus;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @author junqiang.lu
*/
@EnableSwagger2
@SpringBootApplication(scanBasePackages = {"com.ljq.demo.springboot.mybatisplus.*"})
@MapperScan(basePackages = {"com.ljq.demo.springboot.mybatisplus.dao"})
public class DemoMybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(DemoMybatisPlusApplication.class, args);
}
}
注意事项: 需要使用 @MapperScan
指定扫描 mapper 所在的包,即 DAO 层
5.7 Mybatis mapper 文件
使用 Mybatis Plus 之后,依旧可以使用 mapper 文件,尽管一些基本的增删改查不需要再手写 SQL了,但是作者还是建议保留 mapper 文件,对于一些复杂的查询,手写 SQL 可能更加简洁
./demo-mybatis-plus/src/main/resources/mybatis/UserDao.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.ljq.demo.springboot.mybatisplus.dao.UserDao">
<!-- 用户表结果集resultMap -->
<resultMap type="com.ljq.demo.springboot.mybatisplus.model.entity.UserEntity" id="userMap">
<result property="id" column="id"/>
<result property="userName" column="user_name"/>
<result property="userPasscode" column="user_passcode"/>
<result property="userEmail" column="user_email"/>
<result property="userInsertTime" column="user_insert_time"/>
<result property="userUpdateTime" column="user_update_time"/>
<result property="userStatus" column="user_status"/>
<result property="userVersion" column="user_version"/>
<result property="userDel" column="user_del"/>
</resultMap>
<!-- 用户表-基础字段 -->
<sql id="user_base_field">
`id`,
`user_name`,
`user_passcode`,
`user_email`,
`user_insert_time`,
`user_update_time`,
`user_status`,
`user_version`,
`user_del`
</sql>
</mapper>
Mybatis Plus 同样可以指定 mapper 文件中定义的 resultMap
,具体使用到的注解为实体类上 @TableName
5.8 Swagger 接口文档
启动项目,打开浏览器,输入地址:
http://127.0.0.1:8450/swagger-ui.html
即可进行接口测试
至此基于 SpringBoot 2.x 集成 Mybatis Plus 3.4 已经完成
6 推荐学习方法
6.1 学习方法
有问题,先搜索,然后查阅官方文档,找到对应的说明
6.2 实践操作步骤
- 1 按照官方文档启动项目
- 2 实现基本的增删改查
- 3 实现一些高级功能,如指定 resultMap,逻辑删除等等
7 推荐参考资料
8 Github 源码
Gtihub 源码地址 : https://github.com/Flying9001/springBootDemo
个人公众号:404Code,分享半个互联网人的技术与思考,感兴趣的可以关注.