1,MyBatisPlus 的使用
1.1,MyBatisPlus的介绍1.2,通用 CRUD1.3, MP 启动注入 SQL 原理分析2,条件构造器 AbstractWrapper
3,ActiveRecord(活动记录)
4,代码生成器
5,插件扩展
5.1,Mybatis 插件机制简介5.2,分页插件5.3,乐观锁插件5.4,性能分析插件5.5,逻辑删除5.6,公共字段自动填充6,IDEA快速开发插件
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。
官方文档 https://mp.baomidou.com/guide/
2,通用 CRUD 实现方式: - 基于 Mybatis需要编写 XXXMapper 接口,并手动编写 CRUD 方法提供 XXXMapper.xml 映射文件,并手动编写每个方法对应的 SQL 语句 - 基于 MP只需要创建 XXXMapper 接口, 并继承 BaseMapper 使用案例:applicationContext.xml: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 数据源 --> <context:property-placeholder location="classpath:jdbc.properties"/> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"></property> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="user" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!-- 事务管理器 --> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 基于注解的事务管理 --> <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/> <!-- 配置 SqlSessionFactoryBean Mybatis提供的:org.mybatis.spring.SqlSessionFactoryBean MP提供的:com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean --> <bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean"> <!-- 数据源 --> <property name="dataSource" ref="dataSource"></property> <property name="configLocation" value="classpath:mybatis-config.xml"></property> <!-- 别名处理 --> <property name="typeAliasesPackage" value="com.zhou.beans"></property> </bean> <!-- 配置 mybatis 扫描 mapper 接口的路径 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.zhou.mapper"></property> </bean> </beans> Users 实体类: @Data @ToString @AllArgsConstructor @TableName("users") public class Users { /** * @TableId: * value: 指定表中的主键列的列名,如果实体属性名与列名一致,可以省略不指定 * type:指定主键策略 */ @TableId(value = "id", type = IdType.AUTO) private Long id; private String name; private Integer age; @TableField("email") private String email; } UsersMapper 接口: /** * Mapper接口 *基于Mybatis:在Mapper接口中编写CRUD相关的方法,提供Mapper接口所对应的SQL映射文件以及方法对应的SQL语句 * * 基于MP:让UsersMapper接口继承BaseMapper接口即可。 * BaseMapper<T>:泛型指定的就是当前Mapper接口所操作的实体类类型 */ public interface UsersMapper extends BaseMapper<Users> { } 测试类: public class MybatisPlusTest { private ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml"); private UsersMapper usersMapper = ioc.getBean("usersMapper",UsersMapper.class); @Test public void saveUser(){ Users users = new Users(null,"Tommey周",10,"2523@qq.com"); int result = usersMapper.insert(users); System.out.println("影响的行数:" + result); } 3, MP 启动注入 SQL 原理分析xxxMapper 继承了 BaseMapper, BaseMapper 中提供了通用的 CRUD 方法,方法来源于 BaseMapper,有方法就必须有 SQL,因为 MyBatis 最终还是需要通过SQL 语句操作数据
通过现象看到本质
①,usersMapper 的本质 org.apache.ibatis.binding.MapperProxy
②,MapperProxy 中 sqlSession —> SqlSessionFactory
③, SqlSessionFacotry 中 —> Configuration→ MappedStatements
每一个 mappedStatement 都表示 Mapper 接口中的一个方法与 Mapper 映射文件中的一个 SQL。MP 在启动就会挨个分析 xxxMapper 中的方法,并且将对应的 SQL 语句处理好,保存到 configuration 对象中的 mappedStatements 中
AbstractWrapper 的简介:
QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件,Mybatis-Plus 通过 AbstractWrapper(简称 EW,MP 封装的一个查询条件构造器) 来让用户自由的构建查询条件,简单便捷。
注意:使用的是数据库字段,不是 Java 属性!
条件参数说明:
查询方式说明allEq基于map内容 =eq等于 =ne不等于 <>gt大于 >ge大于等于 >=lt小于 <le小于等于 <=betweenBETWEEN 值1 AND 值2notBetweenNOT BETWEEN 值1 AND 值2like模糊查询likenotLike模糊查询notLikelikeLeftLIKE ‘%值’likeRightLIKE ‘值%’isNull字段 IS NULLisNotNull字段 IS NOT NULLin字段 IN (value.get(0), value.get(1), …),字段 IN (v0, v1, …)notIn字段 NOT IN (value.get(0), value.get(1), …),字段 NOT IN (v0, v1, …)inSql字段 IN ( sql语句 )notInSql字段 NOT IN ( sql语句 )groupBy分组:GROUP BY 字段, …orderByAsc排序:ORDER BY 字段, … ASCorderByDesc排序:ORDER BY 字段, … DESCorderBy排序:ORDER BY 字段, …havingHAVING ( sql语句 )or拼接 ORandAND 嵌套nested正常嵌套 不带 AND 或者 ORapply拼接 sql,该方法可用于数据库函数 动态入参的params对应前面applySql内部的{index}部分.无sql注入风险的last无视优化规则直接拼接到 sql 的最后,只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用exists拼接 EXISTS ( sql语句 )notExists拼接 NOT EXISTS ( sql语句 ) 示例代码: /** * 条件构造器 查询操作 */ @Test public void entityWrapperSelectTest() { //分页查询Users表中,年龄eq("name","Tommey周")在1-30之间的且姓名为Tommey周的 Page<Users> usersPage = usersMapper.selectPage(new Page<Users>(1, 1), new QueryWrapper<Users>().eq("name", "Tommey周").between("age",1,30) .or(i -> i.eq("age",18).ne("email","2523@qq.com"))); List<Users> users = usersPage.getRecords(); System.out.println(users); }Active Record(活动记录),是一种领域模型模式,特点是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录。
ActiveRecord 一直广受动态语言( PHP 、 Ruby 等)的喜爱,而 Java 作为准静态语言,对于 ActiveRecord 往往只能感叹其优雅,所以 MP 也在 AR 道路上进行了一定的探索
如何使用 AR ?
仅仅需要让实体类继承 Model 类且实现主键指定方法,即可开启 AR 之旅
/** * MyBatisPlus 会默认使用实体类的类名到数据库中找对应的表 */ @TableName("users") public class Users extends Model<Users> { /** * @TableId: * value: 指定表中的主键列的列名,如果实体属性名与列名一致,可以省略不指定 * type:指定主键策略 */ @TableId(value = "id", type = IdType.AUTO) private Long id; private String name; private Integer age; @TableField("email") private String email; ...... @Override protected Serializable pkVal() { return this.id; } AR 基本 CRUD 示例代码如下: /** * AR 插入操作 */ @Test public void insertUsersARTest(){ Users users = new Users(null,"Tommey周",10,"2523@qq.com"); boolean flag = users.insert(); System.out.println("是否插入成功:" + flag); } /** * AR查询操作 */ @Test public void insertUsersARTest01(){ new Users().selectList(new QueryWrapper<Users>().like("name","Tom")).forEach(System.out::println); } AR 小结: AR 模式提供了一种更加便捷的方式实现 CRUD 操作,其本质还是调用的 Mybatis 对应的方法,类似于语法糖 语法糖是指计算机语言中添加的某种语法,这种语法对原本语言的功能并没有影响,可以更方便开发者使用,可以避免出错的机会,让程序可读性更好 到此,我们简单领略了 Mybatis-Plus 的魅力与高效率,值得注意的一点是:我们提供了强大的代码生成器,可以快速生成各类代码,真正做到了即开即用插件机制:
Mybatis 通过插件(Interceptor) 可以做到拦截四大对象相关方法的执行,根据需求,完成相关数据的动态改变。
Executor StatementHandler ParameterHandler ResultSetHandler
插件原理
四大对象的每个对象在创建时,都会执行 interceptorChain.pluginAll(),会经过每个插件的 plugin()方法,目的是为当前的四大对象创建代理。代理对象就可以拦截到四大对象相关方法的执行,因为要执行四大对象的方法需要经过代理
2,分页插件com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor
applicationContext.xml配置中修改配置
<!-- 配置 SqlSessionFactoryBean Mybatis提供的:org.mybatis.spring.SqlSessionFactoryBean MP提供的:com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean --> <bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean"> <!-- 数据源 --> <property name="dataSource" ref="dataSource"></property> <property name="configLocation" value="classpath:mybatis-config.xml"></property> <!-- 别名处理 --> <property name="typeAliasesPackage" value="com.zhou.sys.entity"></property> <property name="plugins"> <array> <!-- 注册分页插件 --> <bean class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor"> <property name="countSqlParser" ref="countSqlParser"></property> </bean> </array> </property> </bean> <bean id="countSqlParser" class="com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize"> <!-- 设置为 true 可以优化部分 left join 的sql --> <property name="optimizeJoin" value="true"/> </bean> 测试类: /** * 分页插件测试 */ @Test public void testPage(){ Page<Users> page = new Page<>(1,1); Page<Users> users = usersMapper.selectPage(page,null); System.out.println(users.getRecords()); System.out.println("=============获取分页相关的一些信息============="); System.out.println("总页数"+page.getTotal()); System.out.println("当前页码"+page.getCurrent()); System.out.println("总页码"+page.getPages()); System.out.println("每页显示的条数"+page.getSize()); System.out.println("是否有上一页"+page.hasPrevious()); System.out.println("是否有下一页"+page.hasNext()); } 3,乐观锁插件乐观锁:顾名思义十分乐观,它总是认为不会出现问题,无论干什么不去上锁!如果出现了问题,再次更新值测试
悲观锁:顾名思义十分悲观,它总是认为总会出现问题,无论干什么都会上锁,再去操作
乐观锁主要适用场景
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
取出记录时,获取当前version
更新时,带上这个version
执行更新时, set version = newVersion where version = oldVersion
如果version不对,就更新失败
配置spring中配置文件applicationContext.xml:
<bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/> 实体类中添加: @Version private Integer version; 测试: @Test public void testOptimisticLocker(){ //更新操作 Users users = new Users(); users.setId(6l); users.setVersion(1); users.setName("周"); usersMapper.updateById(users); Users user = new Users(); user.setName("周周"); user.setEmail("aa@qq.com"); users.setVersion(1); usersMapper.updateById(user); } 4,性能分析插件我们在平时的开发中,会遇到一些慢sql。测试!
作用:性能分析拦截器,用于输出每条 SQL 语句及其执行时间MP也提供性能分析插件,如果超过这个时间就停止运行!
SpringBoot环境下导入插件(要在SpringBoot中配置环境为dev或者 test 环境):
/** * SQL执行效率插件 */ @Bean @Profile({"dev","test"})// 设置 dev test 环境开启,保证我们的效率 public PerformanceInterceptor performanceInterceptor() { PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor(); performanceInterceptor.setMaxTime(100); // ms设置sql执行的最大时间,如果超过了则不执行 performanceInterceptor.setFormat(true); // 是否格式化代码 return performanceInterceptor; } 5,逻辑删除说明:只对自动注入的sql起效
插入: 不作限制
查找:追加where条件过滤掉已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
更新:追加where条件防止更新到已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
删除: 转变为 更新
示例代码:
配置:com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig
application.yml:
mybatis-plus: global-config: db-config: logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2) logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) 实体类: @TableLogic private Integer deleted; 测试类: /** * 测试逻辑删除 */ @Test public void logicDeleteTest(){ Integer result = usersMapper.deleteById(1l); System.out.println(result); Users users = usersMapper.selectById(1l); System.out.println(users); } 6,公共字段自动填充metaobject:元对象. 是 Mybatis 提供的一个用于更加方便,更加优雅的访问对象的属性,给对象的属性设置值 的一个对象, 还会用于包装对象,支持对 Object 、Map、Collection等对象进行包装本质上 metaObject 获取对象的属性值或者是给对象的属性设置值,最终是要通过 Reflector 获取到属性的对应方法的 Invoker, 最终 invoke
示例代码:
实现元对象处理器接口:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler
注解填充字段:
public class User { // 注意!这里需要标记为填充字段 @TableField(.. fill = FieldFill.INSERT) private String fillField; .... } /** * 自定义公共字段填充处理器 */ @Slf4j @Component public class MyMetaObjectHandler implements MetaObjectHandler { /** * 插入操作 自动填充 */ @Override public void insertFill(MetaObject metaObject) { log.info("insert,{ }", "开始了"); this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); } /** * 修改操作,自动填充 */ @Override public void updateFill(MetaObject metaObject) { log.info("update,{ }", "开始了"); this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); } }MybatisX 辅助 idea 快速开发插件,为效率而生,可以实现 java 与 xml 跳转,根据 Mapper 接口中的方法自动生成 xml 结构
官方安装: File -> Settings -> Plugins -> Browse Repositories 输入 mybatisx 安装下载