1分钟实现Mybatis-Plus获取完整SQL语句

可能大家会在平常的工作遇到这样的需求,在使用MyBatis-plus的时候,想要动态的获取对应的SQL语句,但是用网上的sqlhelper来获取并不能完全实现,所以我这里提供了一种原生的方法SqlSessionFactory来实现,话不多说,直接上代码:

@Autowired
SqlSessionFactory sqlSessionFactory;

public void getSelectListSQL() {

  // 获取对应的数据
LambdaQueryWrapper<RealityBillPOJO> queryWrapper = new LambdaQueryWrapper<>();
    
queryWrapper.eq(!StringUtils.isEmpty(actualBillingDTO.getBillYear()),RealityBillPOJO::getAccountingYear,actualBillingDTO.getBillYear())
                .eq(!StringUtils.isEmpty(actualBillingDTO.getBillGuarter()),RealityBillPOJO::getPeriodBill,actualBillingDTO.getBillGuarter())
                .eq(!StringUtils.isEmpty(actualBillingDTO.getMangCom()),RealityBillPOJO::getReinsureCom,actualBillingDTO.getMangCom())
                .eq(!StringUtils.isEmpty(actualBillingDTO.getBillMonth()),RealityBillPOJO::getAccountingMonth,actualBillingDTO.getBillMonth());
        List<RealityBillPOJO> realityBillPOJOS =realityBillMapper.selectList(queryWrapper);
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("ew", queryWrapper);
// 获取 SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
Configuration configuration = sqlSession.getConfiguration();
// 获取 Mapper 的方法名
String mapperMethod = "com.galaxy.reinsurance.infrastructure.persistent.mapper.RealityBillMapper.selectList";
// 获取 MappedStatement
MappedStatement mappedStatement = configuration.getMappedStatement(mapperMethod);
// 获取 BoundSql 对象
BoundSql boundSql = mappedStatement.getBoundSql(paramMap);
// 获取 SQL 语句
String sql =getExecuteSql(sqlSessionFactory,boundSql, paramMap);
System.out.println(sql);
}

/**
     * 获取执行sql
     * @param boundSql
     * @param paramObject
     * @return
     */
public static String getExecuteSql(SqlSessionFactory sqlSessionFactory,BoundSql boundSql, Object paramObject) {
        // 带有问号占位符的 SQL 语句
        String sql = boundSql.getSql();
        // 参数信息列表
        List<ParameterMapping> paramMappings = boundSql.getParameterMappings();
        // MetaObject 是 mybatis 通过表达式取出对象内容的工具
        MetaObject metaObject = sqlSessionFactory.getConfiguration().newMetaObject(paramObject);
        for (ParameterMapping p : paramMappings) {
            String paramName = p.getProperty();
            Object paramValue = metaObject.getValue(paramName);
            String value = "";
            if (paramValue instanceof String) {
                value = "'" + paramValue + "'";
            } else {
                // todo 其他类型的参数的对应的拼接方式
            }
            sql = sql.replaceFirst("\\?", value);
        }
        return sql;
    }

 具体的原理:

MyBatis 的原理是,在 XML 文件中用模板语言编写 SQL 语句,然后在代码里面编写一个和 XML 的参数和返回值相匹配的接口(Mapper)。调用接口,并传递相应的参数,MyBatis 就会把参数和模板语句结合起来,生成最终需要执行的参数和语句,然后调用 PreparedStatement 执行。

<mapper namespace="org.mybatis.example.BlogMapper">
  <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{id}
  </select>
</mapper>

try (SqlSession session = sqlSessionFactory.openSession()) {
  Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
}

 

Mybatis 的核心是 SqlSessionFactory 类。执行查询,或者获取配置信息都是从这里开始的。程序启动后,Mapper 文件到了代码里面就加载成了一个个的 MappedStatement 对象,一个 MappedStatement 实例对应着一个 Mapper 文件里的一个查询语句:

Configuration configuration = sqlSessionFactory.getConfiguration();
MappedStatement mappedStatement = configuration.getMappedStatement("org.mybatis.example.BlogMapper.selectBlog");

 

可以从 SqlSessionFactory 中获得 MappedStament 的列表

可以从 SqlSessionFactory 中获得 MappedStament 的列表

那 MappedStatement 是怎么和我们传入的参数组合,最后生成 PreparedStatement 的呢?经过一段时间顺藤摸瓜地查找,终于找到了处理逻辑所在的地方:DefaultParameterHandler#setParameters

MyBatis 对语句传入参数的地方

MyBatis 对语句传入参数的地方

boundSql:SQL 语句和参数信息。储存了用问号占位符标记的生成的 SQL 语句,以及每一个参数的类型信息和参数符号表达式(就是用 #{} 包起来的部分);

parameterObject:传入的参数;

MetaObject:这个工具可以读取参数对象,然后根据表达式从参数对象取出对应的值。

有了 BoundSql 和 MetaObject 我们就可以手动把问号 SQL 拼成完整 SQL 了。

最后一个问题:Mybatis-Plus Wrapper 的原理是什么?它是怎么调用 Mybatis 的?

MyBatis-Plus

Mybatis-Plus 全面接管了 Mybatis。结合 Spring 框架,从头到尾都不需要手动加载配置,创建连接了。并且内置了很多方便的增删改查接口,以及条件构造器,可以很方便地进行条件查询。

QueryWrapper 里的内容

QueryWrapper 里的内容

通过对生成的 QueryWrapper 断点调试我们可以发现,wrapper 自己本身就是传递给 mapper 的参数。Wrapper 负责的是生成 where 部分的语句和参数。wrapper 得到的 SQL 语句里面的参数是 #{ew.paramNameValuePairs.MPGENVALx} 的形式,很明显引用了自己(ew 即 EntityWrapper,是 MP 旧版本的 Wrapper 类名)。

MPP selectList 查询方法的注入

MPP selectList 查询方法的注入

在项目启动的时候,MP 会找到 selectList 方法的实现类 SelectList,然后把所有的 SQL 片段拼接起来,动态生成一个 MappedStatement,然后放入 mybatis 的 sqlSessionFactory 里面。

假如我们调用了 service.selectList(wrapper),其实也相当于执行了一次 Mybatis 查询,只不过 Mapper 语句是 MP 帮你生成的(BaseMapper 方法注入),传入的参数也是 MP 帮你生成的(在 wrapper 里面)。

这样,完整的链条就串起来了:

  1. SqlSessionFactory + Mapper id => MappedStatement(SQL 模板)
  2. 拼接查询条件 Wrapper(参数)
  3. Wrapper + MappedStatement => BoundSql(占位符 SQL + 参数信息)
  4. BoundSql + Wrapper => 完整 SQL
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值