Mybatis深入浅出之工作原理

一、简述

1、Mybatis发展简史

  

  Mybatis前生为apache公司开发的ibatis,2010年交由google公司托管维护管理,并更名为Mybatis,MyBatis 是一款优秀的持久层框架。

 

2、ORM

  ORM:Object Relational Mapping,对象与表映射。ORM实现了将数据库操作映射为对象操作,简化了数据库的操作开发,简单地说:ORM将面向数据库的开发转换为面向对象的开发。Mybatis是ORM的一个实现框架,类似实现有JDBC、Hibernate。Mybatis封装简化了JDBC操作,实现数据的持久化。

 

3、Mybatis官网

  • 官网地址:https://mybatis.org/mybatis-3/zh/index.html
  • Mybatis jar包、源码、使用说明文档 都可以从官网中获取。
  • 关于Mybatis配置文件、映射文件等xml文件的模板都可以从Mybatis使用说明文档中获取。

 

 二、分层架构

  • 接口层: 提供外部接口API,用于开发数据库操作功能。
  • 数据处理层:负责具体的SQL实现,执行数据库SQL操作。
  • 框架支撑层:主要负责将公共功能抽离独立,为数据处理层提供最基础的组件服务,比如:事务管理、缓存管理。

 

三、工作流程

  Mybatis底层还是采用原生的jdbc实现对数据库的操作,其数据库操作过程是通过 SqlSessionFactory,SqlSession,Executor,StatementHandler,ParameterHandler,ResultHandler和TypeHandler等处理器封装实现。其中StatementHandler使用ParameterHandler进行参数预编译,使用ResultHandler进行结果处理。而ParameterHandler与ResultHandler都使用TypeHandler进行数据库类型和javaBean类型的映射;。

执行器:Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
参数处理器:ParameterHandler (getParameterObject, setParameters)
结构处理器 ResultSetHandler (handleResultSets, handleOutputParameters)
sql查询处理器:StatementHandler (prepare, parameterize, batch, update, query)

 

流程图:

 

 

1、获取配置Configuration对象

  • 加载解析配置文件,包括config配置和xml映射文件,创建生成Configuration对象;

2、获取SqlSessionFactory对象:

  • 根据Configuration对象创建生成DefaultSqlSessionFactory对象;

3、获取 SqlSession 对象:

  • 创建生成包含Executor和Configuration的DefaultSqlSession对象;

4、获取Mapper接口代理对象MapperProxy(若采用接口代理方式执行sql)

  • getMapper()使用MapperProxyFactory创建一个MapperProxy的代理对象;
  • 代理对象中包含了DefaultSqlSession(Executor);

5、执行SQL

  • 依赖DefaultSqlSession对象中的Executor创建的StatementHandler、ParameterHandler和ResultSetHandler对象完成向数据库发送SQL语句命令查询转换结果集;
StatementHandler: 设置sql语句,预编译,设置参数等相关工作,以及执行增删改查方法;
ParameterHandler: 设置预编译sql参数;
ResultHandler: 处理查询结果集;
TypeHandler: ParameterHandler和ResultHandler都依赖于TypeHandler,进行数据库类型和javaBean类型的映射。

 

四、核心接口/类

1、SqlSessionFactory 

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

public SqlSessionFactory build(Configuration config) {
  return new DefaultSqlSessionFactory(config);
}

 

2、SqlSession 

 

@Override
public SqlSession openSession() {
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

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();
    }
  }

  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }

 

3、Executor 

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    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) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

public <T> T selectOne(String statement, Object parameter) {
  // Popular vote was to return null on 0 results and throw exception on too many.
  List<T> list = this.<T>selectList(statement, parameter);
  if (list.size() == 1) {
    return list.get(0);
  } else if (list.size() > 1) {
    throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
  } else {
    return null;
  }
}
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  try {
    MappedStatement ms = configuration.getMappedStatement(statement);
    return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

 

代理方式:

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }

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 {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

 

五、示例

public class CacheApplication {
    public static void main(String[] args) throws IOException {
        //1.加载配置(Mybaits数据源配置 + 映射xml)
        Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);

        //2. 从SqlSessionFactory中创建一个SqlSession,进行数据库操作
        SqlSession sqlSession = factory.openSession();

        //3.使用SqlSession查询(基于Statement ID方式)
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("truckNo", "1");
        String statement = "com.example.mybatis.cache.dao.mapper.TruckInfoMapper.getTruckInfo";
        TruckInfo truckInfo = sqlSession.selectOne(statement, params);
        System.out.println(truckInfo);

        //4.使用SqlSession查询(基于Mapper接口代理方式)
        TruckInfoMapper truckInfoMapper = sqlSession.getMapper(TruckInfoMapper.class);
        TruckInfo truckInfo2 = truckInfoMapper.getTruckInfo("1");
        System.out.println(truckInfo2);
        sqlSession.close();


    }
}

 

六、代码

 

七、参考

 

posted @ 2020-11-20 14:45  马非白即黑  阅读(619)  评论(0编辑  收藏  举报