mybatis config.xml怎么解析 xml

下次自动登录
现在的位置:
& 综合 & 正文
关于mybatis的xml配置方式的个人理解
学习心得:
学习的时候忌讳墨守陈规,从头学的想法是错误的
学习到了就要先巩固下来
不懂的一定要弄懂,一知半解危害大
1 主配置文件 里面解析
&?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="db.properties"&
&property name="username" value="root"/&
&property name="password" value="root"/&
&/properties&
&typeAliases&
&typeAlias type="User" alias="user"/&
&/typeAliases&
&environments default="development"&
&!-- 配置多个数据库连接环境这里和 db.properties对应 --&
&environment id="development"&
&transactionManager type="JDBC" /&
&dataSource type="POOLED"&
&property name="driver" value="${driver}" /&
&property name="url" value="${url}" /&
&property name="username" value="${username}" /&
&property name="password" value="${password}" /&
&/dataSource&
&/environment&
&/environments&
&!-- &mapper resource="UserMapper.xml"/& --&
&mapper class="UserMapper"/&
&/mappers&
&/configuration&
有几个要点
a: properties载入的话可以直接定义,也可以从properties文件读取,也可以混合,都是可以的。下面要用到
b:typeAliases,就是别名的意思,本来可以不用写,但是后面会经常用到,所以写个别名减少了字符串的书写
c:environments 配置了 jdbc数据库连接情况,必填
d:mappers:这个有两种,用resource 就代表用注解的方式了
用class代表用配置文件xml的方式(用了类,类对应xml,xml中定义了明明空间为map类)
public interface UserMapper {
public User findUser(User user);
public User findUserById(String id);
public User findUserByIdForInt(int id);
简单的定义了一个接口,在测试类里会用一个奇特的方式来调用
3 类User 是最最简单的pojo类,不做描述
4 映射配置文件
&?xml version="1.0" encoding="UTF-8" ?&
&!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" &
&mapper namespace="UserMapper" &
&resultMap type="user" id="baseUserMap" &
&id column="l_id" property="id" jdbcType="INTEGER" /&
&result column="l_sex" property="sex" jdbcType="VARCHAR" /&
&result column="l_name" property="name" jdbcType="VARCHAR" /&
&result column="l_greade" property="grade" jdbcType="VARCHAR" /&
&/resultMap&
&sql id="searchUser"&
&if test="id!=null"&
l_id = #{id}
&if test="id == null"&
l_id is null
&select id="findUser" parameterType="user" resultMap="baseUserMap"&
select * from tb_user
&include refid="searchUser"/&
order by l_grade
&select id="findUserById" parameterType="string" resultMap="baseUserMap"&
select * from tb_user
where l_id = #{id}
order by l_grade
&select id="findUserByIdForInt" parameterType="int" resultMap="baseUserMap"&
select * from tb_user
where l_id = #{id}
order by l_grade
a:这里用了三种方式来测试查询语句,根据主类,根据String类型id,根据int类型id
b:虽然id类本身为int类型,但是用string类型也是可以的,不报错
c:如果用了简单类型例如string、int就不能让引用include带变量了,因为会找不到,报错,试验下就知道了
d :resultMap 定义了User类的属性和表列名一一对应关系,起了一个id名,和对应的 User类的别名,在主配置文件定义过了
e:命名空间要用类名,这个是用来找类名的,具体mybatis怎么处理是框架处理的,只需记得加上别名即可
f: 定义子句
&sql id="searchUser"&
&if test="id!=null"&
l_id = #{id}
&if test="id == null"&
l_id is null
&include refid="searchUser"/&
5 测试语句
import java.io.IOE
import java.io.InputS
import org.apache.ibatis.io.R
import org.apache.ibatis.session.SqlS
import org.apache.ibatis.session.SqlSessionF
import org.apache.ibatis.session.SqlSessionFactoryB
public class SelectTest {
public static void main(String[] args) {
InputStream inputS
inputStream = Resources.getResourceAsStream("mybiatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user2 = new User();
user2.setId(1);
User user = mapper.findUser(user2);
System.out.println(user);
System.out.println(mapper.findUserById(String.valueOf(1)));
System.out.println(mapper.findUserByIdForInt(1));
session.close();
} catch (IOException e) {
e.printStackTrace();
a: 获取字节流
b:获取sessionfactory
c:获取session
d:后去XXMap
e:调用map方法
6 获取结果:
User [id=1, name=112, grade=null, sex=333]
User [id=1, name=112, grade=null, sex=333]
User [id=1, name=112, grade=null, sex=333]
7 关于使用关联的问题,例如
import java.util.L
public class XianUser {
private List&User&
public int getId() {
public void setId(int id) {
public String getLocation() {
public void setLocation(String location) {
this.location =
public List&User& getUsers() {
public void setUsers(List&User& users) {
this.users =
public String toString() {
return "XianUser [id=" + id + ", location=" + location + ", users="
+ users + "]";
这里面多了一个 集合List&user&,所以这里在配置时要使用递归。因为XianUser和User存在了关联,所以我放在了同一个UserMapper.java和UserMapper.xml下面
但是还没确认分开是否可以,应该是可以的理论上
这里处理需要添加集合
&resultMap type="xianUser" id="xaUser"&
&id column="l_id" property="id" jdbcType="INTEGER"/&
&result column="l_location" property="location" jdbcType="VARCHAR"/&
&collection property="users" ofType="user" column="l_p_id" select="findChildUserById" javaType="java.util.ArrayList"&&/collection&
&!--&collection property="sysDictVOList" ofType="sysDictVO" column="sd_id"
select="findSysDictTreeByCOde" javaType="java.util.ArrayList"
&&/collection& --&
&/resultMap&
&select id="findAllUserById" resultMap="xaUser" parameterType="int"&
select * from tb_xianuser where l_id = #{id}
&select id="findChildUserById" resultMap="baseUserMap" parameterType="int"&
select * from tb_user where l_p_id = #{id}
注意查看集合的使用方式
8 在返回里面有可能是多个值所以
返回最好都用list的值表示
import java.util.L
public interface UserMapper {
public User findUser(User user);
public User findUserById(String id);
public User findUserByIdForInt(int id);
public List&XianUser& findAllUserById(int id);
public List&User& findChildUserById(int id);
除非你确定了的确只有一个返回值存在
9 mybatis在insert时的操作
&insert id="insertUser" parameterType="User"&
insert into tb_user(l_name,l_sex,l_grade) values(#{name},#{sex},#{grade});
10 调用时要提交
mapper.insertUser(user);
11 更新时操作
&update id="updateUser" parameterType="user"&
update tb_user
&if test="name!=null"&
l_name = #{name}
l_id = #{id}
user.setId(2);
user.setName("ok");
mapper.updateUser(user);
&delete id="deleteUserById" parameterType="int" &
delete from tb_user where l_id = #{id}
13 有的项目配置的时候加上了这个,有时可以不用
&settings&
&!-- 全局映射器启用缓存 --&
&setting name="cacheEnabled" value="true" /&
&!-- 查询时,关闭关联对象即时加载以提高性能 --&
&setting name="lazyLoadingEnabled" value="true" /&
&!-- 设置关联对象加载的形态,此处为按需加载字段(加载字段由SQL指 定),
不会加载关联表的所有字段,以提高性能 --&
&setting name="aggressiveLazyLoading" value="false" /&
&!-- 对于未知的SQL查询,允许返回不同的结果集以达到通用的效果 --&
&setting name="multipleResultSetsEnabled" value="true" /&
&!-- 允许使用列标签代替列名 --&
&setting name="useColumnLabel" value="true" /&
&!-- 允许使用自定义的主键值(比如由生成的UUID 32位编码作为键值),数据表的PK生成策略将被覆盖 --&
&setting name="useGeneratedKeys" value="true" /&
&!-- 给予被嵌套的resultMap以字段-属性的映射支持 --&
&setting name="autoMappingBehavior" value="FULL" /&
&!-- 对于批量更新操作缓存SQL以提高性能 --&
&setting name="defaultExecutorType" value="BATCH" /&
&!-- 数据库超过25000秒仍未响应则超时 --&
&setting name="defaultStatementTimeout" value="25000" /&
&/settings&
&&&&推荐文章:
【上篇】【下篇】Sponsered by
mybatis – MyBatis 3 | 入门
要使用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于 classpath 中即可。
如果使用 Maven 来构建项目,则需将下面的 dependency 代码置于 pom.xml 文件中:
&dependency&
&groupId&org.mybatis&/groupId&
&artifactId&mybatis&/artifactId&
&version&x.x.x&/version&
&/dependency&
从 XML 中构建 SqlSessionFactory
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory 的实例可以通过SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。
从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。但是也可以使用任意的输入流(InputStream)实例,包括字符串形式的文件路径或者 file:// 的 URL 形式的文件路径来配置。MyBatis 包含一个名叫 Resources 的工具类,它包含一些实用方法,可使从 classpath 或其他位置加载资源文件更加容易。
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
XML 配置文件(configuration XML)中包含了对 MyBatis 系统的核心设置,包含获取数据库连接实例的数据源(DataSource)和决定事务范围和控制方式的事务管理器(TransactionManager)。XML 配置文件的详细内容后面再探讨,这里先给出一个简单的示例:
&?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&
&environments default="development"&
&environment id="development"&
&transactionManager type="JDBC"/&
&dataSource type="POOLED"&
&property name="driver" value="${driver}"/&
&property name="url" value="${url}"/&
&property name="username" value="${username}"/&
&property name="password" value="${password}"/&
&/dataSource&
&/environment&
&/environments&
&mapper resource="org/mybatis/example/BlogMapper.xml"/&
&/mappers&
&/configuration&
当然,还有很多可以在XML 文件中进行配置,上面的示例指出的则是最关键的部分。要注意 XML 头部的声明,用来验证 XML 文档正确性。environment 元素体中包含了事务管理和连接池的配置。mappers 元素则是包含一组 mapper 映射器(这些 mapper 的 XML 文件包含了 SQL 代码和映射定义信息)。
不使用 XML 构建 SqlSessionFactory
如果你更愿意直接从 Java 程序而不是 XML 文件中创建 configuration,或者创建你自己的 configuration 构建器,MyBatis 也提供了完整的配置类,提供所有和 XML 文件相同功能的配置项。
DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
注意该例中,configuration 添加了一个映射器类(mapper class)。映射器类是 Java 类,它们包含 SQL 映射语句的注解从而避免了 XML 文件的依赖。不过,由于 Java 注解的一些限制加之某些 MyBatis 映射的复杂性,XML 映射对于大多数高级映射(比如:嵌套 Join 映射)来说仍然是必须的。有鉴于此,如果存在一个对等的 XML 配置文件的话,MyBatis 会自动查找并加载它(这种情况下, BlogMapper.xml 将会基于类路径和 BlogMapper.class 的类名被加载进来)。具体细节稍后讨论。
从 SqlSessionFactory 中获取 SqlSession
既然有了 SqlSessionFactory ,顾名思义,我们就可以从中获得 SqlSession 的实例了。SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。例如:
SqlSession session = sqlSessionFactory.openSession();
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
} finally {
session.close();
诚然这种方式能够正常工作,并且对于使用旧版本 MyBatis 的用户来说也比较熟悉,不过现在有了一种更直白的方式。使用对于给定语句能够合理描述参数和返回值的接口(比如说BlogMapper.class),你现在不但可以执行更清晰和类型安全的代码,而且还不用担心易错的字符串字面值以及强制类型转换。
SqlSession session = sqlSessionFactory.openSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
} finally {
session.close();
现在我们来探究一下这里到底是怎么执行的。
探究已映射的 SQL 语句
现在,或许你很想知道 SqlSession 和 Mapper 到底执行了什么操作,而 SQL 语句映射是个相当大的话题,可能会占去文档的大部分篇幅。不过为了让你能够了解个大概,这里会给出几个例子。
在上面提到的两个例子中,一个语句应该是通过 XML 定义,而另外一个则是通过注解定义。先看 XML 定义这个,事实上 MyBatis 提供的全部特性可以利用基于 XML 的映射语言来实现,这使得 MyBatis 在过去的数年间得以流行。如果你以前用过 MyBatis,这个概念应该会比较熟悉。不过 XML 映射文件已经有了很多的改进,随着文档的进行会愈发清晰。这里给出一个基于 XML 映射语句的示例,它应该可以满足上述示例中 SqlSession 的调用。
&?xml version="1.0" encoding="UTF-8" ?&
&!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"&
&mapper namespace="org.mybatis.example.BlogMapper"&
&select id="selectBlog" resultType="Blog"&
select * from Blog where id = #{id}
对于这个简单的例子来说似乎有点小题大做了,但实际上它是非常轻量级的。在一个 XML 映射文件中,你想定义多少个映射语句都是可以的,这样下来,XML 头部和文档类型声明占去的部分就显得微不足道了。文件的剩余部分具有很好的自解释性。在命名空间&com.mybatis.example.BlogMapper&中定义了一个名为&selectBlog&的映射语句,这样它就允许你使用指定的完全限定名&org.mybatis.example.BlogMapper.selectBlog&来调用映射语句,就像上面的例子中做的那样:
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
你可能注意到这和使用完全限定名调用 Java 对象的方法是相似的,之所以这样做是有原因的。这个命名可以直接映射到在命名空间中同名的 Mapper 类,并在已映射的 select 语句中的名字、参数和返回类型匹配成方法。这样你就可以向上面那样很容易地调用这个对应 Mapper 接口的方法。不过让我们再看一遍下面的例子:
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
第二种方法有很多优势,首先它不是基于字符串常量的,就会更安全;其次,如果你的 IDE 有代码补全功能,那么你可以在有了已映射 SQL 语句的基础之上利用这个功能。
提示命名空间的一点注释
命名空间(Namespaces)在之前版本的 MyBatis 中是可选的,容易引起混淆因此是没有益处的。现在的命名空间则是必须的,目的是希望能比只是简单的使用更长的完全限定名来区分语句更进一步。
命名空间使得你所见到的接口绑定成为可能,尽管你觉得这些东西未必用得上,你还是应该遵循这里的规定以防哪天你改变了主意。出于长远考虑,使用命名空间,并将它置于合适的 Java 包命名空间之下,你将拥有一份更加整洁的代码并提高了 MyBatis 的可用性。
命名解析:为了减少输入量,MyBatis 对所有的命名配置元素(包括语句,结果映射,缓存等)使用了如下的命名解析规则。
完全限定名(比如&com.mypackage.MyMapper.selectAllThings&)将被直接查找并且找到即用。
短名称(比如&selectAllThings&)如果全局唯一也可以作为一个单独的引用。如果不唯一,有两个或两个以上的相同名称(比如&com.foo.selectAllThings &和&com.bar.selectAllThings&),那么使用时就会收到错误报告说短名称是不唯一的,这种情况下就必须使用完全限定名。
对于像 BlogMapper 这样的映射器类(Mapper class)来说,还有另一招来处理。它们的映射的语句可以不需要用 XML 来做,取而代之的是可以使用 Java 注解。比如,上面的 XML 示例可被替换如下:
package org.mybatis.
public interface BlogMapper {
@Select("SELECT * FROM blog WHERE id = #{id}")
Blog selectBlog(int id);
对于简单语句来说,注解使代码显得更加简洁,然而 Java 注解对于稍微复杂的语句就会力不从心并且会显得更加混乱。因此,如果你需要做很复杂的事情,那么最好使用 XML 来映射语句。
选择何种方式以及映射语句的定义的一致性对你来说有多重要这些完全取决于你和你的团队。换句话说,永远不要拘泥于一种方式,你可以很轻松的在基于注解和 XML 的语句映射方式间自由移植和切换。
范围(Scope)和生命周期
理解我们目前已经讨论过的不同范围和生命周期类是至关重要的,因为错误的使用会导致非常严重的并发问题。
提示 对象生命周期和依赖注入框架
依赖注入框架可以创建线程安全的、基于事务的 SqlSession 和映射器(mapper)并将它们直接注入到你的 bean 中,因此可以直接忽略它们的生命周期。如果对如何通过依赖注入框架来使用 MyBatis 感兴趣可以研究一下 MyBatis-Spring 或 MyBatis-Guice 两个子项目。
SqlSessionFactoryBuilder
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。因此 SqlSessionFactoryBuilder 实例的最佳范围是方法范围(也就是局部方法变量)。你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但是最好还是不要让其一直存在以保证所有的 XML 解析资源开放给更重要的事情。
SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由对它进行清除或重建。使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码&坏味道(bad smell)&。因此 SqlSessionFactory 的最佳范围是应用范围。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的范围是请求或方法范围。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。也绝不能将 SqlSession 实例的引用放在任何类型的管理范围中,比如 Serlvet 架构中的 HttpSession。如果你现在正在使用一种 Web 框架,要考虑 SqlSession 放在一个和 HTTP 请求对象相似的范围中。换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭。下面的示例就是一个确保 SqlSession 关闭的标准模式:
SqlSession session = sqlSessionFactory.openSession();
// do work
} finally {
session.close();
在你的所有的代码中一致性地使用这种模式来保证所有数据库资源都能被正确地关闭。
映射器实例(Mapper Instances)
映射器是创建用来绑定映射语句的接口。映射器接口的实例是从 SqlSession 中获得的。因此从技术层面讲,映射器实例的最大范围是和 SqlSession 相同的,因为它们都是从 SqlSession 里被请求的。尽管如此,映射器实例的最佳范围是方法范围。也就是说,映射器实例应该在调用它们的方法中被请求,用过之后即可废弃。并不需要显式地关闭映射器实例,尽管在整个请求范围(request scope)保持映射器实例也不会有什么问题,但是很快你会发现,像 SqlSession 一样,在这个范围上管理太多的资源的话会难于控制。所以要保持简单,最好把映射器放在方法范围(method scope)内。下面的示例就展示了这个实践:
SqlSession session = sqlSessionFactory.openSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
// do work
} finally {
session.close();MyBatis源码分析(1)-MapConfig文件的解析 - JavaNerd
是一个优秀的轻ORM框架,由最初的iBatis演化而来,可以方便的完成sql语句的输入输出到java对象之间的相互映射,典型的MyBatis使用的方式如下:
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
} finally {
session.close();
这个mybatis-config.xml文件是MyBatis的入口,所有的数据源、全局性的配置、以及SqlMap文件的位置都在这个文件配置,以下是一个MapConfig文件的例子:
&?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="org/apache/ibatis/builder/mapper.properties"&
&property name="driver" value="org.apache.derby.jdbc.EmbeddedDriver"/&
&/properties&
&settings&
&setting name="cacheEnabled" value="true"/&
&setting name="lazyLoadingEnabled" value="false"/&
&setting name="multipleResultSetsEnabled" value="true"/&
&setting name="useColumnLabel" value="true"/&
&setting name="useGeneratedKeys" value="false"/&
&setting name="defaultExecutorType" value="SIMPLE"/&
&setting name="defaultStatementTimeout" value="25"/&
&/settings&
&typeAliases&
&typeAlias alias="Author" type="domain.blog.Author"/&
&/typeAliases&
&typeHandlers&
&typeHandler javaType="String" jdbcType="VARCHAR" handler="org.apache.ibatis.builder.ExampleTypeHandler"/&
&/typeHandlers&
&objectFactory type="org.apache.ibatis.builder.ExampleObjectFactory"&
&property name="objectFactoryProperty" value="100"/&
&/objectFactory&
&plugin interceptor="org.apache.ibatis.builder.ExamplePlugin"&
&property name="pluginProperty" value="100"/&
&/plugins&
&environments default="development"&
&environment id="development"&
&transactionManager type="JDBC"&
&property name="" value=""/&
&/transactionManager&
&dataSource type="UNPOOLED"&
&property name="driver" value="${driver}"/&
&property name="url" value="${url}"/&
&property name="username" value="${username}"/&
&property name="password" value="${password}"/&
&/dataSource&
&/environment&
&/environments&
&mapper resource="org/apache/ibatis/builder/AuthorMapper.xml"/&
&/mappers&
&/configuration&
弄清楚MapConfig文件是如何解析的,以及各个配置项对应的作用,无疑对使用和理解MyBatis框架有着极大的好处。
2. 三个类的介绍
MapConfig文件的解析,我认为必须先介绍一下框架中的三个类,XNode,XPathParser和BaseBuilder。
XNode是MyBatis框架对于XML文件中Node类的扩展,它的成员变量有
public class XNode {
//org.w3c.dom.Node 表示的是xml中的一个节点
private S //节点的名称
private S //节点的文本内容
//全局性的配置变量,在新建XNode实例对象的时候传进来
//节点本身的属性变量
private XPathParser xpathP //节点本身拥有的XPathParser实例,这个XPathParser是一个基于
//XPath机制解析XML文件的工具类
一个XNode对象是一个XML文件中节点的抽象,它提供了一系列公共方法,使得框架的其他的程序能够很方便的访问节点的某个属性,例如这个方法:
public XNode evalNode(String expression) {
return xpathParser.evalNode(node, expression);
evalNode能够更具XPath表达式获取一个节点下的任意一个节点,实际上调用的是XNode实例自身持有的XPathParser实例对象的方法。
2.2 XPathParser
XPathParser是一个工具类,
public class XPathParser {
//持有的Document对象
private boolean
//是否进行DTD验证
private EntityResolver entityR
//DTD验证的接口
//全局性的配置变量
private XP
//XPath接口
对于XNode中的evalNode方法,实际上调用的是XPathParser中的evalNode方法,代码片段如下:
public XNode evalNode(String expression) {
return evalNode(document, expression);
public XNode evalNode(Object root, String expression) {
Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
if (node == null) {
return null;
return new XNode(this, node, variables);
private Object evaluate(String expression, Object root, QName returnType) {
return xpath.evaluate(expression, root, returnType);
} catch (Exception e) {
throw new BuilderException("Error evaluating XPath.
Cause: " + e, e);
可见,通过层层的调用,最后还是调用的Xpath中的evaluate方法,其中第三个参数 XPathConstants.NODE 指明了该方法返回的是一个Node对象,第一个参数是XPath的语法表达式,XPath的教程可以参考。
2.3 BaseBuilder
BaseBuilder是所有MyBatis框架中所有XML文件解析器的基类,之后的XMLConfigBuilder、XMLMapperBuilder、MapperBuilderAssistant都是继承了此类,此类持有的属性有:
public abstract class BaseBuilder {
protected final Configu
//一切的配置信息都会汇总到这个类中
protected final TypeAliasRegistry typeAliasR
//用于别名的解析
protected final TypeHandlerRegistry typeHandlerR
//用于java类型的解析
其中的Configuration类,就是解析XML配置文件,SqlMap配置文件的归宿。
3. XMLConfigBuilder完成的工作
完成SqlConfig解析工作主要是XMLConfigBuilder它继承自BaseBuilder,它对外提供了很多个不同签名的公共构造函数,以满足不同场合的需要,但是所有的这些对外的公共构造函数,最终还是调用的其私有的构造函数:
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment =
this.parser =
首先,调用基类的构造函数,传入一个新的Configuration实例。第二个参数environment,指定的是XMLconfig文件中的environments下environment节点名称,在一个配置文件中,可以配置多个environment节点,这样对应的是不同的环境配置信息,例如你可以配置两个environment节点,一个对应生产,一个对应开发。如果这个参数为空,则用id为default的environment节点对应的环境信息。
建立了XMLConfigBuilder实例之后,主要调用下面的方法,完成所有配置文件的解析:
private void parseConfiguration(XNode root) {
propertiesElement(root.evalNode("properties")); //issue #117 read properties first
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
settingsElement(root.evalNode("settings"));
environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
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);
该方法对应的root节点是SqlConfig文件的根节点,调用root.evelNode(String nodeName)之后,返回的是SqlConfig文件根节点下的某一个节点,之后针对不同的节点,调用不同的函数,进行处理。
propertiesElement(root.evalNode("properties"));
其中root.evalNode(“properties”)实际上返回的是SqlConfig文件下的节点,对应的XML文件片段为
&properties resource="org/apache/ibatis/builder/mapper.properties"&
&property name="driver" value="org.apache.derby.jdbc.EmbeddedDriver"/&
&/properties&
这个节点设置了一个配置文件,仔细来看看拿到这个节点的XNode实例之后,propertiesElement做了一些什么:
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
//将节点下的子节点转换成Properties实例,其中键名由节点的name决定,键值由节点的value指定
Properties defaults = context.getChildrenAsProperties();
//获取节点属性resouce的值,对应一个配置文件的地址
String resource = context.getStringAttribute("resource");
//获取节点属性url的值,对应一个配置文件的地址
String url = context.getStringAttribute("url");
if (resource != null && url != null) {
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.
Please specify one or the other.");
if (resource != null) {
//将resource指定的配置文件的转换成Properties实例,并且和之前的生成的Properties实例合并
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
//将configuration中已经有的Properties实例读取出来,和之前的生成的Properties实例合并
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
//将新生成的Properties实例设置回XpathParser和Configuration实例。
parser.setVariables(defaults);
configuration.setVariables(defaults);
可见,经过这么一段处理以后,properties节点下对应的配置文件,和单独的以子节点形式设置的配置信息,都以Properties实例的形式存储到了最终要生成的Configuration实例中,这个Properties的实例也会贯彻之后的解析过程,供其他的方法使用。至此算是完成了Properties节点的解析。
之后的settings、typeAliases、typeHandlers等节点的解析类似,基本上都是放到不同的私有方法中来完成的,只不过由于解析对象的不同,这些方法的复杂度也不一样。这样的话,当新增一种的新的节点,只需要新增一种新的私有方法。
之前看过iBatis的源码,iBatis对于SqlConfig文件的解析核心是利用了私有类的回调函数来解析的,结合《
》我还花了一整天的时间搞明白,因为里面的代码实在太复杂。MyBatis对于XML文件的解析部分全部重新了,核心是对于XML节点的抽象XNode以及对于XPath二度进行包装的工具类XPathParser,结合GOF中的建造者模式,做到了较清晰的代码结构和逻辑抽象,使得代码更加容易让人理解。
PS:MyBatis的源码分析是我14年上半年的重要工作,这个系列的博客尽量会保持两周一篇的速度。
原文链接:/articles/vAnEZzB
阅读: 167 |

我要回帖

更多关于 mybatis解析xml 的文章

 

随机推荐