Mybatis-Plus使用整理
MyBatis-Plus是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生;
- MyBatis-Plus是怎么增强的呢?
已经封装好了一些crud方法,我们不需要再写xml了,直接调用这些方法就行,类似JPA但优于JPA;
简介:[https://mp.baomidou.com/guide/]
- 导入依赖
1 2 3 4 5 6 7 8 9 10 11 | <dependency> <groupId>mysql< /groupId > <artifactId>mysql-connector-java< /artifactId > < /dependency > <!--mybatis plus和springboot整合--> <dependency> <groupId>com.baomidou< /groupId > <artifactId>mybatis-plus-boot-starter< /artifactId > <version>3.4.1< /version > < /dependency > |
- MyBatis-Plus配置控制台打印日志
1 2 | #配置mybatis plus打印sql日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl |
- BaseMapper使用
Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能;
API使用参考源码:com.baomidou.mybatisplus.core.mapper.BaseMapper
- QueryWrapper使用
查询包装类,可以封装多数查询条件,泛型指定返回的实体类;
可以封装sql对象,包括where条件,order by排序,select哪些字段等等;
核心API:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | eq :等于 ne :不等于 gt:大于 ge :大于等于 lt:小于 le :小于等于 or:拼接or between:两个值中间 notBetween:不在两个值中间 like:模糊匹配 notLike:不像 likeLeft:左匹配,eg: like '%a' likeRight:右边匹配,eg:like 'a%' isNull:字段为空 in : in 范围查询 groupBy:分组 orderByAsc:升序 orderByDesc:降序 having:having查询 |
API使用参考源码:com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
- MyBatis-Plus 常用注解
- @TableName 用于定义表名
-
@TableId 用于定义表的主键
- 属性
1 2 | value 用于定义主键字段名 type 用于定义主键类型(主键策略 IdType) |
-
-
- 主键策略
-
1 2 3 4 5 | IdType.AUTO 主键自增,系统分配,不需要手动输入 IdType.NONE 未设置主键 IdType.INPUT 需要自己输入 主键值 IdType.ASSIGN_ID 系统分配 ID,用于数值型数据(Long,对应 mysql 中 BIGINT 类型) IdType.ASSIGN_UUID 系统分配 UUID,用于字符串型数据(String,对应 mysql 中 varchar(32) 类型) |
-
- @TableField 用于定义表的非主键字段
-
-
- 属性
-
1 2 3 4 5 6 7 8 9 10 | value:用于定义非主键字段名,用于别名匹配,假如Java对象属性和数据库属性不一样 exist:用于指明是否为数据表的字段, true 表示是, false 为不是,假如某个java属性在数据库没对应的字段则要标记为 false fill:用于指定字段填充策略(FieldFill,用的不多) 字段填充策略:一般用于填充 创建时间、修改时间等字段 FieldFill.DEFAULT 默认不填充 FieldFill.INSERT 插入时填充 FieldFill.UPDATE 更新时填充 FieldFill.INSERT_UPDATE 插入、更新时填充 |
- 分页配置
- 旧版本写法(MyBatis-Plus 3.4.0之前)
1 2 3 4 | @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } |
参考源码:com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor
-
- 新版本写法(MyBatis-Plus 3.4.0之后)
1 2 3 4 5 6 7 | @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } |
-
- 测试分页:
建表sql
1 2 3 4 5 6 7 8 9 | CREATE TABLE `banner` ( ` id ` int(11) unsigned NOT NULL AUTO_INCREMENT, `img` varchar(524) DEFAULT NULL COMMENT '图片' , `url` varchar(524) DEFAULT NULL COMMENT '跳转地址' , `weight` int(11) DEFAULT NULL COMMENT '权重' , `version` int(11) DEFAULT '1' , `deleted` int(11) DEFAULT '0' COMMENT '0是未删除,1是已经删除' , PRIMARY KEY (` id `) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; |
实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | @Data @TableName ( "banner" ) public class BannerDO { @TableId (value = "id" ,type = IdType.AUTO) private Integer id; private String img; private String url; @TableField ( "weight" ) private Integer weight; @TableField (exist = false ) private Date createTime; /** * 乐观锁版本号 */ @Version private Integer version; /** * 逻辑删除标识位 */ private Integer deleted; } |
分页测试:
1 2 3 4 5 6 7 8 9 10 11 | @Test public void testPage1() { // 第1页,每页2条 Page<BannerDO> page = new Page<>( 1 , 2 ); IPage<BannerDO> bannerDOIPage = bannerMapper.selectPage(page, null ); log.info( "总条数" + bannerDOIPage.getTotal()); log.info( "总页数" + bannerDOIPage.getPages()); log.info( "当前页数:" + bannerDOIPage.getCurrent()); // 获取当前数据 log.info(bannerDOIPage.getRecords().toString()); } |
执行结果如下:
启用分页插件,MyBatis-Plus会先进行select count(*)对当前表进行统计之后再分页;
- XML自定义分页的写法
自定义Mapper
1 2 3 | public interface BannerMapper extends BaseMapper<BannerDO> { IPage<BannerDO> customizeSelectPage(Page<?> page, @Param ( "weight" ) int weight); } |
自定义XML
1 2 3 | < select id = "customizeSelectPage" resultType= "BannerDO" > select id , img, url, weight, version, deleted from banner where weight= #{weight} < /select > |
测试分页:
1 2 3 4 5 6 7 8 9 10 11 | @Test public void testPage2() { // 第1页,每页2条 Page<BannerDO> page = new Page<>( 1 , 2 ); IPage<BannerDO> bannerDOIPage = bannerMapper.customizeSelectPage(page, 2 ); log.info( "总条数" + bannerDOIPage.getTotal()); log.info( "总页数" + bannerDOIPage.getPages()); log.info( "当前页数:" + bannerDOIPage.getCurrent()); // 获取当前数据 log.info(bannerDOIPage.getRecords().toString()); } |
执行结果如下:
参考:[https://mp.baomidou.com/guide/page.html]
- MyBatis-Plus 自定义xml
配置mapper路径,如果采用默认路径可以不配
1 2 | #默认配置路径 mybatis-plus.mapper-locations=classpath*: /mapper/ *Mapper.xml |
- MyBatis-Plus 全局配置文件
1 2 | #配置最新全局配置文件 mybatis-plus.config-location = classpath*:mybatis-config.xml |
-
- 注意:config-location和configuration不能同时出现,需要注释配置文件里的相关配置
mybatis-config.xml
1 2 3 4 5 6 7 8 | <?xml version= "1.0" encoding= "UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration> <settings> <!--控制台输出日志--> <setting name= "logImpl" value= "STDOUT_LOGGING" /> < /settings > < /configuration > |
- 配置文件配置 自定义xml映射的包扫描路径
eg:
1 2 | #配置文件配置 自定义sql的包扫描 实体类的路径 mybatis-plus. type -aliases-package=org.example.manager.model |
- MyBatis-Plus下划线转驼峰配置,默认就是true
1 | mybatis-plus.configuration.map-underscore-to-camel- case = true |
- MyBatis-Plus配置全局默认主键类型,实体类就不用加 @TableId(value = "id", type = IdType.AUTO)
1 | mybatis-plus.global-config.db-config. id - type =auto |
- MyBatis-Plus指定查询字段
eg:
1 | List<BannerDO> bannerDOList = bannerMapper.selectList( new QueryWrapper<BannerDO>().select( "id" , "url" )); |
题外话,关于SELECT * 与 SELECT指定字段的区别:
-
- 网络I/O
SELECT * 会查出所有的字段,有些是不需要的,当应用程序和服务器不在同一个局域网时,字段过多会影响网络传输的性能
-
- 索引
在指定字段有索引的情况下,MySql是可以不用读磁盘的,直接使用索引里面的值就返回结果的;
但用了SELECT *,这就会有其他列需要从磁盘中读取才会返回结果,这样就造成了额外的性能开销;
- MyBatis-Plus乐观锁使用
乐观锁就是每次去拿数据的时候都认为别人不会修改,更新的时候会通过版本来判断别人是否修改了数据,如果数据被修改了就拒绝更新;
java中的AtomicXXX原子类是通过CAS实现,CAS即比较并更新,属于乐观锁,性能较悲观锁有很大的提高;
悲观锁适合写操作多的场景,乐观锁适合读操作多的场景,乐观锁的吞吐量会比悲观锁多;
数据库的乐观锁大多是基于数据版本 (Version)记录机制实现;数据版本即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来 实现。 读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一;此时,将提交数据的版本数据与数据,库表对应记录的当前版本信息进行比对,如果提交的数据 版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据;
使用:
实体类增加version属性配置
1 2 | @Version private Integer version; |
增加乐观锁插件
1 2 | //乐观锁插件 interceptor.addInnerInterceptor( new OptimisticLockerInnerInterceptor()); |
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @Test public void testOptimisticLocker1() { BannerDO bannerDO = new BannerDO(); //旧版本号,即查询出来的版本号需要做对比的 bannerDO.setVersion( 1 ); bannerDO.setId( 8 ); bannerDO.setUrl( "www.baidu.com" ); bannerMapper.updateById(bannerDO); } public void testOptimisticLocker2() { BannerDO bannerDO = new BannerDO(); //旧版本号,即查询出来的版本号需要做对比的 bannerDO.setVersion( 1 ); bannerDO.setId( 8 ); bannerDO.setUrl( "www.baidu.com" ); bannerMapper.updateById(bannerDO); } |
testOptimisticLocker1执行前,数据库表中的数据;
执行testOptimisticLocker1
执行testOptimisticLocker2,更新失败
注意:
-
- 乐观锁数据类型支持int,integer,long,timestamp;
- 仅支持updateById和update方法
- MyBatis-Plus 逻辑删除配置使用
逻辑删除只不过是更新了标记,不会真正的物理删除;
使用方式:
-
- 数据库增加逻辑删除的标识字段,如deleted,0是未删除,1表示删除;
- 实体类增加属性配置@TableLogic 或者 在配置文件增加指定;
1 2 | @TableLogic private Integer deleted; |
-
- 配置文件新增配置
1 2 3 4 5 6 | #删除是 1 mybatis-plus.global-config.db-config.logic-delete-value= 1 #未删除是 0 mybatis-plus.global-config.db-config.logic-not-delete-value= 0 #逻辑删除的全局标识字段,如果java实体类没加注解 @TableLogic ,则可以配置这个 mybatis-plus.global-config.db-config.logic-delete-field=deleted |
测试代码:
1 2 3 4 | public void testDeleteById() { int rtn = bannerMapper.deleteById( 8 ); log.info( "rtn:{}" , rtn); } |
执行结果:
打印的sql为update,而不是delete;
- Mybatis-Plus-Generator 代码自动生成工具使用
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率;
底层是模板引擎技术,可以自定义生成的java类模板;
导入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 | <!-- 代码自动生成依赖 begin --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version> 3.4 . 1 </version> </dependency> <!-- velocity --> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version> 2.0 </version> </dependency> <!-- 代码自动生成依赖 end--> |
使用如下,配置按需修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | public class MyBatisPlusGenerator { public static void main(String[] args) { //1. 全局配置 GlobalConfig config = new GlobalConfig(); // 是否支持AR模式 config.setActiveRecord( true ) // 作者 .setAuthor( "" ) // 生成路径,最好使用绝对路径 .setOutputDir( "" ) // 文件覆盖 .setFileOverride( true ) // 主键策略 .setIdType(IdType.AUTO) // 数据库时间类型 .setDateType(DateType.ONLY_DATE) // 设置生成的service接口的名字的首字母是否为I,默认Service是以I开头的 .setServiceName( "%sService" ) // 实体类结尾名称 .setEntityName( "%sDO" ) // 生成基本的resultMap .setBaseResultMap( true ) // 不使用AR模式 .setActiveRecord( false ) // 生成基本的SQL片段 .setBaseColumnList( true ); //2. 数据源配置 DataSourceConfig dsConfig = new DataSourceConfig(); // 设置数据库类型 dsConfig.setDbType(DbType.MYSQL) .setDriverName( "com.mysql.cj.jdbc.Driver" ) .setUrl( "" ) .setUsername( "" ) .setPassword( "" ); //3. 策略配置globalConfiguration中 StrategyConfig stConfig = new StrategyConfig(); //全局大写命名 stConfig.setCapitalMode( true ) // 数据库表映射到实体的命名策略 .setNaming(NamingStrategy.underline_to_camel) // 使用lombok .setEntityLombokModel( true ) // 使用restcontroller注解 .setRestControllerStyle( true ) // 生成的表, 支持多表一起生成,以数组形式填写 // 两个方式,直接写,或者使用命令行输入 //.setInclude(""); .setInclude(scanner( "表名,多个英文逗号分割" ).split( "," )); //4. 包名策略配置 PackageConfig pkConfig = new PackageConfig(); pkConfig.setParent( "org.example.manager" ) .setMapper( "mapper" ) .setService( "service" ) .setController( "controller" ) .setEntity( "model" ) .setXml( "mapper" ); //5. 整合配置 AutoGenerator ag = new AutoGenerator(); ag.setGlobalConfig(config) .setDataSource(dsConfig) .setStrategy(stConfig) .setPackageInfo(pkConfig); //6. 执行操作 ag.execute(); System.out.println( "======= 代码生成完毕 ========" ); } /** * <p> * 读取控制台内容 * </p> */ public static String scanner(String tip) { Scanner scanner = new Scanner(System.in); StringBuilder help = new StringBuilder(); help.append( "请输入" + tip + ":" ); System.out.println(help.toString()); if (scanner.hasNext()) { String ipt = scanner.next(); if (StringUtils.isNotBlank(ipt)) { return ipt; } } throw new MybatisPlusException( "请输入正确的" + tip + "!" ); } } |
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 后端思维之高并发处理方案
· 理解Rust引用及其生命周期标识(下)
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· 后端思维之高并发处理方案
· 千万级大表的优化技巧
· 在 VS Code 中,一键安装 MCP Server!
· 想让你多爱自己一些的开源计时器
· 10年+ .NET Coder 心语 ── 继承的思维:从思维模式到架构设计的深度解析