mybatis工作原理1---sqlsession

来源:互联网 发布:js模拟键盘按键 编辑:程序博客网 时间:2024/04/30 06:50

<span style="font-family: 微软雅黑; background-color: rgb(255, 255, 255);">声明:本文参考mybatis官方中文文档http://mybatis.github.io/mybatis-3/zh/index.html,转载请注明出处。</span>

mybatis 版本为3.2.8,测试工具junit版本为4.12,eclipse simple maven工程,具体的dependency如下:

  <dependency>  <groupId>org.mybatis</groupId>  <artifactId>mybatis</artifactId>  <version>3.2.8</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency>

1.从xml配置文件获取SqlSession

每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。SqlSessionFactoryBuilder 可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。然后通过SqlSessionFactory的openSession即可获得SqlSession。
首先我们定义一个mybatis-config.xml的mybatis配置文件内容如下:
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configuration  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration>  <properties resource="conf/jdbc.properties">  </properties>  <environments default="development">    <environment id="development">      <transactionManager type="JDBC"/>      <dataSource type="POOLED">        <property name="driver" value="${driverClassName}"/>        <property name="url" value="${jdbc_url}"/>        <property name="username" value="${jdbc_username}"/>        <property name="password" value="${jdbc_password}"/>      </dataSource>    </environment>  </environments><!--   <mappers> --><!--     <mapper resource="org/mybatis/example/BlogMapper.xml"/> --><!--   </mappers> --></configuration>
可以看到,这个配置应用了一个外部的jdbc.properties文件,里面是具体的jdbc datasource配置。

具体的测试代码如下:

private static SqlSessionFactory sqlSessionFactory;@Beforepublic void setUp() throws Exception {<span style="white-space:pre"></span>String resource = "conf/mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);<span style="white-space:pre"></span>sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);}@Testpublic void testSqlSessionDefault() {<span style="white-space:pre"></span>SqlSession sqlSession = sqlSessionFactory.openSession();<span style="white-space:pre"></span>assertNotEquals(null, sqlSession);}
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由对它进行清除或重建,上述代码中,我们用
new SqlSessionFactoryBuilder().build(inputStream);
创建了一个SqlSessionFactory,这样的方式创建的其实是一个DefaultSqlSessionFactory,查看mybatis的源代码发现build的时候调用如下代码:
  public SqlSessionFactory build(InputStream inputStream) {    return build(inputStream, null, null);  }  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {    try {      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);      return build(parser.parse());    } catch (Exception e) {      throw ExceptionFactory.wrapException("Error building SqlSession.", e);    } finally {      ErrorContext.instance().reset();      try {        inputStream.close();      } catch (IOException e) {        // Intentionally ignore. Prefer previous error.      }    }  }      public SqlSessionFactory build(Configuration config) {    return new DefaultSqlSessionFactory(config);  }
可以看到创建SqlSessionFactory的过程其实是先解析xml,获取一个Configuration对象,然后new出一个DefaultSqlSessionFactory。
在获取到SqlSessionFactory的实例之后,调用openSession()方法。查看源码克可知openSession()方法实际如下:
  public SqlSession openSession() {    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);  }
再来看一下openSessionFromDataSource的源码:
  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();    }  }
实际过程如下:
1.从configuration中获取environment;
2.根据environment创建TransactionFactory,并new一个Transaction;
3.根据configuration获取一个Executor对象,这里要注意,因为Executor对象和后面的很多操作有关;
4.创建一个新的DefaultSqlSession对象。
回头看一下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;  }
newExecutor和transaction,executorType有关,看代码发现transaction都相同,主要的还是executorType,这个参数是一个如下的枚举:
public enum ExecutorType {  SIMPLE, REUSE, BATCH}
newExecutor会根据不同的type创建不同的Executor对象,分别是BatchExecutor,ResueExecutor和SimpleExecutor,其中BatchExecutor专门用于执行批量sql操作,ReuseExecutor会重用statement执行sql操作,SimpleExecutor只是简单执行sql;在cacheEnabled是true的时候会创建以之前创建的Executor对象为参数的CachingExecutor对象,mybatis默认cache是开启的,在使用CachingExecutor对象时,查询数据库前先查找缓存,若没找到的话调用delegate(就是构造时传入的Executor对象)从数据库查询,并将查询结果存入缓存中。
至此为止,从xml配置中获取SqlSession对象的过程结束。

2.不使用 XML 获取SqlSession

我们也可以从java程序直接构造配置而不依赖于xml配置:

@Testpublic void testDataSource() throws IOException{String properties_path = "conf/datasource.properties";Properties props = new Properties();InputStream input =  Resources.getResourceAsStream(properties_path);props.load(input);DataSourceFactory factory = new PooledDataSourceFactory();factory.setProperties(props);DataSource dataSource = factory.getDataSource();TransactionFactory transactionFactory = new JdbcTransactionFactory();Environment environment = new Environment("development", transactionFactory, dataSource);Configuration configuration = new Configuration(environment);sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);SqlSession sqlSession = sqlSessionFactory.openSession();assertNotEquals(null, sqlSession);}

首先,我们从外部读取一个datasource.properties的文件,里面有一些数据源的相关信息。看上面的代码我们发现,其实过程是和读取xml配置类似,只不过Configuration对象由我们自己构建。整个过程中需要注意的是factory.setProperties(props):

  public void setProperties(Properties properties) {    Properties driverProperties = new Properties();    MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);    for (Object key : properties.keySet()) {      String propertyName = (String) key;      if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {        String value = properties.getProperty(propertyName);        driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);      } else if (metaDataSource.hasSetter(propertyName)) {        String value = (String) properties.get(propertyName);        Object convertedValue = convertValue(metaDataSource, propertyName, value);        metaDataSource.setValue(propertyName, convertedValue);      } else {        throw new DataSourceException("Unknown DataSource property: " + propertyName);      }    }    if (driverProperties.size() > 0) {      metaDataSource.setValue("driverProperties", driverProperties);    }  }
这段代码中,和获取到poperties的keySet,metaDataSource.hasSetter(propertyName)是检查medaDataSource是否有改propertyName的set函数,说白了就是检查datasource.properties的key是否和datasource实例的属性是否匹配,如果随意写一个则会抛出异常:“Unknown DataSource property: " + propertyName, 看一下那些属性是ok的,也即可以再peopertie文件里面设置的:

  private String driver;  private String url;  private String username;  private String password;  private Boolean autoCommit;  private Integer defaultTransactionIsolationLevel;
ok,写一下简单的datasource.properties配置:

driver=com.mysql.jdbc.Driverurl=jdbc:mysql://ip:3306/database?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNullusername=userpassword=pwd
后续的过程和从xml获取SqlSession相同。当然,不写datasource.properties也是可以的,无非就是在代码里面props.setProperty(key,value)了,此处就不赘述了。


ps:第一篇blog,争取以后每周写一篇,记录下。。。









0 0