(11)MyBatisPlus————条件构造器,AR,代码生成器以及插件扩展

    技术2022-07-11  107

    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快速开发插件

    简介

    1,MyBatisPlus的介绍

    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

    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); }

    ActiveRecord(活动记录)

    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 的魅力与高效率,值得注意的一点是:我们提供了强大的代码生成器,可以快速生成各类代码,真正做到了即开即用

    代码生成器

    public class CodeGeneratorTest { /** * <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.isNotEmpty(ipt)) { return ipt; } } throw new MybatisPlusException("请输入正确的" + tip + "!"); } public static void main(String[] args) { // 代码生成器 AutoGenerator mpg = new AutoGenerator(); // 1,全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); gc.setOutputDir(projectPath + "/MyBatisPlusDemo/src/main/java");//生成路径 gc.setAuthor("Tommey周"); //作者 gc.setActiveRecord(true); //是否支持AR gc.setBaseColumnList(true); gc.setBaseResultMap(true); gc.setOpen(false); gc.setIdType(IdType.AUTO); //主键策略 gc.setFileOverride(true); //文件覆盖 mpg.setGlobalConfig(gc); //2,数据源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setDbType(DbType.MYSQL); dsc.setUrl("jdbc:mysql://localhost:3306/test?useUnicode=true&useSSL=false&characterEncoding=utf8"); dsc.setDriverName("com.mysql.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("root"); mpg.setDataSource(dsc); //3,包配置 PackageConfig pc = new PackageConfig(); pc.setModuleName(scanner("模块名")); pc.setParent("com.zhou"); mpg.setPackageInfo(pc); // 4,自定义配置 InjectionConfig cfg = new InjectionConfig() { @Override public void initMap() { // to do nothing } }; // 如果模板引擎是 freemarker // String templatePath = "/templates/mapper.xml.ftl"; // 如果模板引擎是 velocity String templatePath = "/templates/mapper.xml.vm"; // 自定义输出配置 List<FileOutConfig> focList = new ArrayList<>(); // 自定义配置会被优先输出 focList.add(new FileOutConfig(templatePath) { @Override public String outputFile(TableInfo tableInfo) { // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! return projectPath + "/MyBatisPlusDemo/src/main/resources/mapper/" + pc.getModuleName() + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; } }); cfg.setFileOutConfigList(focList); mpg.setCfg(cfg); // 5,配置模板 TemplateConfig templateConfig = new TemplateConfig(); templateConfig.setXml(null); mpg.setTemplate(templateConfig); //6,策略配置 StrategyConfig strategy = new StrategyConfig(); strategy.setNaming(NamingStrategy.underline_to_camel);//数据表映射到实体的命名策略 strategy.setColumnNaming(NamingStrategy.underline_to_camel); //数据库表列名 strategy.setEntityLombokModel(true); strategy.setRestControllerStyle(true); strategy.setInclude(scanner("表名,多个英文逗号分割").split(",")); strategy.setControllerMappingHyphenStyle(true); strategy.setTablePrefix(pc.getModuleName() + "_"); mpg.setStrategy(strategy); mpg.setTemplateEngine(new VelocityTemplateEngine()); mpg.execute(); } }

    插件扩展

    1,Mybatis 插件机制简介

    插件机制:

    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()); } }

    IDEA 快速开发插件

    MybatisX 辅助 idea 快速开发插件,为效率而生,可以实现 java 与 xml 跳转,根据 Mapper 接口中的方法自动生成 xml 结构

    官方安装: File -> Settings -> Plugins -> Browse Repositories 输入 mybatisx 安装下载

    Processed: 0.019, SQL: 9