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

1,MyBatisPlus 的使用

2,条件构造器 AbstractWrapper

3,ActiveRecord(活动记录)

4,代码生成器

5,插件扩展

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 值2
notBetweenNOT BETWEEN 值1 AND 值2
like模糊查询like
notLike模糊查询notLike
likeLeftLIKE ‘%值’
likeRightLIKE ‘值%’
isNull字段 IS NULL
isNotNull字段 IS NOT NULL
in字段 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 字段, … ASC
orderByDesc排序:ORDER BY 字段, … DESC
orderBy排序:ORDER BY 字段, …
havingHAVING ( sql语句 )
or拼接 OR
andAND 嵌套
nested正常嵌套 不带 AND 或者 OR
apply拼接 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 安装下载


下一章, Maven的应用


版权声明:本文为qq_41530004原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。