简介 MyBatis-Plus (简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
代码托管:Gitee | Github
我们的愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。
特性
无侵入 :只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小 :启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
强大的 CRUD 操作 :内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
支持 Lambda 形式调用 :通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
支持主键自动生成 :支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
支持 ActiveRecord 模式 :支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
支持自定义全局通用操作 :支持全局通用方法注入( Write once, use anywhere )
内置代码生成器 :采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
内置分页插件 :基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
分页插件支持多种数据库 :支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
内置性能分析插件 :可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
内置全局拦截插件 :提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
框架结构
快速入门 使用第三方组件: 1、导入对应的依赖 2、研究依赖如何配置 3、代码如何编写 4、提高扩展技术能力!
快速开始 1、创建数据库 mybatis_plus
2、创建user表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 DROP TABLE IF EXISTS user; CREATE TABLE user ( id BIGINT(20) NOT NULL COMMENT '主键ID', name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名', age INT(11) NULL DEFAULT NULL COMMENT '年龄', email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱', PRIMARY KEY (id) ); INSERT INTO user (id, name, age, email) VALUES (1, 'Jone', 18, 'test1@baomidou.com'), (2, 'Jack', 20, 'test2@baomidou.com'), (3, 'Tom', 28, 'test3@baomidou.com'), (4, 'Sandy', 21, 'test4@baomidou.com'), (5, 'Billie', 24, 'test5@baomidou.com')
3、编写项目,初始化项目!使用SpringBoot初始化! 4、导入依赖
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 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > druid-spring-boot-starter</artifactId > <version > 1.1.10</version > </dependency > <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > 3.3.2</version > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <optional > true</optional > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency >
使用 mybatis-plus 可以节省我们大量的代码,尽量不要同时导入 mybatis 和 mybatis-plus 5、配置数据库连接
1 2 3 4 5 6 7 8 9 10 11 12 13 spring : datasource : username : root password : **** url : jdbc:mysql://129.204.240.134:3306/mybatis_plus driver-class-name : com.mysql.cj.jdbc.Driver type : com.alibaba.druid.pool.DruidDataSource dbcp2 : initial-size : 5 max-total : 20 min-idle : 5 pool-prepared-statements : true max-open-prepared-statements : 20
6、准备实体类和mapper
1 2 3 4 5 6 7 @Data public class User { private Long id; private String name; private Integer age; private String email; }
1 2 3 4 5 @Repository public interface UserMapper extends BaseMapper <User > { }
注意点 :在主启动类上去扫描我们的mapper包下的所有接口,@MapperScan("com.atomsk.mapper")
7、测试类中测试
1 2 3 4 5 6 7 8 9 10 11 @SpringBootTest class mybatis -plusApplicationTests { @Autowired private UserMapper userMapper; @Test public void testSelect () { System.out.println(("----- selectAll method test ------" )); List<User> userList = userMapper.selectList(null ); userList.forEach(System.out::println); } }
日志配置 1 2 3 mybatis-plus : configuration : log-impl : org.apache.ibatis.logging.stdout.StdOutImpl
CRUD扩展 插入操作 1 2 3 4 5 6 7 8 9 @Test public void testInsert () { User user = new User(); user.setName("atomsk" ); user.setAge(22 ); user.setEmail("atomsk@126.com" ); int result = userMapper.insert(user); System.out.println(user); }
主键生成策略
默认为 ID_WORKER 全局的唯一id
分布式系统唯一id生成:https://www.cnblogs.com/haoxinyue/p/5208136.html
雪花算法 :
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。可以保证几乎全球唯一!
配置为 AUTO 主键自增
在实体类id上增加 @TableId(type = IdType.AUTO)
,或者将全局配置mybatis-plus.global-config.db-config.id-type设置为 AUTO
数据库主键字段一定要勾选上自增选项!
IdType源码
1 2 3 4 5 6 7 public enum IdType { AUTO(0 ), NONE(1 ), INPUT(2 ), ASSIGN_ID(3 ), ASSIGN_UUID(4 ) }
更新操作 1 2 3 4 5 6 7 8 9 @Test public void testUpdate () { User user = new User(); user.setId(6L ); user.setName("范二林" ); user.setEmail("fanerlin@85.com" ); userMapper.updateById(user); System.out.println(user); }
可以看到日志,SQL是根据条件自动生成的
自动填充 阿里巴巴开发手册几乎所有的表都要配置上gmt_create、gmt_modified!而且需要自动化!
方式一:数据库级别
在表中新增字段 create_time、update_time,使用CURRENT_TIMESTAMP
默认值,给update_time勾选更新选项
同步实体类,增加属性
1 2 private Date createTime;private Date updateTime;
进行插入和更新操作后:
日志表明填充工作由数据库 完成
方式二:代码级别
删除数据库字段的默认值和更新操作!
实体类属性上增加注解:
1 2 3 4 @TableField (fill = FieldFill.INSERT)private Date createTime;@TableField (fill = FieldFill.INSERT_UPDATE)private Date updateTime;
3. 编写处理器来处理这个注解 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Slf 4j@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); } }
进行插入和更新操作后:
日志表面填充工作由MP 完成
注意事项 :
字段必须声明TableField
注解,属性fill
选择对应策略,该声明告知Mybatis-Plus
需要预留注入SQL
字段
填充处理器MyMetaObjectHandler
在 Spring Boot 中需要声明@Component
或@Bean
注入
乐观锁 乐观锁:故名思意十分乐观,它总是认为不会出现问题,无论干什么不去上锁!如果出现了问题,再次更新值测试
悲观锁:故名思意十分悲观,它总是认为总是出现问题,无论干什么都会上锁!再去操作!
乐观锁实现方式:
取出记录时,获取当前 version
更新时,带上这个version
执行更新时, set version = newVersion where version = oldVersion
如果version不对,就更新失败
1 2 3 4 5 6 7 乐观锁:1、先查询,获得版本号 version = 1 update user set name = "kuangshen" , version = version + 1 where id = 2 and version = 1 update user set name = "kuangshen" , version = version + 1 where id = 2 and version = 1
使用MP的乐观锁插件
给数据库增加version字段,类型为int,默认值为1
同步实体类
1 2 @Version private Integer version;
注册组件 optimisticLockerInterceptor
1 2 3 4 5 6 7 8 9 10 @MapperScan ("com.atomsk.demo.mapper" )@EnableTransactionManagement @Configuration public class mybatis -plusConfig { @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor () { return new OptimisticLockerInterceptor(); } }
进行更新测试,日志输出:
1 2 3 ==> Preparing: UPDATE user SET name=?, ... , version=? ... WHERE id=? AND version=? ==> Parameters: atomsk(String), ... , 3(Integer), ... , 1(Long), 2(Integer) <== Updates: 1
查询操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Test public void testSelectByBatchId () { List<User> users = userMapper.selectBatchIds(Arrays.asList(1 , 2 , 3 )); users.forEach(System.out::println); } @Test public void testSelectByBatchIds () { HashMap<String, Object> map = new HashMap<>(); map.put("name" , "atomsk" ); map.put("age" , 18 ); List<User> users = userMapper.selectByMap(map); users.forEach(System.out::println); }
分页查询
原始的limit进行分页
pageHelper第三方插件
MP内置的分页插件
如何使用
配置拦截器组件
1 2 3 4 5 @Bean public PaginationInterceptor paginationInterceptor () { return new PaginationInterceptor(); }
直接使用Page对象即可
1 2 3 4 5 6 7 @Test public void testPage () { Page<User> page = new Page<>(1 , 3 ); userMapper.selectPage(page, null ); page.getRecords().forEach(System.out::println); System.out.println(page.getTotal()); }
删除操作 1 2 3 4 5 6 7 8 9 10 11 12 13 @Test public void testDeleteBatchId () { userMapper.deleteBatchIds(Arrays.asList(1 , 2 , 3 )); } @Test public void testDeleteMap () { HashMap<String, Object> map = new HashMap<>(); map.put("name" , "atomsk" ); userMapper.deleteByMap(map); }
逻辑删除 物理删除:从数据库中直接移除
逻辑删除:在数据库中没有被移除,而是通过一个变量来让他失效,deleted=0 -> deleted=1
使用
在数据库中增加一个deleted字段,类型为int,默认值为0
增加配置:
1 2 3 4 5 6 mybatis-plus: global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0
实体类字段上增加注解:
1 2 @TableLogic private Integer deleted;
测试
条件构造器 详情请看:https://mp.baomidou.com/guide/wrapper.html
AbstractWrapper 多个方法间默认用AND 拼接,使用OR 时要显式调用
例: eq("id",1).or().eq("name","老王")
—>id = 1 or name = '老王'
allEq(详见官网)
in、notIn、between、notBetween,isNull、isNotNull:用法略
inSql、notInSql:子查询,也可以和in、notIn一样使用
值比较
eq
ne
gt
ge
lt
le
等于 =
不等于 <>
大于 >
大于等于 >=
小于 <
小于等于 <=
以eq为例子:
1 2 3 4 5 6 7 8 9 10 11 eq(R column, Object val) eq(boolean condition, R column, Object val) @Test void contextLoads () {QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper .isNotNull("name" ) .eq("age" ,22 ); userMapper.selectList(wrapper).forEach(System.out::println); }
模糊查询
like
notLike
likeLeft
likeRight
LIKE ‘%值%’
NOT LIKE ‘%值%’
LIKE ‘%值’
LIKE ‘值%’
例子:
1 2 3 4 5 6 7 8 9 10 11 @Test void test4 () { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper .notLike("name" ,"a" ) .likeRight("email" ,"t" ); List<Map<String, Object>> maps = userMapper.selectMaps(wrapper); maps.forEach(System.out::println); }
mybatis日志:
1 2 ==> Preparing: SELECT id,name,age,... FROM user WHERE deleted=0 AND (name NOT LIKE ? AND email LIKE ?) ==> Parameters: %a%(String), t%(String)
排序
orderBy
orderByAsc
orderByDesc
ORDER BY 字段, …
ORDER BY 字段, … ASC
ORDER BY 字段, … DESC
orderBy,第一个true表示加入sql,第二个true表示按升序排列
1 orderBy(boolean condition, boolean isAsc, R... columns)
例: orderBy(true, true, "id", "name")
—>order by id ASC,name ASC
orderByAsc和orderByDesc用法一样,以orderByAsc为例
1 2 orderByAsc(R... columns) orderByAsc(boolean condition, R... columns)
排序:ORDER BY 字段, … ASC
例: orderByAsc("id", "name")
—>order by id ASC,name ASC
groupBy和Having 1 2 groupBy(R... columns) groupBy(boolean condition, R... columns)
分组:GROUP BY 字段, …
例: groupBy("id", "name")
—>group by id,name
1 2 having(String sqlHaving, Object... params) having(boolean condition, String sqlHaving, Object... params)
HAVING ( sql语句 )
例: having("sum(age) > 10")
—>having sum(age) > 10
例: having("sum(age) > {0}", 11)
—>having sum(age) > 11
QueryWrapper
说明:
继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件
select 1 2 3 4 5 6 7 8 @Test void testQuerryWrapper () { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.select("id,name,age" ); List<User> users = userMapper.selectList(wrapper); users.forEach(System.out::println); }
代码生成器 自动生成pojo、mapper、service、controller
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 public class MPCodeGenerator { 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.isNotEmpty(ipt)) { return ipt; } } throw new mybatis-plusException("请输入正确的" + tip + "!" ); } public static void main (String[] args) { AutoGenerator mpg = new AutoGenerator(); GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir" ); gc.setOutputDir(projectPath + "/mybatis-plus-code-generator/src/main/java" ); gc.setAuthor("atomsk" ); gc.setOpen(false ); gc.setFileOverride(false ); gc.setServiceName("%sService" ); gc.setIdType(IdType.AUTO); gc.setDateType(DateType.ONLY_DATE); gc.setSwagger2(true ); mpg.setGlobalConfig(gc); DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://129.204.240.134:3306/mybatis_plus" ); dsc.setDriverName("com.mysql.cj.jdbc.Driver" ); dsc.setUsername("root" ); dsc.setPassword("****" ); dsc.setDbType(DbType.MYSQL); mpg.setDataSource(dsc); PackageConfig pc = new PackageConfig(); pc.setModuleName(scanner("模块名" )); pc.setParent("com.atomsk" ); mpg.setPackageInfo(pc); StrategyConfig strategy = new StrategyConfig(); strategy.setInclude(scanner("表名,多个则用英文逗号分割" ).split("," )); strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); strategy.setEntityLombokModel(true ); strategy.setVersionFieldName("version" ); strategy.setLogicDeleteFieldName("deleted" ); TableFill createTime = new TableFill("create_time" , FieldFill.INSERT); TableFill updateTime = new TableFill("update_time" , FieldFill.INSERT_UPDATE); ArrayList<TableFill> tableFills = new ArrayList<>(); tableFills.add(createTime); tableFills.add(updateTime); strategy.setTableFillList(tableFills); strategy.setRestControllerStyle(true ); strategy.setControllerMappingHyphenStyle(true ); mpg.setStrategy(strategy); mpg.execute(); } }
生成的项目结构: