陶邦仁 發(fā)布于 2015/12/22 11:06
系列目錄
- 深入淺出MyBatis系列
- 【深入淺出MyBatis系列一】MyBatis入門
- 【深入淺出MyBatis系列二】配置簡介(MyBatis源碼篇)
- 【深入淺出MyBatis系列三】Mapper映射文件配置
- 【深入淺出MyBatis系列四】強大的動態(tài)SQL
- 【深入淺出MyBatis系列五】SQL執(zhí)行流程分析(源碼篇)
- 【深入淺出MyBatis系列六】插件原理
- 【深入淺出MyBatis系列七】分頁插件
- 【深入淺出MyBatis系列八】SQL自動生成插件
- 【深入淺出MyBatis系列九】改造Cache插件
- 【深入淺出MyBatis系列十】與Spring集成
- 【深入淺出MyBatis系列十一】緩存源碼分析
- 【深入淺出MyBatis系列十二】終結(jié)篇:MyBatis原理深入解析
1 SqlSessionFactoryBuilder
上篇例子中,我們以 SqlSessionFactoryBuilder 去創(chuàng)建 SqlSessionFactory, 那么,我們就先從SqlSessionFactoryBuilder入手刷钢, 咱們先看看源碼是怎么實現(xiàn)的卤橄。
SqlSessionFactoryBuilder源碼片段:
public class SqlSessionFactoryBuilder {
//Reader讀取mybatis配置文件筑煮,傳入構(gòu)造方法
//除了Reader外辛蚊,其實還有對應(yīng)的inputStream作為參數(shù)的構(gòu)造方法,
//這也體現(xiàn)了mybatis配置的靈活性
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
public SqlSessionFactory build(Reader reader, String environment) {
return build(reader, environment, null);
}
//mybatis配置文件 + properties, 此時mybatis配置文件中可以不配置properties真仲,也能使用${}形式
public SqlSessionFactory build(Reader reader, Properties properties) {
return build(reader, null, properties);
}
//通過XMLConfigBuilder解析mybatis配置嚼隘,然后創(chuàng)建SqlSessionFactory對象
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
//下面看看這個方法的源碼
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
通過源碼,我們可以看到SqlSessionFactoryBuilder 通過XMLConfigBuilder 去解析我們傳入的mybatis的配置文件袒餐, 下面就接著看看 XMLConfigBuilder 部分源碼:
/**
* mybatis 配置文件解析
*/
public class XMLConfigBuilder extends BaseBuilder {
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
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 = environment;
this.parser = parser;
}
//外部調(diào)用此方法對mybatis配置文件進行解析
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//從根節(jié)點configuration
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
//此方法就是解析configuration節(jié)點下的子節(jié)點
//由此也可看出飞蛹,我們在configuration下面能配置的節(jié)點為以下10個節(jié)點
private void parseConfiguration(XNode root) {
try {
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);
}
}
}
通過以上源碼,我們就能看出灸眼,在mybatis的配置文件中:
- configuration節(jié)點為根節(jié)點卧檐。
- 在configuration節(jié)點之下,我們可以配置10個子節(jié)點焰宣, 分別為:properties霉囚、typeAliases、plugins匕积、objectFactory盈罐、objectWrapperFactory、settings闪唆、environments盅粪、databaseIdProvider、typeHandlers悄蕾、mappers票顾。
2 配置文件元素
2.1 元素1:properties
<configuration>
<!-- 方法一: 從外部指定properties配置文件, 除了使用resource屬性指定外,還可通過url屬性指定url
<properties resource="dbConfig.properties"></properties>
-->
<!-- 方法二: 直接配置為xml -->
<properties>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test1"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>
那么帆调,我要是 兩種方法都同時用了奠骄,那么哪種方法優(yōu)先?當(dāng)以上兩種方法都xml配置優(yōu)先番刊, 外部指定properties配置其次
含鳞。至于為什么,接下來的源碼分析會提到芹务,請留意一下蝉绷。
2.2 元素2:envirements
<environments default="development">
<environment id="development">
<!--
JDBC–這個配置直接簡單使用了JDBC的提交和回滾設(shè)置。它依賴于從數(shù)據(jù)源得到的連接來管理事務(wù)范圍锄禽。
MANAGED–這個配置幾乎沒做什么潜必。它從來不提交或回滾一個連接靴姿。而它會讓容器來管理事務(wù)的整個生命周期(比如Spring或JEE應(yīng)用服務(wù)器的上下文)沃但。
-->
<transactionManager type="JDBC"/>
<!--
UNPOOLED–這個數(shù)據(jù)源的實現(xiàn)是每次被請求時簡單打開和關(guān)閉連接
POOLED–mybatis實現(xiàn)的簡單的數(shù)據(jù)庫連接池類型,它使得數(shù)據(jù)庫連接可被復(fù)用佛吓,不必在每次請求時都去創(chuàng)建一個物理的連接宵晚。
JNDI – 通過jndi從tomcat之類的容器里獲取數(shù)據(jù)源垂攘。
-->
<dataSource type="POOLED">
<!--
如果上面沒有指定數(shù)據(jù)庫配置的properties文件,那么此處可以這樣直接配置
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test1"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
-->
<!-- 上面指定了數(shù)據(jù)庫配置文件淤刃, 配置文件里面也是對應(yīng)的這四個屬性 -->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
<!-- 我再指定一個environment -->
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!-- 與上面的url不一樣 -->
<property name="url" value="jdbc:mysql://localhost:3306/demo"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
environments元素節(jié)點可以配置多個environment子節(jié)點晒他, 怎么理解呢?
假如我們系統(tǒng)的開發(fā)環(huán)境和正式環(huán)境所用的數(shù)據(jù)庫不一樣(這是肯定的)逸贾, 那么可以設(shè)置兩個environment, 兩個id分別對應(yīng)開發(fā)環(huán)境(dev)和正式環(huán)境(final)陨仅,那么通過配置environments的default屬性就能選擇對應(yīng)的environment了
, 例如铝侵,我將environments的deault屬性的值配置為dev, 那么就會選擇dev的environment灼伤。 至于這個是怎么實現(xiàn)的,下面源碼就會講咪鲜。
2.3 解析方法:propertiesElement狐赡、environmentsElement
好啦,上面簡單給大家介紹了一下properties 和 environments 的配置疟丙, 接下來就正式開始看源碼了: 上次我們說過mybatis 是通過XMLConfigBuilder這個類在解析mybatis配置文件的颖侄,那么本次就接著看看XMLConfigBuilder對于properties和environments的解析:
public class XMLConfigBuilder extends BaseBuilder {
private boolean parsed;
// xml解析器
private XPathParser parser;
private String environment;
// 上次說到這個方法是在解析mybatis配置文件中能配置的元素節(jié)點
// 今天首先要看的就是properties節(jié)點和environments節(jié)點
private void parseConfiguration(XNode root) {
try {
// 解析properties元素
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"));
// 解析environments元素
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);
}
}
//下面就看看解析properties的具體方法
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
// 將子節(jié)點的 name 以及value屬性set進properties對象
// 這兒可以注意一下順序,xml配置優(yōu)先享郊, 外部指定properties配置其次
Properties defaults = context.getChildrenAsProperties();
// 獲取properties節(jié)點上 resource屬性的值
String resource = context.getStringAttribute("resource");
// 獲取properties節(jié)點上 url屬性的值, 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.");
}
// 把解析出的properties文件set進Properties對象
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
// 將configuration對象中已配置的Properties屬性與剛剛解析的融合
// configuration這個對象會裝載所解析mybatis配置文件的所有節(jié)點元素览祖,以后也會頻頻提到這個對象
// 既然configuration對象用有一系列的get/set方法, 那是否就標(biāo)志著我們可以使用java代碼直接配置炊琉?
// 答案是肯定的穴墅, 不過使用配置文件進行配置,優(yōu)勢不言而喻
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
// 把裝有解析配置propertis對象set進解析器温自, 因為后面可能會用到
parser.setVariables(defaults);
// set進configuration對象
configuration.setVariables(defaults);
}
}
//下面再看看解析enviroments元素節(jié)點的方法
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
//解析environments節(jié)點的default屬性的值
//例如: <environments default="development">
environment = context.getStringAttribute("default");
}
//遞歸解析environments子節(jié)點
for (XNode child : context.getChildren()) {
//<environment id="development">, 只有enviroment節(jié)點有id屬性玄货,那么這個屬性有何作用?
//environments 節(jié)點下可以擁有多個 environment子節(jié)點
//類似于這樣: <environments default="development"><environment id="development">...</environment><environment id="test">...</environments>
//意思就是我們可以對應(yīng)多個環(huán)境悼泌,比如開發(fā)環(huán)境松捉,測試環(huán)境等, 由environments的default屬性去選擇對應(yīng)的enviroment
String id = child.getStringAttribute("id");
//isSpecial就是根據(jù)由environments的default屬性去選擇對應(yīng)的enviroment
if (isSpecifiedEnvironment(id)) {
//事務(wù)馆里, mybatis有兩種:JDBC 和 MANAGED, 配置為JDBC則直接使用JDBC的事務(wù)隘世,配置為MANAGED則是將事務(wù)托管給容器,
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
//enviroment節(jié)點下面就是dataSource節(jié)點了鸠踪,解析dataSource節(jié)點(下面會貼出解析dataSource的具體方法)
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
//老規(guī)矩丙者,會將dataSource設(shè)置進configuration對象
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
//下面看看dataSource的解析方法
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
//dataSource的連接池
String type = context.getStringAttribute("type");
//子節(jié)點 name, value屬性set進一個properties對象
Properties props = context.getChildrenAsProperties();
//創(chuàng)建dataSourceFactory
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
}
通過以上對mybatis源碼的解讀,相信大家對mybatis的配置又有了一個深入的認識营密。還有一個問題械媒, 上面我們看到,在配置dataSource的時候使用了 ${driver} 這種表達式, 這種形式是怎么解析的纷捞?其實痢虹,是通過PropertyParser這個類解析:
/**
* 這個類解析${}這種形式的表達式
*/
public class PropertyParser {
public static String parse(String string, Properties variables) {
VariableTokenHandler handler = new VariableTokenHandler(variables);
GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
return parser.parse(string);
}
private static class VariableTokenHandler implements TokenHandler {
private Properties variables;
public VariableTokenHandler(Properties variables) {
this.variables = variables;
}
public String handleToken(String content) {
if (variables != null && variables.containsKey(content)) {
return variables.getProperty(content);
}
return "${" + content + "}";
}
}
}
以上就是對于properties 和 environments元素節(jié)點的分析,比較重要的都在對于源碼的注釋中標(biāo)出主儡。
2.4 元素3:typeAliases
typeAliases節(jié)點主要用來設(shè)置別名奖唯,其實這是挺好用的一個功能, 通過配置別名糜值,我們不用再指定完整的包名丰捷,并且還能取別名。
例如: 我們在使用 com.demo.entity. UserEntity 的時候寂汇,我們可以直接配置一個別名user, 這樣以后在配置文件中要使用到com.demo.entity.UserEntity的時候瓢阴,直接使用User即可。
就以上例為例健无,我們來實現(xiàn)一下荣恐,看看typeAliases的配置方法:
<configuration>
<typeAliases>
<!--
通過package, 可以直接指定package的名字, mybatis會自動掃描你指定包下面的javabean,
并且默認設(shè)置一個別名累贤,默認的名字為: javabean 的首字母小寫的非限定類名來作為它的別名叠穆。
也可在javabean 加上注解@Alias 來自定義別名, 例如: @Alias(user)
<package name="com.dy.entity"/>
-->
<typeAlias alias="UserEntity" type="com.dy.entity.User"/>
</typeAliases>
......
</configuration>
再寫一段測試代碼臼膏,看看有沒生效:(我只寫一段偽代碼)
Configuration con = sqlSessionFactory.getConfiguration();
Map<String, Class<?>> typeMap = con.getTypeAliasRegistry().getTypeAliases();
for(Entry<String, Class<?>> entry: typeMap.entrySet()) {
System.out.println(entry.getKey() + " ================> " + entry.getValue().getSimpleName());
}
2.5 解析方法:typeAliasesElement
/**
* 解析typeAliases節(jié)點
*/
private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//如果子節(jié)點是package, 那么就獲取package節(jié)點的name屬性硼被, mybatis會掃描指定的package
if ("package".equals(child.getName())) {
String typeAliasPackage = child.getStringAttribute("name");
//TypeAliasRegistry 負責(zé)管理別名, 這兒就是通過TypeAliasRegistry 進行別名注冊渗磅, 下面就會看看TypeAliasRegistry源碼
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
} else {
//如果子節(jié)點是typeAlias節(jié)點嚷硫,那么就獲取alias屬性和type的屬性值
String alias = child.getStringAttribute("alias");
String type = child.getStringAttribute("type");
try {
Class<?> clazz = Resources.classForName(type);
if (alias == null) {
typeAliasRegistry.registerAlias(clazz);
} else {
typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
重要的源碼在這兒:TypeAliasRegistry.java
public class TypeAliasRegistry {
//這就是核心所在啊, 原來別名就僅僅通過一個HashMap來實現(xiàn)始鱼, key為別名仔掸, value就是別名對應(yīng)的類型(class對象)
private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();
/**
* 以下就是mybatis默認為我們注冊的別名
*/
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);
}
/**
* 處理別名, 直接從保存有別名的hashMap中取出即可
*/
@SuppressWarnings("unchecked")
public <T> Class<T> resolveAlias(String string) {
try {
if (string == null) return null;
String key = string.toLowerCase(Locale.ENGLISH); // issue #748
Class<T> value;
if (TYPE_ALIASES.containsKey(key)) {
value = (Class<T>) TYPE_ALIASES.get(key);
} else {
value = (Class<T>) Resources.classForName(string);
}
return value;
} catch (ClassNotFoundException e) {
throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e);
}
}
/**
* 配置文件中配置為package的時候医清, 會調(diào)用此方法起暮,根據(jù)配置的報名去掃描javabean ,然后自動注冊別名
* 默認會使用 Bean 的首字母小寫的非限定類名來作為它的別名
* 也可在javabean 加上注解@Alias 來自定義別名会烙, 例如: @Alias(user)
*/
public void registerAliases(String packageName){
registerAliases(packageName, Object.class);
}
public void registerAliases(String packageName, Class<?> superType){
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
for(Class<?> type : typeSet){
// Ignore inner classes and interfaces (including package-info.java)
// Skip also inner classes. See issue #6
if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
registerAlias(type);
}
}
}
public void registerAlias(Class<?> type) {
String alias = type.getSimpleName();
Alias aliasAnnotation = type.getAnnotation(Alias.class);
if (aliasAnnotation != null) {
alias = aliasAnnotation.value();
}
registerAlias(alias, type);
}
//這就是注冊別名的本質(zhì)方法负懦, 其實就是向保存別名的hashMap新增值而已, 呵呵柏腻, 別名的實現(xiàn)太簡單了纸厉,對吧
public void registerAlias(String alias, Class<?> value) {
if (alias == null) throw new TypeException("The parameter alias cannot be null");
String key = alias.toLowerCase(Locale.ENGLISH); // issue #748
if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
}
TYPE_ALIASES.put(key, value);
}
public void registerAlias(String alias, String value) {
try {
registerAlias(alias, Resources.classForName(value));
} catch (ClassNotFoundException e) {
throw new TypeException("Error registering type alias "+alias+" for "+value+". Cause: " + e, e);
}
}
/**
* 獲取保存別名的HashMap, Configuration對象持有對TypeAliasRegistry的引用,因此五嫂,如果需要颗品,我們可以通過Configuration對象獲取
*/
public Map<String, Class<?>> getTypeAliases() {
return Collections.unmodifiableMap(TYPE_ALIASES);
}
}
由源碼可見,設(shè)置別名的原理就這么簡單,Mybatis默認給我們設(shè)置了不少別名抛猫,在上面代碼中都可以見到。
2.6 元素4:TypeHandler
Mybatis中的TypeHandler是什么孕荠?
無論是 MyBatis 在預(yù)處理語句(PreparedStatement)中設(shè)置一個參數(shù)時忧饭,還是從結(jié)果集中取出一個值時狼牺,都會用類型處理器將獲取的值以合適的方式轉(zhuǎn)換成 Java 類型
。Mybatis默認為我們實現(xiàn)了許多TypeHandler, 當(dāng)我們沒有配置指定TypeHandler時败匹,Mybatis會根據(jù)參數(shù)或者返回結(jié)果的不同,默認為我們選擇合適的TypeHandler處理
讥巡。
那么掀亩,Mybatis為我們實現(xiàn)了哪些TypeHandler呢? 我們怎么自定義實現(xiàn)一個TypeHandler ? 這些都會在接下來的mybatis的源碼中看到。在看源碼之前欢顷,還是像之前一樣槽棍,先看看怎么配置吧?
<configuration>
<typeHandlers>
<!--
當(dāng)配置package的時候抬驴,mybatis會去配置的package掃描TypeHandler
<package name="com.dy.demo"/>
-->
<!-- handler屬性直接配置我們要指定的TypeHandler -->
<typeHandler handler=""/>
<!-- javaType 配置java類型炼七,例如String, 如果配上javaType, 那么指定的typeHandler就只作用于指定的類型 -->
<typeHandler javaType="" handler=""/>
<!-- jdbcType 配置數(shù)據(jù)庫基本數(shù)據(jù)類型,例如varchar, 如果配上jdbcType, 那么指定的typeHandler就只作用于指定的類型 -->
<typeHandler jdbcType="" handler=""/>
<!-- 也可兩者都配置 -->
<typeHandler javaType="" jdbcType="" handler=""/>
</typeHandlers>
......
</configuration>
2.7 解析方法:typeHandlerElement
上面簡單介紹了一下TypeHandler, 下面就看看mybatis中TypeHandler的源碼了布持。老規(guī)矩豌拙,先從對xml的解析講起:
/**
* 解析typeHandlers節(jié)點
*/
private void typeHandlerElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//子節(jié)點為package時,獲取其name屬性的值题暖,然后自動掃描package下的自定義typeHandler
if ("package".equals(child.getName())) {
String typeHandlerPackage = child.getStringAttribute("name");
typeHandlerRegistry.register(typeHandlerPackage);
} else {
//子節(jié)點為typeHandler時按傅, 可以指定javaType屬性, 也可以指定jdbcType, 也可兩者都指定
//javaType 是指定java類型
//jdbcType 是指定jdbc類型(數(shù)據(jù)庫類型: 如varchar)
String javaTypeName = child.getStringAttribute("javaType");
String jdbcTypeName = child.getStringAttribute("jdbcType");
//handler就是我們配置的typeHandler
String handlerTypeName = child.getStringAttribute("handler");
//resolveClass方法就是我們上篇文章所講的TypeAliasRegistry里面處理別名的方法
Class<?> javaTypeClass = resolveClass(javaTypeName);
//JdbcType是一個枚舉類型胧卤,resolveJdbcType方法是在獲取枚舉類型的值
JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
Class<?> typeHandlerClass = resolveClass(handlerTypeName);
//注冊typeHandler, typeHandler通過TypeHandlerRegistry這個類管理
if (javaTypeClass != null) {
if (jdbcType == null) {
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
}
} else {
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}
接下來看看TypeHandler的管理注冊類:TypeHandlerRegistry.java
/**
* typeHandler注冊管理類
*/
public final class TypeHandlerRegistry {
//源碼一上來唯绍,二話不說,幾個大大的HashMap就出現(xiàn)枝誊,這不又跟上次講的typeAliases的注冊類似么
//基本數(shù)據(jù)類型與其包裝類
private static final Map<Class<?>, Class<?>> reversePrimitiveMap = new HashMap<Class<?>, Class<?>>() {
private static final long serialVersionUID = 1L;
{
put(Byte.class, byte.class);
put(Short.class, short.class);
put(Integer.class, int.class);
put(Long.class, long.class);
put(Float.class, float.class);
put(Double.class, double.class);
put(Boolean.class, boolean.class);
put(Character.class, char.class);
}
};
//這幾個MAP不用說就知道存的是什么東西吧推捐,命名的好處
private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap<Type, Map<JdbcType, TypeHandler<?>>>();
private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();
//就像上篇文章講的typeAliases一樣,mybatis也默認給我們注冊了不少的typeHandler
//具體如下
public TypeHandlerRegistry() {
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
register(Byte.class, new ByteTypeHandler());
register(byte.class, new ByteTypeHandler());
register(JdbcType.TINYINT, new ByteTypeHandler());
register(Short.class, new ShortTypeHandler());
register(short.class, new ShortTypeHandler());
register(JdbcType.SMALLINT, new ShortTypeHandler());
register(Integer.class, new IntegerTypeHandler());
register(int.class, new IntegerTypeHandler());
register(JdbcType.INTEGER, new IntegerTypeHandler());
register(Long.class, new LongTypeHandler());
register(long.class, new LongTypeHandler());
register(Float.class, new FloatTypeHandler());
register(float.class, new FloatTypeHandler());
register(JdbcType.FLOAT, new FloatTypeHandler());
register(Double.class, new DoubleTypeHandler());
register(double.class, new DoubleTypeHandler());
register(JdbcType.DOUBLE, new DoubleTypeHandler());
register(String.class, new StringTypeHandler());
register(String.class, JdbcType.CHAR, new StringTypeHandler());
register(String.class, JdbcType.CLOB, new ClobTypeHandler());
register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());
register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
register(JdbcType.CHAR, new StringTypeHandler());
register(JdbcType.VARCHAR, new StringTypeHandler());
register(JdbcType.CLOB, new ClobTypeHandler());
register(JdbcType.LONGVARCHAR, new ClobTypeHandler());
register(JdbcType.NVARCHAR, new NStringTypeHandler());
register(JdbcType.NCHAR, new NStringTypeHandler());
register(JdbcType.NCLOB, new NClobTypeHandler());
register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
register(JdbcType.ARRAY, new ArrayTypeHandler());
register(BigInteger.class, new BigIntegerTypeHandler());
register(JdbcType.BIGINT, new LongTypeHandler());
register(BigDecimal.class, new BigDecimalTypeHandler());
register(JdbcType.REAL, new BigDecimalTypeHandler());
register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
register(JdbcType.NUMERIC, new BigDecimalTypeHandler());
register(Byte[].class, new ByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
register(byte[].class, new ByteArrayTypeHandler());
register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.BLOB, new BlobTypeHandler());
register(Object.class, UNKNOWN_TYPE_HANDLER);
register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
register(Date.class, new DateTypeHandler());
register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
register(JdbcType.TIMESTAMP, new DateTypeHandler());
register(JdbcType.DATE, new DateOnlyTypeHandler());
register(JdbcType.TIME, new TimeOnlyTypeHandler());
register(java.sql.Date.class, new SqlDateTypeHandler());
register(java.sql.Time.class, new SqlTimeTypeHandler());
register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());
// issue #273
register(Character.class, new CharacterTypeHandler());
register(char.class, new CharacterTypeHandler());
}
public boolean hasTypeHandler(Class<?> javaType) {
return hasTypeHandler(javaType, null);
}
public boolean hasTypeHandler(TypeReference<?> javaTypeReference) {
return hasTypeHandler(javaTypeReference, null);
}
public boolean hasTypeHandler(Class<?> javaType, JdbcType jdbcType) {
return javaType != null && getTypeHandler((Type) javaType, jdbcType) != null;
}
public boolean hasTypeHandler(TypeReference<?> javaTypeReference, JdbcType jdbcType) {
return javaTypeReference != null && getTypeHandler(javaTypeReference, jdbcType) != null;
}
public TypeHandler<?> getMappingTypeHandler(Class<? extends TypeHandler<?>> handlerType) {
return ALL_TYPE_HANDLERS_MAP.get(handlerType);
}
public <T> TypeHandler<T> getTypeHandler(Class<T> type) {
return getTypeHandler((Type) type, null);
}
public <T> TypeHandler<T> getTypeHandler(TypeReference<T> javaTypeReference) {
return getTypeHandler(javaTypeReference, null);
}
public TypeHandler<?> getTypeHandler(JdbcType jdbcType) {
return JDBC_TYPE_HANDLER_MAP.get(jdbcType);
}
public <T> TypeHandler<T> getTypeHandler(Class<T> type, JdbcType jdbcType) {
return getTypeHandler((Type) type, jdbcType);
}
public <T> TypeHandler<T> getTypeHandler(TypeReference<T> javaTypeReference, JdbcType jdbcType) {
return getTypeHandler(javaTypeReference.getRawType(), jdbcType);
}
private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
TypeHandler<?> handler = null;
if (jdbcHandlerMap != null) {
handler = jdbcHandlerMap.get(jdbcType);
if (handler == null) {
handler = jdbcHandlerMap.get(null);
}
}
if (handler == null && type != null && type instanceof Class && Enum.class.isAssignableFrom((Class<?>) type)) {
handler = new EnumTypeHandler((Class<?>) type);
}
@SuppressWarnings("unchecked")
// type drives generics here
TypeHandler<T> returned = (TypeHandler<T>) handler;
return returned;
}
public TypeHandler<Object> getUnknownTypeHandler() {
return UNKNOWN_TYPE_HANDLER;
}
public void register(JdbcType jdbcType, TypeHandler<?> handler) {
JDBC_TYPE_HANDLER_MAP.put(jdbcType, handler);
}
//
// REGISTER INSTANCE
//
/**
* 只配置了typeHandler, 沒有配置jdbcType 或者javaType
*/
@SuppressWarnings("unchecked")
public <T> void register(TypeHandler<T> typeHandler) {
boolean mappedTypeFound = false;
//在自定義typeHandler的時候侧啼,可以加上注解MappedTypes 去指定關(guān)聯(lián)的javaType
//因此牛柒,此處需要掃描MappedTypes注解
MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);
if (mappedTypes != null) {
for (Class<?> handledType : mappedTypes.value()) {
register(handledType, typeHandler);
mappedTypeFound = true;
}
}
// @since 3.1.0 - try to auto-discover the mapped type
if (!mappedTypeFound && typeHandler instanceof TypeReference) {
try {
TypeReference<T> typeReference = (TypeReference<T>) typeHandler;
register(typeReference.getRawType(), typeHandler);
mappedTypeFound = true;
} catch (Throwable t) {
// maybe users define the TypeReference with a different type and are not assignable, so just ignore it
}
}
if (!mappedTypeFound) {
register((Class<T>) null, typeHandler);
}
}
/**
* 配置了typeHandlerhe和javaType
*/
public <T> void register(Class<T> javaType, TypeHandler<? extends T> typeHandler) {
register((Type) javaType, typeHandler);
}
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
//掃描注解MappedJdbcTypes
MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
if (mappedJdbcTypes != null) {
for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
register(javaType, handledJdbcType, typeHandler);
}
if (mappedJdbcTypes.includeNullJdbcType()) {
register(javaType, null, typeHandler);
}
} else {
register(javaType, null, typeHandler);
}
}
public <T> void register(TypeReference<T> javaTypeReference, TypeHandler<? extends T> handler) {
register(javaTypeReference.getRawType(), handler);
}
/**
* typeHandlerhe、javaType痊乾、jdbcType都配置了
*/
public <T> void register(Class<T> type, JdbcType jdbcType, TypeHandler<? extends T> handler) {
register((Type) type, jdbcType, handler);
}
/**
* 注冊typeHandler的核心方法
* 就是向Map新增數(shù)據(jù)而已
*/
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
if (javaType != null) {
Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
if (map == null) {
map = new HashMap<JdbcType, TypeHandler<?>>();
TYPE_HANDLER_MAP.put(javaType, map);
}
map.put(jdbcType, handler);
if (reversePrimitiveMap.containsKey(javaType)) {
register(reversePrimitiveMap.get(javaType), jdbcType, handler);
}
}
ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
}
//
// REGISTER CLASS
//
// Only handler type
public void register(Class<?> typeHandlerClass) {
boolean mappedTypeFound = false;
MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
if (mappedTypes != null) {
for (Class<?> javaTypeClass : mappedTypes.value()) {
register(javaTypeClass, typeHandlerClass);
mappedTypeFound = true;
}
}
if (!mappedTypeFound) {
register(getInstance(null, typeHandlerClass));
}
}
// java type + handler type
public void register(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass));
}
// java type + jdbc type + handler type
public void register(Class<?> javaTypeClass, JdbcType jdbcType, Class<?> typeHandlerClass) {
register(javaTypeClass, jdbcType, getInstance(javaTypeClass, typeHandlerClass));
}
// Construct a handler (used also from Builders)
@SuppressWarnings("unchecked")
public <T> TypeHandler<T> getInstance(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
if (javaTypeClass != null) {
try {
Constructor<?> c = typeHandlerClass.getConstructor(Class.class);
return (TypeHandler<T>) c.newInstance(javaTypeClass);
} catch (NoSuchMethodException ignored) {
// ignored
} catch (Exception e) {
throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e);
}
}
try {
Constructor<?> c = typeHandlerClass.getConstructor();
return (TypeHandler<T>) c.newInstance();
} catch (Exception e) {
throw new TypeException("Unable to find a usable constructor for " + typeHandlerClass, e);
}
}
/**
* 根據(jù)指定的pacakge去掃描自定義的typeHander皮壁,然后注冊
*/
public void register(String packageName) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses();
for (Class<?> type : handlerSet) {
//Ignore inner classes and interfaces (including package-info.java) and abstract classes
if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
register(type);
}
}
}
// get information
/**
* 通過configuration對象可以獲取已注冊的所有typeHandler
*/
public Collection<TypeHandler<?>> getTypeHandlers() {
return Collections.unmodifiableCollection(ALL_TYPE_HANDLERS_MAP.values());
}
}
由源碼可以看到, mybatis為我們實現(xiàn)了那么多TypeHandler, 隨便打開一個TypeHandler哪审,看其源碼蛾魄,都可以看到,它繼承自一個抽象類:BaseTypeHandler, 那么我們是不是也能通過繼承BaseTypeHandler滴须,從而實現(xiàn)自定義的TypeHandler
? 答案是肯定的舌狗, 那么現(xiàn)在下面就為大家演示一下自定義TypeHandler。
@MappedJdbcTypes(JdbcType.VARCHAR)
//此處如果不用注解指定jdbcType, 那么扔水,就可以在配置文件中通過"jdbcType"屬性指定痛侍, 同理, javaType 也可通過 @MappedTypes指定
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);
}
}
然后魔市,就該配置我們的自定義TypeHandler了:
<configuration>
<typeHandlers>
<!-- 由于自定義的TypeHandler在定義時已經(jīng)通過注解指定了jdbcType, 所以此處不用再配置jdbcType -->
<typeHandler handler="ExampleTypeHandler"/>
</typeHandlers>
......
</configuration>
也就是說主届,我們在自定義TypeHandler的時候,可以在TypeHandler通過@MappedJdbcTypes指定jdbcType, 通過 @MappedTypes 指定javaType, 如果沒有使用注解指定待德,那么我們就需要在配置文件中配置
君丁。詳細使用,請參見Mybatis類型轉(zhuǎn)換介紹将宪。
2.8 元素5:objectFactory
objectFactory是干什么的绘闷? 需要配置嗎?
MyBatis 每次創(chuàng)建結(jié)果對象的新實例時较坛,它都會使用一個對象工廠(ObjectFactory)實例來完成
簸喂。默認的對象工廠需要做的僅僅是實例化目標(biāo)類,要么通過默認構(gòu)造方法燎潮,要么在參數(shù)映射存在的時候通過參數(shù)構(gòu)造方法來實例化
喻鳄。默認情況下,我們不需要配置确封,mybatis會調(diào)用默認實現(xiàn)的objectFactory除呵。 除非我們要自定義ObjectFactory的實現(xiàn), 那么我們才需要去手動配置爪喘。
那么怎么自定義實現(xiàn)ObjectFactory颜曾? 怎么配置呢?自定義ObjectFactory只需要去繼承DefaultObjectFactory(是ObjectFactory接口的實現(xiàn)類)秉剑,并重寫其方法即可
泛豪。具體的,本處不多說侦鹏,后面再具體講解诡曙。
寫好了ObjectFactory, 僅需做如下配置:
<configuration>
......
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
<property name="someProperty" value="100"/>
</objectFactory>
......
</configuration>
2.9 元素6:plugins
plugin有何作用? 需要配置嗎略水?
plugins 是一個可選配置价卤。mybatis中的plugin其實就是個interceptor
, 它可以攔截Executor 渊涝、ParameterHandler 慎璧、ResultSetHandler 床嫌、StatementHandler 的部分方法,處理我們自己的邏輯胸私。Executor就是真正執(zhí)行sql語句的東西
厌处, ParameterHandler 是處理我們傳入?yún)?shù)的
,還記得前面講TypeHandler的時候提到過岁疼,mybatis默認幫我們實現(xiàn)了不少的typeHandler, 當(dāng)我們不顯示配置typeHandler的時候阔涉,mybatis會根據(jù)參數(shù)類型自動選擇合適的typeHandler執(zhí)行,其實就是ParameterHandler 在選擇
五续。ResultSetHandler 就是處理返回結(jié)果的
洒敏。
怎么自定義plugin ? 怎么配置龄恋?要自定義一個plugin, 需要去實現(xiàn)Interceptor接口
疙驾,這兒不細說,后面實戰(zhàn)部分會詳細講解郭毕。定義好之后它碎,配置如下:
<configuration>
......
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
......
</configuration>
2.10 元素7:mappers
mappers, 這下引出mybatis的核心之一了,mappers作用 ? 需要配置嗎显押?
mappers 節(jié)點下扳肛,配置我們的mapper映射文件, 所謂的mapper映射文件乘碑,就是讓mybatis 用來建立數(shù)據(jù)表和javabean映射的一個橋梁
挖息。在我們實際開發(fā)中,通常一個mapper文件對應(yīng)一個dao接口兽肤, 這個mapper可以看做是dao的實現(xiàn)
套腹。所以,mappers必須配置。
<configuration>
......
<mappers>
<!-- 第一種方式:通過resource指定 -->
<mapper resource="com/dy/dao/userDao.xml"/>
<!-- 第二種方式资铡, 通過class指定接口电禀,進而將接口與對應(yīng)的xml文件形成映射關(guān)系
不過,使用這種方式必須保證 接口與mapper文件同名(不區(qū)分大小寫)笤休,
我這兒接口是UserDao,那么意味著mapper文件為UserDao.xml
<mapper class="com.dy.dao.UserDao"/>
-->
<!-- 第三種方式尖飞,直接指定包,自動掃描店雅,與方法二同理
<package name="com.dy.dao"/>
-->
<!-- 第四種方式:通過url指定mapper文件位置
<mapper url="file://........"/>
-->
</mappers>
......
</configuration>
本篇僅作簡單介紹政基,更高級的使用以及其實現(xiàn)原理,會在后面的實戰(zhàn)部分進行詳細講解闹啦。
2.11 解析方法:objectFactoryElement腋么、pluginElement、mapperElement
以上幾個節(jié)點的解析源碼亥揖,與之前提到的那些節(jié)點的解析類似珊擂,故此處不再講圣勒。 我將源碼折疊, 需要的可以打開看看摧扇。
/**
* objectFactory 節(jié)點解析
*/
private void objectFactoryElement(XNode context) throws Exception {
if (context != null) {
//讀取type屬性的值圣贸, 接下來進行實例化ObjectFactory, 并set進 configuration
//到此,簡單講一下configuration這個對象扛稽,其實它里面主要保存的都是mybatis的配置
String type = context.getStringAttribute("type");
//讀取propertie的值吁峻, 根據(jù)需要可以配置, mybatis默認實現(xiàn)的objectFactory沒有使用properties
Properties properties = context.getChildrenAsProperties();
ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
factory.setProperties(properties);
configuration.setObjectFactory(factory);
}
}
/**
* plugins 節(jié)點解析
*/
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
//由此可見在张,我們在定義一個interceptor的時候用含,需要去實現(xiàn)Interceptor, 這兒先不具體講,以后會詳細講解
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
configuration.addInterceptor(interceptorInstance);
}
}
}
/**
* mappers 節(jié)點解析
* 這是mybatis的核心之一帮匾,這兒先簡單介紹啄骇,在接下來的文章會對它進行分析
*/
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
//如果mappers節(jié)點的子節(jié)點是package, 那么就掃描package下的文件, 注入進configuration
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
//resource, url, class 三選一
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
//mapper映射文件都是通過XMLMapperBuilder解析
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
2.12 元素8:settings
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="enhancementEnabled" value="false"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25000"/>
</settings>
setting節(jié)點里配置的值會直接改寫Configuration對應(yīng)的變量值,這些變量描述的是Mybatis的全局運行方式
瘟斜,如果對這些屬性的含義不熟悉的話建議不要配置缸夹,使用默認值即可。下面這個表格描述了各個配置項的含義和默認值:
2.13 解析方法:settingsElement
private void settingsElement(XNode context) throws Exception {
if (context != null) {
Properties props = context.getChildrenAsProperties();
// Check that all settings are known to the configuration class
MetaClass metaConfig = MetaClass.forClass(Configuration.class);
for (Object key : props.keySet()) {
if (!metaConfig.hasSetter(String.valueOf(key))) {
throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true));
configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
configuration.setLogPrefix(props.getProperty("logPrefix"));
configuration.setLogImpl(resolveClass(props.getProperty("logImpl")));
configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
}
}