91欧美超碰AV自拍|国产成年人性爱视频免费看|亚洲 日韩 欧美一厂二区入|人人看人人爽人人操aV|丝袜美腿视频一区二区在线看|人人操人人爽人人爱|婷婷五月天超碰|97色色欧美亚州A√|另类A√无码精品一级av|欧美特级日韩特级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

由 Mybatis 源碼暢談軟件設(shè)計(jì)(八):從根上理解 Mybatis 二級(jí)緩存

京東云 ? 來源:jf_75140285 ? 作者:jf_75140285 ? 2025-06-23 11:35 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

1. 驗(yàn)證二級(jí)緩存

在上一篇帖子中的 User 和 Department 實(shí)體類依然要用,這里就不再贅述了,要啟用二級(jí)緩存,需要在 Mapper.xml 文件中指定 cache 標(biāo)簽,如下:

UserMapper.xml

    
        select * from user
    

    
Department.xml

    
        select * from department;
    
    
    

在 Department.xml 中的 cache 標(biāo)簽指定了 readOnly 屬性,因?yàn)樵撆渲孟鄬?duì)比較重要,所以我們?cè)谶@里把它講解一下:

readOnly 默認(rèn)為 false,這種情況下通過二級(jí)緩存查詢出來的數(shù)據(jù)會(huì)進(jìn)行一次 序列化深拷貝。在這里大家需要回想一下介紹一級(jí)緩存時(shí)舉的例子:一級(jí)緩存查詢出來返回的是 該對(duì)象的引用,若我們對(duì)它修改,再查詢 時(shí)觸發(fā)一級(jí)緩存獲得的便是 被修改過的數(shù)據(jù)。但是,二級(jí)緩存的序列化機(jī)制則不同,它獲取到的是 緩存深拷貝的對(duì)象,這樣對(duì)二級(jí)緩存進(jìn)行修改操作不影響后續(xù)查詢結(jié)果。

如果將該屬性配置為 true 的話,那么它就會(huì)變得和一級(jí)緩存一樣,返回的是對(duì)象的引用,這樣做的好處是 避免了深拷貝的開銷。

為什么會(huì)有這種機(jī)制呢?

因?yàn)槎?jí)緩存是 Mapper級(jí)別 的,不能保證其他 SqlSession 不對(duì)二級(jí)緩存進(jìn)行修改,所以這也是一種保護(hù)機(jī)制。

我們驗(yàn)證一下這個(gè)例子,Department 和 User 的查詢都執(zhí)行了兩遍(注意 事務(wù)提交之后 才能使二級(jí)緩存生效):

public static void main(String[] args) {
        InputStream xml = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        // 開啟二級(jí)緩存需要在同一個(gè)SqlSessionFactory下,二級(jí)緩存存在于 SqlSessionFactory 生命周期,如此才能命中二級(jí)緩存
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(xml);

        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        DepartmentMapper departmentMapper1 = sqlSession1.getMapper(DepartmentMapper.class);

        System.out.println("----------department第一次查詢 ↓------------");
        List departments1 = departmentMapper1.findAll();
        System.out.println("----------user第一次查詢 ↓------------");
        List users1 = userMapper1.findAll();

        // 提交事務(wù),使二級(jí)緩存生效
        sqlSession1.commit();

        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
        DepartmentMapper departmentMapper2 = sqlSession2.getMapper(DepartmentMapper.class);

        System.out.println("----------department第二次查詢 ↓------------");
        List departments2 = departmentMapper2.findAll();
        System.out.println("----------user第二次查詢 ↓------------");
        List users2 = userMapper2.findAll();

        sqlSession1.close();
        sqlSession2.close();
}

Department 和 User 的同一條查詢語(yǔ)句都執(zhí)行了兩遍,因?yàn)?Department 指定了 readOnly 為true,那么 兩次查詢返回的對(duì)象均為同一個(gè)引用,而 User 則反之,Debug 試一下:

wKgZO2hYy2iAY8CGAAKQAagnXFA590.png

cache 的其他屬性

屬性 描述 備注
eviction 緩存回收策略 默認(rèn) LRU
type 二級(jí)緩存的實(shí)現(xiàn)類 默認(rèn)實(shí)現(xiàn) PerpetualCache
size 緩存引用數(shù)量 默認(rèn)1024
flushInterval 定時(shí)清除時(shí)間間隔 默認(rèn)無(wú)
blocking 阻塞獲取緩存數(shù)據(jù) 若緩存中找不到對(duì)應(yīng)的 key ,是否會(huì)一直阻塞,直到有對(duì)應(yīng)的數(shù)據(jù)進(jìn)入緩存。默認(rèn) false

接下來我們測(cè)試驗(yàn)證下二級(jí)緩存的生效:

   SqlSession sqlSession1 = sqlSessionFactory.openSession();
   DepartmentMapper departmentMapper1 = sqlSession1.getMapper(DepartmentMapper.class);

   System.out.println("----------department第一次查詢 ↓------------");
   List departments1 = departmentMapper1.findAll();

   // 使二級(jí)緩存生效
   sqlSession1.commit();

   SqlSession sqlSession2 = sqlSessionFactory.openSession();
   DepartmentMapper departmentMapper2 = sqlSession2.getMapper(DepartmentMapper.class);

   System.out.println("----------department第二次查詢 ↓------------");
   List departments2 = departmentMapper2.findAll();

第一次 Query,會(huì)去數(shù)據(jù)庫(kù)中查

wKgZPGhYy2mAOPkcAAJL2vCN4Go519.png

第二次 Query,直接從二級(jí)緩存中取

wKgZO2hYy2qAfHtlAAZ_6zql_9w968.png

2. 二級(jí)緩存的原理

二級(jí)緩存對(duì)象 Cache

在加載 Mapper 文件(org.apache.ibatis.builder.xml.XMLConfigBuilder#mappersElement 方法)時(shí),定義了加載 cache 標(biāo)簽的步驟(org.apache.ibatis.builder.xml.XMLMapperBuilder#configurationElement 方法),代碼如下:

public class XMLMapperBuilder extends BaseBuilder {
    // ...
    
    private void configurationElement(XNode context) {
        try {
            // 若想要在多個(gè)命名空間中共享相同的緩存配置和實(shí)例,可以使用 cache-ref 元素來引用另一個(gè)緩存
            cacheRefElement(context.evalNode("cache-ref"));
            // 配置二級(jí)緩存
            cacheElement(context.evalNode("cache"));
            // ...
        } catch (Exception e) {
            throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
        }
    }
}

具體解析邏輯如下:

public class XMLMapperBuilder extends BaseBuilder {
    // ...
    
    private void cacheElement(XNode context) {
        if (context != null) {
            // 二級(jí)緩存實(shí)現(xiàn)類,默認(rèn) PerpetualCache,我們?cè)谝患?jí)緩存也提到過
            String type = context.getStringAttribute("type", "PERPETUAL");
            Class typeClass = typeAliasRegistry.resolveAlias(type);
            // 緩存清除策略,默認(rèn) LRU
            String eviction = context.getStringAttribute("eviction", "LRU");
            Class evictionClass = typeAliasRegistry.resolveAlias(eviction);
            // 定時(shí)清除間隔
            Long flushInterval = context.getLongAttribute("flushInterval");
            // 緩存引用數(shù)量
            Integer size = context.getIntAttribute("size");
            // readOnly上文我們提到過,默認(rèn) false
            boolean readWrite = !context.getBooleanAttribute("readOnly", false);
            // blocking 默認(rèn) false
            boolean blocking = context.getBooleanAttribute("blocking", false);
            Properties props = context.getChildrenAsProperties();
            // 創(chuàng)建緩存對(duì)象
            builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
        }
    }
}

我們繼續(xù)看創(chuàng)建二級(jí)緩存對(duì)象的邏輯 org.apache.ibatis.builder.MapperBuilderAssistant#useNewCache,可以發(fā)現(xiàn),創(chuàng)建 Cache 對(duì)象使用了 建造者模式

wKgZPGhYy2uAIl6yAAFXBuJ7-SI640.png

建造者 CacheBuilder 并沒有被組合在任意一種緩存的實(shí)現(xiàn)類中,而是根據(jù)如下代碼中 implementation(valueOrDefault(typeClass, PerpetualCache.class)) 邏輯指定了要?jiǎng)?chuàng)建的緩存類型,并在 build 方法中使用反射創(chuàng)建對(duì)應(yīng)實(shí)現(xiàn)類:

public class MapperBuilderAssistant extends BaseBuilder {
    // ...

    public Cache useNewCache(Class typeClass, Class evictionClass, Long flushInterval,
                             Integer size, boolean readWrite, boolean blocking, Properties props) {
        // 建造者模式,將標(biāo)簽屬性賦值
        Cache cache = new CacheBuilder(currentNamespace).implementation(valueOrDefault(typeClass, PerpetualCache.class))
                .addDecorator(valueOrDefault(evictionClass, LruCache.class)).clearInterval(flushInterval).size(size)
                .readWrite(readWrite).blocking(blocking).properties(props).build();

        // 添加到全局配置中
        configuration.addCache(cache);
        currentCache = cache;
        return cache;
    }
}

其中 addDecorator(valueOrDefault(evictionClass, LruCache.class)) 邏輯添加了 裝飾器,使用了 裝飾器模式,將 LruCache 類型的裝飾器添加到 decorators 中:

public class CacheBuilder {

    private final List> decorators;

    public CacheBuilder addDecorator(Class decorator) {
        // 將 LruCache 裝飾器添加到 decorators
        if (decorator != null) {
            this.decorators.add(decorator);
        }
        return this;
    }
    
    // ...
}

在 CacheBuilder#build 方法中,如下為封裝裝飾器的邏輯:

public class CacheBuilder {
    // ...

    public Cache build() {
        setDefaultImplementations();
        // 反射創(chuàng)建 PerpetualCache
        Cache cache = newBaseCacheInstance(implementation, id);
        setCacheProperties(cache);

        // 封裝裝飾器的邏輯
        if (PerpetualCache.class.equals(cache.getClass())) {
            for (Class decorator : decorators) {
                cache = newCacheDecoratorInstance(decorator, cache);
                setCacheProperties(cache);
            }
            // 初始化基礎(chǔ)必要的裝飾器
            cache = setStandardDecorators(cache);
        } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
            cache = new LoggingCache(cache);
        }
        return cache;
    }
    

    private Cache setStandardDecorators(Cache cache) {
        try {
            MetaObject metaCache = SystemMetaObject.forObject(cache);
            if (size != null && metaCache.hasSetter("size")) {
                metaCache.setValue("size", size);
            }
            // 定時(shí)清空二級(jí)緩存
            if (clearInterval != null) {
                cache = new ScheduledCache(cache);
                ((ScheduledCache) cache).setClearInterval(clearInterval);
            }
            // readOnly屬性相關(guān)的讀寫緩存
            if (readWrite) {
                cache = new SerializedCache(cache);
            }
            // 日志緩存和同步緩存(借助 ReentrantLock 實(shí)現(xiàn))
            cache = new LoggingCache(cache);
            cache = new SynchronizedCache(cache);
            // 阻塞屬性的緩存
            if (blocking) {
                cache = new BlockingCache(cache);
            }
            return cache;
        } catch (Exception e) {
            throw new CacheException("Error building standard cache decorators.  Cause: " + e, e);
        }
    }

}

所有裝飾器都在 org.apache.ibatis.cache.decorators 包下,唯獨(dú) PerpetualCache 在org.apache.ibatis.cache.impl 包下:

wKgZO2hYy2yAQVjGAAOEnOdqUQ8112.png

PerpetualCache 中不包含 delegate 屬性表示裝飾器,說明它將作為最基礎(chǔ)的實(shí)現(xiàn)類被其他裝飾器裝飾,而其他裝飾器中均含有 delegate 屬性來裝飾其他實(shí)現(xiàn)。

默認(rèn)創(chuàng)建的二級(jí)緩存類型如下:

wKgZPGhYy22ADgzKAAGFxHV9RUk052.png

類關(guān)系圖如下:

wKgZO2hYy2-AeKElAA3DkuC3ty4349.png

query 方法對(duì)二級(jí)緩存的應(yīng)用

org.apache.ibatis.executor.CachingExecutor#query 方法使用了二級(jí)緩存,如下代碼所示:

public class CachingExecutor implements Executor {

  // 事務(wù)緩存管理器
  private final TransactionalCacheManager tcm = new TransactionalCacheManager();
  
  @Override
  public  List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    // 先獲取二級(jí)緩存,該對(duì)象便是上文中創(chuàng)建的被裝飾器裝飾的 PerpetualCache
    Cache cache = ms.getCache();
    if (cache != null) {
      // 判斷是否需要清除緩存
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, boundSql);
        // 從二級(jí)緩存中取
        @SuppressWarnings("unchecked")
        List list = (List) tcm.getObject(cache, key);
        if (list == null) {
          // 沒取到二級(jí)緩存,嘗試取一級(jí)緩存或去數(shù)據(jù)庫(kù)查詢
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          // “添加二級(jí)緩存”
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    
    // 沒有二級(jí)緩存的話,執(zhí)行的是我們?cè)谝患?jí)緩存中介紹的方法,要么取一級(jí)緩存,否則去數(shù)據(jù)庫(kù)查
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
  
  // ...
}

上述邏輯比較清晰,我們?cè)谏衔闹刑岬竭^,只有 事務(wù)提交的時(shí)候才會(huì)將二級(jí)緩存保存,但是其中有 tcm.putObject(cache, key, list); 邏輯,似乎在這里保存了二級(jí)緩存,而此時(shí)事務(wù)還未提交,這便需要我們一探究竟。它會(huì)執(zhí)行到 TransactionalCacheManager#putObject 方法:

public class TransactionalCacheManager {

    private final Map transactionalCaches = new HashMap();
    
    public void putObject(Cache cache, CacheKey key, Object value) {
        getTransactionalCache(cache).putObject(key, value);
    }
    
    private TransactionalCache getTransactionalCache(Cache cache) {
        return MapUtil.computeIfAbsent(transactionalCaches, cache, TransactionalCache::new);
    }
}

TransactionalCacheManager 事務(wù)緩存管理器會(huì)創(chuàng)建并管理 TransactionalCache 對(duì)象,TransactionalCache 同樣是 Cache 裝飾器,它將裝飾在 SynchronizedCache 上:

public class TransactionalCache implements Cache {

    // 被裝飾對(duì)象,默認(rèn)是 SynchronizedCache
    private final Cache delegate;
    // 該元素將保存在事務(wù) commit 時(shí)被保存的鍵值對(duì)緩存
    private final Map entriesToAddOnCommit;

    @Override
    public void putObject(Object key, Object object) {
        entriesToAddOnCommit.put(key, object);
    }
    
    // ...
}

putObject 執(zhí)行時(shí)便是向 entriesToAddOnCommit 添加元素,記錄二級(jí)緩存鍵值對(duì),并沒有真正添加到二級(jí)緩存 PerpetualCache 對(duì)象中。此外,entriesToAddOnCommit 的命名,也暗示了在事務(wù)提交時(shí)緩存才會(huì)被保存。那么接下來,便需要看一下事務(wù)提交邏輯。

在上文測(cè)試二級(jí)緩存的代碼中,有 sqlSession1.commit(); 邏輯。在事務(wù)提交時(shí),它會(huì)走到 CachingExecutor#commit 方法,其中會(huì)調(diào)用到 TransactionalCacheManager#commit 方法,如下:

public class CachingExecutor implements Executor {
    // ...
    
    private final TransactionalCacheManager tcm = new TransactionalCacheManager();

    
    @Override
    public void commit(boolean required) throws SQLException {
        // ...
        tcm.commit();
    }
    
}

在該方法中,會(huì)遍歷所有的事務(wù)緩存 TransactionalCache,并逐一調(diào)用它們的 commit 方法,

public class TransactionalCacheManager {

    private final Map transactionalCaches = new HashMap();
    
    public void commit() {
        for (TransactionalCache txCache : transactionalCaches.values()) {
            txCache.commit();
        }
    }
    
    // ...

commit 方法會(huì)調(diào)用 delegate.commit 方法,而 delegate 為被裝飾對(duì)象,最后便會(huì)將二級(jí)緩存記錄:

public class TransactionalCache implements Cache {

    private final Map entriesToAddOnCommit;

    public void commit() {
        if (clearOnCommit) {
            delegate.clear();
        }
        flushPendingEntries();
        reset();
    }

    private void flushPendingEntries() {
        // 事務(wù)提交,將 entriesToAddOnCommit 中所有待添加的二級(jí)緩存添加
        for (Map.Entry entry : entriesToAddOnCommit.entrySet()) {
            delegate.putObject(entry.getKey(), entry.getValue());
        }
        for (Object entry : entriesMissedInCache) {
            if (!entriesToAddOnCommit.containsKey(entry)) {
                delegate.putObject(entry, null);
            }
        }
    }

    private void reset() {
        clearOnCommit = false;
        entriesToAddOnCommit.clear();
        entriesMissedInCache.clear();
    }
    
    // ...
}

緩存失效

事務(wù)回滾是不是會(huì)使本次事務(wù)中相關(guān)的二級(jí)緩存失效呢?

public class TransactionalCache implements Cache {

    public void rollback() {
        unlockMissedEntries();
        reset();
    }

    private void reset() {
        clearOnCommit = false;
        entriesToAddOnCommit.clear();
        entriesMissedInCache.clear();
    }

    private void unlockMissedEntries() {
        for (Object entry : entriesMissedInCache) {
            try {
                delegate.removeObject(entry);
            } catch (Exception e) {
                log.warn("Unexpected exception while notifying a rollback to the cache adapter. "
                        + "Consider upgrading your cache adapter to the latest version. Cause: " + e);
            }
        }
    }
    // ...
}

的確如此,它會(huì)將未被緩存的元素清除 reset(),也會(huì)把在本次事務(wù)中操作過的數(shù)據(jù)在二級(jí)緩存中移除 unlockMissedEntries()。

那數(shù)據(jù)發(fā)生新增、修改或刪除呢?同樣會(huì)清除緩存

public class CachingExecutor implements Executor {

    @Override
    public int update(MappedStatement ms, Object parameterObject) throws SQLException {
        flushCacheIfRequired(ms);
        return delegate.update(ms, parameterObject);
    }
    

    private void flushCacheIfRequired(MappedStatement ms) {
        Cache cache = ms.getCache();
        // 默認(rèn) flushCacheRequired 為 true
        if (cache != null && ms.isFlushCacheRequired()) {
            tcm.clear(cache);
        }
    }

它將調(diào)用 TransactionalCache#clear 方法,將待生效的 entriesToAddOnCommit 二級(jí)緩存清除,并標(biāo)記 clearOnCommit 為 true,在事務(wù)提交時(shí),二級(jí)緩存會(huì)執(zhí)行清除緩存的 clear 方法:


    @Override
    public void clear() {
        clearOnCommit = true;
        entriesToAddOnCommit.clear();
    }

    public void commit() {
        if (clearOnCommit) {
            delegate.clear();
        }
        flushPendingEntries();
        reset();
    }

緩存生效范圍

到這里,我們已經(jīng)基本弄清楚二級(jí)緩存生效的原理了,那么接下來我們需要解釋“為什么二級(jí)緩存是 Mapper 級(jí)別的?”其實(shí)也非常簡(jiǎn)單,看如下代碼:

public class CachingExecutor implements Executor {
  
  @Override
  public  List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    // 先獲取二級(jí)緩存,該對(duì)象便是上文中創(chuàng)建的被裝飾器裝飾的 PerpetualCache
    Cache cache = ms.getCache();
    // ...
  }
    // ...
}

在執(zhí)行查詢時(shí),二級(jí)緩存 Cache 是在 MappedStatement 中獲取的,Mapper 中每個(gè) SQL 聲明都對(duì)應(yīng)唯一的 MappedStatement,當(dāng)同一條 SQL 被執(zhí)行時(shí),它們都會(huì)去取同樣的緩存,所以可以說它是 Mapper 級(jí)別的,說成 MappedStatement 級(jí)別更準(zhǔn)確,二級(jí)緩存支持多個(gè) SqlSession 共享。

為什么要在事務(wù)提交后才生效?

在這里我們討論一個(gè)問題:為什么二級(jí)要在事務(wù)提交后才能生效呢

因?yàn)槎?jí)緩存可以在不同的 SqlSession 間生效,畫個(gè)圖你就明白了:

wKgZO2hYy3CASnCGAAMDWZScyoU417.png

如果 SqlSession1先修改了數(shù)據(jù),再查詢數(shù)據(jù),如果二級(jí)緩存在事務(wù)未提交時(shí)就生效,那么 SqlSession2 調(diào)用同樣的查詢時(shí)便會(huì)從 二級(jí)緩存中獲取數(shù)據(jù),但是此時(shí) SqlSession1回滾了事務(wù),那么此時(shí)就會(huì)導(dǎo)致 SqlSession2 從二級(jí)緩存獲取的數(shù)據(jù) 變成臟數(shù)據(jù),這就是為什么二級(jí)緩存要在事務(wù)提交后才能生效的原因。

3. 為什么要擴(kuò)展二級(jí)緩存?

MyBatis 中設(shè)計(jì)一級(jí)緩存和二級(jí)緩存的目的是為了提高數(shù)據(jù)庫(kù)訪問的效率,但它們的作用范圍和使用場(chǎng)景有所不同,各自有其特定的用途和優(yōu)勢(shì)。

一級(jí)緩存 默認(rèn)開啟,是基于 SqlSession 的,也就是說,它的作用范圍僅限于一次數(shù)據(jù)庫(kù)會(huì)話,所以當(dāng)會(huì)話關(guān)閉后,緩存就會(huì)被清除。這意味著不同會(huì)話之間無(wú)法共享緩存數(shù)據(jù)。而 二級(jí)緩存 是基于 Mapper 級(jí)別的,需要顯式配置開啟,可以在多個(gè) SqlSession 之間共享。當(dāng)然也由于二級(jí)緩存的作用范圍更廣,因此需要更復(fù)雜的緩存失效策略和數(shù)據(jù)一致性管理,以避免數(shù)據(jù)不一致的問題。二級(jí)緩存的引入是為了在更大范圍內(nèi)(多個(gè)會(huì)話之間)提高數(shù)據(jù)訪問的效率,特別是在讀多寫少的應(yīng)用場(chǎng)景。

4. 總結(jié)

二級(jí)緩存本質(zhì)上是 HashMap,在 PerpetualCache 實(shí)現(xiàn)類中

二級(jí)緩存是 Mapper 級(jí)別的,可以在不同 SqlSession 間共享

特殊的 readOnly 標(biāo)簽,默認(rèn)為 false,表示二級(jí)緩存中是被深拷貝的對(duì)象

二級(jí)緩存需要在事務(wù)提交后才能生效

執(zhí)行 Insert、Delete、Update 語(yǔ)句會(huì)使 當(dāng)前 Mapper 下的二級(jí)緩存失效

審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 源碼
    +關(guān)注

    關(guān)注

    8

    文章

    685

    瀏覽量

    31319
  • mybatis
    +關(guān)注

    關(guān)注

    0

    文章

    64

    瀏覽量

    7139
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    低功耗同步SRAM擴(kuò)展存儲(chǔ)器結(jié)構(gòu)特點(diǎn)

    ,都能見到它的身影。在計(jì)算機(jī)體系結(jié)構(gòu)中,同步SRAM主要用于實(shí)現(xiàn)CPU內(nèi)部的高速緩存,包括一級(jí)緩存(L1 Cache)和二級(jí)緩存(L2 Cache)。對(duì)于需要高速數(shù)據(jù)處理的系統(tǒng),SRA
    的頭像 發(fā)表于 03-06 16:46 ?52次閱讀

    軟件到硬件的轉(zhuǎn)場(chǎng):一場(chǎng)OceanOS-CM0啟發(fā)的嵌入式思考

    ****知識(shí)結(jié)構(gòu)的重構(gòu) 軟件到硬件的轉(zhuǎn)型,本質(zhì)是知識(shí)結(jié)構(gòu)的重構(gòu)。軟件開發(fā)者擅長(zhǎng)抽象、封裝、接口設(shè)計(jì);硬件開發(fā)者則必須理解具體、物理、時(shí)
    發(fā)表于 01-04 15:52

    天璣9400+芯片助力REDMI K Pad性能火力全開

    REDMI K Pad 搭載天璣 9400+ 旗艦芯,該芯片采用臺(tái)積電 3nm 制程、第代全大核 CPU 架構(gòu),二級(jí)緩存性能較上代翻倍,操控絲滑,應(yīng)用秒開,多窗口切換及多任務(wù)處理順滑如流。深度調(diào)校的狂暴引擎 4.0,深入芯片底層實(shí)現(xiàn)微架構(gòu)
    的頭像 發(fā)表于 11-21 11:31 ?1140次閱讀

    匯編寄存器的知識(shí)

    的運(yùn)算速度遠(yuǎn)高于內(nèi)存的讀寫速度,為了避免被拖慢,CPU 都自帶一級(jí)緩存二級(jí)緩存。基本,CPU 緩存可以看作是讀寫速度較快的內(nèi)存。 但
    發(fā)表于 11-20 06:45

    醫(yī)院隨訪管理系統(tǒng)源碼,三級(jí)隨訪系統(tǒng)源碼,Java+Springboot,Vue,Ant-Design+MySQL5

    :MySQL5及以上 隨訪系統(tǒng)菜單功能介紹: 1.首頁(yè) 首頁(yè)分區(qū)域展示隨訪概覽,表單占比, 科室占比以及Top榜單數(shù)四個(gè)模塊 2.醫(yī)護(hù)隨訪系統(tǒng) 該模塊主要包括一級(jí)隨訪,二級(jí)
    的頭像 發(fā)表于 11-08 14:48 ?520次閱讀
    醫(yī)院隨訪管理系統(tǒng)<b class='flag-5'>源碼</b>,三<b class='flag-5'>級(jí)</b>隨訪系統(tǒng)<b class='flag-5'>源碼</b>,Java+Springboot,Vue,Ant-Design+MySQL5

    二級(jí)差分電路的電阻怎么配置?

    想問下,二級(jí)差分電路的電阻該怎么配置呢?
    發(fā)表于 10-16 15:05

    配電柜二級(jí)浪涌保護(hù)器的安裝選型與行業(yè)應(yīng)用方案

    一、二級(jí)浪涌保護(hù)器的概念與作用 浪涌保護(hù)器(Surge Protective Device,簡(jiǎn)稱SPD)是一類專門用來保護(hù)低壓配電系統(tǒng)及其用電設(shè)備免受雷電感應(yīng)、電磁脈沖及操作過電壓影響的裝置。 按照
    的頭像 發(fā)表于 09-28 14:40 ?953次閱讀
    配電柜<b class='flag-5'>二級(jí)</b>浪涌保護(hù)器的安裝選型與行業(yè)應(yīng)用方案

    德州儀器模擬設(shè)計(jì)|使用第二級(jí)濾波器來減少電壓紋波

    在上期中,我們探討了高速 ADC 模擬輸入前端的各種無(wú)源匹配技巧。 ? 本期,為大家?guī)淼氖恰妒褂玫?b class='flag-5'>二級(jí)濾波器來減少電壓紋波》,將對(duì)比三種控制架構(gòu)在相同輸出電壓紋波下的表現(xiàn),并提供了使用相同電氣規(guī)格
    的頭像 發(fā)表于 09-08 09:23 ?1569次閱讀
    德州儀器模擬設(shè)計(jì)|使用第<b class='flag-5'>二級(jí)</b>濾波器來減少電壓紋波

    耦合之殤,軟件設(shè)計(jì)中的隱疾

    01 引 言 數(shù)據(jù)耦合和控制耦合是復(fù)雜軟件設(shè)計(jì)中常見的一種情況。DO-178C要求高安全等級(jí)軟件( DAL A/ B /C) 的測(cè)試覆蓋分析,必須要確認(rèn)基于需求的測(cè)試執(zhí)行了代碼組件之間的數(shù)據(jù)耦合
    的頭像 發(fā)表于 08-28 15:54 ?1009次閱讀
    耦合之殤,<b class='flag-5'>軟件設(shè)計(jì)</b>中的隱疾

    緩存之美:萬(wàn)文詳解 Caffeine 實(shí)現(xiàn)原理(

    文章將采用“總-分-總”的結(jié)構(gòu)對(duì)配置固定大小元素驅(qū)逐策略的 Caffeine 緩存進(jìn)行介紹,首先會(huì)講解它的實(shí)現(xiàn)原理,在大家對(duì)它有一個(gè)概念之后再深入具體源碼的細(xì)節(jié)之中,理解它的設(shè)計(jì)理念,從中能學(xué)習(xí)到
    的頭像 發(fā)表于 08-05 14:49 ?702次閱讀
    <b class='flag-5'>緩存</b>之美:萬(wàn)文詳解 Caffeine 實(shí)現(xiàn)原理(<b class='flag-5'>上</b>)

    緩存之美:理解 ConcurrentHashMap

    本文將詳細(xì)介紹 ConcurrentHashMap 構(gòu)造方法、添加值方法和擴(kuò)容操作等源碼實(shí)現(xiàn)。 ConcurrentHashMap 是線程安全的哈希表,此哈希表的設(shè)計(jì)主要目的是在最小化更新操作對(duì)哈希
    的頭像 發(fā)表于 08-05 14:48 ?574次閱讀

    HarmonyOS NEXT應(yīng)用元服務(wù)常見列表操作二級(jí)聯(lián)動(dòng)

    二級(jí)聯(lián)動(dòng)場(chǎng)景 場(chǎng)景描述 通過左邊一級(jí)列表的選擇,聯(lián)動(dòng)更新右邊二級(jí)列表的數(shù)據(jù),常用于商品分類選擇、編輯風(fēng)格等二級(jí)類別選擇頁(yè)面。 本場(chǎng)景以商品分類列表頁(yè)面為例,分別通過List組件,對(duì)左側(cè)
    發(fā)表于 07-02 10:29

    二級(jí)浪涌電路之保險(xiǎn)絲選型

    點(diǎn)在哪里呢? 本文通過DC110V接口保護(hù)電路的設(shè)計(jì),講解二級(jí)防雷電路中退耦電感的選型。 、設(shè)計(jì)方案 防護(hù)器件的選擇相對(duì)來講比較直觀,器件廠家會(huì)提供器件的額定工作電壓、擊穿電壓、嵌位電壓等參數(shù),甚至
    發(fā)表于 06-28 22:00

    蔡司培訓(xùn)|提升技能必看——AUKOM 一級(jí)/二級(jí)課程培訓(xùn)

    AUKOM 一級(jí)課程通過系統(tǒng)的分析測(cè)量誤差,即工件、環(huán)境、測(cè)量機(jī)、操作人員、測(cè)量策略,五個(gè)方面分析誤差因子,保證策量結(jié)果的準(zhǔn)確性。 AUKOM 二級(jí)課程是在AUKOM 一級(jí)的基礎(chǔ),更
    發(fā)表于 06-03 14:26 ?2257次閱讀
    蔡司培訓(xùn)|提升技能必看——AUKOM 一<b class='flag-5'>級(jí)</b>/<b class='flag-5'>二級(jí)</b>課程培訓(xùn)

    二級(jí)浪涌電路之退耦電感選型

    二級(jí)浪涌防雷電路設(shè)計(jì)
    的頭像 發(fā)表于 05-12 15:31 ?2292次閱讀
    <b class='flag-5'>二級(jí)</b>浪涌電路之退耦電感選型