MappedStatement 类是 Mybatis 框架的核心类之一,它存储了一个 sql 对应的所有信息。
Mybatis 通过解析 XML 和 mapper 接口上的注解,生成 sql 对应的MappedState
ment 实例,并放入 SqlSessionTemplate 中 configuration 类属性中。
执行 mapper 接口中的方法时,会从 configuration 中找到对应的mappedStatem
ent,然后进行后续的操作。
MappedStatement对象详解
public final class MappedStatement {
//该Mapper.xml的绝对路径
private String resource;
//mybatis所有的配置
private Configuration configuration;
//sql的ID(命令空间+key)
private String id;
//尝试影响驱动程序每次批量返回的结果行数和这个设置值相等
private Integer fetchSize;
//SQL超时时间
private Integer timeout;
//Statement的类型,STATEMENT/PREPARE/CALLABLE
private StatementType statementType;
//结果集类型,FORWARD_ONLY/SCROLL_SENSITIVE/SCROLL_INSENSITIVE
private ResultSetType resultSetType;
//表示解析出来的SQL
private SqlSource sqlSource;
//缓存
private Cache cache;
//已废弃
private ParameterMap parameterMap;
//对应的ResultMap
private List<ResultMap> resultMaps;
private boolean flushCacheRequired;
private boolean useCache;
private boolean resultOrdered;
//SQL类型,INSERT/SELECT/DELETE
private SqlCommandType sqlCommandType;
//和SELECTKEY标签有关
private KeyGenerator keyGenerator;
private String[] keyProperties;
private String[] keyColumns;
private boolean hasNestedResultMaps;
//数据库ID,用来区分不同环境
private String databaseId;
private Log statementLog;
private LanguageDriver lang;
//多结果集时
private String[] resultSets;
MappedStatement() {
// constructor disabled
}
...
}
其中真正表示SQL的字段是SqlSource这个对象。
而SqlSource接口很简单,只有一个getBoundsql方法。
public interface SqlSource {
BoundSql getBoundSql(Object var1);
}
sqlSource有很多实现,需要我们重点关注的是StaticSqlSource,RawSqlSource和DynamicSqlSource。而通过上述的实现,便可以将mybatis特有的sql格式转化成可供PrepareStatement直接执行的sql。
1.mappedStatement —— BoundSql对象
BoundSql boundSql = mappedStatement.getBoundSql(parameter);
上述方法主要是对动态标签的解析,获取完全可执行的sql。对#{ }字符解析,将其替换成?,最后均包装成域表达式供PrepareStatement调用。
解析后的sql保存在sql对象中;
请求参数保存在parameterObject对象中;
#{ }key属性以及相对应的参数映射,比如javaType,jdbcType等信息均保存至BoundSql的parameterMapping属性中。供最后的域表达式对象PrepareStatement赋值使用。
BoundSql对象中的sql对象是对动态标签解析后的完全可执行的sql。
2.BoundSql—ParameterObject对象
该对象为sql执行的参数。也就是我们传入的参数。因为使用了@param注解,故有两种方式可以获取到value的值。
//获取parameterObject对象
Object parameter = null;
if (invocation.getArgs().length > 1) {
parameter = invocation.getArgs()[1];
}
注意:ParameterObject是一个Object对象,上传不同的参数时,该对象的类型不同。
若上传一个参数对象时:为该参数的类型;
若上传多个参数对象时:为ParamMap对象,由于我们使用了@Param注解,故可以使用注解的key,也可以使用param1取出变量。
3.BoundSql—ParameterMapping对象
采用#{var}的形式来引用变量时,其中的变量会在解析Mapper.xml文件中的语句时,就被替换成占位符“?”,同时通过ParameterMapping类记录对应的变量信息。在真正执行对应语句的时候回传递真实的参数。根据parameterMapping信息给ParameterStatement设置参数。
4.MappedStatement——Configuration对象
mybatis会在启动时读取所有的配置文件,然后加载到内存中,Configuration对象就是承载整个配置的类。
Configuration configuration = mappedStatement.getConfiguration();
public class Configuration {
/**
* MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中,
* 比如设置不同的开发、测试、线上配置,在每个配置中可以配置事务管理器和数据源对象.
*/
protected Environment environment;
//允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为false。
protected boolean safeRowBoundsEnabled = false;
//允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为false
protected boolean safeResultHandlerEnabled = true;
//是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。
protected boolean mapUnderscoreToCamelCase = false;
//当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载(参考lazyLoadTriggerMethods).
protected boolean aggressiveLazyLoading = true;
//是否允许单一语句返回多结果集(需要兼容驱动)
protected boolean multipleResultSetsEnabled = true;
//允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。
protected boolean useGeneratedKeys = false;
//使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。
protected boolean useColumnLabel = true;
//配置全局性的cache开关
protected boolean cacheEnabled = true;
/*指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。
注意基本类型(int、boolean等)是不能设置成 null 的。*/
protected boolean callSettersOnNulls = false;
//指定 MyBatis 增加到日志名称的前缀。
protected String logPrefix;
//指定 MyBatis 所用日志的具体实现,未指定时将自动查找
protected Class <? extends Log> logImpl;
/*MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。
默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。
若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。*/
protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
/*当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。
某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。*/
protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
//指定哪个对象的方法触发一次延迟加载。
protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
//设置超时时间,它决定驱动等待数据库响应的秒数。
4.1Configuration — TypeHandlerRegistry对象
类型处理器注册对象。在构建TypeHandlerRegistry对象的时候,便将类型注册了进去。
类型处理器TypeHandlerRegistry简单点就是用于处理javaType与jdbcType之间的类型转换用的处理器,Mybatis针对诸多Java类型与数据库类型进行了匹配处理。
//获取到类型处理器
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
5.BoundSql—MetaObject对象
MetaObject类相当于一个工具类,Mybatis在sql参数设置和结果集映射里经常使用到这个对象。
//原始的对象
private Object originalObject;
//对原始对象的一个包装
private ObjectWrapper objectWrapper;
//这两个属性基本不用,因为在Mybatis中都找不到ObjectWrapperFactory的有效实现类
private ObjectFactory objectFactory;
private ObjectWrapperFactory objectWrapperFactory;