场景:需要动态生成查询的API接口,实现方式打算每个API底层都配置一个MyBatis动态SQL脚本,然后让Mybatis解析并返回查询结果,那么如果手动让Mybatis帮我们解析动态SQL呢?工具类如下:
public class AutoApiSqlMapper {
@Autowired
private SqlSessionFactory sqlSessionFactory;
private SqlBuilderStatement SqlBuilderStatement;
@PostConstruct
public void init() {
this.SqlBuilderStatement = new SqlBuilderStatement(sqlSessionFactory.getConfiguration());
}
public Map<String, Object> sqlSelectOne(String sql) {
List<Map<String, Object>> list = this.selectList(sql);
if (!CollectionUtils.isEmpty(list)) {
return list.get(0);
}
return null;
}
public List<Map<String, Object>> selectList(String sql) {
String msId = this.SqlBuilderStatement.select(sql);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
try {
return sqlSession.selectList(msId);
} finally {
sqlSession.close();
}
}
public <T> List<T> selectList(String sql, Object value, Class<T> resultType) {
Class<?> parameterType = value != null ? value.getClass() : null;
String msId;
if (resultType == null) {
msId = this.SqlBuilderStatement.selectDynamic(sql, parameterType);
} else {
msId = this.SqlBuilderStatement.selectDynamic(sql, parameterType, resultType);
}
SqlSession sqlSession = sqlSessionFactory.openSession(true);
try {
return sqlSession.selectList(msId, value);
} finally {
sqlSession.close();
}
}
private static class SqlBuilderStatement {
private Configuration configuration;
private LanguageDriver languageDriver;
private SqlBuilderStatement(Configuration configuration) {
this.configuration = configuration;
this.languageDriver = configuration.getDefaultScriptingLanguageInstance();
}
private String newMsId(String sql, SqlCommandType sqlCommandType) {
return sqlCommandType.toString() + "." + sql.hashCode();
}
private boolean hasMappedStatement(String msId) {
return this.configuration.hasStatement(msId, false);
}
private void newSelectMappedStatement(String msId, SqlSource sqlSource, final Class<?> resultType) {
MappedStatement ms = (new MappedStatement.Builder(this.configuration, msId, sqlSource, SqlCommandType.SELECT)).resultMaps(new ArrayList<ResultMap>() {
{
this.add((new ResultMap.Builder(SqlBuilderStatement.this.configuration, "defaultResultMap", resultType, new ArrayList(0))).build());
}
}).build();
this.configuration.addMappedStatement(ms);
}
private String select(String sql, Class<?> resultType) {
String msId = this.newMsId(resultType + sql, SqlCommandType.SELECT);
if (!this.hasMappedStatement(msId)) {
StaticSqlSource sqlSource = new StaticSqlSource(this.configuration, sql);
this.newSelectMappedStatement(msId, sqlSource, resultType);
}
return msId;
}
private String select(String sql) {
String msId = this.newMsId(sql, SqlCommandType.SELECT);
if (!this.hasMappedStatement(msId)) {
StaticSqlSource sqlSource = new StaticSqlSource(this.configuration, sql);
this.newSelectMappedStatement(msId, sqlSource, Map.class);
}
return msId;
}
private String selectDynamic(String sql, Class<?> parameterType) {
String msId = this.newMsId(sql + parameterType, SqlCommandType.SELECT);
if (!this.hasMappedStatement(msId)) {
SqlSource sqlSource = this.languageDriver.createSqlSource(this.configuration, sql, parameterType);
this.newSelectMappedStatement(msId, sqlSource, Map.class);
}
return msId;
}
private String selectDynamic(String sql, Class<?> parameterType, Class<?> resultType) {
String msId = this.newMsId(resultType + sql + parameterType, SqlCommandType.SELECT);
if (!this.hasMappedStatement(msId)) {
SqlSource sqlSource = this.languageDriver.createSqlSource(this.configuration, sql, parameterType);
this.newSelectMappedStatement(msId, sqlSource, resultType);
}
return msId;
}
}
}
单测:
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = Application.class)
public class AutoApiSqlTest {
@Autowired
private AutoApiSqlMapper autoApiSqlMapper;
@Test
public void test01() {
Map<String, String> map = new HashMap<>();
map.put("id", null);
//复杂点的查询,这里参数和上面不同的地方,在于传入了一个对象
List<Map> list = autoApiSqlMapper.selectList("<script>" +
"select * from position " +
" <where>" +
" <if test=\"id != null\">" +
" id = #{id}" +
" </if>" +
" </where>" +
"</script>", map, Map.class);
System.out.println(JSON.toJSONString(list));
}
}