1. 背景
MyBatis提供了简单的Java注解,使得我们可以不配置XML格式的Mapper文件,也能方便的编写简单的数据库操作代码:
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{userId}")
User getUser(@Param("userId") String userId);
}
但是注解对动态SQL的支持一直差强人意,即使MyBatis提供了InsertProvider等*Provider注解来支持注解的Dynamic SQL,也没有降低SQL的编写难度,甚至比XML格式的SQL语句更难编写和维护。
注解的优势在于能清晰明了的看见接口所使用的SQL语句,抛弃了繁琐的XML编程方式。但没有良好的动态SQL支持,往往就会导致所编写的DAO层中的接口冗余,所编写的SQL语句很长,易读性差……
Mybatis在3.2版本之后,提供了LanguageDriver接口,我们可以使用该接口自定义SQL的解析方式。故在这里向大家介绍下以此来实现注解方式下的动态SQL。
2. 实现方案
我们先来看下LanguageDriver接口中的3个方法:
public interface LanguageDriver {
ParameterHandler createParameterHandler(MappedStatement var1, Object var2, BoundSql var3);
SqlSource createSqlSource(Configuration var1, XNode var2, Class<?> var3);
SqlSource createSqlSource(Configuration var1, String var2, Class<?> var3);
}
- createParameterHandler方法为创建一个ParameterHandler对象,用于将实际参数赋值到JDBC语句中
- 将XML中读入的语句解析并返回一个sqlSource对象
- 将注解中读入的语句解析并返回一个sqlSource对象
一旦实现了LanguageDriver,我们即可指定该实现类作为SQL的解析器,在XML中我们可以使用 lang 属性来进行指定
<typeAliases>
<typeAlias type="org.sample.MyLanguageDriver" alias="myLanguage"/>
</typeAliases>
<select id="selectBlog" lang="myLanguage">
SELECT * FROM BLOG
</select>
也可以通过设置指定解析器的方式:
<settings>
<setting name="defaultScriptingLanguage" value="myLanguage"/>
</settings>
如果不使用XML Mapper的形式,我们可以使用@Lang注解
public interface Mapper {
@Lang(MyLanguageDriver.class)
@Select("SELECT * FROM users")
List<User> selectUser();
}
LanguageDriver的默认实现类为XMLLanguageDriver和RawLanguageDriver;分别为XML和Raw,Mybatis默认是XML语言,所以我们来看看XMLLanguageDriver中是怎么实现的:
public class XMLLanguageDriver implements LanguageDriver {
public XMLLanguageDriver() {
}
public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
}
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
return builder.parseScriptNode();
}
public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
if(script.startsWith("<script>")) {
XPathParser textSqlNode1 = new XPathParser(script, false, configuration.getVariables(),
new XMLMapperEntityResolver());