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() {
...
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
...
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
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) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
}...
以package方式为例,沿着addMappers方法进入,会先找到所有的接口,根据接口获取到mapper映射文件。
MapperRegistry#addMappers
public void addMappers(String packageName, Class<?> superType) {
...
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
...
try {
knownMappers.put(type, new MapperProxyFactory<>(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
...
MapperAnnotationBuilder#parse
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
loadXmlResource();
...
}
private void loadXmlResource() {
...
String xmlResource = type.getName().replace('.', '/') + ".xml";
...
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
xmlParser.parse();
...
XMLMapperBuilder
无论哪一种mapper设置方式,最终都会调用到XMLMapperBuilder#parse方法。
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
...
}
private void configurationElement(XNode context) {
try {
...
cacheElement(context.evalNode("cache"));
...
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} ...
private void cacheElement(XNode context) {
...
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
}
}
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
for (XNode context : list) {
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) {
cache = new SerializedCache(cache);
}
cache = new LoggingCache(cache);
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");
...
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);
...
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
...
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对象(工厂模式)。
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
...
final Executor executor = configuration.newExecutor(tx, execType);
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);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
*插件执行过程
插件就是为Mybatis四大核心对象(Executor、ParameterHandler 、ResultSetHandler、StatementHandler)做增强,采用责任链+代理模式。
在解析全局配置文件时会将插件plugin放到InterceptorChain中,创建executor时就会为executor执行插件方法做增强。
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public static Object wrap(Object target, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
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)) {
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。
@Override
public <T> T selectOne(String statement, Object parameter) {
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 {
MappedStatement ms = configuration.getMappedStatement(statement);
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 {
BoundSql boundSql = ms.getBoundSql(parameterObject);
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 {
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);
rootSqlNode.apply(context);
...
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
...
MixedSqlNode#apply
@Override
public boolean apply(DynamicContext context) {
contents.forEach(node -> node.apply(context));
return true;
}
*缓存执行过程
public Object getObject(Cache cache, CacheKey key) {
return getTransactionalCache(cache).getObject(key);
}
@Override
public Object getObject(Object key) {
Object object = delegate.getObject(key);
...
}
@Override
public synchronized Object getObject(Object key) {
return delegate.getObject(key);
}
@Override
public Object getObject(Object key) {
requests++;
final Object value = delegate.getObject(key);
...
}
@Override
public Object getObject(Object key) {
Object object = delegate.getObject(key);
return object == null ? null : deserialize((byte[]) object);
}
@Override
public Object getObject(Object key) {
keyMap.get(key);
return delegate.getObject(key);
}
@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 {
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 = ms.getConfiguration();
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 = getConnection(statementLog);
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()) {
...
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) {
...
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对象,以便操作数据库。
|