//使用Mybatis执行查询sql代码示例
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(
Resources.getResourceAsReader("mybatis-config.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = (User)sqlSession.selectOne("UserMapper.selectById", 1);
sqlSession.close();
一、构建SqlSessionFactory
从全局配置文件中得到sqlSessionFactory
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(
Resources.getResourceAsReader("mybatis-config.xml"));
通过SqlSessionFactoryBuilder().build构建SqlSessionFactory对象(建造者模式)。
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} ...
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
- XMLConfigBuilder#parse:解析全局配置文件,解析完成后会生成一个Configration对象,其中包含所有配置信息;
- SqlSessionFactoryBuilder#build:通过Configuration对象创建SqlSessionFactory实现类—>DefaultSqlSessionFactory,其中包含Configration对象。
解析全局配置文件
- 全局配置文件通过XMLConfigBuilder解析
- mapper映射文件通过XMLMapperBuilder解析
- select|delete|insert|update节点通过XMLStatementBuilder解析
XMLConfigBuilder
解析配置文件,最终将XML配置文件中的配置项都设置到Configuration配置类中。
public Configuration parse() {
...
//解析全局配置文件的节点,从最外层configuration开始
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
//真正解析文件方法,解析每个节点到Configuration对象
private void parseConfiguration(XNode root) {
try {
//解析properties节点到Configuration#variables
propertiesElement(root.evalNode("properties"));
//解析settings节点
Properties settings = settingsAsProperties(root.evalNode("settings"));
//VFS虚拟文件系统解析道Configuration#vfsImpl
loadCustomVfs(settings);
//解析定MyBatis所用日志的具体实现到Configuration#logImpl
loadCustomLogImpl(settings);
//解析别名到Configuration#typeAliasRegistry.typeAliases
typeAliasesElement(root.evalNode("typeAliases"));
//解析插件到Configuration#interceptorChain.interceptors
pluginElement(root.evalNode("plugins"));
...
//解析环境到Configuration#environment
environmentsElement(root.evalNode("environments"));
//解析数据库厂商到Configuration#databaseId
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//解析类型处理器节点到Configuration#typeHandlerRegistry.typeHandlerMap
typeHandlerElement(root.evalNode("typeHandlers"));
//解析mapper到Configuration#mapperRegistry.knownMappers
mapperElement(root.evalNode("mappers"));
} ...
解析mapper配置文件
mapper设置方式:
- 批量解析mapper接口,指定mapper接口所在包
- 解析xml文件,指定classpath
- 解析xml文件,指定url
- 解析mapper接口,指定接口路径
XMLConfigBuilder#mapperElement,解析mappers节点,包括上述四种方式。
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
//循环获取mappers节点下的每个mapper节点
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
//批量添加所有package包
configuration.addMappers(mapperPackage);
}...
以package方式为例,沿着addMappers方法进入,会先找到所有的接口,根据接口获取到mapper映射文件。
MapperRegistry#addMappers
public void addMappers(String packageName, Class<?> superType) {
...
//找到package中所有的类class,addMapper
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
public <T> void addMapper(Class<T> type) {
//判断type是不是接口
if (type.isInterface()) {
...
try {
//把Mapper接口保存到knownMappers中
knownMappers.put(type, new MapperProxyFactory<>(type));
//mapper注解构造器
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
//进行解析mapper映射文件
parser.parse();
...
MapperAnnotationBuilder#parse
public void parse() {
String resource = type.toString();
// 是否已经解析mapper接口对应的xml
if (!configuration.isResourceLoaded(resource)) {
//获取xml文件
loadXmlResource();
...
}
//获取mapper.xml文件交给XMLMapperBuilder解析
private void loadXmlResource() {
...
//根据mapper接口全类名拼接.xml,获取xml文件
String xmlResource = type.getName().replace('.', '/') + ".xml";
...
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
//通过XMLMapperBuilder来解析mapper.xml配置文件
xmlParser.parse();
...
XMLMapperBuilder
无论哪一种mapper设置方式,最终都会调用到XMLMapperBuilder#parse方法。
//解析<mapper></mapper>里面所有东西放到configuration
public void parse() {
//判断当前的Mapper是否被加载过
if (!configuration.isResourceLoaded(resource)) {
//通过configurationElement去解析xml中的节点
configurationElement(parser.evalNode("/mapper"));
...
}
private void configurationElement(XNode context) {
try {
...
//解析二级缓存到Configuration#caches
cacheElement(context.evalNode("cache"));
...
//解析select|insert|update|delete节点
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} ...
private void cacheElement(XNode context) {
...
//把缓存节点加入到Configuration中
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
}
}
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
//循环select|delete|insert|update节点
for (XNode context : list) {
//创建一个xmlStatement的构建器对象
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
statementParser.parseStatementNode();
}...
*解析二级缓存
装饰器+责任链模式实现二级缓存:
- 二级缓存是层层包装,由内到外依次是:PerpetualCache > LruCache > SerializedCache > LoggingCache > SynchronizedCache;
- 调用时由外到内依次调用Cache的getObject方法。

MapperBuilderAssistant#useNewCache,构造器模式
public Cache useNewCache(Class<? extends Cache> typeClass,
Class<? extends Cache> evictionClass,
Long flushInterval,
Integer size,
boolean readWrite,
boolean blocking,
Properties props) {
Cache cache = new CacheBuilder(currentNamespace)
.implementation(valueOrDefault(typeClass, PerpetualCache.class))
.addDecorator(valueOrDefault(evictionClass, LruCache.class))//添加装饰器
.clearInterval(flushInterval)
.size(size)
.readWrite(readWrite)
.blocking(blocking)
.properties(props)
.build();
configuration.addCache(cache);
currentCache = cache;
return cache;
}
CacheBuilder
public Cache build() {
setDefaultImplementations();
Cache cache = newBaseCacheInstance(implementation, id);
setCacheProperties(cache);
if (PerpetualCache.class.equals(cache.getClass())) {
//循环装饰器
for (Class<? extends Cache> decorator : decorators) {
cache = newCacheDecoratorInstance(decorator, cache);
setCacheProperties(cache);
}
cache = setStandardDecorators(cache);
...
private Cache setStandardDecorators(Cache cache) {
...
if (readWrite) {
//将LRU装饰到SerializedCache
cache = new SerializedCache(cache);
}
//继续装饰
//将SerializedCache装饰到LoggingCache的委托delegate中
cache = new LoggingCache(cache);
//将LoggingCache装饰到SynchronizedCache的委托delegate中
cache = new SynchronizedCache(cache);
...
SerializedCache,将LRU装饰到SerializedCache的委托delegate中。LoggingCache、SynchronizedCache同理。
public class SerializedCache implements Cache {
private final Cache delegate;
public SerializedCache(Cache delegate) {
this.delegate = delegate;
}
解析select|delete|insert|update
将一个mapper中的sql语句由外到内的每个节点解析成SqlNode,比如、。不会完全解析sql,因为这个时候参数都没确定。参数是调用具体方法时传入的。
XMLStatementBuilder
public void parseStatementNode() {
String id = context.getStringAttribute("id");
...
//获得节点名称:select|insert|update|delete
String nodeName = context.getNode().getNodeName();
//获得SqlCommandType枚举
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
//判断是不是select语句
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
//获取flushCache属性
//查询:默认flushCache=false 增删改:默认flushCache=true
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
//获取useCache属性
//默认值为isSelect:查询:默认useCache=true 增删改:默认useCache=false
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
...
//通过XMLLanguageDriver解析sql脚本对象
//每个节点都会解析成一个SqlNode,比如<where>、<if>。不会完全解析sql,因为这个时候参数都没确定
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
...
//为insert|delete|update|select节点构建成mappedStatment对象
//一个sql对应一个mappedStatment
//把mappedStatment对象加入到配置类configuration中
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
二、获取SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
通过sqlSessionFactory.openSession来获取SqlSession对象(工厂模式)。
//DefaultSqlSessionFactory
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
...
//创建一个sql执行器对象
final Executor executor = configuration.newExecutor(tx, execType);
//创建DefaultSqlSession对象并返回
return new DefaultSqlSession(configuration, executor, autoCommit);
} ...
SqlSession是一个门面,真正执行CRUD都是由执行器Executor来执行。
创建Executor对象
Executor分类:
- CacheExecutor:需要开启二级缓存。查询前先会查询缓存中是否存在结果:
- 如果缓存存在结果,就使用缓存中的结果
- 如果缓存不存在结果,用普通的Executor进行查询,再将查询出来的结果存入缓存
- SimpleExecutor:每执行sql就开启一个Statement对象,用完立刻关闭
- ReuseExecutor:可重复使用Statement对象
- BatchExecutor:批量处理sql
- BaseExecutor:一级缓存。SimpleExecutor、ReuseExecutor、BatchExecutor共同父类。
Executor结构图:

Configuration#newExecutor,装饰器模式
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
...
//判断执行器的类型
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);
}
//判断是否开启二级缓存,默认为true
//如果开启就返回cacheExecutor;负责返回的就是普通的Executor
if (cacheEnabled) {
//把普通执行器包装成CachingExecutor,装饰器模式
executor = new CachingExecutor(executor);
}
//插件,为executor做增强
executor = (Executor) interceptorChain.pluginAll(executor);
//如果做了增强,那么返回的executor就是一个代理类$Proxy
return executor;
}
*插件执行过程
插件就是为Mybatis四大核心对象(Executor、ParameterHandler 、ResultSetHandler、StatementHandler)做增强,采用责任链+代理模式。
在解析全局配置文件时会将插件plugin放到InterceptorChain中,创建executor时就会为executor执行插件方法做增强。
public Object pluginAll(Object target) {
//循环所有插件plugin
for (Interceptor interceptor : interceptors) {
//执行插件方法
target = interceptor.plugin(target);
}
return target;
}
//Interceptor#plugin
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public static Object wrap(Object target, Interceptor interceptor) {
// 获取@Signature的type属性
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
// 获取当前代理类型
Class<?> type = target.getClass();
// 根据代理类型和@signature的type属性进行匹配,配对成功则进行代理
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
//创建动态代理
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
当我们调用executor中的方法,就会来到动态代理的实现Plugin,调用invoke方法,会判断当前执行方法是否和需要拦截方法匹配,如果匹配就会执行自定义拦截器(插件)中的intercept方法,执行增强逻辑。
如果有多个插件,就会采用责任链的方式依次调用。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
//判断方法是否匹配
if (methods != null && methods.contains(method)) {
//执行intercept
//将目标类、方法、参数封装成Invocation
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} ...
创建DeaultSqlSessoin对象
SqlSession中包含Configration对象,所以通过SqlSession能拿到全局配置;
SqlSession中包含Executor对象,所以通过SqlSession能执行CRUD方法。
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
三、执行SQL
User user = (User)sqlSession.selectOne("UserMapper.selectById", 1);
通过sqlSession可以执行CRUD方法,第一个参数:namespace+id。
//DefaultSqlSession
@Override
public <T> T selectOne(String statement, Object parameter) {
//selectOne底层也是调用selectList方法,取出返回结果中第一个元素即可,如果返回多个结果抛异常
List<T> list = this.selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
...
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//通过statement去全局配置类中获取MappedStatement
//一个sql节点就会封装成一个MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
//通过执行器executor去执行sql
//默认情况下是executor为cacheExetory对象,因为默认开启了二级缓存
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
}...
sqlSession只是个门面,具体执行CRUD的还是executor。
二级缓存
CachingExecutor#query,先去二级缓存中获取
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//解析动态sql,调用每一个sqlNode的apply方法
BoundSql boundSql = ms.getBoundSql(parameterObject);
//创建缓存key:sqlId,sql语句,参数,伪分页
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//判断mapper中是否开启了二级缓存<cache></cache>
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);
}
return list;
}
}
//没有二级缓存,直接查询
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
*解析动态sql
sql解析成sqlNode后,通过责任链的方式去调用每一个Node的apply,将所有解析的sql追加到一个sql变量中去。

DynamicSqlSource#getBoundSql
@Override
public BoundSql getBoundSql(Object parameterObject) {
DynamicContext context = new DynamicContext(configuration, parameterObject);
//通过责任链的方式处理每一个node,拼接出完整的sql语句,此时sql中包括#{}
rootSqlNode.apply(context);
...
//将#{}替换成?,并拿到#{}中的参数名解析成parameterMapping
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
...
MixedSqlNode#apply
@Override
public boolean apply(DynamicContext context) {
//循环调用每一个node的apply方法
contents.forEach(node -> node.apply(context));
return true;
}
*缓存执行过程
//TransactionalCacheManager#getObject
public Object getObject(Cache cache, CacheKey key) {
return getTransactionalCache(cache).getObject(key);
}
//TransactionalCache#getObject
@Override
public Object getObject(Object key) {
Object object = delegate.getObject(key);
...
}
//SynchronizedCache#getObject
//主要实现线程安全
@Override
public synchronized Object getObject(Object key) {
return delegate.getObject(key);
}
//LoggingCache#getObject
//主要用来记录命中日志
@Override
public Object getObject(Object key) {
requests++;
final Object value = delegate.getObject(key);
...
}
//SerializedCache#getObject
//主要用来序列化
@Override
public Object getObject(Object key) {
Object object = delegate.getObject(key);
//获取时进行反序列化
return object == null ? null : deserialize((byte[]) object);
}
//LruCache#getObject
//实现最近最少使用防溢出机制
@Override
public Object getObject(Object key) {
keyMap.get(key);
return delegate.getObject(key);
}
//PerpetualCache#getObject
@Override
public Object getObject(Object key) {
return cache.get(key);
}
一级缓存
BaseExecutor#query
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
...
try {
//从一级缓存localCache中获取查询结果
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);
...
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
...
//真正查询方法
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
...
数据库查询
SimpleExecutor#doQuery
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
//获取configuration对象
Configuration configuration = ms.getConfiguration();
//创建StatementHandler对象
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
//执行查询,返回查询结果
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//获取connection对象
Connection connection = getConnection(statementLog);
//获取Statement对象
stmt = handler.prepare(connection, transaction.getTimeout());
//处理参数
handler.parameterize(stmt);
return stmt;
}
PreparedStatementHandler#query
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
//处理结果集
return resultSetHandler.handleResultSets(ps);
}
创建StatementHandler对象
StatementHandler分类:
- SimpleStatementHandler
- PreparedStatementHandler:默认
- CallableStatementHandler
Configuration#newStatementHandler
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
//插件增强
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
RoutingStatementHandler#RoutingStatementHandler
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
...
//默认为PREPARED
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
...
}
PreparedStatementHandler
public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
}
BaseStatementHandler
会先创建ParameterHandler、ResultSetHandler两个对象
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
...
//先创建ParameterHandler、ResultSetHandler两个对象
//创建之后分别进行插件增强
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
Mybatis四大核心对象创建、增强顺序:Executor > ParameterHandler > ResultSetHandler > StatementHandler。
四、Mybatis执行数据库流程
- 先获取SqlSession,作为门面,其中包含Executor执行器;
- Executor会判断是否开启二级缓存,如果开启最后Executor会包装成CacheExecutor;
- 当执行查询操作时,会先从二级缓存CacheExecutor中查询,再去一级缓存BaseExecutor中查询(SimpleExecutor、ReuseExecutor、BatchExecutor没有实现query方法),最后执行SimpleExecutor、ReuseExecutor、BatchExecutor的从数据库中查询;
- 执行数据库查询时会先创建StatementHandler,创建之前会先创建ParameterHandler、ResultSetHandler两个对象,分别用于处理参数与结果集。然后获取connection对象、statement对象,以便操作数据库。