MyIbatisMyIbatis 重要的类 Configuration 的创建
Posted 九师兄
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyIbatisMyIbatis 重要的类 Configuration 的创建相关的知识,希望对你有一定的参考价值。
1.概述
话说,我最近一直在研究 MyBatis ,研究 MyBatis ,必然逃不了研究 Configuration 对象,这个对象简直是太重要了,它是 MyBatis 起步的核心环境配置,下面我们来一起看一下 Configuration 类
2.Configuration 的创建
如果你喜欢一个妹子,你是不是闲得问清楚妹子住在哪?只加微信那就只能望梅止渴,主动出击才是硬道理。否则,就算你租了一辆玛莎拉蒂,你都不知道在哪装B。
想要了解 Configuration,得先问清楚它是如何创建的。
在这之前,我先告诉你一个 MyBatis 的入口类,那就是 SqlSessionFactoryBuilder, 为什么要介绍这个类哦?因为这个类可以创建 SqlSession,想要孩子?没有Builder 的功能怎么行?它的创建在这里
SqlSessionFactoryBuilder 在创建完成 XMLConfigBuilder 之后,会完成 Configuration 的创建工作,也就是说Configuration 对象的创建是在 XMLConfigBuilder 中完成的 ,如下图
看到这里,你是不是有点跃跃欲试想要按住 control 键点进去?如你所愿,看一下 new Configuration 到底生出个什么东西
这就是初始化 Configuration 完成的工作了,图中还有一个很关键的类就是 TypeAliasRegistry, 想要注册?你得先知道 “我” 是谁 。
TypeAliasRegistry 在Configuration 创建的时候就被初始化了
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
so? 看一下 new 都做了一些什么事情
public TypeAliasRegistry()
registerAlias("string", String.class);
registerAlias("byte", Byte.class);
registerAlias("long", Long.class);
registerAlias("short", Short.class);
registerAlias("int", Integer.class);
registerAlias("integer", Integer.class);
registerAlias("double", Double.class);
registerAlias("float", Float.class);
registerAlias("boolean", Boolean.class);
registerAlias("byte[]", Byte[].class);
registerAlias("long[]", Long[].class);
registerAlias("short[]", Short[].class);
registerAlias("int[]", Integer[].class);
registerAlias("integer[]", Integer[].class);
registerAlias("double[]", Double[].class);
registerAlias("float[]", Float[].class);
registerAlias("boolean[]", Boolean[].class);
registerAlias("_byte", byte.class);
registerAlias("_long", long.class);
registerAlias("_short", short.class);
registerAlias("_int", int.class);
registerAlias("_integer", int.class);
registerAlias("_double", double.class);
registerAlias("_float", float.class);
registerAlias("_boolean", boolean.class);
registerAlias("_byte[]", byte[].class);
registerAlias("_long[]", long[].class);
registerAlias("_short[]", short[].class);
registerAlias("_int[]", int[].class);
registerAlias("_integer[]", int[].class);
registerAlias("_double[]", double[].class);
registerAlias("_float[]", float[].class);
registerAlias("_boolean[]", boolean[].class);
registerAlias("date", Date.class);
registerAlias("decimal", BigDecimal.class);
registerAlias("bigdecimal", BigDecimal.class);
registerAlias("biginteger", BigInteger.class);
registerAlias("object", Object.class);
registerAlias("date[]", Date[].class);
registerAlias("decimal[]", BigDecimal[].class);
registerAlias("bigdecimal[]", BigDecimal[].class);
registerAlias("biginteger[]", BigInteger[].class);
registerAlias("object[]", Object[].class);
registerAlias("map", Map.class);
registerAlias("hashmap", HashMap.class);
registerAlias("list", List.class);
registerAlias("arraylist", ArrayList.class);
registerAlias("collection", Collection.class);
registerAlias("iterator", Iterator.class);
registerAlias("ResultSet", ResultSet.class);
好刺激啊,这么一大段代码,不过看起来还是比较清晰明了的,这不就是 MyBatis 常用类型么,并给它们都起了一个各自的别名存起来,用来解析的时候使用。
3.Configuration 的标签以及使用
说完了 Configuration 的创建,我们不直接切入初始化的主题,先来吃点甜点
还记得你是如何搭建一个 MyBatis 项目么?其中很关键的是不是有一个叫做 mybatis-config.xml的这么一个配置?
这个配置就是 <configuration>
标签存在的意义了。
我在最外侧写了一个 configuration 标签,然后 dtd 语言约束就给我提示这么多属性可以设置,它们都是属于 Configuration 内的标签,那么这些标签都是啥呢?别急,慢慢来,掌握好频率和节奏还有力度,别太猛,年轻人要沉稳。
我不想按着标签的顺序来了,请跟好我的节奏。
首先很重要的两个属性就是 properties 和 environments ,properties 就是外部属性配置,你可以这么配置它
<properties resource="config.properties" />
导入外部配置文件,config.properties 文件中是一系列关于数据库的配置,给你举个例子吧,看你着急的
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/kkb
jdbc.username=root
jdbc.password=123456
载入外部属性配置后,需要配置 environments 标签,它可以配置事务管理、数据源、读取配置文件等
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="$jdbc.driver"/>
<property name="url" value="$jdbc.url"/>
<property name="username" value="$jdbc.username"/>
<property name="password" value="$jdbc.password"/>
</dataSource>
</environment>
</environments>
明白否?
还有一个很关键的配置就是 mapper 标签,你可以把它理解为 ComponentScan ,ComponentScan 完成的是 Bean 定义的查找,而 mapper 完成的是 接口的查找,该接口要与对应的 XML 命名空间相匹配才可以。例如
<mappers>
<package name="com.mybatis.dao"/>
</mappers>
再继续深入,来看一下 <setting>
都需要哪些内容,你可以设置下面这些,下面这些配置有些多,你可以查看(http://www.mybatis.org/mybatis-3/zh/configuration.html#settings) 来具体查看这些配置。
<settings>
// 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。
<setting name="cacheEnabled" value="true"/>
// 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。
<setting name="lazyLoadingEnabled" value="true"/>
// 是否允许单一语句返回多结果集(需要驱动支持)。
<setting name="multipleResultSetsEnabled" value="true"/>
// 使用列标签代替列名。不同的驱动在这方面会有不同的表现,具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。
<setting name="useColumnLabel" value="true"/>
// 允许 JDBC 支持自动生成主键,需要驱动支持。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能支持但仍可正常工作(比如 Derby)。
<setting name="useGeneratedKeys" value="false"/>
// 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。
<setting name="autoMappingBehavior" value="PARTIAL"/>
// 指定发现自动映射目标未知列(或者未知属性类型)的行为。
// NONE: 不做任何反应
// WARNING: 输出提醒日志 ('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARN)
// FAILING: 映射失败 (抛出 SqlSessionException)
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
// 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。
<setting name="defaultExecutorType" value="SIMPLE"/>
// 设置超时时间,它决定驱动等待数据库响应的秒数。
<setting name="defaultStatementTimeout" value="25"/>
// 为驱动的结果集获取数量(fetchSize)设置一个提示值。此参数只可以在查询设置中被覆盖。
<setting name="defaultFetchSize" value="100"/>
// 允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false
<setting name="safeRowBoundsEnabled" value="false"/>
// 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。
<setting name="mapUnderscoreToCamelCase" value="false"/>
// MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据
<setting name="localCacheScope" value="SESSION"/>
// 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。
<setting name="jdbcTypeForNull" value="OTHER"/>
// 指定哪个对象的方法触发一次延迟加载。
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
你知道 Oracle 和 MySQL 都可以对表,字段设置别名吗?MyBatis 也可以设置别名,采用的是 typeAliases 属性,比如
<!-- 为每一个实体类设置一个具体别名 -->
<typeAliases>
<typeAlias type="com.kaikeba.beans.Dept" alias="Dept"/>
</typeAliases>
<!-- 为当前包下的每一个类设置一个默认别名 -->
<typeAliases>
<package name="com.mybatis.beans"/>
</typeAliases>
MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId
属性。 MyBatis 会加载不带 databaseId
属性和带有匹配当前数据库 databaseId
属性的所有语句。 如果同时找到带有 databaseId
和不带 databaseId
的相同语句,则后者会被舍弃。 为支持多厂商特性只要像下面这样在 mybatis-config.xml
文件中加入 databaseIdProvider
即可:
<databaseIdProvider type="DB_VENDOR" />
DB_VENDOR 对应的 databaseIdProvider 实现会将 databaseId 设置为 DatabaseMetaData#getDatabaseProductName()
返回的字符串。 由于通常情况下这些字符串都非常长
而且相同产品的不同版本
会返回不同的值,所以你可能想通过设置属性别名
来使其变短
,如下:
<databaseIdProvider type="DB_VENDOR">
<property name="SQL Server" value="sqlserver"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle" />
</databaseIdProvider>
MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory
)实例来完成。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。比如:
// ExampleObjectFactory.java
public class ExampleObjectFactory extends DefaultObjectFactory
public Object create(Class type)
return super.create(type);
public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructorArgs)
return super.create(type, constructorArgTypes, constructorArgs);
public void setProperties(Properties properties)
super.setProperties(properties);
public <T> boolean isCollection(Class<T> type)
return Collection.class.isAssignableFrom(type);
<!-- mybatis-config.xml -->
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
<property name="someProperty" value="100"/>
</objectFactory>
ObjectFactory 的作用就很像是 Spring 中的 FactoryBean ,如果不是很了解关于 FactoryBean 的讲解,请移步至
(https://mp.weixin.qq.com/s/aCFzCopCX1mK6Zg-dT_KgA) 进行了解
MyBatis 留给开发人员的后门是可以进行插件开发的,插件开发在何处体现呢?其实 MyBatis 四大组件都会有体现, MyBatis 的插件开发其实也是代理的一种应用,如图
Configuration.java
这是 Executor 插件开发的调用位置,那么 StatementHandler, ParameterHandler, ResultSetHandler 的调用和 Executor 基本一致,如图
过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。例如官网的这个例子
// ExamplePlugin.java
@Intercepts(@Signature(
type= Executor.class,
method = "update",
args = MappedStatement.class,Object.class))
public class ExamplePlugin implements Interceptor
private Properties properties = new Properties();
public Object intercept(Invocation invocation) throws Throwable
// implement pre processing if need
Object returnObject = invocation.proceed();
// implement post processing if need
return returnObject;
public void setProperties(Properties properties)
this.properties = properties;
只需要再把这个插件告诉 MyBatis, 这里有个插件拦截器,记得用奥
<!-- mybatis-config.xml -->
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
typeHandlers 也叫做类型转换器,主要用在参数转换的地方,哪里进行参数转换呢?其实有两点:
-
PreparedStatementHandler
在解析 SQL 参数,进行参数设置的时候,需要把 Java Type 转换为 JDBC 类型 -
ResultSetHandler
返回的结果集,需要把 JDBC 类型转换为 Java Type
可以编写自己的类型转换器,如下:
// ExampleTypeHandler.java
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ExampleTypeHandler extends BaseTypeHandler<String>
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException
ps.setString(i, parameter);
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException
return rs.getString(columnName);
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException
return rs.getString(columnIndex);
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException
return cs.getString(columnIndex);
也需要告诉 MyBatis ,这里面有个参数转换器,别忘了转换!
<!-- mybatis-config.xml -->
<typeHandlers>
<typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>
4. Configuration 标签的解析
现在有了上面的这些标签的定义,应该在哪解析呢?就好比合适的人在合适的岗位才能创造出最大的价值一样。
现在就需要续上 SqlSessionFactoryBuilder 的第三步了, Configuration 的解析工作
在 XMLConfigBuilder 中
这是不是就和上面的标签对应起来了?解析工作是在这里进行的,这也是一种好的编码习惯,一个方法只做一件事情,应该多多借鉴这种写法。
5.Configuration 子标签的源码分析
假如你能从上向下看到这里,就说明你对这篇文章产生了浓厚的兴趣,恭喜你,你的段位又升级了。我不打王者荣耀,我之前一直打魔兽solo,solo是很需要手速的,同时也需要考虑到各种因素:比如你是 ORC(兽族),你的 BM(剑圣) 开 W(疾风步) 抢怪的时间要掌握好,你骚扰 NE (暗夜精灵) 采木材的时间要掌握好,抢宝的时间要掌握好,比如你玩的是 Turtle Rock(龟岛),你单刷蓝胖的时间也要算好,等等等等。
你既要sky的中规中矩,你也要MOON的不羁,你还要fly100%的沉稳,你也需要TED的坚持。也就印证了一句话,小孩子才做选择,成年人都要!
所以你不仅仅要知其果,还要懂其因。
5.1 第一步:Properties 解析
第一个方法: propertiesElement(root.evalNode("properties"))
,点进去可以看到其源码,我这里已经做了注释,方便你去理解
// 其实一个个 <> 的标签就是 一个个的XNode节点
private void propertiesElement(XNode context) throws Exception
if (context != null)
// 首先判断要解析的属性是否有无子节点
Properties defaults = context.getChildrenAsProperties();
// 解析<properties resource=""/> 解析完成就变为配置文件的 SimpleName
String resource = context.getStringAttribute("resou以上是关于MyIbatisMyIbatis 重要的类 Configuration 的创建的主要内容,如果未能解决你的问题,请参考以下文章
游戏中的STR CON. INT. DEX. 是啥意思啊??好像是属性栏里的