简介
mybatis是一个持久层框架,负责和数据库交互,它的内部封装了jdbc。用户通过使用mybatis框架,仅仅只需要提供mybatis的配置文件和sql,就可以实现和数据库的交互,不再需要编写获取数据库连接、执行sql、解析返回值等代码,这些任务统统被交给了mybatis。
使用mybatis的整体流程,就是读取并解析配置文件,然后创建SqlSession实例,通过SqlSession来调用mapper接口,执行sql。这里介绍两个关键点:mybatis是如何启动的、sql是如何执行的。
mybatis的启动
mybatis的启动过程,就是读取并解析配置文件,然后创建SqlSession实例的过程
相关组件
Resources
mybatis提供的工具类,用于加载外部的资源文件,它的内部封装了类加载器,通过类加载器提供的api来加载资源。
public class Resources {
// 类加载器的包装类
private static ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();
// 调用类加载器提供的API来加载外部资源
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
if (in == null) {
throw new IOException("Could not find resource " + resource);
}
return in;
}
}
// 类加载器的包装类
public class ClassLoaderWrapper {
ClassLoader defaultClassLoader;
ClassLoader systemClassLoader;
// 当前类中可以获取到的类加载器
ClassLoader[] getClassLoaders(ClassLoader classLoader) {
return new ClassLoader[]{
classLoader,
defaultClassLoader,
Thread.currentThread().getContextClassLoader(),
getClass().getClassLoader(),
systemClassLoader};
}
}
SqlSessionFactoryBuilder
建造者模式,创建SqlSessionFactory实例
public class SqlSessionFactoryBuilder {
}
SqlSessionFactory
工厂模式,创建SqlSession实例
SqlSessionFactory接口:
public interface SqlSessionFactory {
// 使用默认配置,创建一个SqlSession对象
SqlSession openSession();
// 获取配置信息
Configuration getConfiguration();
}
SqlSession
SqlSession是mybatis的顶层接口,用户通过操作SqlSession来操作mybatis,它定义了获取连接、获取配置信息、执行sql语句的方法
public interface SqlSession extends Closeable {
/*
* 执行sql语句的方法
*/
<T> T selectOne(String statement, Object parameter);
<E> List<E> selectList(String statement, Object parameter);
int insert(String statement, Object parameter);
int update(String statement, Object parameter);
int delete(String statement, Object parameter);
void commit(boolean force);
void rollback(boolean force);
// 关闭当前SqlSession
@Override
void close();
// 清理缓存
void clearCache();
// 获取配置信息
Configuration getConfiguration();
// 获取mapper接口
<T> T getMapper(Class<T> type);
// 获取连接
Connection getConnection();
}
总结:这里大致介绍了从加载配置文件到创建SqlSession的过程中的相关组件。
- mybatis使用类加载器提供的API来加载外部资源,
- 使用建造者模式来创建一个SqlSession工厂,建造者默认负责解析配置文件,根据用户提供的参数,来创建一个SqlSession工厂
- SqlSession工厂负责创建SqlSession
整体流程
第一步:SqlSessionFactoryBuilder
解析配置文件,创建SqlSessionFactory
// 1、build方法:核心创建方法,参数1是配置文件的输入流,参数2指定使用哪个环境
public SqlSessionFactory build(InputStream inputStream, String environment,
Properties properties) {
try {
// 解析配置文件
XMLConfigBuilder parser =
new XMLConfigBuilder(inputStream, environment, properties);
// 构建SqlSessionFactory的实例
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
// 2、上一步中调用的重载的build方法,这里已经解析好了配置文件,现在创建SqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
第二步:SqlSessionFactory
创建SqlSession,这里暂时不关注SqlSession中的组件,只关注它的建造流程,组件在随后学习sql执行的时候再看
// 参数1 execType,枚举值 SIMPLE, REUSE, BATCH,简单、重用、批量,默认是简单
// 参数2 事务隔离级别
private SqlSession openSessionFromDataSource(ExecutorType execType,
TransactionIsolationLevel level,
boolean autoCommit) {
Transaction tx = null;
try {
// 从配置信息中,获取environments标签中存储的环境信息
final Environment environment = configuration.getEnvironment();
// 创建事务工厂,用于管理数据库连接
final TransactionFactory transactionFactory =
getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory
.newTransaction(environment.getDataSource(), level, autoCommit);
// 创建执行器Executor
final Executor executor = configuration.newExecutor(tx, execType);
// 传入Configuration实例、Executor实例,创建DefaultSqlSession实例
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
总结:
1、创建SqlSession的过程,分别用到了建造者模式和工厂模式,为什么要选择这两种模式?
工厂模式和建造者模式的区别在于,工厂模式,用户不需要知道对象的创建过程,建造者模式,用户需要使用建造者提供的API来手动编排对象的创建过程。这里对应建造者模式和工厂模式的使用,我感觉很容易让人混淆,它们都是传入多个配置参数,创建一个实例,感觉可以互相替代。
解析配置文件
在之前学习创建SqlSessionFactory的时候,提到了解析配置文件,但是并没有深入学习,在这里,深入地学习mybatis是如何把配置文件中的信息解析到内存中。
相关组件
文件解析器
BaseBuilder: 文件解析器的公共父类
public abstract class BaseBuilder {
// 存储解析完的配置信息
protected final Configuration configuration;
protected final TypeAliasRegistry typeAliasRegistry;
protected final TypeHandlerRegistry typeHandlerRegistry;
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
}
主配置文件解析器 XMLConfigBuilder:
public class XMLConfigBuilder extends BaseBuilder {
private boolean parsed; // 解析完成后这个标志置为true
private final XPathParser parser; // 解析器
private String environment; // 使用哪个环境
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
}
mapper.xml文件解析器 XMLConfigBuilder,和主配置文件解析器相同的布局
public class XMLConfigBuilder extends BaseBuilder {
private boolean parsed;
private final XPathParser parser;
private String environment;
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
}
XML文件解析器 XPathParser:这个是最基础的组件,负责解析xml文件,需要用到第三方库提供的API
public class XPathParser {
private final Document document; // xml文件在内存中的实例
private boolean validation;
private EntityResolver entityResolver;
private Properties variables;
private XPath xpath;
}
整体流程
功能入口
创建解析主配置文件的解析器:XMLConfigBuilder
// SqlSessionFactoryBuilder类:使用建造者模式来创建SqlSessionFactory对象
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 创建配置文件解析器
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 解析配置文件并且创建SqlSessionFactory对象
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
第一步:解析主配置文件
XMLConfigBuilder的构造方法:
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
// XPathParser:负责把xml文件转换为document对象
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()),
environment, props);
}
public XPathParser(InputStream inputStream, boolean validation,
Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
// 将xml文件解析为document对象
this.document = createDocument(new InputSource(inputStream));
}
把输入流解析为document对象的方法:
public Configuration parse() {
// 判断是否已经解析过
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// <configuration>标签是主配置文件中的根节点,在这里解析出根节点后,
// 再调用parseConfiguration方法,解析根节点下的每个子节点
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
解析根节点下的每个子节点:
// 解析根节点下的每一个节点,解析完的结果会存在configuration对象中
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
// 解析properties节点
propertiesElement(root.evalNode("properties"));
// 解析settings节点
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
// 解析typeAliases节点
typeAliasesElement(root.evalNode("typeAliases"));
// 解析plugins节点
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析mappers节点
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
解析完节点中的信息后,会把解析结果保存到Configuration对象中,这里以解析typeAliases节点、解析environment节点、解析mappers节点为例。
解析typeAliases节点
typeAliases节点的案例
<typeAliases>
<typeAlias type="org.wyj.beans.User" alias="user" />
</typeAliases>
解析typeAliases节点的源码:
// 解析typeAliases节点
private void typeAliasesElement(XNode parent) {
if (parent != null) {
// 遍历typeAliases节点下的子节点
for (XNode child : parent.getChildren()) {
// 判断typeAliases节点的子节点的名称是否等于package
if ("package".equals(child.getName())) {
String typeAliasPackage = child.getStringAttribute("name");
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
} else {
// 如果子节点的名称不是package,一定是typeAliase,获取type属性和alias属性
String alias = child.getStringAttribute("alias");
String type = child.getStringAttribute("type");
try {
// 调用别名注册器,注册类对象和别名之间的对应关系
Class<?> clazz = Resources.classForName(type);
if (alias == null) {
typeAliasRegistry.registerAlias(clazz);
} else {
typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
别名注册器 TypeAliasRegistry:
// TypeAliasRegistry类
public void registerAlias(Class<?> type) {
// Class类中的getSimleName方法,获取到的是类名
String alias = type.getSimpleName();
// 如果一个类被@Alias注解标注,使用@Alias注解中指定的值,否则默认使用类名来作为类的别名
Alias aliasAnnotation = type.getAnnotation(Alias.class);
if (aliasAnnotation != null) {
alias = aliasAnnotation.value();
}
registerAlias(alias, type);
}
private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<>();
public void registerAlias(String alias, Class<?> value) {
if (alias == null) {
throw new TypeException("The parameter alias cannot be null");
}
// issue #748
String key = alias.toLowerCase(Locale.ENGLISH);
if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null
&& !TYPE_ALIASES.get(key).equals(value)) {
throw new TypeException("The alias '" + alias +
"' is already mapped to the value '" +
TYPE_ALIASES.get(key).getName() + "'.");
}
// 将别名和类对象保存到TypeAliasRegistry类的实例中
TYPE_ALIASES.put(key, value);
}
解析environments节点
environments节点的配置案例:
<environments default="develop" >
<environment id="develop" >
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
解析environments节点的源码
// XMLConfigBuilder类
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
// 获取environments标签的default属性
environment = context.getStringAttribute("default");
}
// 遍历environments标签中的子节点
for (XNode child : context.getChildren()) {
// 获取environment标签的id属性
String id = child.getStringAttribute("id");
// 如果它和default属性中指定的值是一致的,进行if逻辑
if (isSpecifiedEnvironment(id)) {
// 根据transactionManager标签,配置事务管理器
TransactionFactory txFactory =
transactionManagerElement(child.evalNode("transactionManager"));
// 根据dataSource标签,配置数据库连接池工厂
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
// 创建数据库连接池
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
// 将配置信息注册到configuration对象中
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
解析mappers节点
mappers节点的案例:
<mappers>
<mapper resource="userMapper.xml"/>
</mappers>
解析mappers节点的源码:
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
// 遍历mappers节点下的所有子节点
for (XNode child : parent.getChildren()) {
// 如果是package标签,获取并且加载package下所有的文件
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
// 如果子节点的名称不等于package,则子节点的名称一定等于mapper
// 获取resource、url、class属性,这三个属性只有一个有值
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
// 如果resource属性不为空
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
// 解析mapper.xml配置文件
XMLMapperBuilder mapperParser = new XMLMapperBuilder(
inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
// 如果url属性不为空
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(
inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
// 如果mapperClass属性不为空
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException(
"A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
// 最终,不管哪个属性,都是把解析到的信息注册到configuration对象中
}
第二步:解析mapper.xml
解析mapper文件的解析器:XMLMapperBuilder
// XMLMapperBuilder类
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
// 解析mapper文件中的标签
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
解析mapper文件中的标签:
private void configurationElement(XNode context) {
try {
// namespace属性
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
// cache标签
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
// sql标签
sqlElement(context.evalNodes("/mapper/sql"));
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException(
"Error parsing Mapper XML. The XML location is '" + resource +
"'. Cause: " + e, e);
}
}
解析select节点
// XMLStatementBuilder类
public void parseStatementNode() {
// 获取select标签中的所有属性
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
String resultMap = context.getStringAttribute("resultMap");
String resultType = context.getStringAttribute("resultType");
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
Class<?> resultTypeClass = resolveClass(resultType);
String resultSetType = context.getStringAttribute("resultSetType");
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
// Include Fragments before parsing
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
String resultSets = context.getStringAttribute("resultSets");
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
// 最终把数据保存到configuration对象的mappedStatements属性中,id是配置文件的namespace加标签id,值是select语句
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
把MappedStatement保存到configuration实例中:
public MappedStatement addMappedStatement(
String id,
SqlSource sqlSource,
StatementType statementType,
SqlCommandType sqlCommandType,
Integer fetchSize,
Integer timeout,
String parameterMap,
Class<?> parameterType,
String resultMap,
Class<?> resultType,
ResultSetType resultSetType,
boolean flushCache,
boolean useCache,
boolean resultOrdered,
KeyGenerator keyGenerator,
String keyProperty,
String keyColumn,
String databaseId,
LanguageDriver lang,
String resultSets) {
if (unresolvedCacheRef) {
throw new IncompleteElementException("Cache-ref not yet resolved");
}
// sql语句的id实际是上接口名 + 方法名
id = applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
// 生成mappedStatement,它存储了关于一条sql的全部信息
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
.resource(resource)
.fetchSize(fetchSize)
.timeout(timeout)
.statementType(statementType)
.keyGenerator(keyGenerator)
.keyProperty(keyProperty)
.keyColumn(keyColumn)
.databaseId(databaseId)
.lang(lang)
.resultOrdered(resultOrdered)
.resultSets(resultSets)
.resultMaps(getStatementResultMaps(resultMap, resultType, id))
.resultSetType(resultSetType)
.flushCacheRequired(valueOrDefault(flushCache, !isSelect))
.useCache(valueOrDefault(useCache, isSelect))
.cache(currentCache);
ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
if (statementParameterMap != null) {
statementBuilder.parameterMap(statementParameterMap);
}
MappedStatement statement = statementBuilder.build();
// 把数据保存到configuration实例中
configuration.addMappedStatement(statement);
return statement;
}
总结
解析配置文件的大概步骤,依据配置文件中设定好的标签和属性,依次解析。xml文件中的每个标签,都会对应一个实体类。解析完的结果存放到配置类中。需要用到第三方库提供的API来解析xml文件。
SQL的执行流程
整体流程:通过SqlSession的实例,获取mapper接口的实现类,这里的实现类是mybatis通过动态代理技术创建的,通过这个实现类来调用mapper.xml文件中配置的sql语句,执行sql。
过程拆解:
- 创建mapper接口的代理类,也可以理解为mapper接口的实现类
- 通过代理类调用sqlSession
- 通过sqlSession来执行sql
创建Mapper接口的实现类
mybatis会使用jdk提供的动态代理技术,创建Mapper接口的实现类
相关组件
mapper接口注册器 MapperRegistry
MapperRegistry:存储了mapper接口和它的代理类工厂之间的对应关系,代理类工厂负责创建mapper接口的代理类
public class MapperRegistry {
private final Configuration config;
// mapper接口的类对象和它的工厂类,工厂类负责创建代理类
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
}
代理类的工厂类 MapperProxyFactory
MapperProxyFactory:一个工厂类实例对应一个mapper接口,它负责创建这个mapper接口的代理类
public class MapperProxyFactory<T> {
// mapper接口
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
}
代理类的InvocationHandler MapperProxy
mybatis使用jdk提供的技术来创建代理类,代理类中调用InvocationHandler来实现增强功能
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface; // mapper接口
private final Map<Method, MapperMethod> methodCache; // 缓存接口中解析好的方法
}
整体流程
代理类是如何被创建的?
mapper注册器
1、在解析配置文件的时候,mybatis会把mapper接口和mapperProxyFactory之间的对应关系存储到配置类的mapper注册器中
加载Mapper接口的类对象:这一步发生在解析mapper.xml的时候
private void bindMapperForNamespace() {
// 获取namesapce
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
// namespace中的值是Mapper接口的类对象,这里加载类对象
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
//ignore, bound type is not required
}
if (boundType != null) {
if (!configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
// 注册mapper接口到配置类中
configuration.addMapper(boundType);
}
}
}
}
注册Mapper接口到配置类中:这里把配置文件中解析出的mapper接口的全限定名注册到配置类中
// Configuration类
public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
// mapperRegistry:实例化MapperProxyFactory,并且创建它和Mapper接口的对应关系:
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 实例化MapperProxyFactory,并且注册它和Mapper接口的对应关系
knownMappers.put(type, new MapperProxyFactory<>(type));
// 解析mapper接口
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
这里会把mapper接口和MapperProxyFactory之间的对应关系存储到mapperRegistry中,配置类持有mapperRegistry的实例。
动态代理技术
2、用户通过SqlSession来创建mapper接口时,使用动态代理技术创建一个代理类。调用SqlSession中的API来创建代理类,SqlSession持有configuration的实例,configuration持有mapperRegistry的实例,逐级调用,直到mapperRegistry中的getMapper方法
public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public MapperRegistry(Configuration config) {
this.config = config;
}
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 获取Mapper接口对应的MapperProxyFactory
final MapperProxyFactory<T> mapperProxyFactory =
(MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type +
" is not known to the MapperRegistry.");
}
try {
// 创建接口的代理对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
}
MapperProxyFactory:MapperProxy的工厂类, 它用于创建Mapper接口的动态代理类,MapperProxy相当于动态代理类的InvocationHandler
// mapperProxyFactory中的newInstance方法:
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy =
new MapperProxy<>(sqlSession, mapperInterface, methodCache);
// 调用重载的newInstance方法
return newInstance(mapperProxy);
}
// 重载的newInstance方法
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
// 底层是基于jdk提供的技术,在这里应该关注MapperProxy类,它是InvocationHandler接口的实现类
// 动态生成的代理类会执行MapperProxy类中的代码
return (T) Proxy
.newProxyInstance(mapperInterface.getClassLoader(),
new Class[] { mapperInterface }, mapperProxy);
}
总结
代理类的创建过程:
- 在解析配置文件时,把mapper接口和它的工厂类放到一个注册器中
- 创建mapper接口的代理类时,调用它对应的工厂类,创建一个代理类实例,底层使用jdk提供的动态代理技术
- 代理类的创建使用了工厂模式,一个mapper接口对应一个工厂实例
调用mapper接口的实现类
相关组件
MapperMethod
封装了Mapper接口中单个方法的反射信息,主要是方法的返回值类型和sql的类型,通过这些信息来决定下一步调用哪个API执行任务。
public class MapperMethod {
// SqlCommand,内部类,存储sql的类型,insert、select等
private final SqlCommand command;
// MethodSignature,内部类,存储方法的签名
private final MethodSignature method;
// 构造方法
public MapperMethod(Class<?> mapperInterface, Method method,
Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
public static class SqlCommand {
private final String name; // sql的id
private final SqlCommandType type; // sql类型,枚举值 UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH
}
public static class MethodSignature {
// 方法的返回值类型,用于根据返回值来决定调用哪个方法执行sql
private final boolean returnsMany;
private final boolean returnsMap;
private final boolean returnsVoid;
private final boolean returnsCursor;
private final boolean returnsOptional;
private final Class<?> returnType;
private final String mapKey;
private final Integer resultHandlerIndex;
private final Integer rowBoundsIndex;
private final ParamNameResolver paramNameResolver; // 参数解析器,解析@Param注解
}
}
它有两个内部类 SqlCommand、MethodSignature,同时它持有这两个内部类的实例,这两个实例中都存储了Mapper接口中的方法的信息
整体流程
这里涉及到jdk动态代理的执行机制:代理类会执行InvocationHandler接口中的invoke方法,在这里实现InvocationHandler接口的是MapperProxy。所以代理类从MapperProxy的invoke方法开始执行。
public class MapperProxy<T> implements InvocationHandler, Serializable {
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
// 缓存解析好的方法
private final Map<Method, MapperMethod> methodCache;
// 代理类执行invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 如果是Object类中的方法,直接调用
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
// 如果是接口中的default方法,直接调用,接口中的default方法是public、非抽象的的实例方法
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// 生成并且缓存MapperMethod
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 调用MapperMethod中的方法,执行sql
return mapperMethod.execute(sqlSession, args);
}
// 生成并且缓存MapperMethod
private MapperMethod cachedMapperMethod(Method method) {
// 如果缓存中没有方法,创建MapperMethod实例,然后放到缓存中
return methodCache
.computeIfAbsent(method, k -> new MapperMethod(
mapperInterface, method, sqlSession.getConfiguration()));
}
}
MapperMethod中执行sql语句的方法:
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
// 根据sql语句的类型,选择不同的方法,command.getType,获取到的就是标签类型,
// 例如select、update等
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
// select语句,依据返回值,选择不同的方法
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
// 返回值是一个list
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
// 返回值是一个map
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional() &&
(result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
这里以查询多行数据为例:
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
// 获取sql语句的参数,这里会解析@Param标签,把参数的名称和值封装到Object中
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) { // rowRound,如果没有特殊配置,就是没有
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
} else {
// 调取sqlSession的selectList方法
result = sqlSession.<E>selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
总结
用户调用mapper接口,通过mapper接口的代理类来调用SqlSession,实际是通过SqlSession来执行sql的。
mapper接口的代理类,实际上是一种优化措施,使用面向对象的方式来执行sql,所以代理类的实现中,最主要做的事情,就是解析mapper接口中的方法,通过解析结果,来决定调用sqlSession中的哪些api,例如,select语句,调用sqlSession中的selectList方法,
实现类调用SqlSession
sqlSession,最核心的API,它包含了最核心的流程
执行过程中涉及到的组件:
- 执行器:executor,负责执行sql,同时实现缓存功能,调用sqlSrouce解析sql,调研statementHandler执行sql
- sqlSource:负责解析sql,把sql中的mybatis参数替换为问号,例如,select * from user where id = #{id},会被解析为select * from user where id = ?,同时获取参数的值,以及其它相关信息,包括jdbctype、javaType等,这里还会负责解析动态sql
- 语句处理器:statementHandler,负责处理statement实例,设置参数,执行sql语句,解析返回值
- 参数处理器:parameterHandler,语句处理器调用参数处理器来为statement实例设置参数
- 返回值解析器:resultSetHandler:语句处理器调用返回值解析器来解析statement实例返回的resultSet
相关组件
执行器 Executor
执行器,负责执行sql语句
1、顶层接口:Executor
public interface Executor {
// update语句
int update(MappedStatement ms, Object parameter) throws SQLException;
// 查询语句
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
// 事务相关
void commit(boolean required) throws SQLException;
void rollback(boolean required) throws SQLException;
}
2、接口的实现类:执行器采用了模板方法设计模式,父类负责流程编排,子类负责具体事项,在这里,父类是BaseExecutor,实现了一级缓存和流程编排,子类负责具体的查询任务,这里子类以SimpleExectuor为例。
BaseExecutor:
public abstract class BaseExecutor implements Executor {
protected Transaction transaction; // 数据库连接
protected Executor wrapper; // 这里相当于this,它持有自己的实例
protected PerpetualCache localCache; // 一级缓存(SqlSession级别)
// 交给子类实现的扩展点
protected abstract int doUpdate(MappedStatement ms, Object parameter)
throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
}
SimpleExecutor:实现了具体的查询等功能
public class SimpleExecutor extends BaseExecutor {
public SimpleExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}
}
SqlSource
负责解析mybatis参数和动态sql相关标签,返回BoundSql,BoundSql就是可以在数据库中执行的sql了。
1、顶层接口:SqlSource接口:
public interface SqlSource {
BoundSql getBoundSql(Object parameterObject);
}
SqlSource的实现:
StaticSqlSource:存储解析好的结果
public class StaticSqlSource implements SqlSource {
private final String sql; // sql语句
private final List<ParameterMapping> parameterMappings; // 参数
private final Configuration configuration;
}
RawSqlSource:解析普通sql,它会被解析好的结果放到StaticSqlSource中
public class RawSqlSource implements SqlSource {
private final SqlSource sqlSource; // 这里实际上就是StaticSqlSource
}
BoundSql
持有Sql语句、参数映射关系parameterMapping、参数。这里的sql语句,设置参数后,就可以在数据库中执行了
public class BoundSql {
private final String sql; // sql语句
private final List<ParameterMapping> parameterMappings; // 参数映射
private final Object parameterObject;
private final Map<String, Object> additionalParameters;
private final MetaObject metaParameters;
}
statement处理器 StatementHandler
操作jdbc层面的代码,处理Statement实例
1、顶层接口:StatementHandler:
public interface StatementHandler {
Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;
// 为Statement实例设置参数
void parameterize(Statement statement)
throws SQLException;
/*
* 执行sql语句的方法
*/
void batch(Statement statement)
throws SQLException;
int update(Statement statement)
throws SQLException;
<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException;
<E> Cursor<E> queryCursor(Statement statement)
throws SQLException;
BoundSql getBoundSql();
ParameterHandler getParameterHandler();
}
2、它的实现类
BaseStatementHandler:基础实现
public abstract class BaseStatementHandler implements StatementHandler {
// 扩展点,初始化statement实例,不同的子类可以创建不同类型的statement实例
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
}
SimpleStatementHandler:处理普通的statement实例
public class SimpleStatementHandler extends BaseStatementHandler {
// 创建普通的statement实例
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
// 调用jdbc提供的api,创建普通的statement实例
return connection.createStatement();
} else {
return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
// 普通的statement实例不需要设置参数
@Override
public void parameterize(Statement statement) {
// N/A
}
}
PreparedStatementHandler:创建PrepareStatement
public class PreparedStatementHandler extends BaseStatementHandler {
// 创建PrepareStatement
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
// 创建PrepareStatement实例
return connection.prepareStatement(sql);
} else {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
// 为PrepareStatement设置参数
@Override
public void parameterize(Statement statement) throws SQLException {
// 调用参数处理器,为PrepareStatement实例设置参数
parameterHandler.setParameters((PreparedStatement) statement);
}
}
参数处理器 ParameterHandler
操作statement实例,为它设置参数
ParameterHandler:
public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement ps)
throws SQLException;
}
返回值解析器 ResultSetHandler
负责解析返回值
ResultSetHandler:
public interface ResultSetHandler {
// 解析返回值
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
总结
这里介绍了sql执行过程中的组件和它的基本实现,从继承体系来看,mybatis中的继承体系,基本上是 接口 -> 抽象类 -> 具体实现 三层,还有一点,具体实现之间并不是孤立的,很多地方层层代理,组成一个代理链,基于这种模式,如果想要在原有的基础上增加新的功能,不需要继承原来的实现类,只需要创建新的实现类,然后持有原有实现类的实例,就可以了,这个地方比较具有参考价值。
整体流程
大致流程总结:
- SqlSession调用执行器来执行sql
- 执行器调用sqlSource,解析sql
- 执行器调用statementHandler,创建statement实例
- statementHandler调用参数解析器,为statement实例设置参数
- statementHandler执行sql
- statementHandler调用resultSet解析器,解析返回值
SqlSession调用执行器
SqlSession接口中提供了执行sql语句的功能,在这里以selectList为例,mapper接口的代理类就是通过调用selectList等方法来执行sql的。
// 参数1是sql的id
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 获取MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
// 调用执行器来执行sql语句
return executor.query(ms, wrapCollection(parameter), rowBounds,
Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
Executor的整体流程
执行器的创建
这里需要先介绍一下执行器是如何创建的,它不是在执行sql的过程中创建的,而是在获取sqlSession实例的时候创建的
功能入口:在DefaultSqlSessionFactory中,创建SqlSession时,需要为SqlSession创建Executor
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 创建执行器
final Executor executor = configuration.newExecutor(tx, execType);
// 把执行器保存到SqlSession中
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
创建执行器:
// Configuration类
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
// 用户配置的执行器类型,默认SIMPLE
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction); // 批量执行
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction); // 重复使用statement实例
} else {
executor = new SimpleExecutor(this, transaction); // 默认的执行器
}
// 如果开启了缓存,在普通执行器的基础上,再包一层缓存执行器,缓存执行器在执行完缓存功能后,会调用普通执行器,类似于
// 代理机制
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 处理拦截器
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
执行器的执行
BaseExecutor:以query方法为例
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 1、调用sqlSource将sql解析为可执行的sql
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
// 重载的query方法
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
调用重载的query方法:
// BaseExecutor
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
// 查询前是否要清空一级缓存
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
// 一级缓存(SqlSession级别,默认开启)
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
// 如果一级缓存有数据,使用一级缓存的数据
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 执行sql语句
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
queryFromDatabase方法:执行sql语句
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
// 交给子类实现的,具体的查询方法
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
// 向一级缓存中存入结果
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
doQuery方法:真正执行查询功能,这里就是statementHandler、参数处理器、返回值处理器的整体流程
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter,
RowBounds rowBounds,
ResultHandler resultHandler, BoundSql boundSql)
throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 创建StatementHandler的实例
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 创建statement实例,并且为它设置参数
stmt = prepareStatement(handler, ms.getStatementLog());
// 调用StatementHandler执行查询语句
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
// 创建statement实例
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
// 调用statementHandler,创建statement实例
stmt = handler.prepare(connection, transaction.getTimeout());
// 为statement设置参数,这里是statementHandler内部调用参数处理器完成的
handler.parameterize(stmt);
return stmt;
}
总结:执行器中做了两件事情,
- 解析sql,如果sql中包含动态sql,在这里也会负责解析动态sql,生成一个可以执行的sql
- 调用StatementHandler,执行statement实例
- 调用参数处理器,为statement实例设置参数
- 执行statement实例
- 调用返回值解析器,解析statement实例的返回值
执行器调用SqlSource 解析sql
这里只展示最关键的代码:解析sql的功能入口
public class SqlSourceBuilder extends BaseBuilder {
// 解析sql
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
// 创建解析器,解析sql中的参数,也就是 #{key} 之类的
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
String sql = parser.parse(originalSql);
// 存储解析结果,sql和它需要的参数
return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}
}
执行器调用statement处理器
执行器调用statement处理器来执行sql
1、创建statement处理器:RoutingStatementHandler
public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler delegate;
// 构造方法。这里其实可以理解为一个工厂方法,用户持有当前类的实例,由它来负责根据statement的类型,寻找合适的处理类。
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
}
2、使用它来执行sql:这里以PreparedStatementHandler为例,它负责处理PreparedStatement,它支持向sql语句中设置参数,使用设置参数的方式代替字符串拼接,避免sql注入的攻击
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 执行sql,这是jdbc中的代码,之前已经设置好了PreparedStatement,现在可以直接执行了
ps.execute();
// 解析返回值
return resultSetHandler.handleResultSets(ps);
}
调用参数处理器
为sql语句设置参数
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
// 遍历之前解析好的参数
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
// 参数名
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
// 获取参数的值
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// 设置参数
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
调用返回值解析器
解析返回值:
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
// 获取原始的返回值,后续的代码就是需要解析这个返回值
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 配置文件中配置的resultMap标签
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
// 解析返回值
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
总结
这里介绍的只是整体流程,可以看出,基本是对于jdbc的封装,还有很多细节,包括通过反射操作对象、缓存、插件等,随后介绍