在Mybatis中,SqlSessionFactory 是一个重要对象,用来创建 SqlSession,而 SqlSession 是用来操作数据库的。
一. SqlSessionFactory 的生成
我们先来看一段代码:
private static SqlSessionFactory sqlMapper;
@BeforeAll
static void setup() throws Exception {
createBlogDataSource();
final String resource = "org/apache/ibatis/builder/MapperConfig.xml";
final Reader reader = Resources.getResourceAsReader(resource);
sqlMapper = new SqlSessionFactoryBuilder().build(reader);
}
这是一段摘自mybatis测试用例的代码,逻辑是:从一个路径中获取到字符流,通过 SqlSessionFactoryBuilder 的 builder 方法创建 SqlSessionFactoryBuilder。
我们来看下 SqlSessionFactoryBuilder 的源码:
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
// mybatis 可以在不同环境下切换数据源
// 也可以通过 environments 标签的 default 属性指明默认环境
public SqlSessionFactory build(Reader reader, String environment) {
return build(reader, environment, null);
}
public SqlSessionFactory build(Reader reader, Properties properties) {
return build(reader, null, properties);
}
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment) {
return build(inputStream, environment, null);
}
public SqlSessionFactory build(InputStream inputStream, Properties properties) {
return build(inputStream, null, properties);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 从流中解析出 Configuration
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.
}
}
}
/**
* 返回 DefaultSqlSessionFactory
*/
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
不难看出,SqlSessionFactoryBuilder 主要有两类 build 方法,一类加载字节流,一类加载字符流。对应 XMLConfigBuilder 的两种流解析方法。XMLConfigBuilder 从流中解析出另外一个很重要的对象:Configuration(这个后续再说)。而最终,调用 build(Configuration config) 方法,返回一个SqlSessionFactory(DefaultSqlSessionFactory) 。
二、SqlSessionFactory
SqlSessionFactory 主要作用是从 Connection 或 DataSource 中创建一个SqlSession, 其次提供获取 Configuration 的方法。
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
Configuration getConfiguration();
}
三、DefaultSqlSessionFactory
DefaultSqlSessionFactory 是 SqlSessionFactory 的默认实现
public class DefaultSqlSessionFactory implements SqlSessionFactory {
// mybatis 核心 环境配置
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
@Override
public SqlSession openSession(boolean autoCommit) {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
}
@Override
public SqlSession openSession(ExecutorType execType) {
return openSessionFromDataSource(execType, null, false);
}
@Override
public SqlSession openSession(TransactionIsolationLevel level) {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), level, false);
}
@Override
public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
return openSessionFromDataSource(execType, level, false);
}
@Override
public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
return openSessionFromDataSource(execType, null, autoCommit);
}
@Override
public SqlSession openSession(Connection connection) {
return openSessionFromConnection(configuration.getDefaultExecutorType(), connection);
}
@Override
public SqlSession openSession(ExecutorType execType, Connection connection) {
return openSessionFromConnection(execType, connection);
}
@Override
public Configuration getConfiguration() {
return configuration;
}
/**
* 1. 这里有个 ExecutorType 枚举了 SIMPLE, REUSE, BATCH 三种执行器
* 实际上还有第四种执行器 {@link org.apache.ibatis.executor.CachingExecutor}
* 这个执行器是在开启了 Mybatis 二级缓存后使用的,它使用装饰者模式
* 包装 SIMPLE, REUSE, BATCH 三种执行器提供缓存功能
* 2. 事务隔离级别,是一个枚举型,包括JDBC支持的5个级别
* 3. 是否自动提交事务
*/
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 获取 environment
final Environment environment = configuration.getEnvironment();
// 获取事务工厂
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 创建执行器
final Executor executor = configuration.newExecutor(tx, execType);
// 返回 SqlSession (DefaultSqlSession)
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();
}
}
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
try {
boolean autoCommit;
try {
autoCommit = connection.getAutoCommit();
} catch (SQLException e) {
// Failover to true, as most poor drivers
// or databases won't support transactions
autoCommit = true;
}
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
final Transaction tx = transactionFactory.newTransaction(connection);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
/**
* 获取事务工厂
*/
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
// 如果 environment 为 null 或者其事务工厂为 null
if (environment == null || environment.getTransactionFactory() == null) {
// 返回 ManagedTransactionFactory 事务工厂
// Mybatis 管理事务的两种方式
// 1. 使用 JDBC 的事务管理机制,就是利用 java.sql.Connection 对象完成对事务的提交
// 2. 使用 MANAGED 的事务管理机制,这种机制 mybatis 自身不会去实现事务管理,
// 而是让程序的WEB容器(JBOSS,WebLogic,Tomcat)来实现对事务的管理,
// 所有它对事务提交和回滚并不会做任何操作
return new ManagedTransactionFactory();
}
// environment 有两个重要属性。
// DataSource 和 TransactionFactory
// 即数据源和事务工厂
return environment.getTransactionFactory();
}
private void closeTransaction(Transaction tx) {
if (tx != null) {
try {
tx.close();
} catch (SQLException ignore) {
// Intentionally ignore. Prefer previous error.
}
}
}
}
我们看下 TransactionIsolationLevel
public enum TransactionIsolationLevel {
/**
* 无事务
*/
NONE(Connection.TRANSACTION_NONE),
/**
* 读已提交数据,防止脏读,不能处理幻读和不可重复读 (Oracle)
*/
READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED),
/**
* 读未提交数据,可能出现任何事务并发问题,什么都不处理,性能最好
*/
READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED),
/**
* 可重复读,防止脏读和幻读,性能比SERIALIZABLE好(MySql)
*/
REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),
/**
* 串行化,不会出现任何并发问题,但是性能最差
*/
SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE);
private final int level;
TransactionIsolationLevel(int level) {
this.level = level;
}
public int getLevel() {
return level;
}
}