mybatis工作原理1---sqlsession

<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;
@Before
public 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);
}


@Test
public 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配置:

	@Test
	public 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.Driver
url=jdbc:mysql://ip:3306/database?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
username=user
password=pwd
后续的过程和从xml获取SqlSession相同。当然,不写datasource.properties也是可以的,无非就是在代码里面props.setProperty(key,value)了,此处就不赘述了。


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









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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值