目录
XMLConfigBuilder将Mybatis全局配置文件.xml中设置的节点提取赋给Configuration对象
Configuration
Configuration类的结构很简单,包括environment(数据库环境)、typeHandlerRegistry(类型转换器注册器,将多个类型转换器注册到一个handler中)等protected的成员变量和它们的getter(Boolean类型的提供is方法)和setter方法。
mybatis的全局配置文件
mybatis的全局配置文件中引用了mybatis-3-config.dtd
<?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>
</configuration>
mybatis-3-config.dtd中设置了11个属性节点,规定mybatis配置文件中可以设置哪些属性
<!ELEMENT configuration (properties?,
settings?,
typeAliases?,
typeHandlers?,
objectFactory?,
objectWrapperFactory?,
reflectorFactory?,
plugins?,
environments?,
databaseIdProvider?,
mappers?)>
1. properties属性
1)通过property定义
<properties>
<property name="driver" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/testmybatisdb?serverTimezone=GMT%2B8&allowMultiQueries=true" />
<property name="username" value="root" />
<property name="password" value="18721426" />
</properties>
2) 通过resource或url引用资源文件
<!-- 设置或者引用资源文件 url:引用本地或者网络资源 resource:引用资源文件夹中的资源,
比如这里的db.propreties中设置了数据库连接信息,可以给下面的environments数据库环境节点引用
-->
<properties resource="db.properties"/>
#db.propreties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/testmybatisdb?serverTimezone=GMT%2B8&allowMultiQueries=true
name=root
pass=18721426
2. settings设置
这是 MyBatis 中极为重要的调整设置,规定 MyBatis 的运行时行为
<!--设置 -->
<settings>
<!--缓存配置的全局开关:如果这里设置成false,那么即便在映射器中配置开启也无济于事 -->
<setting name="cacheEnabled" value="true" />
<!--延时加载的全局开关 -->
<setting name="lazyLoadingEnabled" value="false" />
<!-- 是否允许单一语句返回多结果集 -->
<setting name="multipleResultSetsEnabled" value="true" />
<!-- 使用列标签代替列名,需要兼容驱动 -->
<setting name="useColumnLabel" value="true" />
<!-- 允许JDBC自动生成主键,需要驱动兼容。如果设置为true,则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍能正常工作 -->
<setting name="useGeneratedKeys" value="false" />
<!-- 指定MyBatis该如何自动映射列到字段或属性:NONE表示取消自动映射;PARTIAL表示只会自动映射,没有定义嵌套结果集和映射结果集;FULL会自动映射任意复杂的结果集,无论是否嵌套 -->
<setting name="autoMappingBehavior" value="PARTIAL" />
<!-- 配置默认的执行器:SIMPLE是普通的执行器;REUSE会重用预处理语句;BATCH会重用语句并执行批量更新 -->
<setting name="defaultExecutorType" value="SIMPLE" />
<!--设置超时时间:它决定驱动等待数据库响应的秒数,任何正整数-->
<setting name="defaultStatementTimeout" value="25"/>
<!--设置数据库驱动程序默认返回的条数限制,此参数可以重新设置,任何正整数 -->
<setting name="defaultFetchSize" value="100" />
<!-- 允许在嵌套语句中使用分页(RowBounds) -->
<setting name="safeRowBoundsEnabled" value="false" />
<!-- 是否开启自动驼峰命名规则,即从a_example到aExample的映射 -->
<setting name="mapUnderscoreToCamelCase" value="true" />
<!-- 本地缓存机制,防止循环引用和加速重复嵌套循环 -->
<setting name="localCacheScope" value="SESSION" />
<!-- 当没有为参数提供特定JDBC类型时,为空值指定JDBC类型。某些驱动需要指定列的JDBC类型,多数情况直接用一般类型即可,如NULL/VARCHAR/OTHER -->
<setting name="jdbcTypeForNull" value="OTHER" />
<!-- 指定触发延迟加载的方法,如equals/clone/hashCode/toString -->
<setting name="lazyLoadTriggerMethods" value="equals" />
</settings>
3. typeAliases类型别名
比如有些pojo对象的完全限定名较长,可以通过typeAliases起别名简化标识
<!--别名:pojo对象的别名 -->
<typeAliases>
<!--对类单独进行别名设置 -->
<typeAlias alias="user" type="com.daily.pojo.User"></typeAlias>
<typeAlias alias="product" type="com.daily.pojo.Product"></typeAlias>
<!-- 对包进行扫描,可以批量进行别名设置,设置规则是:获取类名称,将其第一个字母变为小写 -->
<package name="com.daily.pojo"/>
</typeAliases>
4. typeHandlers类型处理器
在JDBC中,我们在PreparedStatement中设置预编译sql所需的参数或执行sql后根据结果集ResultSet对象获取得到的数据时,需要将数据库中的类型和java中字段的类型进行转换一样,在MyBatis中使用typeHandler来实现。简单来说,typeHandlers就是用来完成javaType和jdbcType之间的转换
比如java pojo对象中的Date类型的date属性和数据库表中一个varchar类型的date字段,在select、insert等操作时需要进行进行java的Date类型和jdbc的varchar类型之间的转换,我们定义一个类型转换器MyTypeHandler
package Plugins;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
/**
* 自定义一个TypeHandler用来将javatype的日期类型和jdbctype的VARCHAR进行转换
*/
public class MyTypeHandler implements TypeHandler<Date> {
static private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd,HH:mm:ss");
@Override
// 设置sql中指定索引的参数,即将javaType转化为jdbcType
public void setParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {
//我的数据库中date字段是varchar类型,yyyy-MM-dd格式
ps.setString(i, sdf.format(parameter));
}
@Override
// 根据列名称从结果集获取值,并将jdbcType转换成javaType
public Date getResult(ResultSet rs, String columnName) throws SQLException {
String columnValue = rs.getString(columnName);
if (null != columnValue) {
try {
Date date = sdf.parse(columnValue);//将格式化的时期对象通过parse方法去解析当前字符串,返回的是日期格式
return date;
} catch (ParseException e) {
e.printStackTrace();
}
}
return null;
}
@Override
// 根据列索引从结果集获取值,并将jdbcType转换成javaType
public Date getResult(ResultSet rs, int columnIndex) throws SQLException {
String columnValue = rs.getString(columnIndex);
if (null != columnValue) {
try {
Date date = sdf.parse(columnValue);//将格式化的时期对象通过parse方法去解析当前字符串,返回的是日期格式
return date;
} catch (ParseException e) {
e.printStackTrace();
}
}
return null;
}
@Override
public Date getResult(CallableStatement cs, int columnIndex) throws SQLException {
String columnValue = cs.getString(columnIndex);
if (null != columnValue) {
try {
Date date = sdf.parse(columnValue);//将格式化的时期对象通过parse方法去解析当前字符串,返回的是日期格式
return date;
} catch (ParseException e) {
e.printStackTrace();
}
}
return null;
}
}
然后在mybatis全局文件中的typeHandlers属性中注册这个类型转换器
<!-- typeHandlers类型处理器,1) 无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型-->
<typeHandlers>
<!-- 注册自定义handler,说明它作用的jdbcType和javaType -->
<typeHandler jdbcType="VARCHAR" javaType="Date" handler="Plugins.MyTypeHandler"/>
</typeHandlers>
在接口中使用
package DAO;
import Entity.User;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.type.JdbcType;
import java.util.Date;
import java.util.List;
@Mapper
public interface UserDAO {
//@Results将数据库中的字段和pojo中的属性映射,只有一个id(标识),和value(是一个Result[]数组),@Results注解必须和@Select注解一起使用
//@Result具体映射
@Select("select * from manage")
@Results(id="userMap", value={
@Result(column="id", property="id"),
@Result(column="name", property="name"),
@Result(column="date", property="date", javaType = Date.class, jdbcType = JdbcType.VARCHAR, typeHandler = Plugins.MyTypeHandler.class),
})
List<User> selectAll();
//@ResultMap复用指定id的@Results描述的映射
@Select("select * from manage where id = #{id}")
@ResultMap("userMap")
User selectById(int id) ;
@Select("select * from manage where name = #{name}")
@ResultMap("userMap")
List<User> selectByName(String name);
@Insert({"insert manage(id,name,date) values(#{id}, #{name}, #{date, typeHandler=Entity.MyTypeHandler})"})
int insertUser(User user);
@Update("update manage set name=#{name} where id=#{id}")
int updateUserById(User user);
@Delete("delete from manage where id = #{id}")
int deleteUserById(int id);
}
5.ObjectFactory对象工厂
在 MyBatis 中,当其 sql 映射配置文件中的 sql 语句所得到的查询结果,被动态映射到 resultType 或其他处理结果集的参数配置对应的 Java 类型,其中就有 JavaBean 等封装类。而 objectFactory 对象工厂就是用来创建实体对象的类。
在 MyBatis 中,默认的 objectFactory 要做的就是实例化查询结果对应的目标类,有两种方式可以将查询结果的值映射到对应的目标类,一种是通过目标类的默认构造方法,另外一种就是通过目标类的有参构造方法。
有时候在 new 一个新对象(构造方法或者有参构造方法),在得到对象之前需要处理一些逻辑,或者在执行该类的有参构造方法时,在传入参数之前,要对参数进行一些处理,这时就可以创建自己的 objectFactory 来加载该类型的对象。
自定义对象工厂只需要继承DefaultObjectFactory,并重写create,setProperties,isCollection等方法
package MyConfig;
import java.util.Collection;
import java.util.Iterator;
import java.util.Properties;
import Entity.User;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
public class MyObjectFactory extends DefaultObjectFactory {
// 反射,创建对象
@Override
public Object create(Class type) {
//意思一下,就添加一个逻辑,判断该对象如果是User类型的,直接转换为User类型
if (type.equals(User.class)) {
User p = (User) super.create(type);
System.out.println(p.toString());
return p;
}
return super.create(type);
}
// 处理配置文件中ObjectFactory属性的property子属性,并打印
@Override
public void setProperties(Properties properties) {
Iterator iterator = properties.keySet().iterator();
while (iterator.hasNext()) {
String keyValue = String.valueOf(iterator.next());
System.out.println(properties.getProperty(keyValue));
}
super.setProperties(properties);
}
@Override
public <T> boolean isCollection(Class<T> type) {
return Collection.class.isAssignableFrom(type);
}
}
然后在mybatis全局配置文件中设置objectFactory属性
<objectFactory type="MyConfig.MyObjectFactory"/>
6. objectWrapperFactory对象加工工厂
MyBatis 提供在构造对象的时候,对于指定的对象进行特殊的加工
mybatis反射包中提供objectWrapperFactory接口的默认实现类DefaultObjectWrapperFactory,自定义对象加功工厂只需要继承DefaultObjectWrapperFactory重写其中的hasWrapperFor和getWrapperFor方法,或者实现ObjectWrapperFactory接口实现其中的hasWrapperFor和getWrapperFor方法
package MyConfig;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapper;
// 自定义对象加工工厂,这里不添加其他功能,就意思一下,很少用
public class MyObjectWrapperFactory extends DefaultObjectWrapperFactory {
@Override
public boolean hasWrapperFor(Object object) {
return super.hasWrapperFor(object);
}
@Override
public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {
return super.getWrapperFor(metaObject, object);
}
}
然后在mybatis全局配置文件中objectWrapperFactory属性中注册MyObjectWrapperFactory
<!-- 对象加工工厂-->
<objectWrapperFactory type="MyConfig.MyObjectWrapperFactory"/>
7. reflectorFactory 反射工厂
MyBatis 用于缓存 Reflector 的功能,ReflectorFactory 接口提供三个方法,mybatis反射包中提供ReflectorFactory 接口的默认实现类DefaultReflectorFactory
package org.apache.ibatis.reflection;
public interface ReflectorFactory {
// 此 Class 的 Reflector 是否需要缓存
boolean isClassCacheEnabled();
// 设置 Class 的 Reflector 是否需要缓存
void setClassCacheEnabled(boolean classCacheEnabled);
// 找到对应 Class 的 Reflector
Reflector findForClass(Class<?> type);
}
//
package org.apache.ibatis.reflection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class DefaultReflectorFactory implements ReflectorFactory {
// 默认开启缓存Class的Reflector
private boolean classCacheEnabled = true;
private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap();
public DefaultReflectorFactory() {
}
public boolean isClassCacheEnabled() {
return this.classCacheEnabled;
}
public void setClassCacheEnabled(boolean classCacheEnabled) {
this.classCacheEnabled = classCacheEnabled;
}
public Reflector findForClass(Class<?> type) {
return this.classCacheEnabled ? (Reflector)this.reflectorMap.computeIfAbsent(type, Reflector::new) : new Reflector(type);
}
}
自定义反射工厂只需要继承DefaultReflectFactory或者实现ReflectFactory
package MyConfig;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.Reflector;
// 自定义反射工厂
public class MyReflectorFactory extends DefaultReflectorFactory {
@Override
public boolean isClassCacheEnabled() {
return super.isClassCacheEnabled();
}
@Override
public void setClassCacheEnabled(boolean classCacheEnabled) {
super.setClassCacheEnabled(classCacheEnabled);
}
@Override
public Reflector findForClass(Class<?> type) {
return super.findForClass(type);
}
}
然后在mybatis全局配置文件中reflectFactory属性中注册MyObjectWrapperFactory
<!-- 反射工厂-->
<reflectorFactory type="MyConfig.MyReflectorFactory"/>
8. plugins 插件机制,设置拦截器
插件,用于在执行相关操作时,在执行前后插入自定义行为,如缓存,分页等,可指定多个插件,每个插件可拦截指定方法,可以在不修改原来的代码的情况下,通过拦截的方式,改变四大核心对象(Executor、StatementHandler、ParameterHandler、ResultSetHandler)的行为,比如处理参数,处理SQL,处理结果,让我们可以根据自己的需要去增强MyBatis 的功能(代理模式的体现)
图片来自https://www.cnblogs.com/wuzhenzhao/p/11120848.html
自定义拦截器
package MyConfig;
import org.apache.ibatis.builder.StaticSqlSource;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.Properties;
//需要拦截的方法
@Intercepts({
@Signature(type = Executor.class,method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class,method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
)})
//自定义拦截器,实现分页
public class MyInterceptor implements Interceptor {
// 用于覆盖被拦截对象的原有方法(在调用代理对象Plugin 的invoke()方法时被调用)
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("将逻辑分页改为物理分页");
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0]; // MappedStatement
BoundSql boundSql = ms.getBoundSql(args[1]); // Object parameter
RowBounds rb = (RowBounds) args[2]; // RowBounds
// RowBounds为空,无需分页
if (rb == RowBounds.DEFAULT) {
return invocation.proceed();
}// 在SQL后加上limit语句
String sql = boundSql.getSql();
String limit = String.format("LIMIT %d,%d", rb.getOffset(), rb.getLimit());
sql = sql + " " + limit;
// 自定义sqlSource
SqlSource sqlSource = new StaticSqlSource(ms.getConfiguration(), sql, boundSql.getParameterMappings());
// 修改原来的sqlSource
Field field = MappedStatement.class.getDeclaredField("sqlSource");
field.setAccessible(true);
field.set(ms, sqlSource);
// 执行被拦截方法
return invocation.proceed();
}
// target 是被拦截对象,这个方法的作用是给被拦截对象生成一个代理对象,并返回它
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
// 设置拦截器配置的参数
@Override
public void setProperties(Properties properties) {
Iterator iterator = properties.keySet().iterator();
while (iterator.hasNext()) {
String keyValue = String.valueOf(iterator.next());
System.out.println(properties.getProperty(keyValue));
}
}
}
然后在mybatis全局配置中plugins属性中注册该拦截器(可以注册多个拦截器)
<!-- plugins 插件机制, 设置拦截器, 通过动态代理机制,可以介入SqlSession四大重要的对象:
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
这些方法的执行
-->
<plugins>
<plugin interceptor="MyConfig.MyInterceptor"></plugin>
</plugins>
9.environments 数据库环境
MyBatis可以配置多种环境,比如开发、测试和生产环境需要有不同的配置,每种环境使用一个environment标签进行配置并指定唯一标识符,environments通过default属性指定默认的数据库环境
environment-指定具体环境
id:指定当前环境的唯一标识
transactionManager:
type: JDBC | MANAGED | 自定义
JDBC:使用了 JDBC 的提交和回滚设置,依赖于从数据源得到的连接来管理事务范围,JdbcTransactionFactory;
MANAGED:不提交或回滚一个连接、让容器来管理事务的整个生命周期,ManagedTransactionFactory;
自定义:实现TransactionFactory接口,type=全类名/别名.
dataSource
type: UNPOOLED | POOLED | JNDI | 自定义
UNPOOLED:不使用连接池, UnpooledDataSourceFactory;
POOLED:使用连接池, PooledDataSourceFactory;
JNDI: 在EJB 或应用服务器这类容器中查找指定的数据源;
自定义:实现DataSourceFactory接口,定义数据源的获取方式。
<!-- 数据库连接环境的配置,可以写多个数据库环境,比如开发、测试和生产环境需要有不同的配置,以id标识,default设置默认使用的数据库环境id -->
<environments default="development">
<environment id="development">
<!--使用JDBC事务管理器-->
<transactionManager type="JDBC"/>
<!--配置数据库连接池的信息 -->
<dataSource type="POOLED">
<!--使用${}获取配置文件db.properties中配置的字段 -->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${name}"/>
<property name="password" value="${pass}"/>
</dataSource>
</environment>
</environments>
10. databaseIdProvider数据库厂商标识
<!-- databaseIdProvider数据库厂商标识-->
<databaseIdProvider type="DB_VENDOR">
<!-- name:数据库厂商标识, value:别名-->
<property name="MySQL" value="mysql"/>
</databaseIdProvider>
11. mappers 映射器
用来在mybatis初始化的时候,告诉mybatis需要引入哪些Mapper映射文件
resource : 引入类路径下的文件
url : 引入网络路径或者是磁盘路径下的文件
class : 引入Mapper接口.
有SQL映射文件 , 要求Mapper接口与 SQL映射文件同名同位置.
没有SQL映射文件 , 使用注解在接口的方法上写SQL语句.
<!-- 引入映射文件-->
<mappers>
<!-- 使用.xml映射文件-->
<!-- <mapper resource="Mapper.xml" />-->
<!-- 引用包下指定的mapper接口或则批量扫描包下所有的mapper接口-->
<mapper class="DAO.UserDAO"/>
<!-- <package name="DAO"/>-->
XMLConfigBuilder将Mybatis全局配置文件.xml中设置的节点提取赋给Configuration对象
首先读取Mybatis全局配置文件.xml的字节流数据inputStream,然后通过SqlSessionFactoryBuilder(建造者模式)创建SqlSessionFactory对象(内部维护一个Configuration对象),下面是SqlSessionFactoryBuilder的build方法中的关键代码:
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
Configuration configuration = parser.parse();
//XMLConfigBuilder
// Configuration对象从BaseBuilder中继承
public Configuration parse() {
// 保证Configuration对象只创建一次
if (this.parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
} else {
this.parsed = true;
// 遍历所有的节点
this.parseConfiguration(this.parser.evalNode("/configuration"));
return this.configuration;
}
}
// 遍历所有的节点
private void parseConfiguration(XNode root) {
try {
// 提取properties属性节点,调用propertiesElement方法赋给Configuration对象
this.propertiesElement(root.evalNode("properties"));
// 提取settings设置节点,调用settingsElement方法赋给Configuration对象
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(settings);
this.loadCustomLogImpl(settings);
this.settingsElement(settings);
// 提取typeAliases类型别名节点,调用typeAliasesElement方法赋给Configuration对象
this.typeAliasesElement(root.evalNode("typeAliases"));
// 提取typeHandlers类型转换器节点,调用typeHandlerElement方法赋给Configuration对象
this.typeHandlerElement(root.evalNode("typeHandlers"));
// 提取objectFactory对象工厂节点,调用objectFactoryElement方法赋给Configuration对象
this.objectFactoryElement(root.evalNode("objectFactory"));
// 提取objectWrapperFactory对象加工工厂节点,调用objectWrapperFactoryElement方法赋给Configuration对象
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 提取reflectorFactory反射工厂节点,调用reflectorFactoryElement方法赋给Configuration对象
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
// 提取plugins插件节点,调用pluginElement方法赋给Configuration对象
this.pluginElement(root.evalNode("plugins"));
// 提取environments数据库环境节点,调用environmentsElement方法赋给Configuration对象
this.environmentsElement(root.evalNode("environments"));
// 提取databaseIdProvider数据库厂商标识节点,调用databaseIdProviderElement方法赋给Configuration对象
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 提取mappers映射器节点,调用mapperElement方法赋给Configuration对象
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
比如提取数据库环境节点提取子节点等数据
// 提取environments数据库环境节点,调用environmentsElement方法赋给Configuration对象
this.environmentsElement(root.evalNode("environments"));
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (this.environment == null) {
// 提取environments的default标签中标识的environment
this.environment = context.getStringAttribute("default");
}
// 获取所有environment的迭代器
Iterator var2 = context.getChildren().iterator();
// 遍历所有environment
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
//environment的id属性标识一个唯一的environment
String id = child.getStringAttribute("id");
if (this.isSpecifiedEnvironment(id)) {
//transactionManager事务管理器
TransactionFactory txFactory = this.transactionManagerElement(child.evalNode("transactionManager"));
//dataSource配置数据库连接池的信息
DataSourceFactory dsFactory = this.dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
//使用事务工厂txFactory和数据库工厂dsFactory创建environmentBuilder对象,提供Environment对象(建造者模式,提供封装数据库连接环境的Environment对象)
Builder environmentBuilder = (new Builder(id)).transactionFactory(txFactory).dataSource(dataSource);
// setter,将Environment对象赋给configuration
this.configuration.setEnvironment(environmentBuilder.build());
}
}
}
}