背景介绍
Mybatis 是一个 Data Mapper Framework,归于 ORM 结构;旨在提供更简略,更方便地完结操作数据库功能,减轻开发⼈员的⼯作量,消除程序冗余代码。
功能结构
主要从功能⽅面配合源码进行解析 Mybatis 的完结过程和原理,功能结构图如下:
接⼝层:
-
运用 Mybatis 提供 API 操作数据库,传递 statementId 和参数 map 给 sqlSession;
-
使⽤ mapper 接⼝⽅式操作数据库,每个接⼝口⽅办法对应⼀一个 mapper 节点<insert/select/update/delete>;
处理层:
-
处理传入参数,一起进行类型转化 —— ParameterHandler 剖析
-
依据传入参数,使⽤ ognl 动态生成 SQL 语句 —— StatementHandler 剖析
-
将 SQL 句子和参数交于执行器进⾏获取成果 —— Executor 剖析
-
处理成果集,类型转化 —— ResultSetHandler 剖析
结构层:
-
数据源和连接池办理理 —— 数据源与连接池剖析
-
业务办理理 —— 业务剖析
-
缓存办理理(⼀级缓存和二级缓存) —— 缓存剖析
-
SQL 语句装备⽅式管理(XML和注解) —— 装备⽅方式剖析
Mybatis 中⽐较中心重要且常⽤的类:
- Configuration MyBatis 一切的装备信息都维持在 Configuration 目标之中。
- TypeHandler 担任 java 数据类型和 jdbc 数据类型之间的映射和转化。
- MappedStatement 保护了一条 <select|update|delete|insert> 节点的封装。
- StatementHandler 封装了 JDBC Statement 操作,担任对 JDBC statement 的操作,如设置参数、将 Statement 成果集转化成 List 调集。
- ParameterHandler 担任对⽤户传递的参数转化成 JDBC Statement 所需要的参数。
- SqlSource 担任依据用户传递的 parameterObject,动态地生成 SQL 语句,将信息封装到 BoundSql 目标中,并回来。
- BoundSql 表示动态生成的 SQL 句子以及相应的参数信息。
- Executor Mybatis 执行器,是 Mybatis 调度的中心,担任 SQL 句子的生成和查询缓存的保护作业。
- SqlSession 作为 Mybatis 作业的主要顶层 API,表示和数据库交互的会话,完结必要数据库增修改查功能。
- ResultSetHandler 担任将 JDBC 回来的 ResultSet 成果集目标转化成 List 类型的调集。
Mybatis 运用
// 1.初始化
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); inputStream.close();
// 2.获取sqlSession目标
SqlSession session = sqlSessionFactory.openSession();
// 3.获取dao署理目标
DspUserDao dspUserDao = session.getMapper(DspUserDao.class);
// 4.执行sql
DspUser user = dspUserDao.selectUserByName("test"); System.out.println(user.getUserName());
源码剖析
装备文件初始化流程
1. SqlSessionFactoryBuilder
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSessionFactoryBuilder 为⼊口点进行传⼊解析 xml。
2. XMLConfigBuilder
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
build(parser.parse());
将 XML 进⾏节点解析,封装为 configuration 目标。
private void parseConfiguration(XNode root) {
try {
// 解析properties
this.propertiesElement(root.evalNode("properties"));
// 解析settings,设置默认值
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(settings);
// 解析typeAliases,设置别名
this.typeAliasesElement(root.evalNode("typeAliases"));
// plugins,设置拦截器链
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); this.reflectorFactoryElement(root.evalNode("reflectorFactory")); this.settingsElement(settings);
// environments,设置数据源,tx相关信息
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 解析typeHandlers,类型处理类
this.typeHandlerElement(root.evalNode("typeHandlers"));
// 解析mapper,处理sql相关信息
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
3.sql.xml解析⼊口
public void parse() {
if (!this.configuration.isResourceLoaded(this.resource)) {
this.configurationElement(this.parser.evalNode("/mapper"));//Map<String, MappedStatement> mappedStatements
this.configuration.addLoadedResource(this.resource);
this.bindMapperForNamespace();//Map<Class<?>, MapperProxyFactory<?>> knownMappers
}
this.parsePendingResultMaps();
this.parsePendingCacheRefs();
this.parsePendingStatements();
}
获取 sqlSession 流程
1. DefaultSqlSessionFactory
public SqlSession openSession() {
return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession var8;
try {
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
Executor executor = this.configuration.newExecutor(tx, execType);
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
2. configuration创立executor目标
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? this.defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Object 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 (this.cacheEnabled) {
executor = new CachingExecutor((Executor)executor);
}
// 此处拦截器调用链加载
Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
return executor;
}
获取 dao 署理理目标流程
1. DefaultSqlSession获取mapper署理理目标
public <T> T getMapper(Class<T> type) {
return this.configuration.getMapper(type, this);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
2. MapperProxyFactory 创立 mapperProxy 的署理理目标
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
执行查询流程
1. mapperProxy 署理理目标调⽤用 invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 若执⾏的object类的办法,直接调⽤
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
if (this.isDefaultMethod(method)) {
return this.invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
// 缓存中获取MapperMethod
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
2. 创立 MapperMethod 执⾏操作
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);
}
// 依据sql类型进⾏执行
public Object execute(SqlSession sqlSession, Object[] args){
....省掉
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
....省掉
}
3. DefaultSqlSession 执⾏ select 操作
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
List var5;
try {
// 从configuration中获取MappedStatement目标
MappedStatement ms = this.configuration.getMappedStatement(statement);
// 经过executor执行MappedStatement
var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception var9) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9);
} finally {
ErrorContext.instance().reset();
}
return var5;
}
4. Executor
baseExecutor类
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 获取动态⽣成的SQL句子以及相应的参数信息
BoundSql boundSql = ms.getBoundSql(parameter);
// 缓存当时查询
CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
return this.query(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 {
....省掉
// simpleExecutor执⾏查询
list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
....省掉
}
simpleExecutor类
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var9;
try {
Configuration configuration = ms.getConfiguration();
// 创立PreparedStatementHandler预编译处理类,包含parameterHandler和resultSetHandler
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 设置参数信息
stmt = this.prepareStatement(handler, ms.getStatementLog());
// 执⾏行行sql,并处理理成果集
var9 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var9;
}
5.创立StatementHandler
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 创立PreparedStatementHandler预编译处理类,包含parameterHandler和resultSetHandler
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
StatementHandler statementHandler = (StatementHandler)this.interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
6.parameterHandler设置参数信息
public void parameterize(Statement statement) throws SQLException {
this.parameterHandler.setParameters((PreparedStatement)statement);
}
7. PreparedStatementHandler执行sql,并处理成果集
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement)statement;
ps.execute();
return this.resultSetHandler.handleResultSets(ps);
}
总结
参加我们
我们来自字节跳动飞书商业应用研发部(Lark Business Applications),目前我们在北京、深圳、上海、武汉、杭州、成都、广州、三亚都设立了办公区域。我们重视的产品范畴主要在企业经验办理软件上,包含飞书 OKR、飞书绩效、飞书招聘、飞书人事等 HCM 范畴系统,也包含飞书批阅、OA、法务、财政、收购、差旅与报销等系统。欢迎各位参加我们。
扫码发现职位&投递简历
官网投递:job.toutiao.com/s/FyL7DRg