1、對源碼先上一個結構圖:java
源代碼主要在org.apache.ibatis目錄下,18個包,其中在應用中主要的包有:builder、session、cache、type、transaction、datasource、jdbc、mapping,提供支撐服務的包有annotation、binding、io、logging、plugin、reflection、scripting、exception、executor、parsingweb
2、從使用入手apache
MyBatis使用的三板斧是SqlSessionFactoryBuilder和SqlSessionFactory、SqlSession緩存
SqlSessionFactoryBuildersession
支持9種構造方法,其實最主要的是包含Configuration對象的構造方法,目的是爲了經過加載配置文件創造SqlSessionFactory對象,真實最終返回的是DefaultSqlSessionFactory對象app
全部的構造方法最終都是調用build(Configuratiron)方法,這就要來研究一下Configuration對象,其實他就是對xml配置文件的對象映射,關於xml文件結構組成可從源碼中看出以下:框架
本文介紹一下Configuration的大框架,後續開個專輯專門研究Configuration的細節ide
?fetch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
propertiesElement(root.evalNode(
"properties"
));
//加載資源文件屬性和當前文件屬性
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"
));
|
環境元素(數據源和事務)、 屬性、類型別名、typeHandler、mapper、setting、插件ui
屬性:
先來看屬性的加載,屬性的加載最重要的是瞭解三種屬性來源(屬性配置文件、當前文件的屬性、java代碼輸入)和三種屬性的加載順序(先加載配置文件,在加載config文件的屬性,最後加載java代碼輸入)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
private
void
propertiesElement(XNode context)
throws
Exception {
if
(context !=
null
) {
Properties defaults = context.getChildrenAsProperties();
String resource = context.getStringAttribute(
"resource"
);
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."
);
}
if
(resource !=
null
) {
defaults.putAll(Resources.getResourceAsProperties(resource));
}
else
if
(url !=
null
) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
Properties vars = configuration.getVariables();
if
(vars !=
null
) {
defaults.putAll(vars);
}
parser.setVariables(defaults);
configuration.setVariables(defaults);
}
}
|
類型別名
主要是類的完整路徑和簡單別名的對應關係加載,保存在容器typeAliasRegistry中,最終映射到configuratiron對象中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
private
void
typeAliasesElement(XNode parent) {
if
(parent !=
null
) {
for
(XNode child : parent.getChildren()) {
if
(
"package"
.equals(child.getName())) {
String typeAliasPackage = child.getStringAttribute(
"name"
);
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
}
else
{
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);
}
}
}
}
}
|
typeHandler
用戶自定義類型處理器,保存在typeHandlerRegistry容器中,最終保存在configuration對象中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
private
void
typeHandlerElement(XNode parent)
throws
Exception {
if
(parent !=
null
) {
for
(XNode child : parent.getChildren()) {
if
(
"package"
.equals(child.getName())) {
String typeHandlerPackage = child.getStringAttribute(
"name"
);
typeHandlerRegistry.register(typeHandlerPackage);
}
else
{
String javaTypeName = child.getStringAttribute(
"javaType"
);
String jdbcTypeName = child.getStringAttribute(
"jdbcType"
);
String handlerTypeName = child.getStringAttribute(
"handler"
);
Class<?> javaTypeClass = resolveClass(javaTypeName);
JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
Class<?> typeHandlerClass = resolveClass(handlerTypeName);
if
(javaTypeClass !=
null
) {
if
(jdbcType ==
null
) {
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
}
else
{
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
}
}
else
{
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}
|
mapper
主要是加載mapper.xml文件,同構mapperParser對文件進行解析,保存在容器mapperRegistry中,最終保存到configuration對象中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
private
void
mapperElement(XNode parent)
throws
Exception {
if
(parent !=
null
) {
for
(XNode child : parent.getChildren()) {
if
(
"package"
.equals(child.getName())) {
String mapperPackage = child.getStringAttribute(
"name"
);
configuration.addMappers(mapperPackage);
}
else
{
String resource = child.getStringAttribute(
"resource"
);
String url = child.getStringAttribute(
"url"
);
String mapperClass = child.getStringAttribute(
"class"
);
if
(resource !=
null
&& url ==
null
&& mapperClass ==
null
) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
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."
);
}
}
}
}
}
|
環境元素
主要加載數據源、和事務配置信息,由Environment.Builder對象進行處理,並將信息保存到configuratiron對象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
private
void
environmentsElement(XNode context)
throws
Exception {
if
(context !=
null
) {
if
(environment ==
null
) {
environment = context.getStringAttribute(
"default"
);
}
for
(XNode child : context.getChildren()) {
String id = child.getStringAttribute(
"id"
);
if
(isSpecifiedEnvironment(id)) {
TransactionFactory txFactory = transactionManagerElement(child.evalNode(
"transactionManager"
));
DataSourceFactory dsFactory = dataSourceElement(child.evalNode(
"dataSource"
));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder =
new
Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
|
setting
加載ibatis自身工做須要配置的全部設置信息,爲configuration對象進行賦值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
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"
)));
configuration.setInjectionFilterEnabled(booleanValueOf(props.getProperty(
"injectionFilterEnabled"
),
false
));
configuration.setInjectionFilter(parseExpression(props.getProperty(
"injectionFilter"
),
"^[a-zA-Z0-9._]*$"
));
}
}
|
二、SqlSessionFactory,真實幹活的DefaultSqlSessionFactory
SqlSessionFactory的做用是承上啓下,做爲SqlSession的工廠,主要是工做必須是提供獲取SqlSession的方法,同時還提供了一個獲取Configuration的方法
其中SqlSessionFactory提供了8種獲取SqlSession的方法,主要涉及4個參數:是否自動提交、事務級別、ExecutorType(Statement類型【普通、預處理、批處理】)、自定義Connection
其中須要注意的是openSession()方法默認不是自動commit的。
1
2
3
4
5
6
7
8
9
|
SqlSession openSession();
SqlSession openSession(
boolean
autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType,
boolean
autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
Configuration getConfiguration();
|
揭開如何提供SqlSession的祕密(在這裏發現了Configuration還能夠提供創造Executor的工做,Connection被封裝在Transaction 中,Configuration作了兩件事情,一個是配置信息存儲,還有內部對象的組裝工做,須要深刻研究這種模式是否能夠經過另外一個Action對象進行管理呢?)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
private
SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level,
boolean
autoCommit) {
Transaction tx =
null
;
try
{
final
Environment environment = configuration.getEnvironment();
final
TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final
Executor executor = configuration.newExecutor(tx, execType);
return
new
DefaultSqlSession(configuration, executor, autoCommit);
}
catch
(Exception e) {
closeTransaction(tx);
// may have fetched a connection so lets call close()
throw
ExceptionFactory.wrapException(
"Error opening session. Cause: "
+ e, e);
}
finally
{
ErrorContext.instance().reset();
}
}
private
SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
try
{
boolean
autoCommit;
try
{
autoCommit = connection.getAutoCommit();
}
catch
(SQLException e) {
// Failover to true, as most poor drivers
// or databases won't support transactions
autoCommit =
true
;
}
final
Environment environment = configuration.getEnvironment();
final
TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
final
Transaction tx = transactionFactory.newTransaction(connection);
final
Executor executor = configuration.newExecutor(tx, execType);
return
new
DefaultSqlSession(configuration, executor, autoCommit);
}
catch
(Exception e) {
throw
ExceptionFactory.wrapException(
"Error opening session. Cause: "
+ e, e);
}
finally
{
ErrorContext.instance().reset();
}
}
|
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
這個須要重點關注。建立事務,將Connection傳遞給tx的過程。
三、SqlSession,真實幹活的是DefaultSqlSession
SqlSession類主要封裝了Configuration對象、Executor對象、是否自動提交
SqlSession是在程序中真實幹活的人,咱們在使用Mybatis中打交道最頻繁的就是SqlSessinon對象,上個結構圖,看看他都在幹啥
看到圖了,就沒有什麼祕密了,他就是在幹數據的平常操做的活,只不過是用他本身封裝的一套東西,好比說Configuration(封裝Connection【來源可使Pool】)、Executor(封裝Statement)、ResultHandler(封裝處理ResultSet對象)、RowBounds(封裝分頁對象),提供了CRUD,提供了緩存機制,提供了根據配置文件獲取Sql語句的方法,提供了事務的提交和回滾等。