可能大家会在平常的工作遇到这样的需求,在使用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 的列表
那 MappedStatement 是怎么和我们传入的参数组合,最后生成 PreparedStatement 的呢?经过一段时间顺藤摸瓜地查找,终于找到了处理逻辑所在的地方:DefaultParameterHandler#setParameters
。
boundSql
:SQL 语句和参数信息。储存了用问号占位符标记的生成的 SQL 语句,以及每一个参数的类型信息和参数符号表达式(就是用 #{}
包起来的部分);
parameterObject
:传入的参数;
MetaObject
:这个工具可以读取参数对象,然后根据表达式从参数对象取出对应的值。
有了 BoundSql 和 MetaObject 我们就可以手动把问号 SQL 拼成完整 SQL 了。
最后一个问题:Mybatis-Plus Wrapper 的原理是什么?它是怎么调用 Mybatis 的?
MyBatis-Plus
Mybatis-Plus 全面接管了 Mybatis。结合 Spring 框架,从头到尾都不需要手动加载配置,创建连接了。并且内置了很多方便的增删改查接口,以及条件构造器,可以很方便地进行条件查询。
通过对生成的 QueryWrapper 断点调试我们可以发现,wrapper 自己本身就是传递给 mapper 的参数。Wrapper 负责的是生成 where 部分的语句和参数。wrapper 得到的 SQL 语句里面的参数是 #{ew.paramNameValuePairs.MPGENVALx}
的形式,很明显引用了自己(ew 即 EntityWrapper,是 MP 旧版本的 Wrapper 类名)。
在项目启动的时候,MP 会找到 selectList 方法的实现类 SelectList
,然后把所有的 SQL 片段拼接起来,动态生成一个 MappedStatement
,然后放入 mybatis 的 sqlSessionFactory 里面。
假如我们调用了 service.selectList(wrapper)
,其实也相当于执行了一次 Mybatis 查询,只不过 Mapper 语句是 MP 帮你生成的(BaseMapper 方法注入),传入的参数也是 MP 帮你生成的(在 wrapper 里面)。
这样,完整的链条就串起来了:
- SqlSessionFactory + Mapper id => MappedStatement(SQL 模板)
- 拼接查询条件 Wrapper(参数)
- Wrapper + MappedStatement => BoundSql(占位符 SQL + 参数信息)
- BoundSql + Wrapper => 完整 SQL