现在的位置: 首页 > 综合 > 正文

MyBatis源码赏析二-顶级接口(1)

2014年10月21日 ⁄ 综合 ⁄ 共 6020字 ⁄ 字号 评论关闭
 MyBatis上层接口使用简单工厂模式,核心接口为org.apache.ibatis.session.SqlSession。其中定义了与数据相关的所有操作,以及和MyBatis本身相关的几个方法,比如getConfiguration和getMapper。下图列出了部分常用方法:

     

     从上面的类图中可以看出,SqlSession已经覆盖了DAO模式中的所有方法。因为是ORM框架,所以都是包装方法。但如果在特殊情况下,一定要用JDBC原始java.sql.Connection接口的话,SqlSession中也提供了getConnection这个方法来获取。可以想象,这只有在很特别的情况下才会用到,因为此后,你对java.sql.Connection所做的任何操作都与MyBatis无关了,它提供的所有功能也因此不再有效,如事务等,都需要自己手动来管理了。


     上面提到,MyBatis的上层核心接口是SqlSession,因此,上层的其它接口和类,均围绕它展开。下面从一个官方应用指南中的经典API例子开始,引出这几个重要的类。

点击(此处)折叠或打开

  1. String evironment = "development";
  2. InputStream is = getClass().getResourceAsStream("/SqlMapperConfig.xml");
  3. SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
  4. SqlSessionFactory factory = factoryBulider.bulid(is,evironment);
  5. SqlSession session = factory.openSession();
  6. String selectId = "test.foo";
  7. Object bean = session.selectOne(selectdId);
  8. session.close;

   这是一个简单的查询,涉及到下面这几个类,它们是使用的MyBatis的入口:

  • org.apache.ibatis.session.SqlSessionFactoryBuilder : SQL会话工厂的构建类
  • org.apache.ibatis.session.SqlSessionFactroy : 打开一个SQL会话的工厂类
  • org.apache.ibatis.session.SqlSession : SQL会话本身
  • org.apache.ibatis.session.Configuration : 整个MyBatis的配置

   似乎有点太简单了,其实不然,这只是入口。关键的地方是SqlSessionFactory这个接口的实例是如何被创建出来的呢?显然答案就在SqlSessionFactoryBuilder这个默认实用类中。另外,org.apache.ibaits.session.Configuration这个类是怎么冒出来的呢?答案也在SqlSessionFactoryBuilder的实现代码里,那还等什么,源码List出来吧:

点击(此处)折叠或打开

  1. public class SqlSessionFactoryBuilder {
  2.   public SqlSessionFactory build(Reader reader) {
  3.     return build(reader, null, null);
  4.   }
  5.   
  6.   public SqlSessionFactory build(Reader reader, String environment) {
  7.     return build(reader, environment, null);
  8.   }
  9.   
  10.   public SqlSessionFactory build(Reader reader, Properties properties) {
  11.     return build(reader, null, properties);
  12.   }
  13.   
  14.   public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
  15.     try {
  16.       XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
  17.       return build(parser.parse());
  18.     } catch (Exception e) {
  19.       throw ExceptionFactory.wrapException("Error
    building SqlSession."
    , e);
  20.     } finally {
  21.       ErrorContext.instance().reset();
  22.       try { reader.close(); catch(IOException e){//Intentionally
    ignore. Prefer previous error.
    }
  23.     }
  24.   }

  25.   public SqlSessionFactory build(InputStream inputStream) {
  26.     return build(inputStream, null, null);
  27.   }

  28.   public SqlSessionFactory build(InputStream inputStream, String environment) {
  29.     return build(inputStream, environment, null);
  30.   }

  31.   public SqlSessionFactory build(InputStream inputStream, Properties properties) {
  32.     return build(inputStream, null, properties);
  33.   }

  34.   public SqlSessionFactory build(InputStream inputStream, String environment, Properties props) {
  35.     try {
  36.       XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, props);
  37.       return build(parser.parse());
  38.     } catch (Exception e) {
  39.       throw ExceptionFactory.wrapException("Error
    building SqlSession."
    , e);
  40.     } finally {
  41.       ErrorContext.instance().reset();
  42.       try{inputStream.close();}catch(IOException e){//Intentionally
    ignore.Prefer previous error.
    }
  43.     }
  44.   }
  45.     
  46.   public SqlSessionFactory build(Configuration config) {
  47.     return new DefaultSqlSessionFactory(config);
  48.   }
  49. }

   从它的类签名和构造方法签名就可以看出,这个类是即用即扔的,正好与MyBatis源码赏析一中提到的生命周期相吻合。这个类只有一个方法,就是build。build方法被重载(overide)了多次,可分为三组:

  1. 通过指定org.apache.ibais.session.Configuration来构建DefaultSqlSessionFactory
  2. 从一个Reader中加载Configuration,并构建SqlSessionFactory
  3. 从一个Stream中加载Configuration,并构建SqlSessionFactory

  2和3又被重载了多次,但最终,这两类build方法的任务都是从不同的源加载配置,而SqlSessionFactory具体的构建过程,则委托给1类这个build方法。由此可见,SqlSessionFactoryBuilder这个类,其实也没做什么,只是从不同的源加载配置,为编程提供入口。而具体的加载过程都委托给了其它类。


  从SqlSessionFactoryBuilder中构建出来的工厂实现类,都是DefaultSqlSessionFactory,这是MyBatis默认的SqlSessionFactory接口的实现类,也是唯一一个真实的实现类,在本文的版本中(3.0.6),还有另外一个实现类:SqlSessionManager,但它并不是一个功能上的实现类,而是对SqlSession在实际应用开发过程中的实用工具类。因此,现在的重点落在了DefaultSqlSessionFactory类。SqlSessionFactory的功能是负责打开一个连接会话,该接口只定义了两个方法如下:

点击(此处)折叠或打开

  1. public interface SqlSessionFactory {
  2.   SqlSession openSession();

  3.   SqlSession openSession(boolean autoCommit);
  4.   SqlSession openSession(Connection connection);
  5.   SqlSession openSession(TransactionIsolationLevel level);

  6.   SqlSession openSession(ExecutorType execType);
  7.   SqlSession openSession(ExecutorType execType, boolean autoCommit);
  8.   SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
  9.   SqlSession openSession(ExecutorType execType, Connection connection);

  10.   Configuration getConfiguration();
  11. }

  可以看出,主要就是openSession方法,通过重载多次,来得到不同的会话特性。再看看DefaultSqlSessionFactory是如何实现openSession这个方法的。这儿就不贴出源码了,所有的openSession方法最终通过转调两个方法来实现的,分别是openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit)和openSessionFromConnection(ExecutorType
execType, Connection connection),这样做显然是因为SqlSessionFactory接口的openSession方法签名就被这样分成了两类,FromDataSource这一类的,显然是通过DataSource来获取java.sql.Connection的,FromConnection则是直接指定Connection的。这里我们可以看出,这样做的目的在最高层提供了灵活性,也是手工编程的入口。虽然我们实际开发过程中,都是通过配置xml的方式来使用MyBatis的。但了解到这些后,为我们自己扩展MyBatis提供了入口。

  

  现在还剩下一个问题,前面提到的几个高层接口都找到了相应的实现类,那SqlSession呢?它的实现类是什么?当然你用Eclipse或是NetBeans这些高级IDE可以一下子找到,不过,我们这儿另辟溪径。我们知道,一个SqlSession的实例是这样得到的:SqlSessionFactoryBuilder.build(...).openSession(...);而build方法返回的是DefaultSqlSessionFactory实现类,查看一下这它的openSession方法不就知道了么。是的,如前段所述,最终我们可以在openSessionFromDataSource和openSessionFromConnection这两个方法中找到,得到的SqlSession实现类是org.apache.ibatis.session.defaults.DefaultSqlSession。再进入到该类中,在这儿,我们就快要接近真相(我们想知道,SqlSession的CRUD操作是如何实现的)了。其实,具体CRUD操作,都委托给了org.apache.ibaits.executors.Executor接口了。DefaultSqlSession只是为Executor提供执行参数和环境,是一个管理者,下面是它开头的部分源码:

点击(此处)折叠或打开

  1. public class DefaultSqlSession implements SqlSession {

  2.   private Configuration configuration;
  3.   private Executor executor;

  4.   private boolean autoCommit;
  5.   private boolean dirty;

  6.   public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
  7.     this.configuration = configuration;
  8.     this.executor = executor;
  9.     this.autoCommit = autoCommit;
  10.     this.dirty = false;
  11.   }
  12.   ....
  13. }

  可以看出,它主要管理维护两个类实例,分别是Executor和Configuration。顾名思义,前者负责执行,后者负责提供数据,而SqlSession本身则身负将二者关联起来,加以管理。


  通过前面的分析,我们大致可以得到MyBatis一个粗略的上层架构类图:



抱歉!评论已关闭.