【简记】Java Web 内幕——Mybatis框架小结

依旧是读后总结(http://blog.csdn.net/luanlouis/article/details/40422941)


一、Mybatis的框架设计

 




1.接口层---和数据库交互的方式


1.1.使用传统的MyBatis提供的API

1.2. 使用Mapper接口

MyBatis 将配置文件中的每一个<mapper> 节点抽象为一个 Mapper 接口,而这个接口中声明的方法和跟<mapper> 节点中的<select|update|delete|insert> 节点项对应,即<select|update|delete|insert> 节点的id值为Mapper 接口中的方法名称,parameterType 值表示Mapper 对应方法的入参类型,而resultMap 值则对应了Mapper 接口表示的返回值类型或者返回结果集的元素类型。



根据MyBatis 的配置规范配置好后,通过SqlSession.getMapper(XXXMapper.class) 方法,MyBatis 会根据相应的接口声明的方法信息,通过动态代理机制生成一个Mapper 实例,我们使用Mapper 接口的某一个方法时,MyBatis 会根据这个方法的方法名和参数类型,确定Statement Id,底层还是通过SqlSession.select("statementId",parameterObject);或者SqlSession.update("statementId",parameterObject); 等等来实现对数据库的操作。


MyBatis 引用Mapper 接口这种调用方式,纯粹是为了满足面向接口编程的需要。(其实还有一个原因是在于,面向接口的编程,使得用户在接口上可以使用注解来配置SQL语句,这样就可以脱离XML配置文件,实现“0配置”)。


2.数据处理层

 数据处理层可以说是MyBatis 的核心,从大的方面上讲,它要完成三个功能:

a. 通过传入参数构建动态SQL语句;

b. SQL语句的执行以及封装查询结果集成List<E>

   

2.1.参数映射和动态SQL语句生成


动态语句生成可以说是MyBatis框架非常优雅的一个设计,MyBatis 通过传入的参数值,使用 Ognl 来动态地构造SQL语句,使得MyBatis 有很强的灵活性和扩展性。


参数映射指的是对于Java 数据类型和jdbc数据类型之间的转换:这里有包括两个过程:查询阶段,我们要将java类型的数据,转换成jdbc类型的数据,通过 preparedStatement.setXXX() 来设值;另一个就是对resultset查询结果集的jdbcType 数据转换成java 数据类型。


 2.2. SQL语句的执行以及封装查询结果集成List<E>

       动态SQL语句生成之后,MyBatis 将执行SQL语句,并将可能返回的结果集转换成List<E> 列表。MyBatis 在对结果集的处理中,支持结果集关系一对多和多对一的转换,并且有两种支持方式,一种为嵌套查询语句的查询,还有一种是嵌套结果集的查询。


3. 框架支撑层


3.1. 事务管理机制

3.2. 连接池管理机制

3.3. 缓存机制

为了提高数据利用率和减小服务器和数据库的压力,MyBatis 会对于一些查询提供会话级别的数据缓存,会将对某一次查询,放置到SqlSession中,在允许的时间间隔内,对于完全相同的查询,MyBatis 会直接将缓存结果返回给用户,而不用再到数据库中查找。

3. 4. SQL语句的配置方式

传统的MyBatis 配置SQL 语句方式就是使用XML文件进行配置的,但是这种方式不能很好地支持面向接口编程的理念,为了支持面向接口的编程,MyBatis 引入了Mapper接口的概念,面向接口的引入,对使用注解来配置SQL 语句成为可能,用户只需要在接口上添加必要的注解即可,不用再去配置XML文件了,但是,目前的MyBatis 只是对注解配置SQL 语句提供了有限的支持,某些高级功能还是要依赖XML配置文件配置SQL 语句。


4 引导层

引导层是配置和启动 MyBatis  配置信息的方式。 MyBatis  提供两种方式来引导 MyBatis  :基于XML配置文件的方式和基于 Java API  的方式


二、MyBatis的主要构件及其相互关系


 从MyBatis代码实现的角度来看,MyBatis的主要的核心部件有以下几个:

  • SqlSession            作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能
  • Executor              MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护
  • StatementHandler   封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。
  • ParameterHandler   负责对用户传递的参数转换成JDBC Statement 所需要的参数,
  • ResultSetHandler    负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;
  • TypeHandler          负责java数据类型和jdbc数据类型之间的映射和转换
  • MappedStatement   MappedStatement维护了一条<select|update|delete|insert>节点的封装, 
  • SqlSource            负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
  • BoundSql             表示动态生成的SQL语句以及相应的参数信息
  • Configuration        MyBatis所有的配置信息都维持在Configuration对象之中。



SqlSession 的工作过程分析:


用sqlSessionFactory创建一个sqlSession对象后,执行以下语句

List<Employee> result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary",params);  

public <E> List<E> selectList(String statement, Object parameter) {  
  return this.selectList(statement, parameter, RowBounds.DEFAULT);  
}  
  
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {  
  try {  
    //1.根据Statement Id,在mybatis 配置对象Configuration中查找和配置文件相对应的MappedStatement      
    MappedStatement ms = configuration.getMappedStatement(statement);  
    //2. 将查询任务委托给MyBatis 的执行器 Executor  
    List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);  
    return result;  
  } catch (Exception e) {  
    throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);  
  } finally {  
    ErrorContext.instance().reset();  
  }  
}  

/** 
* BaseExecutor 类部分代码 
* 
*/  
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {  
        
    // 1.根据具体传入的参数,动态地生成需要执行的SQL语句,用BoundSql对象表示    
    BoundSql boundSql = ms.getBoundSql(parameter);  
    // 2.为当前的查询创建一个缓存Key  
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);  
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);  
 }  
  
  @SuppressWarnings("unchecked")  
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {  
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());  
    if (closed) throw new ExecutorException("Executor was closed.");  
    if (queryStack == 0 && ms.isFlushCacheRequired()) {  
      clearLocalCache();  
    }  
    List<E> list;  
    try {  
      queryStack++;  
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;  
      if (list != null) {  
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);  
      } else {  
        // 3.缓存中没有值,直接从数据库中读取数据    
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);  
      }  
    } finally {  
      queryStack--;  
    }  
    if (queryStack == 0) {  
      for (DeferredLoad deferredLoad : deferredLoads) {  
        deferredLoad.load();  
      }  
      deferredLoads.clear(); // issue #601  
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {  
        clearLocalCache(); // issue #482  
      }  
    }  
    return list;  
  }  
 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {  
    List<E> list;  
    localCache.putObject(key, EXECUTION_PLACEHOLDER);  
    try {  
          
      //4. 执行查询,返回List 结果,然后    将查询的结果放入缓存之中  
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);  
    } finally {  
      localCache.removeObject(key);  
    }  
    localCache.putObject(key, list);  
    if (ms.getStatementType() == StatementType.CALLABLE) {  
      localOutputParameterCache.putObject(key, parameter);  
    }  
    return list;  
  }  

上述的Executor.query()方法几经转折,最后会创建一个StatementHandler对象,然后将必要的参数传递给StatementHandler,使用StatementHandler来完成对数据库的查询,最终返回List结果集。

从上面的代码中我们可以看出,Executor的功能和作用是:

(1、根据传递的参数,完成SQL语句的动态解析,生成BoundSql对象,供StatementHandler使用;

(2、为查询创建缓存,以提高性能

(3、创建JDBCStatement连接对象,传递给StatementHandler对象,返回List查询结果。



StatementHandler 的List<E> query(Statement statement, ResultHandler resultHandler)方法的实现:

//  
// DefaultResultSetHandler 类的handleResultSets(Statement stmt)实现   
//HANDLE RESULT SETS  
//  
  
public List<Object> handleResultSets(Statement stmt) throws SQLException {  
  final List<Object> multipleResults = new ArrayList<Object>();  
  
  int resultSetCount = 0;  
  ResultSetWrapper rsw = getFirstResultSet(stmt);  
  
  List<ResultMap> resultMaps = mappedStatement.getResultMaps();  
  int resultMapCount = resultMaps.size();  
  validateResultMapsCount(rsw, resultMapCount);  
    
  while (rsw != null && resultMapCount > resultSetCount) {  
    ResultMap resultMap = resultMaps.get(resultSetCount);  
      
    //将resultSet  
    handleResultSet(rsw, resultMap, multipleResults, null);  
    rsw = getNextResultSet(stmt);  
    cleanUpAfterHandlingResultSet();  
    resultSetCount++;  
  }  
  
  String[] resultSets = mappedStatement.getResulSets();  
  if (resultSets != null) {  
    while (rsw != null && resultSetCount < resultSets.length) {  
      ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);  
      if (parentMapping != null) {  
        String nestedResultMapId = parentMapping.getNestedResultMapId();  
        ResultMap resultMap = configuration.getResultMap(nestedResultMapId);  
        handleResultSet(rsw, resultMap, null, parentMapping);  
      }  
      rsw = getNextResultSet(stmt);  
      cleanUpAfterHandlingResultSet();  
      resultSetCount++;  
    }  
  }  
  
  return collapseSingleResultList(multipleResults);  
}  


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值