Mybatis源碼學習:總體框架理解

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緩存

  1. 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語句的方法,提供了事務的提交和回滾等。

相關文章
相關標籤/搜索