mybatis
系列–1
–启动流程
文章目录
- `mybatis`系列--`1`--启动流程
- 1 前言
- 2 原生`jdbc`
- 3 `mybatis`的简单用法
- 4 启动流程
- 5 `sqlSession`完成增删改查
- 5.1 `ms.getBoundSql(parameterObject)`
- 5.2 `query(ms, parameterObject, rowBounds, resultHandler, key, boundSql)`
- 6 总结
1 前言
常用的一些框架的源码基本上都已经研究过了,现在就剩
mybatis
了。那么首先我们得知道mybatis
有什么用,他帮我们做了什么?
2 原生jdbc
面试中常问的一道题目:
jdbc
的流程是什么?一般来说分为如下几步:
- 加载驱动
- 获取数据库连接
- 获取
PreparedStatement
对象- 执行
sql
- 处理结果集
- 关闭结果集,
statement
,数据库连接具体的代码如下:
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
Class.forName("org.postgresql.Driver");
connection = DriverManager.getConnection("jdbc:postgresql://192.168.1.7:5432/postgres", "postgres", "123456");
preparedStatement = connection.prepareStatement("select * from cas_base.user where id=?");
preparedStatement.setString(1, "1");
resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
String id = resultSet.getString("id");
String username = resultSet.getString("username");
System.out.println(id);
System.out.println(username);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
这一块代码就不用注释了,大家应该都玩过。现在就有一个问题,每一个查询都要写一遍这个代码,而真正游泳的代码就只有sql
和结果集解析逻辑,其他的代码对于别的查询来说,都是一样的,那么能不能把这一部分逻辑抽取出来,让开发者只需要关注sql
和结果集解析呢?mybatis
就是用来做这个事情的。
3 mybatis
的简单用法
入门可以直接去看mybatis官网
mybatis
提供了两种配置方式:xml
,java
代码
这里先演示xml
方式的配置
mybatis-config.xml
:mybatis
配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="org.postgresql.Driver"/>
<property name="url" value="jdbc:postgresql://192.168.1.7:5432/postgres"/>
<property name="username" value="postgres"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/example/dao/UserMapper.xml"/>
</mappers>
</configuration>
UserDao
:
public interface UserDao {
User findUserById(String id);
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.dao.UserDao">
<select id="findUserById" resultType="org.example.domain.User">
select * from cas_base.user where id = #{id}
</select>
</mapper>
User
:
public class User {
private String id;
private String username;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
启动代码
String resource = "org/example/mybatis-config.xml";
InputStream inputStream = null;
try {
// 加载配置文件
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
// 解析配置文件,并构建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取sql会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = userDao.findUserById("1");
System.out.println(user.getId()+":"+user.getUsername());
4 启动流程
我们就上面那段代码,看看
mybatis
具体是如何连接数据库,查询并处理结果的
4.1 SqlSessionFactoryBuilder().build(inputStream)
public SqlSessionFactory build(InputStream inputStream) {
// 调用重载的build方法
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 创建了一个配置文件解析器,解析配置文件
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 通过Configuration对象构造一个DefaultSqlSessionFactory对象
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
的创建过程
// 避免重复解析
private boolean parsed;
private final XPathParser parser;
private String environment;
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
// 使用自己私有构造方法,构造对象
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
// 创建了一个空的Configuration对象
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
// 这个是mybatis的环境对象,对应月mybatis配置文件中的environment标签中的配置
this.environment = environment;
// 这个XPathParser是真正用来解析配置文件的
this.parser = parser;
}
Configuration
对象是在XMLConfigBuilder
父类BaseBuilder
中定义的
这个创建完成了之后,就开始parser.parse()
解析配置文件了
public Configuration parse() {
// 重复解析,抛异常
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// 使用XPathParser解析器解析配置文件中的configuration节点
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
XPathParser
解析器解析配置文件中的configuration
节点,得到该节点所有信息对象XNode
,具体如何解析的这里不看。
private void parseConfiguration(XNode root) {
try {
// issue #117 read properties first
// 解析下面规定的节点,把解析到的内容封装到configuration对象中
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
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
// 解析environments节点
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
//解析mapper文件(这里非常重要)
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
到这里,mybatis
就将xml
文件中的所有配置解析到Configuration
对象和Environment
对象中了。接下来就是build()
方法构建SqlSessionFactory
对象了
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
总结一下:这里无非就是解析配置文件和
mapper
文件,然后创建SqlSessionFactory
对象
重点看如何解析mapper
文件
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
// 解析mappers标签下的package标签
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
// 对应mapper节点可配的三种属性,我上面例子用的是resource
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
// 加载mapper资源
InputStream inputStream = Resources.getResourceAsStream(resource);
// mapper 解析器,专门用来解析mapper
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
// 解析mapper
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
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) {
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.");
}
}
}
}
}
我们常用的一般都是<mappers> <mapper resource="org/example/dao/UserMapper.xml"/> </mappers>
这种配置,指定mapper
文件的位置,所以我们重点看看mybatis
是如何解析mapper
文件,并生成代理对象的
public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
configuration, resource, sqlFragments);
}
可以发现,和解析配置文件一样,解析mapper
文件也是用的XPathParser
public void parse() {
// 判断是否已经加载过该资源,不会重复加载
// 其实里面就是一个set集合,加载过的,会记录到这个set中
if (!configuration.isResourceLoaded(resource)) {
// 解析mapper文件的mapper节点,4.1.1
configurationElement(parser.evalNode("/mapper"));
// 记录到已加载资源的集合中
configuration.addLoadedResource(resource);
// 创建代理类生成工厂,解析接口上的注解,4.1.2
bindMapperForNamespace();
}
// 和启动主流程无关,先忽略
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
4.1.1 configurationElement(parser.evalNode("/mapper"))
// 构造方法new的对象
private final MapperBuilderAssistant builderAssistant;
private void configurationElement(XNode context) {
try {
// 拿到命名空间
String namespace = context.getStringAttribute("namespace");
// 从这里也可以看出来,命名空间必填
if (namespace == null || namespace.isEmpty()) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
// 保存
builderAssistant.setCurrentNamespace(namespace);
// 解析mapper下面的各种节点
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
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);
}
}
重点看解析增删改查节点
private void buildStatementFromContext(List<XNode> list) {
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
// 取出节点中属性和sql,封装为MappedStatement对象
buildStatementFromContext(list, null);
}
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
for (XNode context : list) {
// 又来一个解析器,专门用来解析增删改查节点的
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
// 解析
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}
}
解析增删改查节点
public void parseStatementNode() {
// 下面这些属性,使用过select标签的应该熟悉吧
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
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());
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
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;
}
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String resultType = context.getStringAttribute("resultType");
Class<?> resultTypeClass = resolveClass(resultType);
String resultMap = context.getStringAttribute("resultMap");
String resultSetType = context.getStringAttribute("resultSetType");
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
if (resultSetTypeEnum == null) {
resultSetTypeEnum = configuration.getDefaultResultSetType();
}
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
String resultSets = context.getStringAttribute("resultSets");
// 将解析到配置封装为一个MappedStatement,并保存到configuration中
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");
}
id = applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
// 构造者模式,构造一个MappedStatement对象
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();
// 将解析好的MappedStatement对象保存到configuration中
configuration.addMappedStatement(statement);
return statement;
}
我们可以发现在configuration
对象中有这么一个map
集合,它用来存储id(方法名)
->MappedStatement
的映射关系
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
.conflictMessageProducer((savedValue, targetValue) ->
". please check " + savedValue.getResource() + " and " + targetValue.getResource());
public void addMappedStatement(MappedStatement ms) {
// map集合,key是方法名,value是MappedStatement
mappedStatements.put(ms.getId(), ms);
}
我们没必要关注StrictMap
是个什么map
,只要是map
那么它存储数据的方式就一定是key->value
4.1.2 bindMapperForNamespace()
private void bindMapperForNamespace() {
// 把4.1.1中解析到namespace取出来
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
// 根据命名空间反射加载类
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
// ignore, bound type is not required
}
if (boundType != null && !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
// 再次将资源名记录到已加载资源的集合中,为spring考虑
configuration.addLoadedResource("namespace:" + namespace);
// 核心,创建dao接口的代理
configuration.addMapper(boundType);
}
}
}
根据命名空间反射加载接口,所以这里namespace
一定要写对,如果加载到了,就开始创建接口对应的代理类了
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
这里又涉及到了一个MapperRegistry
,它是一个mapper
注册中心,用来维护dao
接口和其代理类生成工厂的映射关系
// 核心map,维护了dao接口和其代理类生成工厂的关系
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public <T> void addMapper(Class<T> type) {
// dao必须是接口
if (type.isInterface()) {
// 判断接口是否已创建过代理类生成工厂
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 创建代理类生成工厂
knownMappers.put(type, new MapperProxyFactory<>(type));
// 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.
// 解析接口上的注解,从这里可以看到,注解的优先级大于mapper文件
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
public <T> boolean hasMapper(Class<T> type) {
return knownMappers.containsKey(type);
}
创建接口对应代理类生成工厂,然后解析接口上注解,覆盖mapper
文件上的配置。
4.2 sqlSessionFactory.openSession()
我们可以看到,这个接口中总的来说,就两个方法
openSession()
:获取SqlSession
会话对象getConfiguration()
:获取Configuration
配置对象
很明显SqlSessionFactory
主要用途就是获取SqlSeesion
对象,那么它是如何构建这个会话对象的呢?
接着往下看。
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
executorType
是一个枚举,Configuration
中默认的executorType
为SIMPLE
public enum ExecutorType {
SIMPLE, REUSE, BATCH
}
这三个的用途后面再说,先看默认的。
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// mybatis环境
final Environment environment = configuration.getEnvironment();
// 根据环境配置获取一个事务工厂
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 创建事务管理对象,ManagedTransaction
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// Executor 执行器,mybatis的4大组件之一,执行查询
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();
}
}
下面我们先来粗略的看看上面涉及到的几个组件
4.2.1 TransactionFactory
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
// 环境中没有,就创建
if (environment == null || environment.getTransactionFactory() == null) {
return new ManagedTransactionFactory();
}
return environment.getTransactionFactory();
}
事务工厂,主要用途就是用来创建一次数据库连接的事务对象,mybatis
默认为ManagedTransactionFactory
4.2.2 Transaction
@Override
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
// Silently ignores autocommit and isolation level, as managed transactions are entirely
// controlled by an external manager. It's silently ignored so that
// code remains portable between managed and unmanaged configurations.
return new ManagedTransaction(ds, level, closeConnection);
}
这些方法应该很容易理解,获取连接,提交,回滚,关闭,获取连接超时时间
它有两个实现类ManagedTransaction
和ManagedTransaction
4.2.3 Executor
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
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);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 插件执行
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
从上面的方法可以发现,这个Executor
就是用来负责与数据库交互的,封装了jdbc
的增删改查,它的实现类就比较多了。
粗略一看,是不是有熟悉的,SimpleExecutor
,ReuseExecutor
,BatchExecutor
,是不是与上面说的ExecutorType
一一对应,这里我们只看SimpleExecutor
4.2.4 SqlSession
private final Configuration configuration;
private final Executor executor;
private final boolean autoCommit;
private boolean dirty;
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
这个SqlSession
里面也是定义了增删改查和事务相关方法,这不是和Executor
重复了吗,其实,这个SqlSession
是门面模式的应用,提供给用户友好的操作,将内部实现逻辑屏蔽了,真正与数据库交互的还是Executor
。
它也有两个实现类DefaultSqlSession
和SqlSessionManager
4.2.5 总结
这一步我们看到,只是创建了
Transaction
对象和Executor
对象,并使用这两个对象构建一个SqlSession
会话对象。
4.3 sqlSession.getMapper(UserDao.class)
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
使用configuration
类的getMapper()
方法来获取对应接口的代理类对象
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 我们在4.1.1中解析了mapper文件,所以如果type是我们在mapper的namespace中配置的,
//那么这里是肯定能拿到该接口对应代理类生成工厂的
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);
}
}
工厂生成代理类
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
// 生成代理对象
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
// jdk动态代理生成代理对象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
mybatis
使用jdk
动态代理生成代理对象,了解jdk
动态代理的人就应该知道,代理类的控制逻辑就在InvocationHandler
中,而这里mybatis
用的是MapperProxy
,点进去一看,继承了InvocationHandler
,好家伙,那我们直接关注invoke(Object proxy, Method method, Object[] args)
方法即可
// 缓存
private final Map<Method, MapperMethodInvoker> methodCache;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 基础方法,直接反射执行MapperProxy的方法
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
// A workaround for https://bugs.openjdk.java.net/browse/JDK-8161372
// It should be removed once the fix is backported to Java 8 or
// MyBatis drops Java 8 support. See gh-1929
// 先拿缓存
MapperMethodInvoker invoker = methodCache.get(method);
if (invoker != null) {
return invoker;
}
return methodCache.computeIfAbsent(method, m -> {
// java8开始有的接口的默认方法
if (m.isDefault()) {
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {
// 普通方法走这里
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
invoker
翻译为调用者,普通方法对应的调用者是PlainMethodInvoker
,这里是策略模式应用,默认方法对应的调用者是DefaultMethodInvoker
。而这样做的好处就是逻辑分开,避免一堆的if-else
。但是MapperMethod
又是什么呢?
4.3.1 new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())
// 方法名->sql类型的映射关系
private final SqlCommand command;
// 封装了法返回值信息
private final MethodSignature method;
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
// 解析得到方法对应sql类型
this.command = new SqlCommand(config, mapperInterface, method);
// 解析方法返回值类型
this.method = new MethodSignature(config, mapperInterface, method);
}
SqlCommand
是MapperMethod
的静态内部类
// 方法名
private final String name;
// 方法对应sql类型。增删改查的哪一种
private final SqlCommandType type;
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
final String methodName = method.getName();
// 声明方法的类
final Class<?> declaringClass = method.getDeclaringClass();
// 获取到该方法对应的MappedStateMent对象
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
configuration);
if (ms == null) {
if (method.getAnnotation(Flush.class) != null) {
name = null;
type = SqlCommandType.FLUSH;
} else {
throw new BindingException("Invalid bound statement (not found): "
+ mapperInterface.getName() + "." + methodName);
}
} else {
// 方法名
name = ms.getId();
// 增删改查,解析mapper文件增删改查节点得到的
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
Class<?> declaringClass, Configuration configuration) {
String statementId = mapperInterface.getName() + "." + methodName;
if (configuration.hasStatement(statementId)) {
return configuration.getMappedStatement(statementId);
// 这个判断是什么意思?就是说如果找到了声明方法所在的接口,还没有找到对应的
// MappedStatement,那肯定就不存在对应方法的MappedStatement了
} else if (mapperInterface.equals(declaringClass)) {
return null;
}
// 递归解析接口的接口
for (Class<?> superInterface : mapperInterface.getInterfaces()) {
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
本质就是为了得到方法对应的
sql
是增删改查的哪一种
MethodSignature
也是MapperMethod
的静态内部类
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;
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
// 方法返回值类型
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
if (resolvedReturnType instanceof Class<?>) {
this.returnType = (Class<?>) resolvedReturnType;
// 基础类型。4类8种
} else if (resolvedReturnType instanceof ParameterizedType) {
this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
} else {
// 其他类型,比如void
this.returnType = method.getReturnType();
}
this.returnsVoid = void.class.equals(this.returnType);
// 判断是否是集合或数组
this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
this.returnsCursor = Cursor.class.equals(this.returnType);
// 判断是否是Optional容器
this.returnsOptional = Optional.class.equals(this.returnType);
this.mapKey = getMapKey(method);
this.returnsMap = this.mapKey != null;
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
本质就是解析方法返回值
4.3.2 cachedInvoker(method).invoke(proxy, method, args, sqlSession)
我们已经知道,cachedInvoker(method)
最终返回的是PlainMethodInvoker
private static class PlainMethodInvoker implements MapperMethodInvoker {
private final MapperMethod mapperMethod;
public PlainMethodInvoker(MapperMethod mapperMethod) {
super();
this.mapperMethod = mapperMethod;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
// 执行MapperMethod的execute方法完成数据库交互
return mapperMethod.execute(sqlSession, args);
}
}
最终是通过MapperMethod
的execute(sqlSession, args)
方法执行增删改查的
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
// 增删改查+FLUSH
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:
// 返回值是void
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
// 返回多条数据
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
// 返回map
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
// 返回游标
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
// 单条数据,或者返回容器Optional
} else {
Object param = method.convertArgsToSqlCommandParam(args);
// 最终使用sqlSession的selectOne()方法完成查询
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
// 最终使用sqlSession的flushStatements()方法完成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;
}
这里我们只看select
返回多条数据这一种
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
// 方法入参
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
// 最终使用sqlSession的selectList()方法完成查询
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
// 最终使用sqlSession的selectList()方法完成查询
result = sqlSession.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;
}
综上:我们完全可以得出结论,代理类中最终使用sqlSession
中方法完成增删改查。
所以我们接下来的工作就变成了sqlSession
是如何完成增删改查的
5 sqlSession
完成增删改查
@Override
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
// 单条也是先按找茬多条逻辑来的
List<T> list = this.selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 拿到方法名对应的MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
// 使用执行器执行查询,并解析结果
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();
}
}
使用执行器执行查询,并解析结果
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 获取sql,里面封装了sql相关信息,以及sql对应的入参,见5.1
BoundSql boundSql = ms.getBoundSql(parameterObject);
// 缓存,mybatis的一级缓存
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
// 执行查询,并解析结果
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
5.1 ms.getBoundSql(parameterObject)
获取sql
,里面封装了sql
相关信息,以及sql
对应的入参
// 解析mapper文件后,会将sql和解析好的占位符信息封装到这个对象中
private SqlSource sqlSource;
public BoundSql getBoundSql(Object parameterObject) {
// sql,解析好的占位符信息,占位符对应的参数值
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
// 占位符和参数的映射关系
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings == null || parameterMappings.isEmpty()) {
boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
}
// check for nested result maps in parameter mappings (issue #30)
// 合并嵌套的结果集
for (ParameterMapping pm : boundSql.getParameterMappings()) {
String rmId = pm.getResultMapId();
if (rmId != null) {
ResultMap rm = configuration.getResultMap(rmId);
if (rm != null) {
hasNestedResultMaps |= rm.hasNestedResultMaps();
}
}
}
return boundSql;
}
重点说下这个sqlSource
public interface SqlSource {
BoundSql getBoundSql(Object parameterObject);
}
接口,且只有一个方法(用途是把占位符参数传进去构建一个BoundSql
)
它的实现类一共有上述4
种,RawSqlSource
只是对其他三个的代理,它内部持有一个SqlSource
private final SqlSource sqlSource;
@Override
public BoundSql getBoundSql(Object parameterObject) {
return sqlSource.getBoundSql(parameterObject);
}
以StaticSqlSource
为例
// sql信息
private final String sql;
// 映射关系
private final List<ParameterMapping> parameterMappings;
// 配置对象
private final Configuration configuration;
@Override
public BoundSql getBoundSql(Object parameterObject) {
// 这里是把参数值也放进去了,重新构建了一个BoundSql对象,包含了sql执行的所有的信息
return new BoundSql(configuration, sql, parameterMappings, parameterObject);
}
5.2 query(ms, parameterObject, rowBounds, resultHandler, key, boundSql)
// SimpleExecutor
private final Executor delegate;
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
// 缓存
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
// 查库
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
这里使用的是CachingExecutor
,很明显的装饰者模式,在原来的功能基础上加上缓存的功能,真正执行jdbc
的是SimpleExecutor
protected int queryStack;
private boolean closed;
protected PerpetualCache localCache;
@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++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 查库
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;
}
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;
}
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
// 这个就是jdbc里面的Statement
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 获取操作StatementHandler的处理器,5.2.1
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 获取statement对象,5.2.2
stmt = prepareStatement(handler, ms.getStatementLog());
// 执行查询并解析结果集,5.2.3
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
这里和原生的
jdbc
流程差不多
5.2.1 configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql)
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 封装了Statement执行sql所需要的信息(这是一个外壳,真正干活的是它内部的StatementHandler)
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
// 又一次执行了插件
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
封装Statement
执行sql
所需要的信息,并第二次执行插件
看看StatementHandler
这个接口
实现类
干活的其实就最下面三个
PreparedStatementHandler
:创建PrepareStatement
对象
SimpleStatementHandler
:创建Statement
对象
CallableStatementHandler
:执行存储过程的
那这个RoutingStatementHandler
是干嘛的呢,我们来看下它的构造方法
// 干活的
private final StatementHandler delegate;
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 根据配置创建不同的StatementHandler
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());
}
}
简单来说就是将if-else
抽到单独一个类中管理嘛
5.2.2 prepareStatement(handler, ms.getStatementLog())
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
// 获取连接
Connection connection = getConnection(statementLog);
// 获取Statement对象
stmt = handler.prepare(connection, transaction.getTimeout());
// 将statement对象保存到参数处理器中
handler.parameterize(stmt);
return stmt;
}
protected Connection getConnection(Log statementLog) throws SQLException {
// 通过事务来获取连接
Connection connection = transaction.getConnection();
if (statementLog.isDebugEnabled()) {
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
} else {
return connection;
}
}
mybatis
是通过事务对象获取数据库连接的,那么,提交事务和回滚事务也必然是通过事务对象来的
接下来我们再看看StatementHandler
是如何获取jdbc
的Statement
对象的
// 实际干活的
private final StatementHandler delegate;
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
return delegate.prepare(connection, transactionTimeout);
}
PreparedStatementHandler
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
// 获取Statement对象
statement = instantiateStatement(connection);
// 设置sql执行超时时间(底层使用stmt.setQueryTimeout(queryTimeout))
setStatementTimeout(statement, transactionTimeout);
// 设置要向数据库获取的行数(底层使用stmt.setFetchSize(defaultFetchSize))
setFetchSize(statement);
return statement;
// 异常需要关闭statement
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
@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) {
return connection.prepareStatement(sql);
} else {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
底层使用原生jdbc
的connection.prepareStatement(sql)
来获取Statement
对象
还有最后一点,handler.parameterize(stmt)
这个是干嘛的
@Override
public void parameterize(Statement statement) throws SQLException {
delegate.parameterize(statement);
}
// 参数处理器,处理预编译sql占位符对应的参数
protected final ParameterHandler parameterHandler;
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
把Statement
对象保存到参数处理器中
@Override
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 {
// 通过类型转换器,转换参数值类型,然后设置到Statement对象中
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
看看mybatis
自定义的类型转化器
有兴趣的可以了解下
总结:这个方法会获取连接,获取PrePareStatement
对象,设置sql
,设置占位符值。
原生的jdbc
流程走到这里,就剩最后三步了,执行sql
,解析结果集,关闭连接
5.2.3 handler.query(stmt, resultHandler)
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
return delegate.query(statement, resultHandler);
}
// 结果集处理器
protected final ResultSetHandler resultSetHandler;
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 原生jdbc执行sql的操作
ps.execute();
// 处理结果集
return resultSetHandler.handleResultSets(ps);
}
原生jdbc
执行sql
的操作很简单,重点是后面结果集解析
DefaultResultSetHandler
:默认的结果集解析器(也是唯一的)
//
// HANDLE RESULT SETS
//
@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);
// 结果集映射
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
// 顺序获取ResultMap
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
// 根据ResultMap上配置的规则解析结果集
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);
}
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
// 原生jdbc获取结果集
ResultSet rs = stmt.getResultSet();
while (rs == null) {
// move forward to get the first resultset in case the driver
// doesn't return the resultset as the first result (HSQLDB 2.1)
if (stmt.getMoreResults()) {
rs = stmt.getResultSet();
} else {
if (stmt.getUpdateCount() == -1) {
// no more results. Must be no resultset
break;
}
}
}
// 包装结果集
return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}
根据ResultMap
上配置的规则解析结果集
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
// 结果处理器
if (resultHandler == null) {
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
// 处理结果
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
// 多结果集的先不看
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}
//
// HANDLE ROWS FOR SIMPLE RESULTMAP
//
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
// 嵌套的结果,比如collection或者association
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
// 无嵌套的简单结果
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
ResultSet resultSet = rsw.getResultSet();
skipRows(resultSet, rowBounds);
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
// 真正解析结果集,得到接口方法返回值
Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
// 储藏结果,一级缓存
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
// 创建结果对象,此时对象中的属性值还是空的 5.2.3.1
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
// 获取结果对象的元数据信息
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
if (shouldApplyAutomaticMappings(resultMap, false)) {
// 使用自动映射,给结果对象属性赋值 5.2.3.2
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
// 使用resultMap中定义的映射关系 5.2.3.3
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
// 最终结果
return rowValue;
}
执行sql
,并解析最终的结果集
5.2.3.1 createResultObject(rsw, resultMap, lazyLoader, columnPrefix)
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
this.useConstructorMappings = false; // reset previous mapping result
final List<Class<?>> constructorArgTypes = new ArrayList<>();
final List<Object> constructorArgs = new ArrayList<>();
// 创建结果对象
Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
// issue gcode #109 && issue #149
if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
break;
}
}
}
// 是否使用有参构造方法实例化的对象
this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
return resultObject;
}
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
throws SQLException {
final Class<?> resultType = resultMap.getType();
// 获取类的元数据信息,实际上就是类的方法,属性,构造器等等
final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
if (hasTypeHandlerForResultObject(rsw, resultType)) {
return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
} else if (!constructorMappings.isEmpty()) {
return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
// 返回值类型是接口或者包含默认构造方法
} else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
// 使用objectFactory反射创建对象(使用默认构造方法实例化)
return objectFactory.create(resultType);
} else if (shouldApplyAutomaticMappings(resultMap, false)) {
return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);
}
throw new ExecutorException("Do not know how to create an instance of " + resultType);
}
很明显,此处创建结果对象,但并没有向对象中的属性值赋值
5.2.3.2 applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix)
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
// 自动映射关系
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (!autoMapping.isEmpty()) {
for (UnMappedColumnAutoMapping mapping : autoMapping) {
// 从结果集中取出该属性对应值
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
// gcode issue #377, call setter on nulls (value is not 'found')
// 使用setter方法给对应属性赋值
metaObject.setValue(mapping.property, value);
}
}
}
return foundValues;
}
private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
final String mapKey = resultMap.getId() + ":" + columnPrefix;
List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
if (autoMapping == null) {
autoMapping = new ArrayList<>();
final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
for (String columnName : unmappedColumnNames) {
String propertyName = columnName;
if (columnPrefix != null && !columnPrefix.isEmpty()) {
// When columnPrefix is specified,
// ignore columns without the prefix.
if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
propertyName = columnName.substring(columnPrefix.length());
} else {
continue;
}
}
final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
if (property != null && metaObject.hasSetter(property)) {
if (resultMap.getMappedProperties().contains(property)) {
continue;
}
final Class<?> propertyType = metaObject.getSetterType(property);
if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
} else {
configuration.getAutoMappingUnknownColumnBehavior()
.doAction(mappedStatement, columnName, property, propertyType);
}
} else {
configuration.getAutoMappingUnknownColumnBehavior()
.doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
}
}
autoMappingsCache.put(mapKey, autoMapping);
}
return autoMapping;
}
本质上就是反射解析对象属性,拿到属性名,然后把属性名对应的属性从结果集中取出来,最后使用setter
方法设置结果对象中
5.2.3.3 applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix)
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
// 获取xml中配置的映射属性
final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
boolean foundValues = false;
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
if (propertyMapping.getNestedResultMapId() != null) {
// the user added a column attribute to a nested result map, ignore it
column = null;
}
if (propertyMapping.isCompositeResult()
|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
|| propertyMapping.getResultSet() != null) {
Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
// issue #541 make property optional
final String property = propertyMapping.getProperty();
if (property == null) {
continue;
} else if (value == DEFERRED) {
foundValues = true;
continue;
}
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
// gcode issue #377, call setter on nulls (value is not 'found')
// settter方法设置属性值
metaObject.setValue(property, value);
}
}
}
return foundValues;
}
使用xml
中配置的映射属性,再次从结果集中取值,设置到结果对象中
6 总结
4
大组件
Executor
:执行器MappedStatement
:封装了xml
上配置的sql
信息StatementHandle
:获取连接,设置sql
,设置参数ResultHandler
:执行sql
,处理结果集,关闭连接