Mybatis-Plus在工作中的使用
现在一般项目使用的持久层都是Mybatis,而我们介绍的MybatisPlus就是Mybatis的增强版,在MybatisPlus的文档中就有描述:
MybatisPlus把自己定位在了Mybatis最好的搭档,在工作中使用mybatisPlus确实简化了很多我们的CRUD操作( CRUD是指在做计算处理时的增加(Create)、 检索(Retrieve) 、更新(Update) 、和删除(Delete) 几个单词的首字母简写。CRUD主要被用在描述软件系统中数据库或者持久层的基本操作功能。 )
MybatisPlus依赖配置:
其实mybatisPlus的官方指南已经写的很明白了。
首先在StringBoot项目的POM.xml文件中引入Spring-start父工程。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.4</version>
<relativePath/>
</parent>
接下来引入需要的依赖:
第一个是用于简化实体类的工具lombok
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
第二个是数据库的驱动mysql
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
第三个就是mybatisPlus的依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.2</version>
</dependency>
导入依赖后,就可以使用mybatisPlus了,导入了mybatisPlus就不需要再次导入mybatis的依赖了。
Mybatis使用方式:
从前我们使用mybatis的步骤为:
1.创建mapper接口,添加@Mapper注解
2.创建mapper.xml文件,与mapper接口进行绑定,在文件中编写具体的SQL,进行执行。
3.在SpringBoot的启动器上添加==@MapperScan==,使Spring可以扫描到mapper文件。
但是现在使用mybatisPlus,我们一些简单的SQL就不需要写在mapper文件当中了,简化了mapper文件的内容,甚至可能不需要mapper.xml文件。
MybatisPlus使用方式:
1.创建Entity实体类,使用注解与数据库字段进行关联。
2.创建mapper接口,添加@Mapper注解,继承BaseMapper接口,泛型为Entity实体类,这样就与实体类进行关联。
3.在SpringBoot的启动器上添加==@MapperScan==,使Spring可以扫描到mapper文件。
4.此时创建mapper接口对象,mybatisPlus就已经帮我们封装了一些常用的CRUD方法,此时我们还可以在Service层继承ServiceImpl直接调用mybatisPlus在serviceImpl中封装的方法。
5.在SpringBoot的启动器上添加==@MapperScan==,使Spring可以扫描到mapper文件。
这就是创建的Entity实体类
mapper接口文件:
当Service使用时:
时间添加小窍门(工作中不允许进行数据库的操作):
在工作中我们设计表的时候,一定会有两个字段,一个是创建时间,一个是更新时间。
1.当我们希望创建的时候能够自动填写当前时间的时候,我们就在默认内容中添加表达式==current_timestamp ==默认添加当前时间戳。
2.当我们希望每次更新的时候自动更新时间,我们就在字段上勾选根据当前时间戳更新。
这种情况我们不在数据库中进行操作,那我们可以使用mybatisPlus在代码中进行操作。
针对字段进行设置,我们使用==@TableField注解对字段进行设置。
1.设置注解
我们需要在@TableField注解添加fill属性==,表明字段在添加时进行操作,还是更新时进行操作,还是两种情况都要进行操作。
2.编写处理器来进行处理
编写一个handle(处理器) 实现MetaObjectHandle,重写其中的两个方法,一个是插入时的方法,一个是更新时的方法。下图为mybatisPlus的介绍文档中的处理器使用,如果想要在更新或插入时自动修改字段值,我们要使用该方法(setFieldValueByName方法已过时)。
乐观锁与悲观锁
乐观锁:就是认为操作不会出现失误,每次操作都会成功,所以不会上锁。
悲观锁:就是认为每次操作都会出现失误,每次操作都会上锁。
乐观锁在操作时就是添加version版本字段,每次插入时都会校验上一次的version版本,如果版本符合,则更新操作,顺便version变为version+1,用于多线程的时候。
在mybatisPlus中有专门注解用于乐观锁==@Version==
首先我们要在创建一个config文件,将 @MapperScan 交给mybatisPlus的config文件扫描, @EnableTransactionManagement 用于开启事务, @Configuration 表明为配置类。
/**
* 注册乐观锁插件
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
完成配置。
只有执行updateById与update方法时生效。会先查询此条数据,然后进行更新。
查询:
我们可以直接通过mybatisPlus封装的方法进行查询,也可以创建Map集合(Map集合仅可以进行等于查询),集合Key为字段名,value为条件。使用selectByMap()方法,可以根据条件进行查询。
分页查询:
分页查询也需要配置一个拦截器。与乐观锁配置在同一个文件中
在mybatisPlus中有两个页面的变量,一个是Page一个是IPage,两者的关系为Page实现了IPage。IPage为接口,Page为实体类,在工作中可能有互相调用的关系,在Feign接口中,是不能用IPage接收的,因为接口无法被实例化,需要使用Page接收。
Service文件:
//获取查询条件
QueryWrapper<user> queryWrapper = getQueryWrapper(pageQueryDto.getParams());
//创建IPage对象 user为PO对象
IPage<user> page = new Page<>(pageQueryDto.getPageNum(), pageQueryDto.getPageSize());
//调用分页查询方法
page = page(page, queryWrapper);
当我们查询的逻辑比较复杂时,就不能使用mybatisPlus自带的分页查询了,我们还是需要在mapper.xml中进行sql的编写,但是此时我们可以使用QueryWrapper对象构建条件。
mapper接口文件:
将IPage对象与queryWrapper对象传入,wrapper对象的写法为固定模式,修改后不生效。
List<user> selectToPage(IPage<user> page, @Param("ew") Wrapper<user> inDto);
mapper.xml文件:
由于wrapper为条件的集合,则在xml文件中使用此表达式,就可以自动拼接上查询条件。
SELECT id, name, age FROM user ${ew.customSqlSegment}
逻辑删除:
在工作中删除一般为逻辑删除,意思就是不会真正的在数据库进行删除,而是通过一种方式表明此条数据为作废数据。
mybatisPlus文档这这么描述的:
我学习的视频中还说需要添加(不知道是否有用,如有需要则添加)
/**
* 注册逻辑删除插件
*/
@Bean
public ISqlInjector sqlInjector () {
return new LogicSqlInjector();
}
条件构造器
在MybatisPlus中,条件构造器是很核心的内容,其实条件构造器就是一个对象。
QueryWrapper<Entity实体类名> queryWrapper = getQueryWrapper(Entity实体类对象);
创建了这个对象后,我们可以使用很多的方法,进行条件的拼接,这些在mybatisPlus的文档中都有介绍。
那我们如何去使用呢?我举一个例子
queryWrapper.in("name", inDto.getList());
这个就是条件构造器的对象的方法,表示在name字段中in查询,如果name字段的值在list里面,则显示,相当于in(a, b, c),诸如此类的方法还有很多中。
自动代码生成器:
3.0.3版本以后,自动代码生成器就需要额外引入依赖了。
此时我们引入依赖。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.2</version>
</dependency>
还需要引入模板:
<!-- 默认模板-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
<!-- freeMarker模板-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
最后创建一个test类,然后编写代码生成器的配置就可以了
注意:如果需要自定义模板,则需要创建模板文件
//手动输入自己的包名
String myPackage = "";
//作者名
String author = "";
//表名
String tableName = "";
if (StringUtils.isNotEmpty(myPackage) && StringUtils.isNotEmpty(author) && StringUtils.isNotEmpty(tableName)) {
// 1、创建代码生成器
AutoGenerator mpg = new AutoGenerator();
// 2、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir"); //获取当前工程的src路径
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor(author); //作者
gc.setOpen(false); //生成后是否打开资源管理器
gc.setFileOverride(false); //重新生成时文件是否覆盖
gc.setSwagger2(true);//开启Swagger2模式
gc.setServiceName("%sService"); // 自定义Service模板 文件名
mpg.setGlobalConfig(gc);
// 3、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("数据库地址");
dsc.setDriverName("数据库连接");
dsc.setUsername("用户名");
dsc.setPassword("密码");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 4、包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName(null); //模块名
pc.setParent("com.包名");
pc.setController("controller." + myPackage);
pc.setEntity("entity." + myPackage);
pc.setService("service." + myPackage);
pc.setMapper("mapper." + myPackage);
mpg.setPackageInfo(pc);
//5、自定义service模板(因项目中不需要serviceImpl层,所以自定义service模板)
TemplateConfig templateConfig = new TemplateConfig();
// 关闭原有生成
templateConfig.setService(null);
templateConfig.setServiceImpl(null);
templateConfig.setMapper(null);
templateConfig.setXml(null);
templateConfig.setController(null);
mpg.setTemplate(templateConfig);
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
String serviceTemplatePath = "/templates/tempalteService.java.vm";
// 自定义输出配置
List<FileOutConfig> fileOutConfigList = new ArrayList<>();
// 自定义Service模板 (如不需要就去掉)
fileOutConfigList.add(new FileOutConfig(serviceTemplatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return projectPath + "/src/main/java/com/包名/" + "service/" + myPackage + "/"
+ tableInfo.getEntityName() + "Service" + StringPool.DOT_JAVA;
}
});
String mapperTemplatePath = "/templates/tempalteMapper.java.vm";
// 自定义Mapper接口模板 (如不需要就去掉)
fileOutConfigList.add(new FileOutConfig(mapperTemplatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return projectPath + "/src/main/java/com/包名/" + "mapper/" + myPackage + "/"
+ tableInfo.getEntityName() + "Mapper" + StringPool.DOT_JAVA;
}
});
String mapperXmlTemplatePath = "/templates/tempalteMapperXml.xml.vm";
// 自定义XML文件模板 (如不需要就去掉)
fileOutConfigList.add(new FileOutConfig(mapperXmlTemplatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return projectPath + "/src/main/resources/" + "mapper/" + myPackage + "/"
+ tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
String controllerTemplatePath = "/templates/tempalteController.java.vm";
// 自定义Controller文件模板 (如不需要就去掉)
fileOutConfigList.add(new FileOutConfig(controllerTemplatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return projectPath + "/src/main/java/com/包名/" + "controller/" + myPackage + "/"
+ tableInfo.getEntityName() + "Controller" + StringPool.DOT_JAVA;
}
});
cfg.setFileOutConfigList(fileOutConfigList);
mpg.setCfg(cfg);
// 6、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude(tableName);//表名
strategy.setNaming(NamingStrategy.underline_to_camel);//下划线转驼峰
if ("st_".equals(tableName.substring(0, 3))) {
strategy.setTablePrefix("st_"); //生成实体时去掉表前缀
}
strategy.setColumnNaming(NamingStrategy.underline_to_camel);//下划线转驼峰
strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作
strategy.setRestControllerStyle(true); //restful api风格控制器
strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符
mpg.setStrategy(strategy);
// 7、执行
mpg.execute();
}else {
System.out.println("输入必填参数");
}
}
另一件事,如果需要分页查询,但是发现查询出来的total与实际数据不符,可以试试如下方法,关闭自动优化。
page.setOptimizeCountSql(false);
如果有需要自定义配置的小伙伴不会的也可以私聊我进行帮助哦!