学习mybatis,绕不开一个核心类 Configuration。这个类相当于一个小型数据库,把mybatis里面所有的配置信息基本全部给存储起来了。
package org.apache.ibatis.session;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.ibatis.binding.MapperRegistry;
import org.apache.ibatis.builder.CacheRefResolver;
import org.apache.ibatis.builder.ResultMapResolver;
import org.apache.ibatis.builder.annotation.MethodResolver;
import org.apache.ibatis.builder.xml.XMLStatementBuilder;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.decorators.FifoCache;
import org.apache.ibatis.cache.decorators.LruCache;
import org.apache.ibatis.cache.decorators.SoftCache;
import org.apache.ibatis.cache.decorators.WeakCache;
import org.apache.ibatis.cache.impl.PerpetualCache;
import org.apache.ibatis.datasource.jndi.JndiDataSourceFactory;
import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;
import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
import org.apache.ibatis.executor.BatchExecutor;
import org.apache.ibatis.executor.CachingExecutor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.ReuseExecutor;
import org.apache.ibatis.executor.SimpleExecutor;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.loader.ProxyFactory;
import org.apache.ibatis.executor.loader.cglib.CglibProxyFactory;
import org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.DefaultResultSetHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.io.VFS;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl;
import org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl;
import org.apache.ibatis.logging.log4j.Log4jImpl;
import org.apache.ibatis.logging.log4j2.Log4j2Impl;
import org.apache.ibatis.logging.nologging.NoLoggingImpl;
import org.apache.ibatis.logging.slf4j.Slf4jImpl;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMap;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.InterceptorChain;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.LanguageDriverRegistry;
import org.apache.ibatis.scripting.defaults.RawLanguageDriver;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeAliasRegistry;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
/**
* @author Clinton Begin
*/
public class Configuration {
protected Environment environment;
/* 是否启用行内嵌套语句**/
protected boolean safeRowBoundsEnabled;
protected boolean safeResultHandlerEnabled = true;
/* 是否启用数据组A_column自动映射到Java类中的驼峰命名的属性**/
protected boolean mapUnderscoreToCamelCase;
/*当对象使用延迟加载时 属性的加载取决于能被引用到的那些延迟属性,否则,按需加载(需要的是时候才去加载)**/
protected boolean aggressiveLazyLoading;
/*是否允许单条sql 返回多个数据集 (取决于驱动的兼容性) default:true **/
protected boolean multipleResultSetsEnabled = true;
/*-允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。 default:false**/
protected boolean useGeneratedKeys;
/* 使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。**/
protected boolean useColumnLabel = true;
/*配置全局性的cache开关,默认为true**/
protected boolean cacheEnabled = true;
protected boolean callSettersOnNulls;
protected boolean useActualParamName = true;
protected boolean returnInstanceForEmptyRow;
/* 日志打印所有的前缀 **/
protected String logPrefix;
/* 指定 MyBatis 所用日志的具体实现,未指定时将自动查找**/
protected Class extends Log> logImpl;
protected Class extends VFS> vfsImpl;
/* 设置本地缓存范围,session:就会有数据的共享,statement:语句范围,这样不会有数据的共享**/
protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
/* 设置但JDBC类型为空时,某些驱动程序 要指定值**/
protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
/* 设置触发延迟加载的方法**/
protected Set lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));
/* 设置驱动等待数据响应超时数**/
protected Integer defaultStatementTimeout;
/* 设置驱动返回结果数的大小**/
protected Integer defaultFetchSize;
/* 执行类型,有simple、resue及batch**/
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
/*指定 MyBatis 应如何自动映射列到字段或属性*/
protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
protected Properties variables = new Properties();
protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
/*MyBatis每次创建结果对象的新实例时,它都会使用对象工厂(ObjectFactory)去构建POJO*/
protected ObjectFactory objectFactory = new DefaultObjectFactory();
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
/*延迟加载的全局开关*/
protected boolean lazyLoadingEnabled = false;
/*指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具*/
protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL
protected String databaseId;
/**
* Configuration factory class.
* Used to create Configuration for loading deserialized unread properties.
*
* @see Issue 300 (google code)
*/
protected Class> configurationFactory;
/*插件集合*/
protected final InterceptorChain interceptorChain = new InterceptorChain();
/*TypeHandler注册中心*/
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
/*TypeAlias注册中心*/
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
//-------------------------------------------------------------
/*mapper接口的动态代理注册中心*/
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
/*mapper文件中增删改查操作的注册中心*/
protected final Map mappedStatements = new StrictMap<>("Mapped Statements collection");
/*mapper文件中配置cache节点的 二级缓存*/
protected final Map caches = new StrictMap<>("Caches collection");
/*mapper文件中配置的所有resultMap对象 key为命名空间+ID*/
protected final Map resultMaps = new StrictMap<>("Result Maps collection");
protected final Map parameterMaps = new StrictMap<>("Parameter Maps collection");
/*mapper文件中配置KeyGenerator的insert和update节点,key为命名空间+ID*/
protected final Map keyGenerators = new StrictMap<>("Key Generators collection");
/*加载到的所有*mapper.xml文件*/
protected final Set loadedResources = new HashSet<>();
/*mapper文件中配置的sql元素,key为命名空间+ID*/
protected final Map sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");
protected final Collection incompleteStatements = new LinkedList<>();
protected final Collection incompleteCacheRefs = new LinkedList<>();
protected final Collection incompleteResultMaps = new LinkedList<>();
protected final Collection incompleteMethods = new LinkedList<>();
/*
* A map holds cache-ref relationship. The key is the namespace that
* references a cache bound to another namespace and the value is the
* namespace which the actual cache is bound to.
*/
protected final Map cacheRefMap = new HashMap<>();
public Configuration(Environment environment) {
this();
this.environment = environment;
}
public Configuration() {
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
languageRegistry.register(RawLanguageDriver.class);
}
以下代码省略
}
这里,我先介绍介个比较重要的变量:
typeAliasRegistry : 存放我们自定义的接口的别名 mapperRegistry : 存放包装了我们业务接口的动态代理类 mappedStatements : 增、删、改、查语句的包装类,即注册中心 resultMaps : resultMap返回值类型 loadedResources : 存放我们加载到的所有*mapper.xml文件 sqlFragments : mapper文件中配置的sql元素,key为命名空间+ID objectFactory : MyBatis每次创建结果对象的新实例时,它都会使用对象工厂(ObjectFactory)去构建POJO lazyLoadingEnabled : 延迟加载的全局开关 proxyFactory : 指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具
先介绍这么多,剩下的用到的时候再介绍。下面再来看一张图形:
XMLConfigBuilder :负责解析mybatis配置的全局信息。包括数据源、数据库连接池、是否懒加载、类的别名信息等等
XMLMapperBuilder :负责解析具体的***Mapper.xml文件。比如里面的resultMap、sql、缓存等信息
XMLStatementBuilder :负责解析***Mapper.xml文件中的 增、删、改、查等信息
MapperBuilderAssistant :助手类,负责对 XMLMapperBuilder 和 XMLStatementBuilder中创建对象提供帮助。说白了就是上面2个负责解析、而MapperBuilderAssistant负责把解析的内容封装成对象。
而以上这些类解析的信息,全部会存放到Configuration类中。Configuration类是应用级的生命周期,只要mybatis不停,它就会一直纯在而且是全局唯一的,不存在多实例的情况。
1. 首先定义一个mybatis的全局文件
2. 定义一个接口和一个对应的xml文件
package com.enjoylearning.mybatis.mapper;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Param;
import com.enjoylearning.mybatis.entity.EmailSexBean;
import com.enjoylearning.mybatis.entity.TJobHistory;
import com.enjoylearning.mybatis.entity.TUser;
public interface TUserMapper {
TUser selectByPrimaryKey(Integer id);
List selectAll();
List selectTestResultMap();
List selectAllTest();
int deleteByPrimaryKey(Integer id);
int insert1(TUser record);
int insert2(TUser record);
int insertSelective(TUser record);
int updateByPrimaryKeySelective(TUser record);
int updateByPrimaryKey(TUser record);
List selectUserPosition1();
List selectUserPosition2();
List selectUserJobs1();
List selectUserJobs2();
List selectUserHealthReport();
List selectUserRole();
List selectByEmailAndSex1(Map param);
List selectByEmailAndSex2(@Param("email")String email,@Param("sex")Byte sex);
List selectByEmailAndSex3(EmailSexBean esb);
List selectBySymbol(@Param("tableName")String tableName,
@Param("inCol")String inCol,
@Param("orderStr")String orderStr,
@Param("userName")String userName);
List selectIfOper(@Param("email")String email,@Param("sex")Byte sex);
List selectIfandWhereOper(@Param("email")String email,@Param("sex")Byte sex);
List selectChooseOper(@Param("email")String email,@Param("sex")Byte sex);
int updateIfOper(TUser record);
int updateIfAndSetOper(TUser record);
int insertIfOper(TUser record);
List selectForeach4In(String[] names);
int insertForeach4Batch(List users);
}
id, userName, realName, sex, mobile, email, note,
position_id
update t_user
set
userName = #{userName,jdbcType=VARCHAR},
realName = #{realName,jdbcType=VARCHAR},
sex = #{sex,jdbcType=TINYINT},
mobile = #{mobile,jdbcType=VARCHAR},
email = #{email,jdbcType=VARCHAR},
note = #{note,jdbcType=VARCHAR}
where id = #{id,jdbcType=INTEGER}
update t_user
userName = #{userName,jdbcType=VARCHAR},
realName = #{realName,jdbcType=VARCHAR},
sex = #{sex,jdbcType=TINYINT},
mobile = #{mobile,jdbcType=VARCHAR},
email = #{email,jdbcType=VARCHAR},
note = #{note,jdbcType=VARCHAR},
where id = #{id,jdbcType=INTEGER}
insert into t_user (
id,
userName,
realName,
sex,
mobile,
email,
note
)
values(
#{id,jdbcType=INTEGER},
#{userName,jdbcType=VARCHAR},
#{realName,jdbcType=VARCHAR},
#{sex,jdbcType=TINYINT},
#{mobile,jdbcType=VARCHAR},
#{email,jdbcType=VARCHAR},
#{note,jdbcType=VARCHAR}
)
insert into t_user (userName, realName,
sex, mobile,email,note,
position_id)
values
(
#{user.userName,jdbcType=VARCHAR},
#{user.realName,jdbcType=VARCHAR},
#{user.sex,jdbcType=TINYINT},
#{user.mobile,jdbcType=VARCHAR},
#{user.email,jdbcType=VARCHAR},
#{user.note,jdbcType=VARCHAR},
#{user.position.id,jdbcType=INTEGER}
)
delete from t_user
where id = #{id,jdbcType=INTEGER}
insert into t_user (id, userName, realName,
sex, mobile,
email,
note, position_id)
values (#{id,jdbcType=INTEGER},
#{userName,jdbcType=VARCHAR},
#{realName,jdbcType=VARCHAR},
#{sex,jdbcType=TINYINT}, #{mobile,jdbcType=VARCHAR},
#{email,jdbcType=VARCHAR},
#{note,jdbcType=VARCHAR},
#{position.id,jdbcType=INTEGER})
select
LAST_INSERT_ID()
insert into t_user (id, userName, realName,
sex, mobile,
email,
note,
position_id)
values (#{id,jdbcType=INTEGER},
#{userName,jdbcType=VARCHAR},
#{realName,jdbcType=VARCHAR},
#{sex,jdbcType=TINYINT}, #{mobile,jdbcType=VARCHAR},
#{email,jdbcType=VARCHAR},
#{note,jdbcType=VARCHAR},
#{position.id,jdbcType=INTEGER})
insert into t_user
id,
userName,
realName,
sex,
mobile,
email,
note,
#{id,jdbcType=INTEGER},
#{userName,jdbcType=VARCHAR},
#{realName,jdbcType=VARCHAR},
#{sex,jdbcType=TINYINT},
#{mobile,jdbcType=VARCHAR},
#{email,jdbcType=VARCHAR},
#{note,jdbcType=VARCHAR},
update t_user
userName = #{userName,jdbcType=VARCHAR},
realName = #{realName,jdbcType=VARCHAR},
sex = #{sex,jdbcType=TINYINT},
mobile = #{mobile,jdbcType=VARCHAR},
email = #{email,jdbcType=VARCHAR},
note = #{note,jdbcType=VARCHAR},
position_id = #{position.id,jdbcType=INTEGER},
where id = #{id,jdbcType=INTEGER}
update t_user
set
userName = #{userName,jdbcType=VARCHAR},
realName =
#{realName,jdbcType=VARCHAR},
sex = #{sex,jdbcType=TINYINT},
mobile =
#{mobile,jdbcType=VARCHAR},
email = #{email,jdbcType=VARCHAR},
note =
#{note,jdbcType=VARCHAR},
position_id = #{position.id,jdbcType=INTEGER}
where id = #{id,jdbcType=INTEGER}
3、开启测试类
4. 其实,我们就是创建了一个 SqlSessionFactory 工厂类而已,它是负责生产SqlSession对象的。而SqlSession是连接数据库的核心类。但是,在创建SqlSessionFactory之前,我们还生成了一个XMLConfigBuilder对象,并且调用了parse方法进行mybatis整个配置信息的解析。
1. 进入parse方法中,我们发现它解析的开始标签为 "/configuration". 而这个正是我们mybatis全局信息的根节点
2. 进一步查看, 其实逻辑很简单。就是根据每个标签进行逐个解析然后封装到Configuration对象中。
3. 这里我们重点说一下 mapperElement方法,它就是单独解析我们自己写sql语句的xml文件的。也就是说,我们使用mybatis写的增删改查语句是放在特定的xml文件中的,而这个方法就是专门解析我们业务sql的。
此时,我们就引出了 XMLMapperBuilder 对象了。
configurationElement方法就是负责解析具体的业务xml的,就是解析咱们的sql信息的。configuration.addLoadedResource(resource) 就会解析完以后,把当前的xml放入loadedResources变量中
bindMapperForNamespace 就是把当前的接口信息绑定到Configuration对象的mapperRegistry变量中。
4. 全部解析完以后,生成一个SqlSessionFactory工厂对象,这个工厂持有Configuration对象。最终返回。
到现在为止,一个大的解析就结束了。但是,里面还有很多的细节值得返回研究。比如ResultMap是如何解析与封装的、 sql的增删改查是如何封装的、 好了缓存。太多重要的信息了。下一篇继续对这些比较重要的细节进行分析