1、概述
SqlSession对象表示Mybatis框架与数据库建立的会话,可以通过SqlSession实例完成对数据库的增删改查操作。SqlSession的创建过程分为3个阶段:Configuration实例的创建过程,SqlSessionFactory实例的创建过程和SqlSession实例化的过程。
2、XPath方式解析XML文件
Mybatis的主配置文件和Mapper配置都使用的是XML格式。Mybatis中的Configuration组件用于描述主配置文件信息,框架在启动时会解析XML配置,将配置信息转换为Configuration对象。
JDK API中提供了3种方式解析XML,分别为DOM、SAX和XPath。这三种方式都有各自的特点。在这三种方式中,API最易于使用的就是XPath方式,Mybatis框架中也采用XPath方式解析XML文件中的配置信息。
如下所示。
<?xml version="1.0" encoding="UTF-8" ?>
<users>
<user id = "1">
<name>张三</name>
<createTime>2020-03-28 00:00:00</createTime>
<password>100000</password>
<phone>100000</phone>
</user>
<user id = "2">
<name>李四</name>
<createTime>2020-04-28 00:00:00</createTime>
<password>200000</password>
<phone>200000</phone>
</user>
</users>
解析代码如下:
// 创建日期格式转换器
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 创建表示XML文档的Document对象。
// 无论使用何种方式解析XML,都需要先创建表示XML文档的Document对象。
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// Document对象依赖于DocumentBuilder对象,DocumentBuilder采用工厂模式创建。
DocumentBuilder builder = factory.newDocumentBuilder();
// 读取test.xml文件内容。
InputStream inputStream = Resources.getResourceAsStream("mapper/test.xml");
// 解析成Document
Document document = builder.parse(inputStream);
// 使用XPath对象执行表达式,获取XML内容
XPath xPath = XPathFactory.newInstance().newXPath();
// 获取根结点为users下的所有子节点列表
NodeList nodeList = (NodeList)xPath.evaluate("/users/*", document, XPathConstants.NODESET);
List<User> users = new ArrayList<>();
// 遍历全部子节点
for (int i = 1; i < nodeList.getLength() + 1; i++) {
// 子节点的路径为/users/user[0|1]
String path = "/users/user[" + i + "]";
// 获取子节点的id属性
String id = (String)xPath.evaluate(path + "/@id", document, XPathConstants.STRING);
// 获取子节点的name节点的内容
String name = (String)xPath.evaluate(path + "/name", document, XPathConstants.STRING);
// 获取子节点的password的内容
String password = (String)xPath.evaluate(path + "/password", document, XPathConstants.STRING);
// 获取子节点的createTime的内容
String createTime = (String)xPath.evaluate(path + "/createTime", document, XPathConstants.STRING);
// 获取子节点的phone的内容
String phone = (String)xPath.evaluate(path + "/phone", document, XPathConstants.STRING);
// 构造结果
User user = new User();
user.setId(Long.valueOf(id));
user.setCreateTime(simpleDateFormat.parse(createTime));
user.setName(name);
user.setPassword(password);
user.setPhone(phone);
users.add(user);
}
System.out.println(users);
结果如下:
[
User(id=1, name=张三, createTime=Sat Mar 28 00:00:00 CST 2020, password=100000, phone=100000),
User(id=2, name=李四, createTime=Tue Apr 28 00:00:00 CST 2020, password=200000, phone=200000)
]
为了简化XPath解析操作,Mybatis通过XPathParser工具类对XML的解析操作,同时使用XNode类增强了对XML节点的操作。
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
List<User> users = new ArrayList<>();
Reader reader = Resources.getResourceAsReader("mapper/test.xml");
XPathParser pathParser = new XPathParser(reader);
List<XNode> xNodeList = pathParser.evalNodes("/users/*");
for (XNode xNode : xNodeList) {
User user = new User();
user.setId(xNode.getLongAttribute("id"));
List<XNode> xNodes = xNode.getChildren();
for (XNode childNode : xNodes) {
switch (childNode.getName()) {
case "name":
user.setName(childNode.getStringBody());
break;
case "phone":
user.setPhone(childNode.getStringBody());
break;
case "password":
user.setPassword(childNode.getStringBody());
break;
case "createTime":
user.setCreateTime(format.parse(childNode.getStringBody()));
break;
}
}
users.add(user);
}
System.out.println(users);
结果如下:
[
User(id=1, name=张三, createTime=Sat Mar 28 00:00:00 CST 2020, password=100000, phone=100000),
User(id=2, name=李四, createTime=Tue Apr 28 00:00:00 CST 2020, password=200000, phone=200000)
]
3、Configuration实例创建过程
Configuration是Mybatis中比较重要的组件,主要有以下3个作用:
(1)、用于描述Mybatis配置信息,例如settings标签配置的参数信息。
(2)、作为容器注册Mybatis其他组件。例如TypeHandler、MappedStatement等。
(3)、提供工厂方法,创建ResultSetHandler、StatementHandler、Executor、ParameterHandler等组件实例。
在SqlSession实例化之前,首先解析Mybatis主配置文件及所有Mapper文件,创建Configuration实例。
Mybatis通过XMLConfiguration类完成Configuration对象的构建工作。
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
XMLConfigBuilder builder = new XMLConfigBuilder(reader);
Configuration configuration = builder.parse();
接下来会调用XMLConfiguration对象的parse()方法创建Configuration对象
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
在XMLConfiguration类的parse()方法中,首先调用XPathParser对象的evalNode()方法获取XML配置文件中configuration节点对应的XNode对象,接着调用parseConfiguration()方法通过该XNode对象获取更多的配置信息。
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);
}
}
在parseConfiguration()方法中,对于configuration标签的子节点,都有一个单独的方法处理。
Mybatis主配置文件中所有标签的用途如下:
(1)、properties:用于配置属性信息,这些属性的值可以通过$方式引用。
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="123"/>
</properties>
(2)、settings:通过一些属性来控制Mybatis运行时的一些行为。例如,指定日志实现、默认的Executor类型等。
<settings>
<setting name="useGeneratedKeys" value="true"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="logImpl" value="LOG4J"/>
</settings>
(3)、typeAliases:用于配置类型别名,目的是为Java类型设置一个更短的名字,它的存在的意义仅在于用来减少类完全限定名的冗余。typeAliases标签的配置案例如下:
<typeAliases>
<typeAlias type="com.matrix.dao.User" alias="user"/>
<typeAlias type="com.matrix.bean.Order" alias="order"/>
<package name="com.matrix.bean"/>
</typeAliases>
批量指定package,只要指定包名即可,之后程序会为包下的所有类都自动加上别名,其定义别名的规范就是对应包装类的类名首字母变为小写。
(4)、plugins:用于注册用户自定义的插件信息。plugins标签的配置信息如下:
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
(5)、objectFactory:Mybatis通过对象工厂ObjectFactory创建参数对象和结果集映射对象,默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认的构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。objectFactory标签用于配置用户自定义的对象工厂,该标签配置如下:
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
<property name="someProperty" value="100"/>
</objectFactory>
(6)、objectWrapperFactory:Mybatis通过ObjectWrapperFactory创建ObjectWrapper对象,通过ObjectWrapper对象能够很方便的获取对象的属性、方法名字等反射信息。objectWrapperFactory标签用于配置用户自定义ObjectWrapperFactor。
<objectWrapperFactory type="org.mybatis.example.ExampleObjectWrapperFactory"/>
(7)、reflectorFactory:mybatis通过反射工厂ReflectorFactory创建描述Java类型反射信息的Reflector对象,通过Reflector对象能够很方便的获取Class对象的Setter/Getter方法、属性等信息。reflectorFactory用于配置自定义的反射工厂,配置内容如下:
<reflectorFactory type="org.mybatis.example.ExampleReflectorFactory"/>
(8)、environment:用于配置Mybatis数据连接相关环境及事务管理器信息。通过该标签可以配置多个环境信息,然后指定具体使用哪个。environments标签的配置信息如下:
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC">
<property name="" value=""/>
</transactionManager>
<dataSource type="UNPOOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://*.*.*.*:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
(9)、databaseIdProvider:Mybatis能够根据不同的数据库厂商执行不同的SQL语句,该标签用于配置数据库厂商信息。databaseIDProvider标签的使用如下:
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
</databaseIdProvider>
(10)、typeHandlers:用于注册用户自定义的类型处理器(TypeHandler)。该标签的配置如下:
<typeHandlers>
<typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>
(11)、mappers:用于配置Mybatis Mapper信息。 mapper标签的配置如下:
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
Mybatis框架启动后,首先创建Configuration对象,然后解析所有配置信息,将解析后的配置信息存放在Configuration对象中。每个标签的解析细节在解析源码的时候会详细说明。
4、SqlSession实例创建过程
Mybatis中的SqlSession实例使用工厂模式创建,所以在创建SqlSession实例之前需要先创建SqlSessionFactory工厂对象,然后调用SqlSessionFactory对象的openSession()方法,代码如下:
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
factory.openSession();
为了创建SqlSessionFactory对象,首先创建了一个SqlSessionFactoryBuilder对象,然后以Mybatis主配置文件输入流作为参数,调用SqlSessionFactoryBuilder对象的build()方法。
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.
}
}
}
在build方法中,首先创建一个XMLConfigBuilder对象,然后调用XMLConfigBuilder对象的parse()方法对主配置文件进行解析,生成Configuration对象。以Configuration对象作为参数,调用重载的build()方法,该方法的实现如下:
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
SqlSessionFactory接口只有一个默认的实现,即DefaultSqlSessionFactory。这里直接new创建一个DefaultSqlSessionFactory对象。
然后直接调用DefaultSqlSessionFactory的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主配置文件中指定的Exector类型创建对应的Exector实例
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();
}
}
首先通过Configuration对象获取Mybatis主配置文件中通过environment标签配置的环境信息,然后根据配置的事务管理器类型创建对应的事务管理工厂。Mybatis提供了两种事务管理器,分别为JdbcTransaction和ManagedTransaction。其中JdbcTransaction是使用JDBC中的Connection对象实现事务管理的,而ManagedTransaction表示事务由外部容器管理。这两种事务管理器分别由对应的工厂类JdbcTransactionFactory和ManagedTransactionFactory创建。
事务管理器对象创建完毕后,接着调用Configuration对象的newExecutor()方法,根据Mybatis主配置文件中指定的Executor类型创建对应的Executor对象,最后以Executor对象和Configuration对象作为参数,创建一个DefaultSqlSession对象。DefaultSqlSession对象中持有Executor对象的引用,真正执行SQL操作的是Executor对象。