Mybatis 执行流程

1. 基本的使用过程

public static void testAttribute() throws IOException {
//1,读取 mybatis-config.xml 文件
InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
//2,构件 sqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);
//3,打开 sqlSession
SqlSession sqlSession = factory.openSession();
//4,获取 Mapper 接口对象
TestMapper mapper = sqlSession.getMapper(TestMapper.class);
//mapper.attribute$Where$Test(null, "张三");
Test test = new Test();
test.setType("测试");
mapper.like$Test("长", test);
}

2. 读取资源

通过 IO 来读取配置,读取配置资源

/*
* Returns a resource on the classpath as a Stream object
*
* @param resource The resource to find
* @return The resource
* @throws java.io.IOException If the resource cannot be found or read
*/
public static InputStream getResourceAsStream(String resource) throws IOException {
return getResourceAsStream(null, resource);
}
/*
* Returns a resource on the classpath as a Stream object
*
* @param loader The classloader used to fetch the resource
* @param resource The resource to find
* @return The resource
* @throws java.io.IOException If the resource cannot be found or read
*/
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
if (in == null) {
throw new IOException("Could not find resource " + resource);
}
return in;
}

3. 解析配置,创建 SqlSessionFactory

调用 SqlSessionFactoryBuilder 的 build 方法创建 SqlSessionFactory。

3.1 解析配置

第一步先创建 XMLConfigBuilder 实例,调用 parse 方法来解析配置。

public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
// 分步骤解析
//issue #117 read properties first
// 1.properties
propertiesElement(root.evalNode("properties"));
// 2.类型别名
typeAliasesElement(root.evalNode("typeAliases"));
// 3.插件
pluginElement(root.evalNode("plugins"));
// 4.对象工厂
objectFactoryElement(root.evalNode("objectFactory"));
// 5.对象包装工厂
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 6.设置
settingsElement(root.evalNode("settings"));
// read it after objectFactory and objectWrapperFactory issue #631
// 7.环境
environmentsElement(root.evalNode("environments"));
// 8.databaseIdProvider
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 9.类型处理器
typeHandlerElement(root.evalNode("typeHandlers"));
// 10.映射器
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}

3.2 解析 xxxMapper.xml 配置

XMLConfigBuilder 类中 parseConfiguration() 最后一步调用 mapperElement() 来解析 mapper 配置文件。

private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
// 10.4自动扫描包下所有映射器
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
// 10.1使用类路径
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
// 映射器比较复杂,调用XMLMapperBuilder
// 注意在for循环里每个mapper都重新new一个XMLMapperBuilder,来解析
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
// 10.2使用绝对url路径
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
// 映射器比较复杂,调用XMLMapperBuilder
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
// 10.3使用java类名
Class<?> mapperInterface = Resources.classForName(mapperClass);
// 直接把这个映射加入配置
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}

最终通过 configuration.addMapers() 方法将解析完成的 mapper 存放到一个 hashMap 中,是个代理。

configuration.addMapers():

public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}

MapperRegistry.addMapper()

//用来存放
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<T>(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.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}

3.3 创建 SqlSessionFactory

根据解析好的配置文件,生成 sqlSessionFactory 对象,默认使用 DefaultSqlSessionFactory 实现。

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
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.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}

4. 创建 SqlSession 对象

通过 SqlSessionFactory.OperSession() 开启一个 sqlSession。

@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 通过事务工厂来产生一个事务
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 生成一个执行器(事务包含在执行器里)
final Executor executor = configuration.newExecutor(tx, execType);
// 然后产生一个DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
// 如果打开事务出错,则关闭它
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
// 最后清空错误上下文
ErrorContext.instance().reset();
}
}

4.1. 创建 Transaction 事务

创建一个事务工厂,然后开启一个事务(根据配置文件)

private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
if (environment == null || environment.getTransactionFactory() == null) {
return new ManagedTransactionFactory();
}
return environment.getTransactionFactory();
}

默认创建的是 JdbcTransaction

@Override
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new JdbcTransaction(ds, level, autoCommit);
}

4.2 创建 Executor 执行器

// 产生执行器
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
// 这句再做一下保护,囧,防止粗心大意的人将defaultExecutorType设成null?
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
// 然后就是简单的3个分支,产生3种执行器BatchExecutor/ReuseExecutor/SimpleExecutor
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);
}
// 如果要求缓存,生成另一种CachingExecutor(默认就是有缓存),装饰者模式,所以默认都是返回CachingExecutor
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 此处调用插件,通过插件可以改变Executor行为
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}

4.3 返回一个 SqlSesson 对象

return new DefaultSqlSession(configuration, executor, autoCommit);
//DefaultSqlSession 构造方法
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}

5. 获取 mapper

创建会话后,通过会话对象session获取mapper。其中会委派给Configuration对象获取。(就是从 3.2 过程中放到的那个 hashMap 去取)

@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}

Configuration又会委派给映射器注册机MapperRegistry去获取。

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}

MapperRegistry中通过MapperProxyFactory创建代理mapper的。

@SuppressWarnings("unchecked")
// 返回代理类
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 获取mapper代理工厂
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
// 在map中找不到则表示没有将mapper类注册进来,抛出BindingException
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 使用工厂进行创建一个实例,本质上是通过代理模式创建了一个代理类,创建过程中出现异常抛出BindingException
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}

MapperProxyFactory最终交由JDK动态代理类Proxy创建mapper代理类的。

@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader()
, new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession
, mapperInterface, methodCache);
return newInstance(mapperProxy);
}

6. 执行 mapper 的方法

通过 mapper 调用实现方法,实际是调用代理的方法。

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}

实际内容在 mapperMethod.execute()

public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
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:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}

到这里实际执行的是 sqlSession 提供的方法,以 insert 为例,继续向下:

DefaultSqlSession.insert()

@Override
public int insert(String statement, Object parameter) {
return update(statement, parameter);
}
@Override
public int update(String statement, Object parameter) {
try {
dirty = true;
MappedStatement ms = configuration.getMappedStatement(statement);
//这里继续执行 executor 代理方法
return executor.update(ms, wrapCollection(parameter));
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}

BaseExecutor.update()

@Override
public int update(MappedStatement ms, Object parameter) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
//清空缓存
clearLocalCache();
return doUpdate(ms, parameter);
}

SimpleExecutor.doUpdate()

@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = prepareStatement(handler, ms.getStatementLog());
// 最终是一个statement进行处理
return handler.update(stmt);
} 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.update()

@Override
public int update(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
int rows = ps.getUpdateCount();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
return rows;
}

底层用的就是PreparedStatement的execute()方法了

参考文档

https://blog.csdn.net/qq_57756904/article/details/125105656

本文作者:primaryC

本文链接:https://www.cnblogs.com/cnff/p/17952372

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   primaryC  阅读(173)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.