Mybatis源碼淺析

Mybatis xml解析流程

Xml解析的常見方式:DOM SAX Xpath ,Mybatis使用的時Xpath宠漩,因其足夠簡單。

對應(yīng)代碼:

 String resource="mybatis-config.xml";
 InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

執(zhí)行流程:
1豆同、 執(zhí)行SqlSessionFactoryBuilder 中的build

//以下3個方法都是調(diào)用下面第8種方法
  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }

  public SqlSessionFactory build(InputStream inputStream, String environment) {
    return build(inputStream, environment, null);
  }

  public SqlSessionFactory build(InputStream inputStream, Properties properties) {
    return build(inputStream, null, properties);
  }

  //第8種方法和第4種方法差不多碗啄,Reader換成了InputStream
  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      //創(chuàng)建一個XMLConfigBuilder對象
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      //解析xml文件各個結(jié)點返回Configuration對象蚤认,傳入build對象返回DefaultSqlSessionFactory
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

  //最后一個build方法使用了一個Configuration作為參數(shù),并返回DefaultSqlSessionFactory
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

}

2、執(zhí)行XMLConfigBuilder的構(gòu)造方法

 public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    //構(gòu)建XPathParser對象  進行xml解析
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  }


  //上面6個構(gòu)造函數(shù)最后都合流到這個函數(shù)公般,傳入XPathParser
  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    //首先調(diào)用父類初始化Configuration
    super(new Configuration());
    //錯誤上下文設(shè)置成SQL Mapper Configuration(XML文件配置),以便后面出錯了報錯用吧
    ErrorContext.instance().resource("SQL Mapper Configuration");
    //將Properties全部設(shè)置到Configuration里面去
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

3、new Configuration() (屬性都為默認值)

設(shè)置configuration的屬性參數(shù)

設(shè)置XMLConfigBuilder的其他參數(shù)(document對象也在其中創(chuàng)建 即將xml 轉(zhuǎn)化為Document對象)

此時XMLConfigBuilder構(gòu)建完成

 public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    //調(diào)用createDocument() 構(gòu)建document 胡桨,底層使用SAX解析  將進來的xml文件  轉(zhuǎn)換為document
    this.document = createDocument(new InputSource(inputStream));
  }

4官帘、調(diào)用parser.parse()獲得Configuration 對象

//解析配置
  public Configuration parse() {  //只解析一次
    //如果已經(jīng)解析過了,報錯
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;  //未解析過  將標(biāo)志位置為true
//  <?xml version="1.0" encoding="UTF-8" ?>
//  <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
//  "http://mybatis.org/dtd/mybatis-3-config.dtd">
//  <configuration>
//  <environments default="development">
//  <environment id="development">
//  <transactionManager type="JDBC"/>
//  <dataSource type="POOLED">
//  <property name="driver" value="${driver}"/>
//  <property name="url" value="${url}"/>
//  <property name="username" value="${username}"/>
//  <property name="password" value="${password}"/>
//  </dataSource>
//  </environment>
//  </environments>
//  <mappers>
//  <mapper resource="org/mybatis/example/BlogMapper.xml"/>
//  </mappers>
//  </configuration>

    //根節(jié)點是configuration   parser Xpath解析器
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

  //解析配置
  private void parseConfiguration(XNode root) {
    try {
      //分步驟解析
      //issue #117 read properties first
      //1.properties
      propertiesElement(root.evalNode("properties"));
      //2.類型別名
      typeAliasesElement(root.evalNode("typeAliases"));
      //3.插件
      pluginElement(root.evalNode("plugins"));
      //4.對象工廠
      objectFactoryElement(root.evalNode("objectFactory"));
      //5.對象包裝工廠
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      //6.設(shè)置
      settingsElement(root.evalNode("settings"));
      // read it after objectFactory and objectWrapperFactory issue #631
      //7.環(huán)境
      environmentsElement(root.evalNode("environments"));
      //8.databaseIdProvider
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      //9.類型處理器
      typeHandlerElement(root.evalNode("typeHandlers"));
      //10.映射器
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

parser是XPathParser對象昧谊,使用parser解析xml的configuration結(jié)點刽虹,得到Xnode結(jié)點。返回Xnode結(jié)點呢诬,Xnod中包含了其他結(jié)點的信息

5涌哲、解析對應(yīng)結(jié)點下的各個結(jié)點內(nèi)容胖缤,放進Configuration

//1.properties
  //<properties resource="org/mybatis/example/config.properties">
  //    <property name="username" value="dev_user"/>
  //    <property name="password" value="F2Fa3!33TYyg"/>
  //</properties>
  private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
      //如果在這些地方,屬性多于一個的話,MyBatis 按照如下的順序加載它們:

      //1.在 properties 元素體內(nèi)指定的屬性首先被讀取。
      //2.從類路徑下資源或 properties 元素的 url 屬性中加載的屬性第二被讀取,它會覆蓋已經(jīng)存在的完全一樣的屬性阀圾。
      //3.作為方法參數(shù)傳遞的屬性最后被讀取, 它也會覆蓋任一已經(jīng)存在的完全一樣的屬性,這些屬性可能是從 properties 元素體內(nèi)和資源/url 屬性中加載的哪廓。
      //傳入方式是調(diào)用構(gòu)造函數(shù)時傳入,public XMLConfigBuilder(Reader reader, String environment, Properties props)

      //1.XNode.getChildrenAsProperties函數(shù)方便得到孩子所有Properties
      Properties defaults = context.getChildrenAsProperties();
      //2.然后查找resource或者url,加入前面的Properties
      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));
      }
      //3.Variables也全部加入Properties
      Properties vars = configuration.getVariables();
      if (vars != null) {
        defaults.putAll(vars);
      }
      parser.setVariables(defaults);
      configuration.setVariables(defaults);
    }
  }

mapperElement節(jié)點解析

//10.映射器
//  10.1使用類路徑
//  <mappers>
//    <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
//    <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
//    <mapper resource="org/mybatis/builder/PostMapper.xml"/>
//  </mappers>
//
//  10.2使用絕對url路徑
//  <mappers>
//    <mapper url="file:///var/mappers/AuthorMapper.xml"/>
//    <mapper url="file:///var/mappers/BlogMapper.xml"/>
//    <mapper url="file:///var/mappers/PostMapper.xml"/>
//  </mappers>
//
//  10.3使用java類名
//  <mappers>
//    <mapper class="org.mybatis.builder.AuthorMapper"/>
//    <mapper class="org.mybatis.builder.BlogMapper"/>
//    <mapper class="org.mybatis.builder.PostMapper"/>
//  </mappers>
//
//  10.4自動掃描包下所有映射器
//  <mappers>
//    <package name="org.mybatis.builder"/>
//  </mappers>
  private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          //10.4自動掃描包下所有映射器
          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) {
            //10.1使用類路徑
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //映射器比較復(fù)雜初烘,調(diào)用XMLMapperBuilder
            //注意在for循環(huán)里每個mapper都重新new一個XMLMapperBuilder涡真,來解析
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            //10.2使用絕對url路徑
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            //映射器比較復(fù)雜,調(diào)用XMLMapperBuilder
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            //10.3使用java類名
            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.");
          }
        }
      }
    }
  }

//Configuration類下
public void addMappers(String packageName) {
    mapperRegistry.addMappers(packageName);
  }

  //查找包下所有類   MapperRegistry類下
  public void addMappers(String packageName) {
    addMappers(packageName, Object.class);
  }

  //MapperRegistry類下
  public void addMappers(String packageName, Class<?> superType) {
    //查找包下所有是superType的類
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
    for (Class<?> mapperClass : mapperSet) {
      addMapper(mapperClass);    //將set里面的xxxMapper使用動態(tài)代理創(chuàng)建出來
    }
  }

  //看一下如何添加一個映射   
  public <T> void addMapper(Class<T> type) {
    //mapper必須是接口肾筐!才會添加
    if (type.isInterface()) {
      if (hasMapper(type)) {
        //如果重復(fù)添加了哆料,報錯
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        //創(chuàng)建mapper的動態(tài)代理,使用JDK的動態(tài)代理實現(xiàn)
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();  //解析成對應(yīng)的mapper.xml 文件
        loadCompleted = true;
      } finally {
        //如果加載過程中出現(xiàn)異常需要再將這個mapper從mybatis中刪除
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

6吗铐、調(diào)用build得到會話工廠DefaultSqlSessionFactory

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      //創(chuàng)建一個XMLConfigBuilder對象
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      //解析xml文件各個結(jié)點返回Configuration對象东亦,傳入build對象返回DefaultSqlSessionFactory
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

  //最后一個build方法使用了一個Configuration作為參數(shù),并返回DefaultSqlSessionFactory
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

流程圖:

源碼分析流程圖


SqlSession創(chuàng)建流程

SqlSessionFactoryBuilder

    使用**建造者模式** 

這個類可以被實例化、使用和丟棄抓歼,一旦創(chuàng)建了 SqlSessionFactory讥此,就不再需要它了。 因此 SqlSessionFactoryBuilder 實例的最佳作用域是方法作用域(也就是局部方法變量)谣妻。 你可以重用 SqlSessionFactoryBuilder 來創(chuàng)建多個 SqlSessionFactory 實例萄喳,但最好還是不要一直保留著它,以保證所有的 XML 解析資源可以被釋放給更重要的事情蹋半。

SqlSessionFactory

使用工廠模式他巨,單例模式

SqlSessionFactory 一旦被創(chuàng)建就應(yīng)該在應(yīng)用的運行期間一直存在,沒有任何理由丟棄它或重新創(chuàng)建另一個實例减江。 使用 SqlSessionFactory 的最佳實踐是在應(yīng)用運行期間不要重復(fù)創(chuàng)建多次染突,多次重建 SqlSessionFactory 被視為一種代碼“壞習(xí)慣”。因此 SqlSessionFactory 的最佳作用域是應(yīng)用作用域辈灼。 有很多方法可以做到份企,最簡單的就是使用單例模式或者靜態(tài)單例模式

SqlSession

每個線程都應(yīng)該有它自己的 SqlSession 實例巡莹。SqlSession 的實例不是線程安全的司志,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域降宅。 絕對不能將 SqlSession 實例的引用放在一個類的靜態(tài)域骂远,甚至一個類的實例變量也不行。 也絕不能將 SqlSession 實例的引用放在任何類型的托管作用域中腰根,比如 Servlet 框架中的 HttpSession激才。 如果你現(xiàn)在正在使用一種 Web 框架,考慮將 SqlSession 放在一個和 HTTP 請求相似的作用域中。 換句話說瘸恼,每次收到 HTTP 請求劣挫,就可以打開一個 SqlSession,返回一個響應(yīng)后钞脂,就關(guān)閉它揣云。


Mybatis核心對象

mybatis核心對象分為兩類:存儲類對象和 操作類對象
SqlSesson封裝了JDBC(Statement,Prepared Statement ,Callable Statement ,ResultSet),還包含了SqlSessionFactory冰啃,mybatis-config,.xml,Mapper.xml的信息

Mybatis的核心對象及其作用

mybatis核心對象

存儲類對象

概念:在java中(jvm)對Mybatis相關(guān)的配置信息進行封裝

Configrution

mybatis-config.xml ----------------->Configuration

  1. 封裝了mybatis-config.xml
  2. 封裝了mapper文件 MappedStatement
  3. 創(chuàng)建了Mybatis其他相關(guān)的對象
public class Configuration {

  //環(huán)境
  protected Environment environment;

  //---------以下都是<settings>節(jié)點-------
  protected boolean safeRowBoundsEnabled = false;
  protected boolean safeResultHandlerEnabled = true;
  protected boolean mapUnderscoreToCamelCase = false;   //數(shù)據(jù)庫表字段駝峰
  protected boolean aggressiveLazyLoading = true;
  protected boolean multipleResultSetsEnabled = true;
  protected boolean useGeneratedKeys = false;          //自增主鍵
  protected boolean useColumnLabel = true;
  //默認啟用緩存
  protected boolean cacheEnabled = true;
  protected boolean callSettersOnNulls = false;

  protected String logPrefix;
  protected Class <? extends Log> logImpl;
  //緩存作用域  本地緩存使用范圍為SESSION  對相同SqlSession的不同調(diào)用將不會共享數(shù)據(jù)
  protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
  protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
  protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
  protected Integer defaultStatementTimeout;
  //默認為簡單執(zhí)行器   實際上因為cacheEnabled = true 緩存開啟而使用了CachingExecutor
  protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
  protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
  //---------以上都是<settings>節(jié)點-------

  protected Properties variables = new Properties();
  //對象工廠和對象包裝器工廠
  protected ObjectFactory objectFactory = new DefaultObjectFactory();
  protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
  //映射注冊機
  protected MapperRegistry mapperRegistry = new MapperRegistry(this);

  //默認禁用延遲加載
  protected boolean lazyLoadingEnabled = false;
  protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL

  protected String databaseId;
  /**
   * Configuration factory class.
   * Used to create Configuration for loading deserialized unread properties.
   *
   * @see <a >Issue 300</a> (google code)
   */
  protected Class<?> configurationFactory;
  //攔截器鏈  在Executor創(chuàng)建的時候賦值在
  protected final InterceptorChain interceptorChain = new InterceptorChain();
  //類型處理器注冊機
  protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
  //類型別名注冊機
  protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
  protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

  //映射的語句,存在Map里   對應(yīng)這各個xxxDAOMapper.xml文件,將各個Mapper信息進行了匯總
  protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
  //緩存,存在Map里
  protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
  //結(jié)果映射,存在Map里    所有的ResultMap 全都放在resultMaps中
  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");

  //路徑  對應(yīng)xml中的mapper
  protected final Set<String> loadedResources = new HashSet<String>();
  protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");

  //不完整的SQL語句
  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>();

  public Configuration(Environment environment) {
    this();
    this.environment = environment;
  }

  public Configuration() {
    //注冊更多的類型別名刘莹,至于為何不直接在TypeAliasRegistry里注冊阎毅,還需進一步研究
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class); //事務(wù)處理器
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);  //這個數(shù)據(jù)源實現(xiàn)是為了能在如 EJB 或應(yīng)用服務(wù)器這類容器中使用,容器可以集中或在外部配置數(shù)據(jù)源点弯,然后放置一個 JNDI 上下文的數(shù)據(jù)源引用
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class); // 緩存策略
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);  //raw 圖像格式

    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);  //日志
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class); //動態(tài)代理
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);   //默認使用xml
    languageRegistry.register(RawLanguageDriver.class);
  }
//創(chuàng)建元對象
  public MetaObject newMetaObject(Object object) {
    return MetaObject.forObject(object, objectFactory, objectWrapperFactory);
  }

  //創(chuàng)建參數(shù)處理器
  public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    //創(chuàng)建ParameterHandler
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    //插件在這里插入
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
  }

  //創(chuàng)建結(jié)果集處理器
  public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
      ResultHandler resultHandler, BoundSql boundSql) {
    //創(chuàng)建DefaultResultSetHandler(稍老一點的版本3.1是創(chuàng)建NestedResultSetHandler或者FastResultSetHandler)
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    //插件在這里插入
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }

  //創(chuàng)建語句處理器
  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    //創(chuàng)建路由選擇語句處理器
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    //插件在這里插入
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }
public Executor newExecutor(Transaction transaction) {
    return newExecutor(transaction, defaultExecutorType);
  }

  //產(chǎn)生執(zhí)行器
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    //這句再做一下保護,囧,防止粗心大意的人將defaultExecutorType設(shè)成null?
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    //然后就是簡單的3個分支扇调,產(chǎn)生3種執(zhí)行器BatchExecutor/ReuseExecutor/SimpleExecutor
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    //如果要求緩存,生成另一種CachingExecutor(默認就是有緩存),裝飾者模式,所以默認都是返回CachingExecutor
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    //此處調(diào)用插件,通過插件可以改變Executor行為
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

   ************************** get()和set()方法省略**************
}

Configrution小結(jié):

  1. 設(shè)置配置文件中的對應(yīng)屬性

  2. 設(shè)置默認執(zhí)行器 (簡單執(zhí)行器)

    protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
    
  3. 創(chuàng)建攔截器鏈 InterceptorChain

  4. 創(chuàng)建類型處理器注冊機 TypeHandlerRegistry

  5. 創(chuàng)建類型別名注冊機 TypeAliasRegistry

  6. 將各個mapper文件中的信息進行匯總存放

    protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
    
  7. 為系統(tǒng)內(nèi)部使用的類定義別名注冊(在構(gòu)造函數(shù)中設(shè)置)

  8. 創(chuàng)建元對象

  9. 創(chuàng)建參數(shù)處理器 parameterHandler

  10. 創(chuàng)建結(jié)果集處理器resultSetHandler

  11. 創(chuàng)建語句處理器statementHandler

  12. 創(chuàng)建執(zhí)行器executor

MappedStatement

xxxDaoMapper.xml -----------------> MappedStatement

MappedStatement對象

對應(yīng)的就是Mapper文件中的一個一個的配置

<select id 和 < delete id 對應(yīng)不同的MappedStatement對象 Mapper文件中的語句都被打散了抢肛,不以整個mapper出現(xiàn)

 //映射的語句,存在Map里   對應(yīng)這各個xxxDAOMapper.xml文件狼钮,將各個Mapper信息進行了匯總
  protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
<select id="getRole" parameterType="long" resultMap="roleMap" >
        select
        id,role_name as roleName,note from role where id=#{id}
    </select>

mapper中的一條標(biāo)簽語句對應(yīng)著一個MappedStatement
MappedStatment 中使用BoundSql來封裝sql語句

public final class MappedStatement {

  //對應(yīng)的屬性
  private String resource;
  private Configuration configuration;
  private String id;    //sql的id namespace.id
  private Integer fetchSize;
  private Integer timeout;
  private StatementType statementType;
  private ResultSetType resultSetType;

  //SQL源碼
  private SqlSource sqlSource;   //解析出來的sql   對應(yīng)一條sql語句
  private Cache cache;
  private ParameterMap parameterMap;   //以廢棄
  private List<ResultMap> resultMaps;   //對應(yīng)的ResultMap
  private boolean flushCacheRequired;
  private boolean useCache;
  private boolean resultOrdered;
  private SqlCommandType sqlCommandType;
  private KeyGenerator keyGenerator;
  private String[] keyProperties;
  private String[] keyColumns;
  private boolean hasNestedResultMaps;
  private String databaseId;  //數(shù)據(jù)庫ID,用來區(qū)分不同環(huán)境
  private Log statementLog;
  private LanguageDriver lang;
  private String[] resultSets;

  MappedStatement() {
    // constructor disabled
  }

  //靜態(tài)內(nèi)部類捡絮,建造者模式
  public static class Builder {
    private MappedStatement mappedStatement = new MappedStatement();

    public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
      mappedStatement.configuration = configuration;
      mappedStatement.id = id;
      mappedStatement.sqlSource = sqlSource;
      mappedStatement.statementType = StatementType.PREPARED; //默認使用PREPARED
      mappedStatement.parameterMap = new ParameterMap.Builder(configuration, "defaultParameterMap", null, new ArrayList<ParameterMapping>()).build();
      mappedStatement.resultMaps = new ArrayList<ResultMap>();
      mappedStatement.timeout = configuration.getDefaultStatementTimeout();
      mappedStatement.sqlCommandType = sqlCommandType;   //select ||delete|| update ||insert || UNKNOWN
      mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
      String logId = id;
      if (configuration.getLogPrefix() != null) {
        logId = configuration.getLogPrefix() + id;
      }
      mappedStatement.statementLog = LogFactory.getLog(logId);
      mappedStatement.lang = configuration.getDefaultScriptingLanuageInstance();
    }

    public Builder resource(String resource) {
      mappedStatement.resource = resource;
      return this;
    }

    public String id() {
      return mappedStatement.id;
    }

    public Builder parameterMap(ParameterMap parameterMap) {
      mappedStatement.parameterMap = parameterMap;
      return this;
    }

    public Builder resultMaps(List<ResultMap> resultMaps) {
      mappedStatement.resultMaps = resultMaps;
      for (ResultMap resultMap : resultMaps) {
        mappedStatement.hasNestedResultMaps = mappedStatement.hasNestedResultMaps || resultMap.hasNestedResultMaps();
      }
      return this;
    }

    public Builder fetchSize(Integer fetchSize) {
      mappedStatement.fetchSize = fetchSize;
      return this;
    }

    public Builder timeout(Integer timeout) {
      mappedStatement.timeout = timeout;
      return this;
    }

    public Builder statementType(StatementType statementType) {
      mappedStatement.statementType = statementType;
      return this;
    }

    public Builder resultSetType(ResultSetType resultSetType) {
      mappedStatement.resultSetType = resultSetType;
      return this;
    }

    public Builder cache(Cache cache) {
      mappedStatement.cache = cache;
      return this;
    }

    public Builder flushCacheRequired(boolean flushCacheRequired) {
      mappedStatement.flushCacheRequired = flushCacheRequired;
      return this;
    }

    public Builder useCache(boolean useCache) {
      mappedStatement.useCache = useCache;
      return this;
    }

    public Builder resultOrdered(boolean resultOrdered) {
      mappedStatement.resultOrdered = resultOrdered;
      return this;
    }

    public Builder keyGenerator(KeyGenerator keyGenerator) {
      mappedStatement.keyGenerator = keyGenerator;
      return this;
    }

    public Builder keyProperty(String keyProperty) {
      mappedStatement.keyProperties = delimitedStringtoArray(keyProperty);
      return this;
    }

    public Builder keyColumn(String keyColumn) {
      mappedStatement.keyColumns = delimitedStringtoArray(keyColumn);
      return this;
    }

    public Builder databaseId(String databaseId) {
      mappedStatement.databaseId = databaseId;
      return this;
    }

    public Builder lang(LanguageDriver driver) {
      mappedStatement.lang = driver;
      return this;
    }

    public Builder resulSets(String resultSet) {
      mappedStatement.resultSets = delimitedStringtoArray(resultSet);
      return this;
    }

    public MappedStatement build() {
      assert mappedStatement.configuration != null;
      assert mappedStatement.id != null;
      assert mappedStatement.sqlSource != null;
      assert mappedStatement.lang != null;
      mappedStatement.resultMaps = Collections.unmodifiableList(mappedStatement.resultMaps);
      return mappedStatement;
    }
  }
***************************get()和set()方法省略********************************************
}

MappedStatement 小結(jié):

  1. 使用建造者模式構(gòu)建
  2. 內(nèi)部有configuration便于操作和使用
  3. 含有xml某個結(jié)點中的完整信息

操作類對象

Executor:Mybatis的執(zhí)行器

主要功能:

  1. 增刪改 update
  2. 查 query
  3. 事務(wù) 提交 回滾
  4. 緩存


    Executo功能
Executo的實現(xiàn)類

SimpleExecutor 常用Excutor
ReuseExecutor 復(fù)用Excutor 條件和參數(shù)都不改變的情況下可以使用
BatchExecutor JDBC中批處理 批處理熬芜;一個連接上進行多個sql

Executor小結(jié) :

  1. 調(diào)用StatementHandler來進行數(shù)據(jù)庫相關(guān)的改、查操作
  2. 維護緩存

StatementHandler

是Mybatis真正封裝JDBC Statement 的部分 福稳, 是Mybatis訪問數(shù)據(jù)庫的核心涎拉。實現(xiàn)了Excutor中增刪改查的功能

ParameterHandler

解決sql中的參數(shù)類型和占位符(?的圆,鼓拧?)

ResultSetHandler

對JDBC中 查詢結(jié)果集ResultSet封裝

TypeHandler

類型轉(zhuǎn)化
例如:將java類型轉(zhuǎn)化為數(shù)據(jù)庫類型(String varchar)


Mybatis工作流程源碼分析

將核心對象與SqlSession建立聯(lián)系

對應(yīng)代碼:

RoleMapper roleMapper=sqlSession.getMapper(RoleMapper.class);
//sqlSession.selectList("com.wxx.mapper.RoleMapper.getRole");  上面語句是下面語句的封裝

執(zhí)行流程:

1、DefaultSqlSession(sqlSession默認實現(xiàn)方式)調(diào)用getMapper()方法 將xxxMapper.class 和 當(dāng)前進行會話的sqlsession傳入進去

//DefaultSqlSession類
  public <T> T getMapper(Class<T> type) {
    //最后會去調(diào)用MapperRegistry.getMapper
    return configuration.<T>getMapper(type, this);
  }

2越妈、調(diào)用mapper注冊機的getMapper()方法

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }
  1. 程序中的mapper都放在一個hashmap中季俩,先查找這個hashmap中有沒有key為當(dāng)前類的代理工廠類。如果有則為當(dāng)前mapper創(chuàng)建一個代理對象梅掠,并返回酌住;如果沒有則進行異常處理。
 @SuppressWarnings("unchecked")
  //返回代理類
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

代理實現(xiàn)原理分析:

對應(yīng)代碼:

mapperProxyFactory.newInstance(sqlSession)
  1. 動態(tài)代理創(chuàng)建具體實現(xiàn)類
  2. 具體實現(xiàn)類內(nèi)部調(diào)用對應(yīng)的sqlsession的方法
mybatis源碼中的這些核心對象瓤檐,在sqlsession調(diào)用對應(yīng)功能時候建立聯(lián)系
例如:
    SqlSession.insert()
        DefaultSqlSession
            Exctutor
                StatementHandler

MyBatis 完成代理創(chuàng)建核心類型 ---> DAO接口的實現(xiàn)類
執(zhí)行順序如下:

MapperProxy  implements InvocationHandler
                invoke(類加載器赂韵,方法名,參數(shù))
                        sqlSession.insert
                                            update
                                            delete
                                            selectOne
                                            selectList
MapperProxyFactory
            Proxy.newProxyInstrance()

MapperProxy 實現(xiàn)InvocationHandler接口
實現(xiàn)invoke()方法

public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethod> getMethodCache() {
    return methodCache;
  }

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {   //生成xxxDAO的實現(xiàn)類
    //用JDK自帶的動態(tài)代理生成映射器
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {     //傳進sqlSession和對應(yīng)mapperInterface 生成代理對象  methodCache是方法緩存
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

}

public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //代理以后挠蛉,所有Mapper的方法調(diào)用時祭示,都會調(diào)用這個invoke方法
    //并不是任何一個方法都需要執(zhí)行調(diào)用代理對象進行執(zhí)行,如果這個方法是Object中通用的方法(toString、hashCode等)無需執(zhí)行
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    //這里優(yōu)化了质涛,去緩存中找MapperMethod
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //執(zhí)行
    return mapperMethod.execute(sqlSession, args);
  }

  //去緩存中找MapperMethod
  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      //找不到才去new
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

}

invoke()中的method.invoke(this, args); 底層對應(yīng)的相應(yīng)的SqlSession.insert 稠歉、updatedelete汇陆、selectOne怒炸、selectList方法。

public class MapperMethod {

  private final SqlCommand command;  //對應(yīng)的標(biāo)簽 是什么類型   才能具體的知道調(diào)用對應(yīng)crud所對應(yīng)的方法
  private final MethodSignature method;   //返回值  和 參數(shù)
    //需求Configuration對象
  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, method);
  }

  //執(zhí)行
  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    //可以看到執(zhí)行時就是4種情況毡代,insert|update|delete|select阅羹,分別調(diào)用SqlSession的4大類方法
    if (SqlCommandType.INSERT == command.getType()) {   //insert
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
    } else if (SqlCommandType.UPDATE == command.getType()) { //update
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
    } else if (SqlCommandType.DELETE == command.getType()) { //delete
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
    } else if (SqlCommandType.SELECT == command.getType()) { //select
      if (method.returnsVoid() && method.hasResultHandler()) {
        //如果有結(jié)果處理器
        executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        //如果結(jié)果有多條記錄
        result = executeForMany(sqlSession, args);
      } else if (method.returnsMap()) {
        //如果結(jié)果是map
        result = executeForMap(sqlSession, args);
      } else {
        //否則就是一條記錄   selectOne
        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);
      }
    } else {
      throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName()
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }
    其他部分省略
}
//SQL命令,靜態(tài)內(nèi)部類
  public static class SqlCommand {

    private final String name;  //name.id
    private final SqlCommandType type; //select || update || insert || delete

    public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
      String statementName = mapperInterface.getName() + "." + method.getName();
      MappedStatement ms = null;
      if (configuration.hasStatement(statementName)) {
        ms = configuration.getMappedStatement(statementName);
      } else if (!mapperInterface.equals(method.getDeclaringClass().getName())) { // issue #35
        //如果不是這個mapper接口的方法教寂,再去查父類
        String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
        if (configuration.hasStatement(parentStatementName)) {
          ms = configuration.getMappedStatement(parentStatementName);
        }
      }
      if (ms == null) {
        throw new BindingException("Invalid bound statement (not found): " + statementName);
      }
      name = ms.getId();
      type = ms.getSqlCommandType();   //select delete update insert
      if (type == SqlCommandType.UNKNOWN) {
        throw new BindingException("Unknown execution method for: " + name);
      }
    }

    public String getName() {
      return name;
    }

    public SqlCommandType getType() {
      return type;
    }
  }
//方法簽名捏鱼,靜態(tài)內(nèi)部類
  public static class MethodSignature { //對應(yīng)sql語句中的參數(shù)

    private final boolean returnsMany;
    private final boolean returnsMap;
    private final boolean returnsVoid;
    private final Class<?> returnType;
    private final String mapKey;
    private final Integer resultHandlerIndex;
    private final Integer rowBoundsIndex;
    private final SortedMap<Integer, String> params;
    private final boolean hasNamedParameters;

    public MethodSignature(Configuration configuration, Method method) {
      this.returnType = method.getReturnType();
      this.returnsVoid = void.class.equals(this.returnType);
      this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
      this.mapKey = getMapKey(method);
      this.returnsMap = (this.mapKey != null);
      this.hasNamedParameters = hasNamedParams(method);
      //以下重復(fù)循環(huán)2遍調(diào)用getUniqueParamIndex,是不是降低效率了
      //記下RowBounds是第幾個參數(shù)
      this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
      //記下ResultHandler是第幾個參數(shù)
      this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
      this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters));
    }

StatementHandler執(zhí)行流程:
時序圖(圖片來自于http://www.coderead.cn/

mybatis執(zhí)行時序圖

核心對象總結(jié)

configuration

默認初始化時酪耕,默認構(gòu)造函數(shù)為各種類型起別名:

                1. 事務(wù)處理器
                2. 數(shù)據(jù)源類型 
                3. 緩存策略
                4. 圖像格式
                5. 日志類型
                6. 動態(tài)代理類型
                7. 默認驅(qū)動類(xml方式)

其他配置:

  1. 默認使用簡單執(zhí)行器ExecutorType.SIMPLE
  2. 緩存作用域在session范圍內(nèi)LocalCacheScope.SESSION
  3. 創(chuàng)建攔截器鏈
  4. 創(chuàng)建類型處理器注冊機
  5. 創(chuàng)建類型別名注冊機

mappedStatement

用來表示xml中的一個sql导梆,例如:

 <select id="findRole" parameterType="long" resultMap="roleMap">
        select
        id,role_name,note from role where role_name like CONCAT('%',#{roleName
        javaType=string,
        jdbcType=VARCHAR,typeHandler=com.wxx.handler.MyStringHandler},'%')
    </select>
用來描述<select|update|insert|delete>或者@Select、@Update等注解配置的SQL信息迂烁。

sqlsession

使用門面模式看尼,只設(shè)定api,具體參數(shù)讓executor來執(zhí)行

executor

簡單執(zhí)行器(SimpleExecutor)盟步,可實現(xiàn)sqlsession中的全部功能 一次一次的執(zhí)行

重用執(zhí)行器(ReuseExecutor)藏斩,先查緩存中時候有相應(yīng)的sql語句,若有址芯,則不再進行sql的預(yù)編譯

批處理執(zhí)行器(BatchExecutor)灾茁,一次插入一批數(shù)據(jù),只對操作有效谷炸,對查詢操作和簡單執(zhí)行器一樣

四大對象

StatementHandler

ParameterHandler 用來處理StatementHandler中的參數(shù)

ResultSetHandler 用來處理StatementHandler 中的結(jié)果集

TypeHandler 用來幫助ParameterHandlerResultSetHandler處理javaType和數(shù)據(jù)庫類型的相互轉(zhuǎn)換


緩存

一級緩存在BaseExecutor中實現(xiàn) 二級緩存使用裝飾器模式在CachingExecutor中實現(xiàn)

緩存邏輯圖

mybaits緩存邏輯圖

一級緩存

命中場景(會話級別緩存)

運行時參數(shù)相關(guān):

  1. sql語句 和參數(shù)相同
  2. statementid需要相同
  3. sqlsession也必須相同
  4. RowBounds返回行范圍必須相同

操作與配置相關(guān):

①②③ 都會調(diào)用clearLocalCache()來對緩存進行清空

  1. 未手動清空 (提交 回滾)
  2. 未調(diào)用flushCache == true 的查詢
  3. 未執(zhí)行update操作 update操作會清空全部緩存
  4. 緩存作用域 不是STATEMENT

spring整合mybatis 以及緩存失效原因


使用攔截器(動態(tài)代理)來控制方法的調(diào)用北专,最終達到實現(xiàn)事務(wù)的效果

二級緩存

作用范圍是整個應(yīng)用,而且可以跨線程調(diào)用



使用流程:


二級緩存執(zhí)行流程

使用場景

運行時參數(shù)相關(guān):

  1. 會話提交之后
  2. sql語句 和參數(shù)相同
  3. statement的id需要相同
  4. RowBounds 返回行范圍必須相同

為何提交之后才能命中緩存

會話1和會話2本來是不可見的旬陡,但是使用二級緩存后邊的可見了拓颓。例如: 會話2進行了查詢、修改操作描孟,若此時將操作填充到二級緩存中驶睦,會話1此時進行查詢,拿到了這條緩存數(shù)據(jù)匿醒。而后會話2 進行了回滾操作场航,會話1拿到的數(shù)據(jù)就是數(shù)據(jù)庫中不存在的,導(dǎo)致了臟讀廉羔。

解決方案:


二級緩存存儲圖

為每個會話創(chuàng)建一個事務(wù)緩存管理器(transactionCaches)溉痢,每次訪問一個新的xxxMapper都會創(chuàng)建一個暫存區(qū),對應(yīng)著相應(yīng)的緩存區(qū)


Mybatis中使用的設(shè)計模式

單例模式

configuration 使用單例 保證整個程序運行期間都可使用,且不重復(fù)創(chuàng)建孩饼,放置浪費資源

構(gòu)造器模式

????????在Mybatis的初始化的主要工作是加載并解析mybatis-config.xml的配置文件髓削、映射配置文件以及相關(guān)的注解信息。因為使用了建造者模式镀娶,BashBuilder抽象類即為建造者接口的角色

public abstract class BaseBuilder {
    //需要配置立膛,類型別名注冊,類型處理器注冊3個東西
  protected final Configuration configuration;
  protected final TypeAliasRegistry typeAliasRegistry;
  protected final TypeHandlerRegistry typeHandlerRegistry;

  public BaseBuilder(Configuration configuration) {
    this.configuration = configuration;
    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
  }
   ………………
}

適配器模式

(Excutor 等核心對象梯码,使用缺省適配器模式) 使用extends關(guān)鍵字來繼承宝泵,只用實現(xiàn)想實現(xiàn)的功能

缺省適配模式 為一個接口提供缺省實現(xiàn),這樣的類型可以從這個缺省實現(xiàn)進行擴展忍些,而不必從原有接口進行擴展鲁猩。當(dāng)原接口中定義的方法太多,而其中大部分又不被需要時罢坝,這種模式非常實用。由缺省適配器類直接實現(xiàn)目標(biāo)(Target)角色接口搅窿,并為所有方法提供缺省的空實現(xiàn)嘁酿。用戶類就只需要繼承適配器類,只實現(xiàn)自己需要實現(xiàn)的方法就行了男应。

interface Test{
   void eat();
   void drink();
   void walk();
}


abstract class abstractClass implements Test{
   public void eat() {
       // TODO Auto-generated method stub
       
   }
   @Override
   public void drink() {
       // TODO Auto-generated method stub
       
   }

   @Override
   public void walk() {
       // TODO Auto-generated method stub
       
   }
}

class eatClass extends abstractClass{  
       @Override
       public void eat() {
           // TODO Auto-generated method stub
           super.eat();
       }
   }
   class drinkClass extends abstractClass{
       @Override
       public void eat() {
           // TODO Auto-generated method stub
           super.eat();
       }
   
   }

代理模式(動態(tài)代理)

 RoleMapper roleMapper=sqlSession.getMapper(RoleMapper.class);  實現(xiàn)類使用動態(tài)字節(jié)碼技術(shù)來創(chuàng)建闹司,在jvm中運行時創(chuàng)建
    等價于
Proxy.newProxyInstance(Test.class.getClassLoader(),new Class[]{RoleMapper.class},
                                                            new MyMapperProxy(sqlSession,RoleMapper.class));

工廠模式

使用mapper工廠來創(chuàng)建mapper

MapperProxyFactory  和  MapperProxy
    
protected T newInstance(MapperProxy<T> mapperProxy) {
    //用JDK自帶的動態(tài)代理生成映射器
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

門面模式

sqlsession使用門面模式,只定義api 具體的使用讓Executor來進行實現(xiàn)

裝飾器模式

在不改變原有類結(jié)構(gòu)和繼承的情況下沐飘,通過包裝原對象去擴展一個新功能

Caching Executor 實現(xiàn)二級緩存游桩,而其他的操作,如同獲取連接等操作BaseExecutor已經(jīng)做了耐朴,直接使用BaseExecutor

責(zé)任鏈模式

二級緩存中的責(zé)任鏈模式


二級緩存責(zé)任鏈
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末借卧,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子筛峭,更是在濱河造成了極大的恐慌铐刘,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件影晓,死亡現(xiàn)場離奇詭異镰吵,居然都是意外死亡,警方通過查閱死者的電腦和手機挂签,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門疤祭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人饵婆,你說我怎么就攤上這事勺馆。” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵谓传,是天一觀的道長蜈项。 經(jīng)常有香客問我,道長续挟,這世上最難降的妖魔是什么紧卒? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮诗祸,結(jié)果婚禮上跑芳,老公的妹妹穿的比我還像新娘。我一直安慰自己直颅,他們只是感情好博个,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著功偿,像睡著了一般盆佣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上械荷,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天共耍,我揣著相機與錄音,去河邊找鬼吨瞎。 笑死痹兜,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的颤诀。 我是一名探鬼主播字旭,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼崖叫!你這毒婦竟也來了遗淳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤归露,失蹤者是張志新(化名)和其女友劉穎洲脂,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體剧包,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡恐锦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了疆液。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片一铅。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖堕油,靈堂內(nèi)的尸體忽然破棺而出潘飘,到底是詐尸還是另有隱情肮之,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布卜录,位于F島的核電站戈擒,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏艰毒。R本人自食惡果不足惜筐高,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望丑瞧。 院中可真熱鬧柑土,春花似錦、人聲如沸绊汹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽西乖。三九已至狐榔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間获雕,已是汗流浹背荒叼。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留典鸡,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓坏晦,卻偏偏與公主長得像萝玷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子昆婿,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容