SqlSource用于描述通过@Select、@Insert、@Delete、@Update、@SelectProvider、@InsertProvider、@DeleteProvider和@UpdateProvider等注解配置的SQL信息,或者通过XML配置文件配置的SQL信息。SqlSource接口又定义了BoundSql getBoundSql(Object parameterObject)方法来获取对SQL信息进行封装的BoundSql实例。
SqlSource接口中只定义了一个getBoundSql()方法,该方法返回一个BoundSql实例,如下源代码所示:
public interface SqlSource {
BoundSql getBoundSql(Object parameterObject);
}
Mybatis针对SqlSource接口一共提供DynamicSqlSource、ProviderSqlSource、RawSqlSource和StaticSqlSource四种实现,
ProviderSqlSource用于描述通过@SelectProvider、@InsertProvider、@DeleteProvider和@UpdateProvider注解配置的SQL信息,如下述代码块所示,通过@SelectProvider注解指定SqlGenerator类中的selectIds方法来构造SQL语句。
@SelectProvider(type = SqlGenerator.class, method = "selectIds")
List<Integer> selectIds();
ProviderSqlSource是通过其构造函数来完成构造当前指定的SQL信息,如下源代码逻辑,ProviderSqlSource构造函数的逻辑可分为两大类,一类是正常的给属性复制,一类则是处理通过注解type和method属性指定构造SQL的信息,详情如下:
public ProviderSqlSource(Configuration configuration, Annotation provider,
Class<?> mapperType, Method mapperMethod) {
String candidateProviderMethodName;
Method candidateProviderMethod = null;
try {
// 省略当前configuration、mapperMethod和languageDriver属性的赋值
// 获取当前provider注解指定的SQL构造类,如代码块中的type = SqlGenerator.class
this.providerType = getProviderType(configuration, provider, mapperMethod);
// 获取当前provider注解指定的方法,如代码块中的method = "selectIds"
candidateProviderMethodName = (String) provider.annotationType()
.getMethod("method").invoke(provider);
if (candidateProviderMethodName.length() == 0
&& ProviderMethodResolver.class.isAssignableFrom(this.providerType)) {
// 如果没有执行method且当前provider注解与ProviderMethodResolver是父子关系
candidateProviderMethod = ((ProviderMethodResolver) this.providerType.getDeclaredConstructor().newInstance())
.resolveMethod(new ProviderContext(mapperType, mapperMethod, configuration.getDatabaseId()));
}
if (candidateProviderMethod == null) {
// 确认用于构造SQL的方法
candidateProviderMethodName = candidateProviderMethodName.length() == 0
? "provideSql" : candidateProviderMethodName;
// 从指定构造类中找到构造SQL的方法的Method
for (Method m : this.providerType.getMethods()) {
if (candidateProviderMethodName.equals(m.getName())
&& CharSequence.class.isAssignableFrom(m.getReturnType())) {
if (candidateProviderMethod != null) {
throw new BuilderException("");
}
candidateProviderMethod = m;
}
}
}
}
// 省略异常处理和部分赋值代码
}
DynamicSqlSource用于描述XML配置文件和@Select、@Update等注解,通过参数占位符或者动态标签配置的动态SQL信息,其也是通过构造函数来完成对SQL信息的描述。其构造函数相对简单,就只是对属性值进行赋值,如下源代码所示。
public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
this.configuration = configuration;
this.rootSqlNode = rootSqlNode;
}
RawSqlSource用于描述XML配置文件和@Select、@Update等注解,不包含参数占位符和动态标签的SQL配置信息。RawSqlSource构造函数除了正常给属性赋值以外,还多了一步解析SQL的操作。
public RawSqlSource(Configuration configuration, SqlNode rootSqlNode,
Class<?> parameterType) {
this(configuration, getSql(configuration, rootSqlNode), parameterType);
}
public RawSqlSource(Configuration configuration, String sql,
Class<?> parameterType) {
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> clazz = parameterType == null ? Object.class : parameterType;
sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
}
private static String getSql(Configuration configuration, SqlNode rootSqlNode) {
DynamicContext context = new DynamicContext(configuration, null);
rootSqlNode.apply(context);
return context.getSql();
}
BoundSql是对SQL语句及参数信息的封装,是SqlSource解析后的结果。Executor接口所定义的方法上可以清晰地看见BoundSql是作为入参传入的,所以不难推导出Executor是将BoundSql作为SQL信息来执行SQL语句的。
BoundSql源代码就是只是封装了SQL语句、参数映射、Mapper调用时传入的参数对象和一个反射工具类,其这几个属性对应的值都是在构造BoundSql实例时通过构造函数传入的。
public class BoundSql {
private final String sql;
private final List<ParameterMapping> parameterMappings;
private final Object parameterObject;
private final Map<String, Object> additionalParameters;
private final MetaObject metaParameters;
public BoundSql(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {
this.sql = sql;
this.parameterMappings = parameterMappings;
this.parameterObject = parameterObject;
this.additionalParameters = new HashMap<>();
this.metaParameters = configuration.newMetaObject(additionalParameters);
}
}