引言
????????Mybatis Plus 是继承了Java的精髓:MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。(引用官方原话)
? ? ? ? 在这里我想说几句题外话:
1.推荐跳过Mybatis ,直接学MybatisPlus
2.要掌握最好熟练使用SQL
3.本文尽提取常用的一些知识点,这个远远没有官方资料更详尽!所以遇到问题多去官网查文档(文档是中文的,不用担心)。
4.Mybatis Plus 只是简化操作,不代表它是万能的,所以复杂的业务仍然需要通过Mybatis 中写XML实现。
5.针对业务,如增、删、改 都是有逻辑的,所以需要对其方法进行重构,但简单查询则不需要,所以说MybatisPlus可以减少我们15%的工作量,再多了就夸张了!
6.注意我用的MybatisPlus版本,另外,开发环境我使用的是Mac
筹备
数据库:mysql?8.0.27
? ? ? ????????? 表:
????????????????学校表?
DROP TABLE IF EXISTS `school`;
CREATE TABLE `school` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL COMMENT '名称',
`rem` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '描述',
`status` tinyint DEFAULT '0' COMMENT '状态。0:正常;1:禁用',
`creater` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`create_id` int DEFAULT NULL COMMENT '创建人ID',
`modifier` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '修改人',
`modifier_id` int DEFAULT NULL COMMENT '修改人ID',
`modifier_time` datetime DEFAULT NULL COMMENT '修改时间',
`is_delete` int DEFAULT '0' COMMENT '删除标识',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
SET FOREIGN_KEY_CHECKS = 1;
? ? ? ? ? ? ? ? 学生表
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int NOT NULL AUTO_INCREMENT,
`school_id` int DEFAULT NULL COMMENT '学校ID',
`name` varchar(255) DEFAULT NULL COMMENT '姓名',
`age` int DEFAULT NULL COMMENT '年龄',
`rem` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '描述',
`status` tinyint DEFAULT '0' COMMENT '状态。0:正常;1:禁用',
`creater` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`create_id` int DEFAULT NULL COMMENT '创建人ID',
`modifier` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '修改人',
`modifier_id` int DEFAULT NULL COMMENT '修改人ID',
`modifier_time` datetime DEFAULT NULL COMMENT '修改时间',
`is_delete` int DEFAULT '0' COMMENT '删除标识',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
SET FOREIGN_KEY_CHECKS = 1;
配置注意项目
数据库连接时如果连接不成功,会提示serverTimezone 找不到,原因是时区可能不被识别,把相关时区去除就可以了,如下:
&serverTimezone=CTT
maven配置
<!-- mysql 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>8.0.27</scope>
</dependency>
<!-- lombok 插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- druid 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<!-- mybatis-plus 目前是最新版 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
配置连接
#Spring 连接配置
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://127.0.1:3306/MybatisPlusTest?useUnicode=true&characterEncoding=utf-8&useSSL=true&nullCatalogMeansCurrent=true&allowMultiQueries=true
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
# 端口配置
server:
port: 10086
一、使用Mybatis Plus 先起步(开胃小菜)
? ? ?目标? ?:对学校表实现增、删、改、查
1.1 创建一个Spring boot web项目
? ? ? ? 这里忽略了,不会的请看我之前的教程 (点这里)
1.2 配置相关类与实现
????????
package com.example.mydemo.po;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.sql.Date;
@Data
@TableName("school")
public class School {
@TableId(type = IdType.AUTO) //标记自增长
private Integer id;
@TableField("name")
private String name;
@TableField("rem")
private String rem;
@TableField("status")
private Integer status;
@TableField("creater")
private String creater;
@TableField("create_time") //定义数据库字段
private Date createTime;
@TableField("create_id")//定义数据库字段
private Integer createId;
@TableField("modifier")
private String modifier;
@TableField("modifier_id")//定义数据库字段
private Integer modifierId;
@TableField("modifier_time")//定义数据库字段
private Date modifierTime;
@TableField("is_delete")
private Integer isDelete;
}
1.3 定义Mapper接口
package com.example.mydemo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.mydemo.po.School;
public interface SchoolDao extends BaseMapper<School> {
}
1.4 测试
1.新增
@SpringBootTest
class MydemoApplicationTests {
@Autowired
private SchoolDao schoolDao;
@Test
void contextLoads() {
System.out.println("----------------");
//新增
schoolDao.insert(new School(){{setName("上海大学");}});
//List<School> schools=schoolDao.selectList(null);
//System.out.println(schools);
}
}
2.修改
schoolDao.updateById(new School(){{setId(1);setName("苏州大学");}});
3.查询?
List<School> schools=schoolDao.selectList(null);
System.out.println(schools);
4.删除
schoolDao.deleteById(new School(){{setId(1);}});
1.5 显示具体SQL执行语句配置
配置?application.yml
#Spring 连接配置
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://127.0.1:3306/MybatisPlusTest?useUnicode=true&characterEncoding=utf-8&useSSL=true&nullCatalogMeansCurrent=true&allowMultiQueries=true
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
# 端口配置
server:
port: 10086
# SQL 日志显示
logging:
level:
root: info
com.example.mydemo: debug
效果:
二、聊一聊常见的注解 (核心)
操作 | 注解 | 说明 | 示例 | 类、表 对应 | @TableName("数据库表名") | 对实体类注解到数据库表上 | @TableName("school")
public class School {
......
} | 对应数据库中的字段配置 | @TableId(IdType.AUTO) | 设置类元素对应字段自增长 | @TableId(type = IdType.AUTO) //标记自增长
private Integer id; | @TableField("对应数据库字段") | 设置类元素对应字段对应数据库字段 | @TableField("create_time") //定义数据库字段
private Date createTime; | @TableField(exist=false) | 设置元素不为数据库字段(默认为true) | | @TableField(fill = FieldFill.数据库操作) | 数据库操作自动填充(需要再写一个自动填充实现类) | @TableField(value = "create_time",fill = FieldFill.INSERT) //定义数据库字段
private Date createTime; | @Version | 定义字段为版本(注意此处为int类型) | | @TableLogic | 逻辑字段,用于假删除 | |
示例 自动填充 & 逻辑删除(假删除)
1.创建自动填充类
package com.example.mydemo.complete;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "modifierTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐)
}
}
2.定义实体类
*? 注意我定义的日期类型
package com.example.mydemo.po;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.sql.Date;
import java.time.LocalDateTime;
@Data
@TableName("school")
public class School {
@TableId(type = IdType.AUTO) //标记自增长
private Integer id;
@TableField("name")
private String name;
@TableField("rem")
private String rem;
@TableField("status")
private Integer status;
@TableField("creater")
private String creater;
@TableField(value = "create_time",fill = FieldFill.INSERT) //定义数据库字段
private LocalDateTime createTime;
@TableField("create_id")//定义数据库字段
private Integer createId;
@TableField("modifier")
private String modifier;
@TableField("modifier_id")//定义数据库字段
private Integer modifierId;
@TableField(value = "modifier_time",fill = FieldFill.UPDATE) //定义数据库字段
private LocalDateTime modifierTime;
@TableField("is_delete")
@TableLogic
private Integer isDelete;
}
3.测试
schoolDao.insert(new School(){{setName("山东大学");}});
schoolDao.updateById(new School(){{setId(9);setName("大学");}});
?
schoolDao.deleteById(new School(){{setId(9);}});
三、CRUD (更上一层楼)
如上例所示,并不是所有的仅使用Mapper实际业务是不符合Spring 开发思想的,所以我们还要建立service 层及Impl 实现层
service
package com.example.mydemo.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.mydemo.po.School;
/**
* 接口 继承 Iservice<T> 来实现内部通用接口
*/
public interface SchoolService extends IService<School> {
}
impl
/**
* 1.继承通用实现
* 2.实现接口进行挂靠
* 3.注意交给Service 注册
*/
@Service("schoolService")
public class SchoolServiceImpl extends ServiceImpl<SchoolDao, School> implements SchoolService {
}
常用接口(增、删、改、查)
增 | boolean save(T entity); | 单增 | boolean saveBatch(Collection<T> entityList); | 批增 | 改 | boolean update(T updateEntity, Wrapper<T> whereWrapper); | 根据 whereWrapper 条件,更新记录 | boolean updateById(T entity); | 根据 ID 选择修改 | boolean updateBatchById(Collection<T> entityList); | 根据ID 批量更新 | boolean update(Wrapper<T> updateWrapper); | 根据 UpdateWrapper 条件,更新记录 需要设置sqlset | 增/改 | boolean saveOrUpdate(T entity); | 注解存在更新记录,否插入一条记录 | boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper); | 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法 | boolean saveOrUpdateBatch(Collection<T> entityList); | 批量修改插入 | 删 | boolean remove(Wrapper<T> queryWrapper); | 根据 entity 条件,删除记录 | boolean removeById(Serializable id); | 根据 ID 删除 | boolean removeByMap(Map<String, Object> columnMap); | 根据 columnMap 条件,删除记录 | 查 | ??T getById(Serializable id); | 根据 ID 查询 | T getOne(Wrapper<T> queryWrapper); | 根据 Wrapper,查询一条记录 | List<T> list(); | 全部 | List<T> list(Wrapper<T> queryWrapper); | 查询列表 |
四、条件构造器(万能钥匙)
QueryWrapper 与 UpdateWarpper这个对象通常作为载体实现条件构造
条件 | SQL | allEq() | == | eq() | = | ne | <> | gt | > | ge | >= | lt | < | le | <= | between | between | notBetween | not between | like | like | notLike | not like | likeLeft | like '%s' | likeRight | like 's%' | isNull | is null | in | in | notIn | not in | inSql | in (sql...) | groupBy | group by | orderByAsc | order? by asc | orderByDesc | order? by desc | orderBy | order by | having | having | func | func(函数) | or? | or? | and? | and |
示例
@Test
void contextLoads() {
System.out.println("------查询示例----------");
List<Integer> list=new ArrayList<>();
list.add(2);
list.add(3);
list.add(4);
QueryWrapper<School> queryWrapper=new QueryWrapper<>();
//如下操作翻译为:select * from school where id in(2,3,4) and name='苏州大学'
queryWrapper.in("id",list).eq("name","苏州大学");
List<School> schools1=schoolService.list(queryWrapper);
System.out.println(schools1);
System.out.println("------更新示例----------");
UpdateWrapper<School> updateWrapper=new UpdateWrapper<>();
//如下操作翻译为:update school set name='随便测试' where id=2
updateWrapper.eq("id",2).set("name","随便测试");
schoolService.update(updateWrapper);
}
五、常用到的插件(锦上添花)
5.1 分页
? ? ? ? 5.1.1常见接口
| 接口 | 描述 | | IPage<T> page(IPage<T> page); | 无条件分页查询 | | IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper); | 条件分页查询 |
? ? ? ? 5.1.2 启用分页配置
? ? ? ? * 1.注意我的MybatisPlus版本,低版本不可以
? ? ? ? ? ? 2.我的数据库是Mysql 所以在设置时注意选择使用:Dbtype.Mysql
package com.example.mydemo.complete;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.example.mydemo.mapper")
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor=new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
? ? ? ? 5.1.3 测试效果
@Test
void contextLoads() {
System.out.println("------分页示例(按条件)----------");
IPage<School> iPage =new Page<>(1,2);
QueryWrapper<School>queryWrapper=new QueryWrapper<>();
queryWrapper.like("name","浙江");
iPage=schoolService.page(iPage,queryWrapper);
List<School> schools=iPage.getRecords();
System.out.println(schools);
System.out.println(iPage.getPages());
}
5.2 日志
5.2.1 maven
<!-- SQL 日志打印-->
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>
5.2.2 合局配置
#Spring 连接配置
spring:
datasource:
username: root
password: 123456
#url: jdbc:mysql://127.0.1:3306/MybatisPlusTest?useUnicode=true&characterEncoding=utf-8&useSSL=true&nullCatalogMeansCurrent=true&allowMultiQueries=true
#driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:p6spy:mysql://127.0.1:3306/MybatisPlusTest?useUnicode=true&characterEncoding=utf-8&useSSL=true&nullCatalogMeansCurrent=true&allowMultiQueries=true
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
type: com.alibaba.druid.pool.DruidDataSource
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
# 端口配置
server:
port: 10086
# SQL 日志显示
logging:
level:
root: info
com.example.mydemo: debug
mybatis-plus:
global-config:
db-config:
id-type: auto
5.2.3 创建配置文件
spy.properties?
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2
5.2.4 测试
?5.2.5 总结
其实如果有这个SQL语句就不需要自带的SQL 输出了,可以把原来的注释掉(这样可以提高运行速度)
六、代码自动生成器(甩手掌柜)
6.1 Maven依赖
<!-- 代码生成器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.2</version>
</dependency>
<!-- 代码生成器模板引擎 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<!-- swagger可理解为接口文档规范 -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.6.2</version>
</dependency>
6.2 实现代码
package com.example.mydemo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.fill.Column;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class CodeGenerator {
public static void main(String[] args) {
DataSourceConfig.Builder builder1 = new DataSourceConfig.
Builder("jdbc:mysql://127.0.1:3306/MybatisPlusTest?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=true",
"root", "123456");
FastAutoGenerator.create(builder1)
.globalConfig((scanner, builder) -> builder.outputDir(System.getProperty("user.dir") + "/src/main/java")
.author(scanner.apply("作者名字"))
.enableSwagger()//开户swagger
)
.packageConfig((scanner, builder) -> builder.parent(scanner.apply("包名字")))
.strategyConfig((scanner, builder) -> builder.addInclude(getTables(scanner.apply("表名字,多个用?分隔开?all")))
.controllerBuilder().enableRestStyle().enableHyphenStyle().serviceBuilder().formatServiceFileName("%sService")
.entityBuilder().enableLombok().enableTableFieldAnnotation().addTableFills(
new Column("create_time", FieldFill.INSERT)
).build())
.templateEngine(new FreemarkerTemplateEngine())
.execute();
}
protected static List<String> getTables(String tables) {
return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
}
}
6.4 效果
?
后记
MybatisPlus 能解决15%的工作量,别外的85%是需要Mybatis(自定义接口)来完成,即便如此,我们仍然要怀着一种感恩的心来看待它,不仅仅它是中国人自己写的,毕竟你少花了15%的时间就相当于创建价值。所以不要做键盘侠,多一些支持,在这里推荐大家关注 MybatisPlus官网
全文完,如觉得本文对您多少还有些帮助,麻烦您点赞支持一下!
原创不易,转载请注明出处
|