本篇文章讲述的基于注解配置mybatis及如何解析mybatis的配置文件,下一章会讲解mybatis如何具体结合spring来生成对应的bean代理对象 @Configuration("dataSourceConfig")//定义bean配置 @MapperScan(basePackages = DataSourceConfig.DaoPackage, sqlSessionFactoryRef = "SqlSessionFactory")设置mybatis接口扫描包路径,设置引入的sessionFactory的bean public class DataSourceConfig {//定义mybatis数据源配置 // 针对对应的dao包路径来关联数据源,这样在多数据源时有用 public static final String DaoPackage = "com.biz.dao"; public static final String SqlMapLocation = "classpath*:/sqlmap/*.xml"; @Value("${mdc.db.url}") private String url; @Value("${mdc.db.username}") private String user; @Value("${mdc.db.password}") private String password; @Value("${mdc.db.driver}") private String driverClass; @Value("${mdc.db.initialSize}") private int initialSize; @Value("${mdc.db.minIdle}") private int minIdle; @Value("${mdc.db.maxActive}") private int maxActive; @Value("${mdc.db.maxWait}") private long maxWait; @Bean(name = "DataSource") public DataSource dataSource() {//数据源具体配置并生成bean(带连接池的数据源) DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(driverClass); dataSource.setUrl(url); dataSource.setUsername(user); dataSource.setPassword(password); dataSource.setInitialSize(initialSize); dataSource.setMinIdle(minIdle); dataSource.setMaxActive(maxActive); dataSource.setMaxWait(maxWait); return dataSource; } @Bean(name = "TransactionManager")//定义事务管理bean public DataSourceTransactionManager transactionManager(@Qualifier("DataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource);//内部方法逻辑在下面解释 } public DataSourceTransactionManager(DataSource dataSource) { this();//调用默认无参构造方法 this.setDataSource(dataSource);//设置数据源 this.afterPropertiesSet();//判断数据源是否设置成功 }
public DataSourceTransactionManager() {//默认无参数构造器 this.enforceReadOnly = false;//强制只读置为false this.setNestedTransactionAllowed(true);//是否允许嵌套事务为true }
public void setDataSource(DataSource dataSource) {//设置数据源 if (dataSource instanceof TransactionAwareDataSourceProxy) {//判断是否为带代理的数据源(我们这里不是) this.dataSource = ((TransactionAwareDataSourceProxy)dataSource).getTargetDataSource();//如果是代理数据源则获取对应的代理对象 } else {//否则直接设置数据源 this.dataSource = dataSource; } }
public void afterPropertiesSet() {//判断数据源是否设置成功 if (this.getDataSource() == null) {//如果数据源为null则抛异常 throw new IllegalArgumentException("Property 'dataSource' is required"); } }
@Bean(name = "SqlSessionFactory") public SqlSessionFactory masterSqlSessionFactory(@Qualifier("mdcDataSource") DataSource dataSource) throws Exception {//创建sqlSessionFactory的bean final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();//实例化SqlSessionFactoryBean对象 sessionFactory.setDataSource(dataSource);//设置数据源,逻辑与事务管理设置数据源一致,这里就不说了 sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources(DataSourceConfig.SqlMapLocation)//这里是从mybatis的xml路径里加载指定的资源文件);//将解析后返回的Resources数组放在SqlSessionFactoryBean对象里,便于后面进行解析 return sessionFactory.getObject();//获取具体的SqlSessionFactory对象 }
public Resource[] getResources(String locationPattern) throws IOException {//根据指定路径读取资源文件并封装成Resource[] Assert.notNull(locationPattern, "Location pattern must not be null");//断言非空判断 if (locationPattern.startsWith("classpath*:")) {//如果路径配置在classpath下 return this.getPathMatcher().isPattern(locationPattern.substring("classpath*:".length())) ? this.findPathMatchingResources(locationPattern) : this.findAllClassPathResources(locationPattern.substring("classpath*:".length()));//匹配判断(如果有通配符(*或者?)则根据通配符进行查找,否则直接根据具体的路径去查找) } else {//如果不是classpath路径 int prefixEnd = locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 : locationPattern.indexOf(":") + 1;//获取要解析的起始下标 return this.getPathMatcher().isPattern(locationPattern.substring(prefixEnd)) ? this.findPathMatchingResources(locationPattern) : new Resource[]{this.getResourceLoader().getResource(locationPattern)};//根据路径去加载 } }
public boolean isPattern(String path) {//根据指定path来进行匹配 return path.indexOf(42) != -1 || path.indexOf(63) != -1;//如果不存在42的index和63的index返回false,否则true(在ASSCII码表中42代表*63代表?,这里调用的是String类里的indexOf方法,String底层是由char数组构成的) }
public SqlSessionFactory getObject() throws Exception {//获取SqlSessionFactory对象
if (this.sqlSessionFactory == null) {//如果是第一次创建
this.afterPropertiesSet(); //调用创建方法
}
return this.sqlSessionFactory;//返回SqlSessionFactory对象
}
public void afterPropertiesSet() throws Exception {//创建SqlSessionFactory Assert.notNull(this.dataSource, "Property 'dataSource' is required");//判断数据源有没有配置,如果为null抛异常 Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");//判断sqlSessionFactoryBuilder有没有生成,如果为null抛异常(可以看出来,这里用到了建造者模式和工程模式) Assert.state(this.configuration == null && this.configLocation == null || this.configuration == null || this.configLocation == null, "Property 'configuration' and 'configLocation' can not specified with together");//这里判断相关对象是否为空,理论上是第一次创建sqlSessionFactory对象的话,这些对象暂时都是null,否则抛异常 this.sqlSessionFactory = this.buildSqlSessionFactory();调用建造者方法创建sqlSessionFactory }
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {//建造SqlSessionFactory方法 XMLConfigBuilder xmlConfigBuilder = null;//定义解析xml的对象 Configuration configuration; if (this.configuration != null) {//如果configuration非空(第一次创建时理论上都是null) configuration = this.configuration;//获取configuration对象 configuration是一个配置类,里面有以下属性: ( cacheEnabled: 全局开启或关闭配置文件中所有 mapper 已经配置的缓存。默认值:true lazyLoadingEnabled: 全局开启或关闭延迟加载。开启时,所有关联对象都会延迟加载。特定关联关系中可以通过设置 fetchType 属性来取覆盖该项的开关状态。默认值:false aggressiveLazyLoading: 开启或关闭属性的延迟加载。开启时,任何方法调用都将加载该对象的所有延迟属性。关闭时,每个属性将按需加载(参考 lazyLoadTriggerMethods)。默认:false multipleResultSetsEnabled: 是否允许单个语句中返回多个结果集(需要兼容驱动)。默认:true useColumnLabe: 使用列别名替代列名。不同驱动在这方面表现不同。具体可参考驱动文档,或者通过测试判断这两种模式下不同驱动的表现。默认:true useGeneratedKeys: 描述:是否允许 JDBC 支持主键自动生成。需要兼容驱动。若设置为 true,则强制使用主键自动生成,尽管一些驱动不兼容也可以正常工作(如 Derby)。 默认:false autoMappingBehavior: 指定 Mybatis 是否自动映射列到字段或属性。默认值:PARTIAL NONE:不允许自动映射。 PARTIAL:只自动映射没有定义嵌套结果集映射的结果集。 FULL:自动映射任意复杂的结果集(无论是否嵌套) autoMappingUnknownColumnBehavior: 指定发现自动映射目标未知列(或未知属性类型)的行为。默认值:NONE NONE:什么也不做。 WARNING:输出警告日志( ‘org.apache.ibatis.session.AutoMappingUnknownColumnBehavior’ 的日志级别要设置为 WARN)。 FAILING:映射失败(抛出 SqlSessionException 异常)。 defaultExecutorType: 配置默认执行器。默认值:SIMPLE SIMPLE:普通的执行器。 REUSE:重复执行预处理语句(prepared statements)。 BATCH:重复执行语句并执行批量更新。 defaultStatementTimeout: 设置驱动等待数据库响应的超时秒数。默认值:Not Set (null) defaultFetchSize: 为驱动的结果集获取数量设置一个提示值。这个参数值可以被查询设置覆盖。默认值:Not Set (null) safeRowBoundsEnabled :允许在嵌套语句中使用分页 RowBound。如果允许,设置为 false。默认值:false safeResultHandlerEnabled: 允许在嵌套语句中使用 ResultHandler。如果允许,设置为 false;默认值:true mapUnderscoreToCamelCase: 开启驼峰规则命名的自动映射,即从经典数据库列名命名 A_COLUMN 到经典 Java 属性命名 aColumn 的自动映射。默认值:false localCacheScope: :Mybatis 使用本地缓存机制(local cache)来防止循环引用和加速重复嵌套查询。默认值 session SESSION:一个 session 期间的所有查询执行都将被缓存。 STATEMENT:本地 session 仅在语句执行时使用,且对同一个 session 的不同调用不会共享数据。 jdbcTypeForNull: 未对参数指定 JDBC 类型时,当参数为空值时指定 JDBC 类型。某些驱动要求指定列的 JDBC 类型,某些驱动用一般类型,比如 NULL、VARCHAR 或 OTOthHER 即可。默认值:OTOthHER lazyLoadTriggerMethods: 指定对象的哪些方法触发一次延迟加载。默认值:equals,clone,hashCode,toString defaultScriptingLanguage: 指定动态 SQL 生成的默认语言。默认值:org.apache.ibatis.scripting.xmltags.XMLLanguageDriver 可选值:A type alias or fully qualified class name.类型别名或完全限定类名 defaultEnumTypeHandler: 指定 Enum 默认使用的 TypeHandler(从3.4.5开始) 可选值:A type alias or fully qualified class name. //类型别名或完全限定类名 callSettersOnNulls: 指定当结果集中值为空时是否调用映射对象的 setter 方法或 map 的 put 方法。如果你依赖于 Map.keySet() 或者在 null 值初始化时,该设置有用。注意,基本类型比如 int 、boolean 等不能被设置为 null;默认:false returnInstanceForEmptyRow: 当返回行的所有列都是 null 时,Mybatis 默认返回 null 。当该设置被开启时,Mybatis 会返回一个空实例。注意这将同样适用于嵌套结果集(如 collection 和 association)。从 3.4.2 开始。默认:false logPrefix: 指定 Mybatis 添加到日志名称的前缀。默认值:Not Set logImpl: 指定 Mybatis 使用的日志工具。如果此项未设置,将会自动查找日志工具。默认值:Not Set 可选值:SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING proxyFactory: 指定 Mybatis 创建具有懒加载能力的对象的代理工具;默认值:JAVASSIST (MyBatis 3.3 or above) vfsImpl: 指定 VFS 的具体实现。默认值:Not Set 可选值:Fully qualified class names of custom VFS implementation separated by commas. //逗号分隔的自定义 VFS 具体实现的完全限定类名。 useActualParamName: 允许将方法签名中定义的真实名称作为语句参数名称。为了使用这一特性,你的项目必须采用 java 8 编译,且必须加上 -parameters 选项。(从 3.4.1 开始)默认值:true configurationFactory: 指定提供 Configuration 实例的类。返回的 Configuration 实例用来加载被反序列化对象的懒加载属性。这个类必须有一个 static Configuration getConfiguration() 签名方法。(从 3.2.3 开始)默认值:Not Set 可选值:A type alias or fully qualified class name. //类型别名或完全限定类名
protected Environment environment;
// 允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为false。默认为false
protected boolean safeRowBoundsEnabled;
// 允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为false。
protected boolean safeResultHandlerEnabled = true;
// 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。默认false
protected boolean mapUnderscoreToCamelCase;
// 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载。默认值false (true in ≤3.4.1)
protected boolean aggressiveLazyLoading;
// 是否允许单一语句返回多结果集(需要兼容驱动)。
protected boolean multipleResultSetsEnabled = true;
// 允许 JDBC 支持自动生成主键,需要驱动兼容。这就是insert时获取mysql自增主键/oracle sequence的开关。注:一般来说,这是希望的结果,应该默认值为true比较合适。
protected boolean useGeneratedKeys;
// 使用列标签代替列名,一般来说,这是希望的结果
protected boolean useColumnLabel = true;
// 是否启用缓存
protected boolean cacheEnabled = true;
// 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。
protected boolean callSettersOnNulls;
// 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的工程必须采用Java 8编译,并且加上-parameters选项。(从3.4.1开始)
protected boolean useActualParamName = true;
//当返回行的所有列都是空时,MyBatis默认返回null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集 (i.e. collectioin and association)。(从3.4.2开始) 注:这里应该拆分为两个参数比较合适, 一个用于结果集,一个用于单记录。通常来说,我们会希望结果集不是null,单记录仍然是null
protected boolean returnInstanceForEmptyRow;
// 指定 MyBatis 增加到日志名称的前缀。
protected String logPrefix;
// 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。一般建议指定为slf4j或log4j
protected Class <? extends Log> logImpl;
// 指定VFS的实现, VFS是mybatis提供的用于访问AS内资源的一个简便接口
protected Class <? extends VFS> vfsImpl;
// MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。
protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
// 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。
protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
// 指定对象的哪个方法触发一次延迟加载。
protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
// 设置超时时间,它决定驱动等待数据库响应的秒数。默认不超时
protected Integer defaultStatementTimeout;
// 为驱动的结果集设置默认获取数量。
protected Integer defaultFetchSize;
// SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
// 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。
protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
// 指定发现自动映射目标未知列(或者未知属性类型)的行为。这个值应该设置为WARNING比较合适
protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
// settings下的properties属性
protected Properties variables = new Properties();
// 默认的反射器工厂,用于操作属性、构造器方便
protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
// 对象工厂, 所有的类resultMap类都需要依赖于对象工厂来实例化
protected ObjectFactory objectFactory = new DefaultObjectFactory();
// 对象包装器工厂,主要用来在创建非原生对象,比如增加了某些监控或者特殊属性的代理类
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
// 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。
protected boolean lazyLoadingEnabled = false;
// 指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。MyBatis 3.3+使用JAVASSIST
protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL
// MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。
protected String databaseId;
/**
* Configuration factory class.
* Used to create Configuration for loading deserialized unread properties.
* 指定一个提供Configuration实例的类. 这个被返回的Configuration实例是用来加载被反序列化对象的懒加载属性值. 这个类必须包含一个签名方法static Configuration getConfiguration(). (从 3.2.3 版本开始)
*/
protected Class<?> configurationFactory;
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
// mybatis插件列表
protected final InterceptorChain interceptorChain = new InterceptorChain();
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
// 类型注册器, 用于在执行sql语句的出入参映射以及mybatis-config文件里的各种配置比如<transactionManager type="JDBC"/><dataSource type="POOLED">时使用简写, 后面会详细解释
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");
protected final Set<String> loadedResources = new HashSet<String>();
protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");
protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();
/*
* 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<String, String> cacheRefMap = new HashMap<String, String>()
) if (configuration.getVariables() == null) {//如果properties属性为为空 configuration.setVariables(this.configurationProperties);//用当前对象里的properties对象为其赋值 } else if (this.configurationProperties != null) {//如果不为空 configuration.getVariables().putAll(this.configurationProperties);//将当前对象里的properties对象全部存到properties对象里 } } else if (this.configLocation != null) {//如果当前对象的configLocation不为空 xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), (String)null, this.configurationProperties);//直接创建xmlConfigBuilder对象并解析configLocation对象 configuration = xmlConfigBuilder.getConfiguration();获取Configuration对象,该对象会在调用上面的方法里被创建 } else {//如果为空 if (LOGGER.isDebugEnabled()) {//如果日志级别 为debug则会输出打印日志,使用mybatis默认Configuration配置 LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration"); } configuration = new Configuration();//创建Configuration对象 if (this.configurationProperties != null) {//如果当前对象properties不为空 configuration.setVariables(this.configurationProperties);//设置properties } } if (this.objectFactory != null) {//如果当前对象的objectFactory对象不为空 configuration.setObjectFactory(this.objectFactory);//赋值 } if (this.objectWrapperFactory != null) {//如果当前对象的objectWrapperFactory对象不为空 configuration.setObjectWrapperFactory(this.objectWrapperFactory);//赋值 } if (this.vfs != null) {//如果当前对象的vfs对象不为空(VFS是mybatis提供的用于访问AS内资源的一个简便接口) configuration.setVfsImpl(this.vfs);//赋值 } String[] typeHandlersPackageArray;//定义一个类型处理包名的数组 String[] var4; int var5; int var6; String packageToScan;//扫描包路径 if (StringUtils.hasLength(this.typeAliasesPackage)) {//如果扫描包路径不为空 typeHandlersPackageArray = StringUtils.tokenizeToStringArray(this.typeAliasesPackage, ",; \t\n");//将多个包路径转成数组 var4 = typeHandlersPackageArray;//包路径数组对象 var5 = typeHandlersPackageArray.length;//数组长度 for(var6 = 0; var6 < var5; ++var6) {//遍历 packageToScan = var4[var6];//获取单个要扫描的自定义包路径 configuration.getTypeAliasRegistry().registerAliases(packageToScan, this.typeAliasesSuperType == null ? Object.class : this.typeAliasesSuperType);//循环添加别名注册扫描包信息 if (LOGGER.isDebugEnabled()) {//如果日志级别为debug则输入日志 LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases"); } } } int var27; if (!ObjectUtils.isEmpty(this.typeAliases)) {//如果当前对象里的typeAliases不为空 Class[] var25 = this.typeAliases; var27 = var25.length; for(var5 = 0; var5 < var27; ++var5) {//遍历进行注册类别别名信息 Class<?> typeAlias = var25[var5]; configuration.getTypeAliasRegistry().registerAlias(typeAlias); if (LOGGER.isDebugEnabled()) {//如果日志级别为debug则输入日志 LOGGER.debug("Registered type alias: '" + typeAlias + "'"); } } } if (!ObjectUtils.isEmpty(this.plugins)) {//插件对象不为空 即有添加自定义拦截器 Interceptor[] var26 = this.plugins; var27 = var26.length; for(var5 = 0; var5 < var27; ++var5) {//遍历 Interceptor plugin = var26[var5]; configuration.addInterceptor(plugin);//添加拦截器到configuration对象,便于后续调用(责任链模式) if (LOGGER.isDebugEnabled()) {//如果日志级别为debug则输入日志 LOGGER.debug("Registered plugin: '" + plugin + "'"); } } } if (StringUtils.hasLength(this.typeHandlersPackage)) {//如果当前对象里存在typeHandlersPackage typeHandlersPackageArray = StringUtils.tokenizeToStringArray(this.typeHandlersPackage, ",; \t\n");//转成数组 var4 = typeHandlersPackageArray;//包路径数组 var5 = typeHandlersPackageArray.length;//数组长度 for(var6 = 0; var6 < var5; ++var6) {//遍历注册 packageToScan = var4[var6]; configuration.getTypeHandlerRegistry().register(packageToScan); if (LOGGER.isDebugEnabled()) {//如果日志级别为debug则输出日志 LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers"); } } } if (!ObjectUtils.isEmpty(this.typeHandlers)) {//如果当前对象的typeHandlers(基于注解配置的别名类型)不为空 TypeHandler[] var28 = this.typeHandlers;//数组 var27 = var28.length;//数组长度 for(var5 = 0; var5 < var27; ++var5) {//遍历注册 TypeHandler<?> typeHandler = var28[var5]; configuration.getTypeHandlerRegistry().register(typeHandler); if (LOGGER.isDebugEnabled()) {//如果日志级别为debug则输出日志 LOGGER.debug("Registered type handler: '" + typeHandler + "'"); } } } if (this.databaseIdProvider != null) {//如果数据库提供方的id不为空 try { configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));//设置数据库id } catch (SQLException var24) {//异常则报错 throw new NestedIOException("Failed getting a databaseId", var24); } } if (this.cache != null) {//如果当前对象缓存设置不为空 configuration.addCache(this.cache);//设置缓存 } if (xmlConfigBuilder != null) {//xmlConfigBuilder对象不为空 try { xmlConfigBuilder.parse();//解析xml文件 if (LOGGER.isDebugEnabled()) {//如果日志级别为debug则输出日志 LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'"); } } catch (Exception var22) {//异常则报错 throw new NestedIOException("Failed to parse config resource: " + this.configLocation, var22); } finally { ErrorContext.instance().reset();//始终进行重置 } } if (this.transactionFactory == null) {//如果事务工厂为空 this.transactionFactory = new SpringManagedTransactionFactory();//默认使用Spring事务管理工厂 } configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));//设置配置环境 if (!ObjectUtils.isEmpty(this.mapperLocations)) {//如果mapperLocations对象不为空,其实本质上是resource数组不为空 Resource[] var29 = this.mapperLocations;//获取数组对象 var27 = var29.length;//数组长度 for(var5 = 0; var5 < var27; ++var5) {//遍历数组 Resource mapperLocation = var29[var5]; if (mapperLocation != null) {//如果不为空 try { XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), configuration, mapperLocation.toString(), configuration.getSqlFragments());//创建XMLMapperBuilder对象 xmlMapperBuilder.parse();//解析Resource对象 } catch (Exception var20) {//异常则报错 throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", var20); } finally { ErrorContext.instance().reset();//始终重置 } if (LOGGER.isDebugEnabled()) {//如果日志级别为debug则输出日志 LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'"); } } } } else if (LOGGER.isDebugEnabled()) {//如果日志级别为debug则输出日志 LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found"); } return this.sqlSessionFactoryBuilder.build(configuration);//调用具体的build方法 }
public SqlSessionFactory build(Configuration config) {//具体的build方法 return new DefaultSqlSessionFactory(config);//从这里可以看出返回的是DefaultSqlSessionFactory对象 }
public void parse() {//解析resource if (!this.configuration.isResourceLoaded(this.resource)) {//判断有没有被加载过,没有加载过则进行解析,第一次创建SqlSessionFactory默认是没有 this.configurationElement(this.parser.evalNode("/mapper"));//获取mapper节点内的所有节点并解析 this.configuration.addLoadedResource(this.resource);//将加载过的resource放在已加载过的set集合里,避免重复加载 this.bindMapperForNamespace();//根据nameSpace进行mapper类型绑定 } this.parsePendingResultMaps(); this.parsePendingCacheRefs(); this.parsePendingStatements(); }
private void configurationElement(XNode context) { try { String namespace = context.getStringAttribute("namespace");//获取mapper节点里的namespace元素 if (namespace != null && !namespace.equals("")) {//如果namespace元素不为null或者空字符串 this.builderAssistant.setCurrentNamespace(namespace);//设置当前namespace this.cacheRefElement(context.evalNode("cache-ref"));//设置cache-ref节点 this.cacheElement(context.evalNode("cache"));//设置cache节点 this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));//设置parameterMap节点 this.resultMapElements(context.evalNodes("/mapper/resultMap"));//设置resultMap节点 this.sqlElement(context.evalNodes("/mapper/sql"));//设置sql节点 this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));//设置select|insert|update|delete节点 } else {//为空则抛异常 throw new BuilderException("Mapper's namespace cannot be empty"); } } catch (Exception var3) {//解析失败抛异常 throw new BuilderException("Error parsing Mapper XML. Cause: " + var3, var3); } }
private void bindMapperForNamespace() {//类型绑定 String namespace = this.builderAssistant.getCurrentNamespace();//获取nameSpace,本质上是一个类路径 if (namespace != null) {//如果不为空 Class boundType = null; try { boundType = Resources.classForName(namespace);//利用反射根据类路径获取class } catch (ClassNotFoundException var4) {//有异常则忽略,从这里可以看出来就算mapper配置文件里的类路径写的不对也是不会报错的 } if (boundType != null && !this.configuration.hasMapper(boundType)) {//如果不为空并且当前类并没有被加载过 this.configuration.addLoadedResource("namespace:" + namespace);//放在set集合里,格式namespace:具体的namespace this.configuration.addMapper(boundType);//调用addMapper方法 } } }
public <T> void addMapper(Class<T> type) {//addMapper方法 this.mapperRegistry.addMapper(type);//调用最终的addMapper方法 }
public <T> void addMapper(Class<T> type) {//最终的addMapper方法 if (type.isInterface()) {//判断传入的class类型是不是一个接口 if (this.hasMapper(type)) {//判读是否已经加载过 throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { this.knownMappers.put(type, new MapperProxyFactory(type));//创建一个mapper的工厂代理类,并放在map里,key类型是class MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);//这里开启对查询注解的支持 parser.parse();//具体进行解析 loadCompleted = true;//加载完成 } finally { if (!loadCompleted) {//未加载完成 this.knownMappers.remove(type);//将当前class从map里移除 } } } }
}