目录
1.MyBatis-plus基础
1.1.mybatis-plus简介
1.2.基本使用
1.3.注解映射
1.4.命名转换问题
1.4.1.自动转换命名
1.4.2.关闭命名转换功能
2.MyBatis-plus的CRUD
2.1.BaseMapper核心接口
2.2.基础增删改查
2.3.QueryWrapper的用法
2.4.UpdateWrapper的用法
2.5.分页查询
2.6.其它复杂操作
3.MyBatis-plus逆向工程
MyBatis-Plus(简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
MyBatis-plus官网:MyBatis-Plus
特点:
- 润物无声:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑。
- 效率至上:只需简单配置,即可快速进行单表 CRUD 操作,从而节省大量时间。
- 丰富功能:代码生成、自动分页、逻辑删除、自动填充等功能一应俱全
下面我们就按照官网的 “快速开始” 教程,开始mybatis-plus的使用。
SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for dept -- ---------------------------- DROP TABLE IF EXISTS `dept`; CREATE TABLE `dept` ( `deptno` int(2) NOT NULL AUTO_INCREMENT, `dname` varchar(14) NOT NULL, `loc` varchar(50) DEFAULT NULL, PRIMARY KEY (`deptno`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records -- ---------------------------- INSERT INTO `dept` VALUES (1, '总裁办公室', '北京'); INSERT INTO `dept` VALUES (2, '技术部', '沈阳'); INSERT INTO `dept` VALUES (3, '销售部', '大连');
4.0.0 org.springframework.boot spring-boot-starter-parent 2.6.6 com.neusoft smp 0.0.1-SNAPSHOT smp Demo project for Spring Boot 1.8 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test com.baomidou mybatis-plus-boot-starter 3.5.1 mysql mysql-connector-java 5.1.49 org.springframework.boot spring-boot-maven-plugin
server: port: 8080 servlet: context-path: /smp logging: level: #org.springframework: debug com.neusoft.smp.mapper: debug spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/empinfo?characterEncoding=utf-8 username: root password: 123
package com.neusoft.smp; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan("com.neusoft.smp.mapper") public class SmpApplication { public static void main(String[] args) { SpringApplication.run(SmpApplication.class, args); } }
package com.neusoft.smp.po; public class Dept { private Integer deptno; private String dname; private String loc; @Override public String toString() { return this.deptno + "\t" + this.dname + "\t" + this.loc; } ... }
package com.neusoft.smp.mapper; import org.apache.ibatis.annotations.Mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.neusoft.smp.po.Dept; //在主启动类已经使用@MapperScan注解统一引入,所以这里不需要@Mapper注解了 public interface DeptMapper extends BaseMapper{}
package com.neusoft.smp; import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import com.neusoft.smp.mapper.DeptMapper; import com.neusoft.smp.po.Dept; @SpringBootTest class SmpApplicationTests { @Autowired private DeptMapper deptMapper; @Test public void testSelect() { Listlist = deptMapper.selectList(null); for(Dept dept : list) { System.out.println(dept); } } }
上面实例中,实体类名与表名,包括属性名与字段名都是完全一致的,所以mybatis-plus是可以识别的。但在实际开发中,很有可能会出现表名与实体类名不一致,或者属性名与字段名不一致的情况。此时就需要使用注解来进行映射。
注意:如果实体类名与属性名与数据库完全一致,那么上面注解都可以省略。
SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for deptinfo -- ---------------------------- DROP TABLE IF EXISTS `deptinfo`; CREATE TABLE `deptinfo` ( `deptid` int(2) NOT NULL AUTO_INCREMENT, `deptname` varchar(14) NOT NULL, `deptloc` varchar(50) DEFAULT NULL, PRIMARY KEY (`deptid`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records -- ---------------------------- INSERT INTO `deptinfo` VALUES ('1', '总裁办公室', '北京'); INSERT INTO `deptinfo` VALUES ('2', '技术部', '沈阳'); INSERT INTO `deptinfo` VALUES ('3', '销售部', '大连');
@TableName("deptinfo") public class Dept { @TableId(value="deptid",type=IdType.AUTO) private Integer deptno; @TableField("deptname") private String dname; @TableField("deptloc") private String loc; ... }
SELECT deptid AS deptno,deptname AS dname,deptloc AS loc FROM deptinfo
附录:
主键生成策略类型 |
描述 |
AUTO |
数据库 ID 自增 |
INPUT |
insert 前自行 set 主键值 |
ASSIGN_ID |
分配 ID(主键类型为 Number(Long 和 Integer)或 String) |
ASSIGN_UUID |
分配 UUID,主键类型为 String |
在实际开发中,项目中的类名、属性名,包括数据库中的表名、字段名,这些命名要严格遵守规范。一般来说:
mybatis-plus在生成sql语句时,会自动将java中类名或属性名中的驼峰命名,转换为下划线命名。
SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for dept -- ---------------------------- DROP TABLE IF EXISTS `dept`; CREATE TABLE `dept` ( `dept_id` int(11) NOT NULL AUTO_INCREMENT, `dept_name` varchar(20) NOT NULL, `dept_loc` varchar(20) DEFAULT NULL, PRIMARY KEY (`dept_id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records -- ---------------------------- INSERT INTO `dept` VALUES ('1', '总裁办公室', '北京'); INSERT INTO `dept` VALUES ('2', '技术部', '沈阳'); INSERT INTO `dept` VALUES ('3', '销售部', '大连');
public class Dept { private Integer deptId; private String deptName; private String deptLoc; @Override public String toString() { return this.deptId + "\t" + this.deptName + "\t" + this.deptLoc; } //... }
SELECT dept_id,dept_name,dept_loc FROM dept
如果数据库中没有采用下划线命名法,那么可以在SpringBoot的application.yml配置文件中关闭此功能:
mybatis-plus: configuration: map-underscore-to-camel-case: false
关闭此功能后,MyBatis-plus自动生成的sql语句如下
SELECT deptId,deptName,deptLoc FROM dept
查看BaseMapper核心接口的源代码,这里详细说明了封装的增删改查方法:
/** * Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能 */ public interface BaseMapperextends Mapper { /** * 插入一条记录 * @param entity 实体对象 */ int insert(T entity); /** * 根据 ID 删除 * @param id 主键ID */ int deleteById(Serializable id); /** * 根据实体(ID)删除 * @param entity 实体对象 */ int deleteById(T entity); /** * 根据 columnMap 条件,删除记录 * @param columnMap 表字段 map 对象 */ int deleteByMap(@Param(Constants.COLUMN_MAP) Map columnMap); /** * 根据 entity 条件,删除记录 * @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句) */ int delete(@Param(Constants.WRAPPER) Wrapper queryWrapper); /** * 删除(根据ID或实体 批量删除) * @param idList 主键ID列表或实体列表(不能为 null 以及 empty) */ int deleteBatchIds(@Param(Constants.COLLECTION) Collection> idList); /** * 根据 ID 修改 * @param entity 实体对象 */ int updateById(@Param(Constants.ENTITY) T entity); /** * 根据 whereEntity 条件,更新记录 * @param entity 实体对象 (set 条件值,可以为 null) * @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句) */ int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper updateWrapper); /** * 根据 ID 查询 * @param id 主键ID */ T selectById(Serializable id); /** * 查询(根据ID 批量查询) * @param idList 主键ID列表(不能为 null 以及 empty) */ List selectBatchIds(@Param(Constants.COLLECTION) Collection extends Serializable> idList); /** * 查询(根据 columnMap 条件) * @param columnMap 表字段 map 对象 */ List selectByMap(@Param(Constants.COLUMN_MAP) Map columnMap); /** * 根据 entity 条件,查询一条记录 * 查询一条记录,例如 qw.last("limit 1") 限制取一条记录, 注意:多条数据会报异常
* @param queryWrapper 实体对象封装操作类(可以为 null) */ default T selectOne(@Param(Constants.WRAPPER) WrapperqueryWrapper) { List ts = this.selectList(queryWrapper); if (CollectionUtils.isNotEmpty(ts)) { if (ts.size() != 1) { throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records"); } return ts.get(0); } return null; } /** * 根据 Wrapper 条件,判断是否存在记录 * @param queryWrapper 实体对象封装操作类 * @return */ default boolean exists(Wrapper queryWrapper) { Long count = this.selectCount(queryWrapper); return null != count && count > 0; } /** * 根据 Wrapper 条件,查询总记录数 * @param queryWrapper 实体对象封装操作类(可以为 null) */ Long selectCount(@Param(Constants.WRAPPER) Wrapper queryWrapper); /** * 根据 entity 条件,查询全部记录 * @param queryWrapper 实体对象封装操作类(可以为 null) */ List selectList(@Param(Constants.WRAPPER) Wrapper queryWrapper); /** * 根据 Wrapper 条件,查询全部记录 * @param queryWrapper 实体对象封装操作类(可以为 null) */ List
//@TableName("dept") public class Dept { @TableId(type=IdType.AUTO) private Integer deptno; //@TableField("dname") private String dname; //@TableField("loc") private String loc; ... }
package com.neusoft.smp; import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import com.neusoft.smp.mapper.DeptMapper; import com.neusoft.smp.po.Dept; @SpringBootTest class SmpApplicationTests { @Autowired private DeptMapper deptMapper; //全查询 @Test public void testSelect() { Listlist = deptMapper.selectList(null); for(Dept dept : list) { System.out.println(dept); } } //根据主键查询 @Test public void testSelectById() { //注意:根据ID查询时要指定主键生成策略 Dept dept = deptMapper.selectById(41); System.out.println(dept); } //插入 @Test public void testInsert() { //注意:Insert时要指定主键生成策略 int result = deptMapper.insert(new Dept(null,"技术部","沈阳市")); System.out.println(result); } //更新 @Test public void testUpdate() { int result = deptMapper.updateById(new Dept(45,"财务部","沈阳市")); System.out.println(result); } //删除 @Test public void testDelete() { int result = deptMapper.deleteById(46); System.out.println(result); } }
MyBatis-plus封装了条件构造器 QueryWrapper,用于进行多条件的查询、更新、删除(insert不需要条件)。
@Test public void testQueryWrapper() { QueryWrapper qw = new QueryWrapper(); qw.gt("deptid", 40); qw.lt("deptId", 44); qw.like("deptName", "术"); //多条件查询 Listlist = deptMapper.selectList(qw); for(Dept dept : list) { System.out.println(dept); } //多条件更新 //int result = deptMapper.update(new Dept(null,"aaa","bbb"), qw); //多条件删除 //int result = deptMapper.delete(qw); }
运行之后可以查看日志:
[main]: ==> Preparing: SELECT deptId,deptName,deptLoc FROM dept WHERE (deptId > ? AND deptid < ? AND deptname LIKE ?) [main]: ==> Parameters: 40(Integer), 44(Integer), %术%(String) [main]: <== Total: 1
附录:常用QueryWrapper对象方法
QueryWrapper对象方法 |
描述 |
eq |
等于 = |
ne |
不等于 <> |
gt |
大于 > |
ge |
大于等于 >= |
lt |
小于 < |
le |
小于等于 <= |
between |
BETWEEN 值1 AND 值2( 例: ) |
like |
LIKE '%值%' |
isNull |
字段 IS NULL |
in |
字段 IN ( 例: ) |
orderBy |
排序 (例: orderBy(true, true, "id", "name")) |
UpdateWrapper用于update时非常方便,因为它封装了set方法,可以将更新数据使用set方法传入。这样在更新时只需传入一个UpdateWrapper对象即可。
@Test public void testUpdateWrapper() { UpdateWrapper uw = new UpdateWrapper(); uw.set("deptname", "XX部"); uw.set("deptloc", "YY市"); uw.gt("deptId", 1); uw.lt("deptId", 3); uw.like("deptname", "术"); int result = deptMapper.update(null, uw); System.out.println(result); }
生成的sql语句如下
UPDATE dept SET deptname=?,deptloc=? WHERE (deptId > ? AND deptId < ? AND deptname LIKE ?)
通过MyBatis-plus提供的自动分页插件:PaginationInnerInterceptor,就可以方便的实现分页功能。
package com.neusoft.mp; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }
package com.neusoft.mp; import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.neusoft.mp.mapper.DeptMapper; import com.neusoft.mp.po.Dept; @SpringBootTest class MpApplicationTests { @Autowired private DeptMapper deptMapper; @Test public void testPage() { //创建IPage对象,设置参数:第几页、每页显示最大行数 IPagepage = new Page<>(3, 4); //调用selectPage方法进行分页查询,返回值为IPage对象。 IPage deptPage = deptMapper.selectPage(page, null); //通过IPage对象获取当前满足条件总行数 System.out.println(deptPage.getTotal()); //通过IPage对象获取每页显示最大条数 System.out.println(deptPage.getSize()); //通过IPage对象获取当前页 System.out.println(deptPage.getCurrent()); //通过IPage对象获取当前分页总页数 System.out.println(deptPage.getPages()); //通过IPage对象获取业务数据 List list = deptPage.getRecords(); for(Dept dept : list) { System.out.println(dept); } } }
当然,我们在实际开发中会遇到很多复杂操作,比如:多表连接查询,批量操作等等。此时官方推荐我们使用Mapper映射文件来书写复杂的SQL语句,就像是在MyBatis中一样。
mybatis-plus: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.neusoft.smp.po
package com.neusoft.smp.mapper; import org.apache.ibatis.annotations.Mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.neusoft.smp.po.Dept; //@Mapper //也可以在主启动类中使用@MapperScan注解统一引入 public interface DeptMapper extends BaseMapper{ public Dept getDeptById(Integer deptno); }
MyBatis-plus逆向工程可以根据数据库结构,自动生成开发中需要的类:controller、service、mapper等等。
1.添加MyBatis-plus逆向工程依赖
com.baomidou
mybatis-plus-generator
3.5.1
org.freemarker
freemarker
2.3.31
2.添加逆向工程启动类(注意:下面代码适合于3.5.1版本)
package com.neusoft.mp;
import java.util.Collections;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
public class MyGenerator {
private static final String URL = "jdbc:mysql://localhost:3306/emp?characterEncoding=utf-8";
private static final String USERNAME = "root";
private static final String PASSWORD = "123";
private static final String PACKAGE_NAME = "com.neusoft.mp";
private static final String AUTHOR_NAME = "zhangsan";
private static final String OUTDIR_JAVA = "D:\\mywork\\mp\\src\\main\\java";
private static final String OUTDIR_XML = "D:\\mywork\\mp\\src\\main\\resources\\mapper";
public static void main(String[] args) {
FastAutoGenerator.create(URL, USERNAME, PASSWORD)
.globalConfig(builder -> {
builder.author(AUTHOR_NAME) // 设置作者
.fileOverride() // 覆盖已生成文件
.outputDir(OUTDIR_JAVA) // 指定输出目录
.disableOpenDir(); // 禁止打开输出目录
})
.packageConfig(builder -> {
builder.parent(PACKAGE_NAME) // 设置包名
.entity("po") //设置实体类包名
.pathInfo(Collections.singletonMap(OutputFile.mapperXml, OUTDIR_XML)); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("emp") // 设置需要生成的表名
.controllerBuilder() //这里写controllerBuilder,表示将开始controller配置
.enableRestStyle();
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}
3. 运行逆向工程类即可生成代码。