关于后端:Mybatiss-框架解说

原因

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

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理