mybatis-plus使用和原理剖析之条件构造器

mybatis-plus使用和原理剖析之条件构造器

一、QueryWrapper

1.QueryWrapper

  • QueryWrapper
String name = "张三";
final QueryWrapper<Student> wrapper = new QueryWrapper<>();
// 字符串包含非空字符时才拼接查询条件(默认总是拼接)
wrapper.eq(StrUtil.isNotBlank(name), "name", name);
final List<Student> students = this.list(wrapper);
  • QueryWrapper With Entity
final Student student = new Student();
student.setName("张三");
// 非空时才拼接查询条件
final QueryWrapper<Student> wrapper = new QueryWrapper<>(student);
final List<Student> students = this.list(wrapper);

若想实现同StrUtil.isNotBlank(name)一样仅当包含非空字符是才拼接查询条件,可以更改全局配置mybatis-plus.global-config.db-config.where-strategy=not_empty(默认为not_null)

mybatis-plus:
  global-config:
    db-config:
      where-strategy: not_empty
  • IService 链式调用
String name = "张三";
// 字符串包含非空字符时才拼接查询条件(默认总是拼接)
final List<Student> students = this.query().eq(StrUtil.isNotBlank(name), "name", name).list();

2.LambdaQueryWrapper

  • LambdaQueryWrapper
String name = "张三";
final LambdaQueryWrapper<Student> wrapper = new QueryWrapper<Student>().lambda()
// 字符串包含非空字符时才拼接查询条件(默认总是拼接)
    .eq(StrUtil.isNotBlank(name), Student::getName, name);
final List<Student> students = this.list(wrapper);
  • IService 链式调用lambda 式
String name = "张三";
// 字符串包含非空字符时才拼接查询条件(默认总是拼接)
final List<Student> list = this.lambdaQuery().eq(StrUtil.isNotBlank(name), Student::getName, name).list();

3.总结

1.推荐使用Lambda方式,可以避免列名的硬编码;

2.与前端约定,数据为空时传null或者不传值到后端(vue作为前端框架可能传空字符串),避免使用QueryWrapper With Entity筛选数据时,出现与预期不符的问题;spring-boot-starter-validation校验框架正则校验也是对null值放行,对空字符串拦截。

二、UpdateWrapper

1.UpdateWrapper

  • UpdateWrapper
Integer age = 12;
final UpdateWrapper<Student> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq(StrUtil.isNotBlank(name), "name", name);
updateWrapper.set(ObjectUtil.isNotNull(age), "age", age);
final boolean update = this.update(updateWrapper);
  • UpdateWrapper With Entity
Integer age = 12;
// 查询条件
final Student student = new Student();
student.setName("张三");
final UpdateWrapper<Student> updateWrapper = new UpdateWrapper<>(student);
updateWrapper.set(ObjectUtil.isNotNull(age), "age", age);
final boolean update = this.update(updateWrapper);
final Student student = new Student();
student.setName("张三");
final Student saveData = new Student();
saveData.setAge(12);
final UpdateWrapper<Student> updateWrapper = new UpdateWrapper<>(student);
final boolean update = this.update(saveData, updateWrapper);
  • IService 链式调用
String name = "张三";
this.update().eq(StrUtil.isNotBlank(name), "name", name).set(ObjectUtil.isNotNull(age), "age", age).update();

2.LambdaUpdateWrapper

  • LambdaUpdateWrapper
String name = "张三";
Integer age = 12;
final LambdaUpdateWrapper<Student> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(StrUtil.isNotBlank(name), Student::getName, name);
updateWrapper.set(ObjectUtil.isNotNull(age), Student::getAge, age);
final boolean update = this.update(updateWrapper);
String name = "张三";
final Student saveData = new Student();
saveData.setAge(12);
final LambdaUpdateWrapper<Student> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(StrUtil.isNotBlank(name), Student::getName, name);
final boolean update = this.update(saveData, updateWrapper);
  • IService 链式调用lambda 式
String name = "张三";
final boolean update = this.lambdaUpdate().eq(StrUtil.isNotBlank(name), Student::getName, name)
    .set(ObjectUtil.isNotNull(age), Student::getAge, age)
    .update();
String name = "张三";
final Student saveData = new Student();
saveData.setAge(12);
final boolean update = this.lambdaUpdate().eq(StrUtil.isNotBlank(name), Student::getName, name)
    .update(saveData);

3.总结

1.推荐使用Lambda方式,可以避免列名的硬编码;

2.注意set语法部分,必须存在否则SQL异常;

3.注意查询条件缺失全表更新问题;

三、原理剖析

1.Lambda方法引用原理

QStudent::getName怎样对应到数据库列名?

String name = "张三";
final LambdaQueryWrapper<Student> wrapper = new QueryWrapper<Student>().lambda()
    .eq(StrUtil.isNotBlank(name), Student::getName, name);
final List<Student> students = this.list(wrapper);
  • 泛型说明
// T-实体类 R-列名提供者类型 Children-AbstractWrapper子类型
AbstractWrapper<T, R, Children extends AbstractWrapper<T, R, Children>>

// T-实体类 R-列名提供者类型(String) Children-AbstractWrapper子类型
QueryWrapper<T> extends AbstractWrapper<T, String, QueryWrapper<T>>

// T-实体类 R-列名提供者类型(SFunction<T, ?>) Children-AbstractWrapper子类型
// SFunction<T, ?> - 支持序列化的 Function,输入T的实例返回对象,对象的返回值(类型为?)不重要,方法名本身才重要
AbstractLambdaWrapper<T, Children extends AbstractLambdaWrapper<T, Children>>
    extends AbstractWrapper<T, SFunction<T, ?>, Children>
// T-实体类  
LambdaQueryWrapper<T> extends AbstractLambdaWrapper<T, LambdaQueryWrapper<T>>
  • 调用链

=> 调用抽象父类方法

com.baomidou.mybatisplus.core.conditions.AbstractWrapper#eq

=>

com.baomidou.mybatisplus.core.conditions.AbstractWrapper#addCondition

=> 列名提供者类型R转sql片段(String)

com.baomidou.mybatisplus.core.conditions.AbstractWrapper#columnToSqlSegment

=> 列名提供者类型R转列名字符串 QueryWrapper直接调用AbstractWrapper#columnToString;Lambda方式调用AbstractLambdaWrapper#columnToString

com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper#columnToString(com.baomidou.mybatisplus.core.toolkit.support.SFunction<T,?>)

=> 根据列名提供者类型R,通过com.baomidou.mybatisplus.core.toolkit.LambdaUtils提取LambdaMeta元数据(包含实体类名和实现方法名),获取引用的实现方法名称(重点

com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper#getColumnCache(com.baomidou.mybatisplus.core.toolkit.support.SFunction<T,?>)

=> 利用mybatis包提供的工具类将方法名转为实体属性名重点

org.apache.ibatis.reflection.property.PropertyNamer#methodToProperty

=> 初始化实体

com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper#tryInitCache

=> 将实体属性名映射为数据库表列名

com.baomidou.mybatisplus.core.toolkit.LambdaUtils#getColumnMap

=> 根据实体类名,使用TableInfoHelper工具类获取TableInfo信息(包含当前实体配置的属性名和列名的映射关系、主键策略、逻辑删除、乐观锁等配置)

com.baomidou.mybatisplus.core.metadata.TableInfoHelper#getTableInfo(java.lang.Class<?>)

注: TableInfo信息的初始化在工程启动的时候完成,此处不再详述!

com.baomidou.mybatisplus.core.metadata.TableInfoHelper#initTableInfo(org.apache.ibatis.builder.MapperBuilderAssistant, java.lang.Class<?>)

  • 核心方法
    /**
     * 该缓存可能会在任意不定的时间被清除
     *
     * @param func 需要解析的 lambda 对象
     * @param <T>  类型,被调用的 Function 对象的目标类型
     * @return 返回解析后的结果
     */
    public static <T> LambdaMeta extract(SFunction<T, ?> func) {
        // 1. IDEA 调试模式下 lambda 表达式是一个代理
        if (func instanceof Proxy) {
            return new IdeaProxyLambdaMeta((Proxy) func);
        }
        // 2. 反射读取
        try {
            Method method = func.getClass().getDeclaredMethod("writeReplace");
            return new ReflectLambdaMeta((SerializedLambda) ReflectionKit.setAccessible(method).invoke(func));
        } catch (Throwable e) {
            // 3. 反射失败使用序列化的方式读取
            return new ShadowLambdaMeta(com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda.extract(func));
        }
    }
  public static String methodToProperty(String name) {
    if (name.startsWith("is")) {
      name = name.substring(2);
    } else if (name.startsWith("get") || name.startsWith("set")) {
      name = name.substring(3);
    } else {
      throw new ReflectionException("Error parsing property name '" + name + "'.  Didn't start with 'is', 'get' or 'set'.");
    }

    if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {
      name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
    }

    return name;
  }
    /**
     * 获取实体对应字段 MAP
     *
     * @param clazz 实体类
     * @return 缓存 map
     */
    public static Map<String, ColumnCache> getColumnMap(Class<?> clazz) {
        return CollectionUtils.computeIfAbsent(COLUMN_CACHE_MAP, clazz.getName(), key -> {
            TableInfo info = TableInfoHelper.getTableInfo(clazz);
            return info == null ? null : createColumnCacheMap(info);
        });
    }

2.TableInfo初始化过程分析

说在前面:记住TableInfoHelper这个工具类你可能会派上大用场,例如获取表名、字段名、列名、主键名、主键策略、实体属性名和数据库列名互转等等。

(1)XML支线
  • 调用链

=> 注入Bean对象

com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration#sqlSessionFactory

=>

com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean#afterPropertiesSet

=> 工厂Bean

com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean#buildSqlSessionFactory

=> XML解析

org.apache.ibatis.builder.xml.XMLMapperBuilder#parse

com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder#build(org.apache.ibatis.session.Configuration)

=> 名称空间绑定

org.apache.ibatis.builder.xml.XMLMapperBuilder#bindMapperForNamespace

=> 增加Mapper

com.baomidou.mybatisplus.core.MybatisConfiguration#addMapper

=> MybatisMapperRegistry仓库

com.baomidou.mybatisplus.core.MybatisMapperRegistry#addMapper

=> 解析

com.baomidou.mybatisplus.core.MybatisMapperAnnotationBuilder#parse

=> 解析注入

com.baomidou.mybatisplus.core.MybatisMapperAnnotationBuilder#parserInjector

=>

com.baomidou.mybatisplus.core.injector.AbstractSqlInjector#inspectInject

=>

com.baomidou.mybatisplus.core.metadata.TableInfoHelper#initTableInfo(org.apache.ibatis.builder.MapperBuilderAssistant, java.lang.Class<?>)

(2)MapperFactoryBean支线

使用MapperScans导入

=> org.mybatis.spring.annotation.MapperScans

=> 导入MapperScannerConfigurer

org.mybatis.spring.annotation.MapperScannerRegistrar.RepeatingRegistrar

=>

org.mybatis.spring.mapper.MapperScannerConfigurer#postProcessBeanDefinitionRegistry

=>

org.mybatis.spring.mapper.ClassPathMapperScanner#doScan

对象实例化

=> Mapper对象实例化

org.mybatis.spring.mapper.MapperFactoryBean#getObject

=>

com.baomidou.mybatisplus.core.MybatisConfiguration#getMapper

=>动态代理

com.baomidou.mybatisplus.core.override.MybatisMapperProxyFactory#newInstance(org.apache.ibatis.session.SqlSession)

=>

org.springframework.dao.support.DaoSupport#afterPropertiesSet

=> 解析

org.mybatis.spring.mapper.MapperFactoryBean#checkDaoConfig

以下同XML支线

=> 增加Mapper

com.baomidou.mybatisplus.core.MybatisConfiguration#addMapper

=> MybatisMapperRegistry仓库

com.baomidou.mybatisplus.core.MybatisMapperRegistry#addMapper

=> 解析

com.baomidou.mybatisplus.core.MybatisMapperAnnotationBuilder#parse

=> 解析注入

com.baomidou.mybatisplus.core.MybatisMapperAnnotationBuilder#parserInjector

=>

com.baomidou.mybatisplus.core.injector.AbstractSqlInjector#inspectInject

=>

com.baomidou.mybatisplus.core.metadata.TableInfoHelper#initTableInfo(org.apache.ibatis.builder.MapperBuilderAssistant, java.lang.Class<?>)

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Mybatis-Plus是一个基于Mybatis的增强工具,提供了许多实用的功能,如自动生成代码、分页查询、条件构造、性能分析等。Mybatis-Plus Service是Mybatis-Plus的一个模块,提供了一些常用的Service层接口和实现类,如IService、ServiceImpl等,方便开发者快速构建Service层。 ### 回答2: Mybatis-Plus是一个开源的、能够和Mybatis无缝衔接并扩展出更多实用功能的框架。在实际项目开发中,通常会使用到基于Mybatis-Plus的Service层,以下是关于Mybatis-Plus Service的一些介绍。 Mybatis-Plus的Service层是基于Mybatis-Plus框架进行封装的,旨在简化开发者编写Service层代码的流程。使用Mybatis-Plus Service可以有效地减少重复代码的编写,提高开发效率。 在Mybatis-Plus Service中,通常包含了一些常见的CRUD方法,如查询列表、根据ID查询、插入、更新和删除等。我们可以通过继承BaseService或者IService来使用这些方法。同时,Mybatis-Plus Service还提供了一些强大的查询构建,如LambdaQueryWrapper和QueryWrapper等,可以快速构建复杂的查询条件。 另外,Mybatis-Plus Service还支持事务管理。它提供了一种@Transactional注解来实现声明式事务,我们只需在Service层的方法上添加该注解,即可完成事务的配置。 除了基本的CRUD操作和事务管理外,Mybatis-Plus Service还具有其他扩展的功能,例如分页查询、批量操作、逻辑删除等。这些功能都能够极大地简化开发者的编码工作。 总而言之,Mybatis-Plus Service是一种基于Mybatis-Plus框架的封装,用于简化Service层代码的编写。它提供了一系列的CRUD方法、事务管理以及其他实用功能,可以提高开发效率并减少冗余代码的编写。使用Mybatis-Plus Service,开发者能够更加专注于业务逻辑的实现,提高开发效率和代码质量。 ### 回答3: MyBatis-Plus是一个在MyBatis基础上的增强工具,提供了更加便捷的CRUD操作方式。其中的Service是MyBatis-Plus提供的一层封装,主要用于处理业务逻辑和数据库操作之间的关系。 MyBatis-Plus的Service主要有以下几个功能: 1. 提供通用的CRUD方法:Service提供了常见的增删改查方法,如save、remove、update等,可以直接调用这些方法来操作数据库,无需手写SQL语句。 2. 封装条件构造:Service的方法中可以使用MyBatis-Plus提供的条件构造来进行查询条件的组装,例如可以使用eq、like、in等方法来构建查询条件。 3. 支持分页查询:Service提供了分页查询的方法,可以方便地进行数据分页查询操作。可以设置每页的数量、当前页码等参数来实现分页效果。 4. 支持事务控制:Service可以通过注解的方式来对方法添加事务控制,保证在业务逻辑中的多个操作要么全部成功,要么全部失败。可以使用@Transactional注解来标记需要进行事务控制的方法。 总的来说,MyBatis-Plus的Service提供了一种更加便捷的数据库操作方式,简化了开发过程中的数据库操作代码,提高了开发效率。同时,它还具备一些高级功能,如条件构造、分页查询和事务控制,使得开发者可以更加灵活和方便地进行业务逻辑的处理和数据库操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搬山境KL攻城狮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值