原因
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,剩下的工作交给框架解决,节俭开发人员精力。框架的原理进行了梳理,执行流程根本阐明,篇幅无限,其中的技术细节可自行延申。