<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,争取以后每周写一篇,记录下。。。