原因
Mybatis 框架为作为Java开发人员的必备工具,当初大多应用 mybatis-plus,然而mybatis框架中的运行原理和技术点还是值得一探到底的。
简述
Mybatis官网 下面介绍的十分精炼,反对自定义SQL、存储过程以及高级映射,不过存储过程当初曾经很少应用,其余两点应用频率十分的高。框架最大的特点,也是ORM最大的特点,代替开发人员编写JDBC代码、参数设置、获取后果集,开发人员只有在注解或者xml文件中编辑SQL。加重开发人员工作量,可能在更有意义的事件上投入更多的精力。
注释
框架替开发人员做了很多事件,这些事件是如何做到的呢?本文会娓娓道来。
一个Mybatis利用的中有一个实例十分的要害,就是 SqlSessionFactory。能够从它是怎么来的、起到了什么作用两个方面来具体说说。
SqlSessionFactotry的由来
SqlSessionFactory 是一个接口类,有两个实现类 SqlSessionManager\ DefaultSqlSessionFactory,咱们先把注意力放在 DefaultSqlSessionFactory 下面,它的创立则是在 SqlSessionFactoryBuilder 中,显然是通过结构者模式进行的创立,若一个对象创立的过程不是简略两行代码的事件,有必要应用结构器模式,将创立逻辑独立进去,与类的定义进行隔离。
SqlSessionFactoryBuilder 中的构建逻辑最终目标就是 new DefaultSqlSessionFactory(Configuration config)
,所有的构建形式最终会生成 Configuration,能够是 XMLConfigBuilder.parse(),也能够应用new Configuration。两种形式都会将开发人员对Mybatis利用的配置转换到 SqlSessionFactory 的创立过程中。
SqlSessionFactotry的应用
SqlSessionFactory 的目标就是为了创立一个SqlSession,有多个重载的接口办法 openSession(),最终会执行到 new DefaultSqlSession(configuration, executor)
,configuration还是相熟的那个,Executor 是一个接口,有几个简略的实现 BatchExecutor 、ReuseExecutor、SimpleExecutor、CachingExecutor,能够依据参数执行指定,两外开发人员自定义的Interceptor也会集成到Executor实现当中,Executor 的结构须要 Configuration 和 transaction(后续独自阐明)。
SqlSession接口定义了crud 和 事务相干 缓存相干 的办法。最终的执行还是会应用 Executor 实现类。比方所有的查询方法会进入到 List<E> selectList(String statement, Object parameter, RowBounds rowBounds)
,statement就是开发人员指定的SQL语句标识符,调用executor之前,通过 configuration.getMappedStatement(statement) 获取到 MappedStatement 作为执行对象,参加执行过程。
Executor 执行器的query(),次要是调用 doQuery(),在调用前会创立缓存key,doQuery()中存在sqlSession 级别的缓存逻辑,缓存没有命中会进入 queryFromDatabase()。
queryFromDatabase() 办法会将执行后果放入缓存,执行逻辑是在 doQuery(ms, parameter, rowBounds, resultHandler, boundSql) 中,此办法是一个形象办法,进入 SimpleExecutor的形象实现中。
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 依据开发人员定义的sql语句时指定的 StatementType 生成对应的 StatementHandler
// 这一点也是一个扩大实现
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
// 创立 statement ,并将sql参数放入到 statement 中
stmt = prepareStatement(handler, ms.getStatementLog());
// 执行查问,并映射后果集
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
// 执行查问,并映射后果集
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
以上就是mybatis利用中 sqlSessionFactory 大抵执行逻辑,上面会从框架的扩展性方面阐明。
扩展性
StatementHandler 扩大
其实现类 RoutingStatementHandler 采纳简略工厂和装璜器的形式,依据 StatementType 枚举创立不同的实现。
switch (ms.getStatementType()) {
case STATEMENT:
// sql 间接执行
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
// 应用 PreparedStatement 预处理参数,避免sql注入
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
// 应用 CallableStatement 形式,能够指定额定的回调
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
Executor 扩大
Configuration 类中可依据 ExecutorType 创立指定的 Executor 实现类
public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor 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 (cacheEnabled) {
// 装璜模式,减少sqlSessionFactory级别的缓存
// 二级缓存当中的事务也须要特地解决
executor = new CachingExecutor(executor, autoCommit);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
插件机制
SQL执行过程中,能够对其进行监控和拦挡调用。插件的执行逻辑入口在 configuration中,ParameterHandler、ResultSetHandler、StatementHandler、Executor 获取过程中都会执行 interceptorChain.pluginAll() 办法。
interceptorChain负责收集开发人员定义的interceptor,并执行pluginAll() 对指标对象进行加强,采纳动静代理的形式,将指标对象进行包装。
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public static Object wrap(Object target, Interceptor 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;
}
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);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
Mapper 代理对象
开发过程当中总会在应用依赖注入的形式引入 Mapper 对象,开发业务性能时总会定义一些UserMapper 之类的接口,办法下面应用注解SQL或者xml文件与Mapper绑定的形式编写SQL。
自身是一个接口,却能够当作对象引入,必然是Mybatis提供了动静代理机制,将一个接口生成为一个代理对象,供其余类应用。
SqlSession 提供一个接口办法 <T> T getMapper(Class<T> type);
定义获取Mapper代理对象的行为,DefaultSqlSession 中应用了 configuration.getMapper(type,this); –> mapperRegistry.getMapper(type, sqlSession); 具体实现是在 mapperRegistry当中:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null)
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
try {
// mapperProxyFactory 必定会创立一个 mapperProxy 对象,代理对象mapper的执行 也会在 mapperProxy 当中
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
// 实现 InvocationHandler 接口
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
// MapperMethod 对象代表一个userMapper中的接口办法
final MapperMethod mapperMethod = cachedMapperMethod(method);
// execute 办法中的执行逻辑最终会调用到 sqlSession 实现与数据库的交互
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
事务
开发人员可指定事务管理 TransactionFactory,不过个别都是 JdbcTransactionFactory,应用数据库事务,获取sqlSession 指定是事务隔离级别,作用在JdbcTransactionFactory 创立数据库连贯时指定到连贯当中。
JdbcTransactionFactory 创立的 JdbcTransaction 会参加到执行器executor的创立中,执行器中的事务办法最终会调用到 JdbcTransaction 执行事务调用。
public void commit(boolean required) throws SQLException {
if (closed) throw new ExecutorException("Cannot commit, transaction is already closed");
// 清空一级缓存
clearLocalCache();
flushStatements();
if (required) {
// 事务提交
transaction.commit();
}
}
public void rollback(boolean required) throws SQLException {
if (!closed) {
try {
clearLocalCache();
flushStatements(true);
} finally {
if (required) {
// 事务回滚
transaction.rollback();
}
}
}
}
总结
Mybatis框架提供了半orm机制,开发人员能够编辑SQL,剩下的工作交给框架解决,节俭开发人员精力。框架的原理进行了梳理,执行流程根本阐明,篇幅无限,其中的技术细节可自行延申。
发表回复