Mybatis源码-SqlSession的创建过程
目录
读取XML文件 #
MyBatis的主配置文件和Mapper配置都使用的是XML格式。MyBatis中的Configuration组件用于描述主配置文件信息,框架在启动时会解析XML配置,将配置信息转换为Configuration对象。
JDKAPI中提供了3种方式解析XML,分别为DOM、SAX和XPath。这3种方式都有各自的特点,具体优缺点读者可参考相关资料。在这3种方式中,API最易于使用的就是XPath方式,MyBatis框架中也采用XPath方式解析XML文件中的配置信息。
使用JDK提供的XPath相关API解析XML需要以下几步:
- 创建表示XML文档的Document对象
- 创建用于执行XPath表达式的XPath对象
- 使用XPath对象执行表达式,获取XML内容
为了简化XPath解析操作,MyBatis通过XPathParser 工具类封装了对XML的解析操作,同时使用XNode类增强了对XML节点的操作。使用XNode对象,我们可以很方便地获取节点的属性、子节点等信息。
@Test
public void testXPathParser() throws Exception {
Reader resource = Resources.getResourceAsReader("users.xml");
XPathParser parser = new XPathParser(resource);
// 注册日期转换器
DateConverter dateConverter = new DateConverter(null);
dateConverter.setPattern("yyyy-MM-dd HH:mm:ss");
ConvertUtils.register(dateConverter, Date.class);
List<UserEntity> userList = new ArrayList<>();
// 调用evalNodes()方法获取XNode列表
List<XNode> nodes = parser.evalNodes("/users/*");
// 对XNode对象进行遍历,获取user相关信息
for (XNode node : nodes) {
UserEntity userEntity = new UserEntity();
Long id = node.getLongAttribute("id");
BeanUtils.setProperty(userEntity, "id", id);
List<XNode> childNods = node.getChildren();
for (XNode childNode : childNods) {
BeanUtils.setProperty(userEntity, childNode.getName(),
childNode.getStringBody());
}
userList.add(userEntity);
}
System.out.println(JSON.toJSONString(userList));
}
创建Configuration实例 #
MyBatis通过XMLConfigBuilder 类完成Configuration 对象的构建工作,关于Configuration下的子标签可以看:
@Test
public void testConfiguration() throws IOException {
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
// 创建XMLConfigBuilder实例
XMLConfigBuilder builder = new XMLConfigBuilder(reader);
// 调用XMLConfigBuilder.parse()方法,解析XML创建Configuration对象
Configuration conf = builder.parse();
}
// 追踪代码
public Configuration parse() {
// 防止parse()方法被同一个实例多次调用
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// 调用XPathParser.evalNode()方法,创建表示configuration节点的XNode对象。
// 调用parseConfiguration()方法对XNode进行处理
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
创建SqlSession实例 #
@Test
public void testSqlSession() throws IOException {
// 获取Mybatis配置文件输入流
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
// 通过SqlSessionFactoryBuilder创建SqlSessionFactory实例
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
// 调用SqlSessionFactory的openSession()方法,创建SqlSession实例
SqlSession session = sqlSessionFactory.openSession();
}
// 代码追踪
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
// 生成config对象,parser.parse()就是configuration实例的创建过程
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.
}
}
}
// 根据Configuration 构造参数直接创建 SqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
接下来研究一下sqlSessionFactory.openSession();
方法
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 获取Mybatis主配置文件配置的环境信息
final Environment environment = configuration.getEnvironment();
// 创建事务管理器工厂
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 创建事务管理器
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 根据Mybatis主配置文件中指定的Executor类型创建对应的Executor实例
final Executor executor = configuration.newExecutor(tx, execType);
// 创建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();
}
}
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}