思考 https://www.zhihu.com/question/270387939/answer/360487647
https://blog.csdn.net/isea533/category_2092001.html
Mybatis 客户端调用过程
// 加载配置
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 构建核心工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, "development");
// 创建会话
SqlSession session = sqlSessionFactory.openSession();
// 动态代理,生成Dao层接口对象
IUser userMapper = session.getMapper(IUser.class);
SqlSessionFactoryBuilder
顾名思义,这是SqlSessionFactory的建造者类;内部通过XmlConfigBuilder去解析mybatis的config配置xml文件得到Configuration对象。
// 构造器
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration()); // 创建Configuration对象
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
XMLConfigBuilder中解析config.xml文件方法的具体内容:
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
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
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
详细的Configuration配置及属性含义可以参考官网 https://mybatis.org/mybatis-3/zh/configuration.html
XmlConfigBuilder内部又会通过XMLMapperBuilder类去解析mapper.xml文件,并补将结果存入Configuration对象中(此处省略)。
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);
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();
}
}
上面是SqlSessionFactory创建sqlSesion的过程,因此一旦Configuration对象确定了,SqlSession也就可以生产出来了,根据传参execType选择使用的哪个执行器Executor。
Executor
执行器 定义了如query,update,commit,rollback等数据库操作的基本方法。
Executor是DefaultSqlSession的成员变量,在openSession时创建,可以指定使用哪个Executor的实现类,未指定默认使用ExecutorType.SIMPLE;也可以通过配置settings.defaultExecutorType属性来指定默认执行器
CachingExecutor:二级缓存执行器(settings.cacheEnabled未配置情况下,默认开启);使用装饰器模式,内部引用了BaseExecutor。
BaseExecutor:是SimpleExecutor,ReuseExecutor,BatchExecutor三个执行器的抽象父类。
Plugins
MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
private Properties properties = new Properties();
public Object intercept(Invocation invocation) throws Throwable {
// implement pre processing if need
Object returnObject = invocation.proceed();
// implement post processing if need
return returnObject;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
<!-- mybatis-config.xml -->
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
上面的插件将会拦截在 Executor 实例中所有的 “update” 方法调用, 这里的 Executor 是负责执行底层映射语句的内部对象。