MyBatis-Plus之快速启动
MyBatis-Pluis官网
在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹:图中blog具体看项目
@MapperScan("com.example.blog.mapper")
1.核心功能——代码生成器
1.1添加依赖
配置自动生成时的错误:mybatis-plus自动生成的时候报错 java.lang.NoClassDefFoundError
参考 https://blog.csdn.net/wangjinb/article/details/106488308
MyBatis-Plus 从 3.0.3 之后移除了代码生成器与模板引擎的默认依赖,需要手动添加相关依赖:
添加 代码生成器 依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.3.2</version>
</dependency>
添加 模板引擎 依赖,MyBatis-Plus 支持 Velocity(默认)、Freemarker、Beetl,用户可以选择自己熟悉的模板引擎,如果都不满足您的要求,可以采用自定义模板引擎。
Velocity(***默认就是说该依赖必须添加,否则会报错 ***):
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
Freemarker:
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
Beetl:
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId>
<version>3.1.8.RELEASE</version>
</dependency>
注意!如果您选择了非默认引擎,需要在 AutoGenerator 中 设置模板引擎。
AutoGenerator generator = new AutoGenerator();
// set freemarker engine
generator.setTemplateEngine(new FreemarkerTemplateEngine());
// set beetl engine
generator.setTemplateEngine(new BeetlTemplateEngine());
// set custom engine (reference class is your custom engine class)
generator.setTemplateEngine(new CustomTemplateEngine());
// other config
...
1.2编写生成文件:
我的生成文件 CodeCreate.java
package com.example.CodeGenerator;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.mysql.cj.xdevapi.Table;
//代码生成器
public class CodeCreate {
public static void main(String[] args){
//DataSourceConfig 数据源配置,通过该配置,指定需要生成代码的具体数据库
//StrategyConfig 数据库表配置,通过该配置,可指定需要生成哪些表或者排除哪些表
//PackageConfig 包名配置,通过该配置,指定生成代码的包路径
//TemplateConfig 模板配置,可自定义代码生成的模板,实现个性化操作
//GlobalConfig 全局策略配置,具体请查看
//InjectionConfig 注入配置,通过该配置,可注入自定义参数等操作以实现个性化操作
AutoGenerator mpg = new AutoGenerator();//构建一个代码自动生成器对象
String projectPath = System.getProperty("user.dir");
//1.全局配置 -> GlobalConfig
GlobalConfig gc = new GlobalConfig();
gc.setAuthor("lzy"); //开发人员,默认null
gc.setActiveRecord(false);//开启 ActiveRecord 模式
gc.setBaseResultMap(false);//开启 BaseResultMap
gc.setBaseColumnList(false);//开启 baseColumnList
gc.setDateType(DateType.ONLY_DATE); //时间类型对应策略,默认值:TIME_PACK
gc.setIdType(IdType.ID_WORKER);//指定生成的主键的ID类型
gc.setEnableCache(false);//是否在xml中添加二级缓存配置
gc.setFileOverride(true);//是否覆盖已有文件,默认false
gc.setKotlin(false);//开启 Kotlin 模式
gc.setOpen(false); //是否打开输出目录
gc.setSwagger2(true);//开启 swagger2 模式
gc.setOutputDir(projectPath+"/src/main/java");//生成文件的输出目录
gc.setControllerName("%sController");//controller 命名方式
gc.setEntityName("%sEntity");//实体命名方式
gc.setMapperName("%sMapper");//mapper 命名方式
gc.setServiceName("%sService");//service 命名方式,%s填充实体属性,%s为占位符
gc.setServiceImplName("%sServiceImpl");//service impl 命名方式
gc.setXmlName("%sXml");//Mapper xml 命名方式
mpg.setGlobalConfig(gc);
//2.配置数据源 -> dataSourceConfig
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver"); //驱动名称
dsc.setUsername("root"); //数据库连接用户名
dsc.setPassword("123456"); //数据库连接密码
dsc.setDbType(DbType.MYSQL); //数据库类型,该类内置了常用的数据库类型
//dsc.setDbQuery()数据库信息查询类,默认由 dbType 类型决定选择对应数据库内置实现,实现 IDbQuery 接口自定义数据库查询 SQL 语句 定制化返回自己需要的内容
//dsc.setKeyWordsHandler();
//dsc.setSchemaName(); 数据库 schema name
//dsc.setTypeConvert();类型转换,默认由 dbType 类型决定选择对应数据库内置实现
mpg.setDataSource(dsc);
//4.PackageConfig -> 包的相关配置
PackageConfig pc = new PackageConfig();
pc.setParent("com.example"); //父包名。如果为空,将下面子包名必须写全部, 否则就只需写子包名
pc.setModuleName("blog"); //父包模块名
pc.setEntity("entity"); //Entity目录名
pc.setMapper("mapper"); //Mapper目录名
pc.setController("controller"); //Controller目录名
pc.setService("service"); //Service目录名
pc.setServiceImpl("serviceImpl");//Service Impl包名
pc.setXml("xml"); // Mapper XML包名
//pc.setPathInfo() 路径配置信息
mpg.setPackageInfo(pc);
//3.数据库表配置 -> StrategyConfig
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("User"); //设置映射表名
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
//strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
strategy.setEntityLombokModel(true); //自动lombok
strategy.setRestControllerStyle(true);//生成 @RestController 控制器
strategy.setLogicDeleteFieldName("deleted");//逻辑删除名字
//isCapitalMode是否大写命名,skipView是否跳过视图,
mpg.setStrategy(strategy);
//5.模板配置 -> TemplateConfig
// TemplateConfig tc = new TemplateConfig();
// tc.setEntity();
// tc.setEntityKt();
// tc.setService();
// tc.setServiceImpl();
// tc.setMapper();
// tc.setXml();
// tc.setController();
// mpg.setPackageInfo(tc);
//6.注入配置 -> injectionConfig
mpg.execute(); //执行
}
}
1.3我的目录结构:
以one为例运行CodeCreate可以生成如下图的目录结构
在实际编写中service层和impl、xml层并未实际用处可以删除,编写一下controller层即可返回数据。
package com.example.blog.controller;
import com.example.blog.entity.UserEntity;
import com.example.blog.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* <p>
* 前端控制器
* </p>
*
* @author lzy
* @since 2020-07-01
*/
@RestController
public class UserController {
@Autowired
UserMapper userMapper;
@RequestMapping(value = "/qurry")
public UserEntity qurryOne(){
return userMapper.selectById(4L);
}
@RequestMapping(value = "/list")
public List<UserEntity> queryAllUser(){
List<UserEntity> users = userMapper.selectList(null);
return users;
}
@RequestMapping(value = "/insert/{name}/{age}/{email}")
public String insertUser(@PathVariable("name")String name,@PathVariable("age")Integer age,@PathVariable("email")String email){
UserEntity user = new UserEntity();
user.setName(name);
user.setAge(age);
user.setEmail(email);
userMapper.insert(user);
return "insert is running";
}
@RequestMapping(value = "/update/{id}/{name}/{age}/{email}")
public String update(@PathVariable("id")Long id,@PathVariable("name")String name,@PathVariable("age")Integer age,@PathVariable("email")String email){
UserEntity user = new UserEntity();
user.setId(id);
user.setName(name);
user.setAge(age);
user.setEmail(email);
userMapper.updateById(user);
return "update is running";
}
@RequestMapping(value = "/delete/{id}") //删除
public String DeleteUserById(@PathVariable("id")Long id){
int r = userMapper.deleteById(id);
return "Delete is running";
}
}
如下图是访问/hello2得到 list里面的数据。
2 插件扩展——逻辑删除
2.1配置文件
application.properties文件配置如下:
#配置逻辑删除
mybatis-plus.global-config.db-config.logic-delete-field= flag #全局逻辑删除字段值 3.3.0开始支持,详情看下面。
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
application.yml文件配置如下:(如果你的默认值和mp默认的一样,该配置可无)
global-config:
db-config:
logic-delete-field: flag #全局逻辑删除字段值 3.3.0开始支持,详情看下面。
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
2.2数据库表中添加字段deleted
在数据库中书写deleted字段,默认为0
2.3 Entity层User/UserEntity文件下添加字段(@TableLogic)
字段支持所有数据类型(推荐使用 Integer,Boolean,LocalDateTime)
如果使用LocalDateTime,建议逻辑未删除值设置为字符串null,逻辑删除值只支持数据库函数例如now() 效果:
使用mp自带方法删除和查找都会附带逻辑删除功能 (自己写的xml不会)
设置逻辑删除后,执行删除操作实际上是更新操作,更新deleted为1,从而数据库可见,用户不可见
说明:
example
删除 update user set deleted=1 where id =1 and deleted=0
查找 select * from user where deleted=0
全局逻辑删除: begin 3.3.0
如果公司代码比较规范,比如统一了全局都是flag为逻辑删除字段。
使用此配置则不需要在实体类上添加 @TableLogic。
但如果实体类上有 @TableLogic 则以实体上的为准,忽略全局。 即先查找注解再查找全局,都没有则此表没有逻辑删除。
mybatis-plus:
global-config:
db-config:
logic-delete-field: flag #全局逻辑删除字段值
附件说明
逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。 如果你需要再查出来就不应使用逻辑删除,而是以一个状态去表示。
如: 员工离职,账号被锁定等都应该是一个状态字段,此种场景不应使用逻辑删除。若确需查找删除数据,如老板需要查看历史所有数据的统计汇总信息,请单独手写sql。
3.条件构造器——查询
3.1编写测试类 WrapperTest
package com.example;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.blog.mapper.UserMapper;
import com.example.blog.entity.UserEntity;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
@SpringBootTest
public class WrapperTest {
@Autowired
private UserMapper userMapper;
@Test
void test1(){
//查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于12
QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
wrapper
.isNotNull("name")
.isNotNull("email")
.ge("age",12);
userMapper.selectList(wrapper).forEach(System.out::println);
}
@Test
void test2(){
//查询名字风花雪月
QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
wrapper.eq("name","风花雪月");
UserEntity user = userMapper.selectOne(wrapper);
System.out.println(user);
}
@Test
void test3(){
//查询年龄在18-30
QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
wrapper.between("age",18,30);
Integer count = userMapper.selectCount(wrapper);
System.out.println(count);
}
@Test
void test4(){
//查询名字没有e并且右边以t开头
QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
wrapper
.notLike("name","e")
.likeRight("email","t");
List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
maps.forEach(System.out::println);
}
@Test
void test5(){
//id在子查询中查询出来
QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
wrapper.inSql("id","select id from user where id>3");
List<Object> objects = userMapper.selectObjs(wrapper);
objects.forEach(System.out::println);
}
@Test
void test6(){
//通过id排序
QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
wrapper.orderByAsc("id");
List<UserEntity> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
}
4.核心功能——分页插件
4.1编写配置类MyBatisPlusConfig (取自官网拿来即用)
package com.example.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement
@Configuration
@MapperScan("com.example.blog.mapper") //扫描文件夹
public class MyBatisPlusConfig {
//分页插件
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
// paginationInterceptor.setOverflow(false);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
// paginationInterceptor.setLimit(500);
// 开启 count 的 join 优化,只针对部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
}
4.2测试函数以及效果图
5.插件扩展——自动填充功能
5.1步骤一:数据库添加字段
5.2步骤二:在UserEntity中添加字段,也可以在自动生成中配置
5.3步骤三:自定义实现类 MyMetaObjectHandler
package com.example.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
@Slf4j
@Component //
public class MyMetaObjectHandler implements MetaObjectHandler {
//插入时的更新策略
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill...");
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
}
//更新时的填充策略
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill...");
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
5.4注意事项:
字段必须声明TableField注解,属性fill选择对应策略,该声明告知Mybatis-Plus需要预留注入SQL字段
填充处理器MyMetaObjectHandler在 Spring Boot 中需要声明@Component或@Bean注入
要想根据注解FieldFill.xxx和字段名以及字段类型来区分必须使用父类的strictInsertFill或者strictUpdateFill方法
不需要根据任何来区分可以使用父类的fillStrategy方法**
5.5 FieIdFill策略在5.2中的应用
public enum FieldFill {
/**
* 默认不处理
*/
DEFAULT,
/**
* 插入填充字段
*/
INSERT,
/**
* 更新填充字段
*/
UPDATE,
/**
* 插入和更新填充字段
*/
INSERT_UPDATE
}