Analyse du code source Mybaits (5) L'explication la plus détaillée du cache de premier niveau et du cache de second niveau

Analyse du code source de Mybaits (5) Explication détaillée du cache de premier niveau et du cache de second niveau

Avant-propos: l'article précédent expliquait la source de données mybaits, cet article expliquait l'utilisation de base du cache de premier niveau mybaits, du cache de deuxième niveau et de l'implémentation principale.

Cet article est principalement divisé en les parties suivantes:

L'utilisation et le test du cache de premier niveau et du cache de second niveau

Introduction aux classes liées au cache mybaits

Cache de niveau 1 détaillé

Cache de second niveau détaillé

1. Utilisation et test du cache de premier niveau et du cache de second niveau

Dans mybaits, le cache de premier niveau est activé par défaut, le cycle de vie du cache est au niveau sqlsession et la configuration globale du cache de second niveau est activée par défaut, mais vous devez également l'activer dans l'espace de noms pour l'utiliser le cache de second niveau, le cache de second niveau Le cycle de vie est sqlsessionFactory, et la portée de l'opération de cache est que chaque mappeur correspond à un cache (c'est pourquoi il doit être activé dans l'espace de noms configuré par le mappeur pour prendre effet)

1. Ajoutez la configuration suivante dans SqlMapper, cacheEnabled est responsable de l'activation du cache de deuxième niveau, logImpl est responsable de l'impression de sql (nous pouvons tester si le cache est parti en fonction de l'impression du vrai sql)

	<settings>
        <!--打印执行sql用
	  	<setting name="logImpl" value="STDOUT_LOGGING"/>
	  	<!-- 默认为true -->
	  	<setting name="cacheEnabled" value="true"/>
	</settings>

2. Test

	
	/**
	 * 一级缓存测试 :
	 * 	测试前,需要加下面logImpl这个打印sql语句的配置
		 <settings>
		  	<setting name="logImpl" value="STDOUT_LOGGING"/> //  是为了打印sql语句用途
		  	<setting name="cacheEnabled" value="true"/> // 二级缓存默认就是开启的
		</settings>
		  测试结果,会发出一次sql语句,一级缓存默认开启,缓存生命周期是SqlSession级别
	 */
	@Test
	public void test2() throws Exception {
		InputStream in = Resources.getResourceAsStream("custom/sqlMapConfig3.xml");
		SqlSessionFactory factory2 =  new SqlSessionFactoryBuilder().build(in);
		SqlSession openSession = factory2.openSession();
		UserMapper mapper = openSession.getMapper(UserMapper.class);
		User user1 = mapper.findUserById(40); // 会发出sql语句
		System.out.println(user1);
		
		User user2 = mapper.findUserById(40); // 会发出sql语句
		System.out.println(user2);
		
		openSession.close();
	}

	/**
	 * 二级缓存测试
	 * 二级缓存全局配置默认开启,但是需要每个名称空间配置<cache></cache>,
	 * 即需要全局和局部同时配置,缓存生命周期是SqlSessionFactory级别。
	 */
	@Test
	public void test3() throws Exception {
		InputStream in = Resources.getResourceAsStream("custom/sqlMapConfig3.xml");
		SqlSessionFactory factory2 =  new SqlSessionFactoryBuilder().build(in);
		SqlSession openSession = factory2.openSession();
		UserMapper mapper = openSession.getMapper(UserMapper.class);
		User user1 = mapper.findUserById(40);
		System.out.println(user1);
		
		openSession.close(); // 关闭session
		
		openSession = factory2.openSession();
		mapper = openSession.getMapper(UserMapper.class);
		
		User user3 = mapper.findUserById(40); // 二级缓存全局和局部全部开启才会打印sql
		System.out.println(user3);
	}
	

D'après les résultats du test, on peut voir que lorsque mybaits n'ouvre pas le cache de second niveau, une requête avec la même session sqlsession est à nouveau exécutée. S'il n'y a pas de fermeture, le cache de premier niveau sera vérifié. Lorsque le second- le cache de niveau est activé, si le cache de second niveau est activé, si la requête est fermée, Le cache peut toujours être utilisé (le cache de second niveau est utilisé), le mécanisme de cache est: la première requête, la seconde- le cache de niveau sera vérifié en premier, le cache de premier niveau n'est pas trouvé, et la base de données n'est pas vérifiée à nouveau, et la base de données est d'abord trouvée dans le cache de premier niveau, puis placez-la dans le cache de second niveau. deuxième requête, le cache de second niveau sera vérifié en premier, et si le cache de second niveau est en stock, il sera renvoyé.

Deuxièmement, l'introduction des classes liées au cache mybaits

Les interfaces de niveau supérieur du cache de niveau 1 et du cache de niveau 2 de mybaits sont les mêmes, qui sont toutes deux des classes de cache. Par défaut, l'implémentation du cache de mybaits est un package hashMap, et de nombreux packages implémentent la classe de cache. .

1. Jetons un coup d'œil à la classe Cache et à la structure du package de l'interface de niveau supérieur.

public interface Cache {

  String getId();

  void putObject(Object key, Object value);

  Object getObject(Object key);

  Object removeObject(Object key);

  void clear();

  int getSize();

  ReadWriteLock getReadWriteLock();

}

À l'exception de PerpetualCache , d'autres implémentations de Cache utilisent le mode décorateur. Le PerpetualCache sous-jacent complète le cache réel et ajoute d'autres fonctions sur cette base.

SynchronizedCache : en verrouillant la méthode get / put, il est garanti qu'un seul thread exploite le cache

FifoCache , LruCache : lorsque le cache atteint la limite supérieure, supprimez le cache via la stratégie FIFO ou LRU (premier accès à la mémoire) ( les deux peuvent être utilisées lorsque la stratégie d'invalidation du cache est configurée lorsque l'espace de noms démarre <cache> )

ScheduledCache: avant d'effectuer des opérations telles que get / put / remove / getSize, déterminez si le temps de cache a dépassé le temps de cache maximal défini (la valeur par défaut est d'une heure), si c'est le cas, effacez le cache, c'est-à-dire effacez le cache. de temps en temps

SoftCache / WeakCache: le cache est implémenté via des références logicielles JVM et des références faibles. Lorsque la mémoire JVM est insuffisante, ces caches seront automatiquement nettoyés

TranscationCache: empaquetage transactionnel, c'est-à-dire qu'une opération sur ce cache ne sera pas mise à jour immédiatement dans le cache des délégués. L'opération sera divisée en suppression et ajout de maintenance au conteneur de carte. Une fois la méthode de validation appelée, l'opération réelle est mise en cache. .

2. La principale logique d'implémentation de la classe d'implémentation du cache

1)  PerpetualCache 

public class PerpetualCache implements Cache {

  private String id;

  private Map<Object, Object> cache = new HashMap<Object, Object>();

PerpetualCache est un cache de carte. Il s'agit de l'implémentation de cache par défaut de mybaits. Le cache de premier niveau utilise cette classe. Si le cache de second niveau souhaite utiliser un cache tiers, il existe des packages JAR prêts à l'emploi qui peuvent être utilisés. n'est pas décrit ici.

2)FifoCache  

FifoCache est un package de mise en cache qui implémente la mise en cache du premier entré, premier sorti. Son principe de mise en œuvre est d'utiliser le mécanisme du premier entré, premier sorti de LinkedList.

private final Cache delegate;
  private LinkedList<Object> keyList;
  private int size;

  public FifoCache(Cache delegate) {
    this.delegate = delegate;
    this.keyList = new LinkedList<Object>();
    this.size = 1024;
  }

 public void putObject(Object key, Object value) {
    cycleKeyList(key);
    delegate.putObject(key, value);
  }
  // 主要实现就是添加缓存的时候,顺便添加到list中,这样如果添加前,判断list的尺寸大于size
  // 就移除list中的第一个,并且delegate缓存也移除。
  private void cycleKeyList(Object key) {
    keyList.addLast(key);
    if (keyList.size() > size) {
      Object oldestKey = keyList.removeFirst();
      delegate.removeObject(oldestKey);
    }
  }

3)  LruCache

LruCache est un package de cache qui implémente le mécanisme d'élimination des LRU dont le principe principal est d'utiliser la structure à trois paramètres de LinkList.

new LinkedHashMap <Object, View> (size, 0.75f, true), la fonction du troisième paramètre accessOrder est de savoir s'il faut ajouter l'élément à la fin de la liste liée si on accède à l'élément. Combiné avec une méthode removeEldestEntry protégée de LinkedHashMap, LRU (c'est-à-dire la suppression qui n'a pas été accédée depuis le plus longtemps) peut être implémentée.

 	private MyCache delegate;
	private Map<Object, Object> keyMap;

	public LRUCache(MyCache delegate) {
		super();
		this.delegate = delegate;
		setSize(1024);
	}

	private void setSize(int initialCapacity) {
		keyMap = new LinkedHashMap<Object, Object>(initialCapacity, 0.75f, true) {
			private static final long serialVersionUID = 4267176411845948333L;
			protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
				boolean tooBig = size() > initialCapacity; // 大于尺寸
				if (tooBig) { // ture 移除delegate缓存
					delegate.removeObject(eldest.getKey());
				}
				return tooBig; // 返回true会自动移除LinkHashMap的缓存
			}
		};
	}


   // 其他操作的时候KeyMap进行同步

4) 、SynchronizedCache

SynchronizedCache est un package de verrouillage synchrone. C'est simple, c'est-à-dire que toutes les méthodes d'implémentation qui dépendent des changements d'état du cache sont verrouillées.

@Override
	public synchronized void putObject(Object key, Object value) {
		delegate.putObject(key, value);
	}

	@Override
	public synchronized Object getObject(Object key) {
		return delegate.getObject(key);
	}

	@Override
	public synchronized Object removeObject(Object key) {
		return delegate.removeObject(key);
	}

5) 、 TranscationCache

L'empaquetage de TranscationCache consiste à gérer deux cartes en interne. Une carte installe l'opération de suppression du cache et l'autre carte installe l'opération de mise en cache. Lors de la validation, elle parcourt les deux cartes et exécute l'opération mise en cache. Si elle est réinitialisée, elle sera effacée. Ces deux cartes temporaires. Les variables membres suivantes de cahce et deux classes internes (ces deux classes internes doivent encapsuler l'opération de cache).

	/**
	 * 对需要添加元素的包装,并且传入了delegate缓存,调用此commit就可以用delegate缓存put进添加元素
	 */
	private static class AddEntry {
		private Object key;
		private Object value;
		private MyCache delegate;
		public AddEntry(Object key, Object value, MyCache delegate) {
			super();
			this.key = key;
			this.value = value;
			this.delegate = delegate;
		}
		public void commit() {
			this.delegate.putObject(key, value);
		}
	}

	/**
	 * 对需要移除的元素的包装,并且传入了delegate缓存,调用此commit就可以用delegate移除元素。
	 */
	private static class RemoveEntry {
		private Object key;
		private MyCache delegate;
		public RemoveEntry(Object key, MyCache delegate) {
			super();
			this.key = key;
			this.delegate = delegate;
		}

		public void commit() {
			this.delegate.removeObject(key);
		}
	}
    // 包装的缓存
	private MyCache delegate;
    // 待添加元素的map
	private Map<Object, AddEntry> entriesToAddOnCommit;
	// 待移除元素的map
	private Map<Object, RemoveEntry> entriesToRemoveOnCommit;

Ce qui suit est l'implémentation de base

// 添加的时候,操作的是二个map,既然添加,那么临时remove的map需要remove这个key
	@Override
	public void putObject(Object key, Object value) {
		this.entriesToRemoveOnCommit.remove(key);
		this.entriesToAddOnCommit.put(key, new AddEntry(key, value, delegate));
	}

	@Override
	public Object getObject(Object key) {
		return this.delegate.getObject(key);
	}
    
	// 移除的时候,操作的是二个map,既然移除,那么临时add的map需要remove这个可以。
	@Override
	public Object removeObject(Object key) {
		this.entriesToAddOnCommit.remove(key);
		this.entriesToRemoveOnCommit.put(key, new RemoveEntry(key, delegate));
		return this.delegate.getObject(key); // 这里是为了获得返回值,注意不能用removeObject
	}

	@Override
	public void clear() {
		this.delegate.clear();
		reset();
	}
	
	// 提交
	public void commit() {
		delegate.clear(); // delegate移除
		for (RemoveEntry entry : entriesToRemoveOnCommit.values()) {
			entry.commit(); // 移除remove里的元素
		}
		for (AddEntry entry : entriesToAddOnCommit.values()) {
			entry.commit(); // 添加add里的元素
		}
		reset();
	}

	public void rollback() {
		reset();
	}

3. Implémentation de la clé de cache

La clé mise en cache par mybaits est implémentée sur la base d'une classe CacheKey, et son mécanisme de base est le suivant:

 * Le mécanisme de mise en œuvre de la clé de cache : grâce à la méthode de mise à jour, calculez le hashcode et ajoutez-le à la collection de liste interne pour déterminer s'il est le même, également en fonction des éléments de la liste interne sont tous les mêmes.
 * Mécanisme de création de CacheKey de mybaits : même instruction, offset de paramètre de page interne, limite de paramètre de page interne, instruction sql précompilée, mappage de paramètres.

/**
	 * CacheKey的组装
	 */
	public static void main(String[] args) {
		String mappedStatementId = "MappedStatementId"; // 用字符串描述mybaits的cachekey的元素。
		String rowBounds_getOffset = "rowBounds_getOffset";
		String rowBounds_getLimit = "rowBounds_getLimit";
		String buondSql_getSql = "buondSql_getSql";
		List<String> parameterMappings = new ArrayList<>();
		parameterMappings.add("param1");
		parameterMappings.add("param2");
		
		CacheKey cacheKey = new CacheKey(); // 创建CacheKey
		 
		cacheKey.update(mappedStatementId); // 添加元素到CacheKey
		cacheKey.update(rowBounds_getOffset);
		cacheKey.update(rowBounds_getLimit);
		cacheKey.update(buondSql_getSql);
		cacheKey.update(parameterMappings);
		System.out.println(cacheKey); 
	}

Troisièmement, l' explication détaillée du cache de premier niveau

Nous avons déjà compris l'interface de la classe de cache et son implémentation ci-dessus, et nous pouvons maintenant explorer comment le niveau de cache suivant est utilisé dans mybaits.

Avant de parler du cache de premier niveau, examinons que sqlsession appelle en fait Executor pour fonctionner, et BaseExecutor est l'implémentation de base d'Executor. Il a plusieurs autres implémentations. Nous utilisons SimpleExecutor et une autre

CachingExecutor implémente le cache de deuxième niveau. Commençons par regarder la création du cache lors de la création d'une session, puis BaseExecutor commencera à regarder le cache de premier niveau.

1. Processus de création du cache (la création du cache est lors de la création de SqlSession, nous regardons directement l'openSessionFromDataSource de SqlSsession)

  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();
    }
  }
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) { // 根据Executor类型创建不同的Executor
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction); // 默认的Executor
    }
    if (cacheEnabled) { // 如果开启全局二级缓存
      executor = new CachingExecutor(executor); // 就创建CachingExecutor
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

 Bien que ce qui précède soit le processus de création de l'Executor, il s'agit en fait du processus de création du cache de premier niveau. Le cache de premier niveau est une variable membre d'un Cache dans l'Executor. Le cache de deuxième niveau est réalisé par l'empaquetage d'Executor par CachingExecutor , qui sera analysé en détail plus tard.

2. Interrogez l’entrée BaseExecutor.query pour démarrer l’analyse.

Toutes les requêtes de SqlSession sont implémentées par la méthode de requête de l'Executor appelé. Le code de la classe d'implémentation BaseExecutor est le suivant:

  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); // 缓存key创建
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }

Cette requête consiste principalement à créer une clé de cache (la logique de création a été discutée ci-dessus), et à récupérer sql. Regardez la méthode de requête surchargée appelée par requête.

 public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
 
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache(); // 如果配置需要清除就清除本地缓存
    }
    List<E> list;
    try {
      queryStack++; 
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else { // 从缓存没有取到,查数据库
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) { 
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load(); //处理循环引用?
      }
      deferredLoads.clear(); 
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        clearLocalCache(); // 如果是statement的本地缓存,就直接清除!
      }
    }
    return list; // 取到返回
  }

Dans cette méthode de requête, retirez d'abord le cache local localCache (il s'agit de PerpetualCache), et renvoyez-le s'il est trouvé, et vérifiez la méthode de base de données queryFromDatabase si elle n'est pas trouvée. En outre, le cache de premier niveau peut être configuré en tant que plage d'instructions, c'est-à-dire que le cache local sera effacé pour chaque requête. Regardons à nouveau la méthode queryFromDatabase.

 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER); 
    try { // 查询语句
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    localCache.putObject(key, list); // 查询出来放入一级缓存
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

En outre, si des opérations telles que la mise à jour, le cache local sera supprimé.

public int update(MappedStatement ms, Object parameter) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
    if (closed) throw new ExecutorException("Executor was closed.");
    clearLocalCache();
    return doUpdate(ms, parameter);
  }

Quatrièmement, la réalisation du cache de deuxième niveau

Comme mentionné ci-dessus, le cache de deuxième niveau repose principalement sur l'empaquetage de CachingExecutor, nous pouvons donc comprendre le cache de deuxième niveau en analysant directement cette classe.

1. Explication détaillée des membres de CachingExecutor et de TransactionalCacheManager

public class CachingExecutor implements Executor {

  private Executor delegate;
  private TransactionalCacheManager tcm = new TransactionalCacheManager(); // TransactionalCache的管理类

TransactionalCacheManager : Utilisé pour gérer l'objet de cache de second niveau utilisé par CachingExecutor, un seul champ transactionalCaches est défini

Map finale privée <Cache, TransactionalCache > transactionalCaches = new HashMap <Cache, TransactionalCache> ();

Sa clé est l'objet de cache de deuxième niveau utilisé par CachingExecutor, et la valeur est l'objet TransactionalCache correspondant. Voyons son implémentation ci-dessous.

public class TransactionalCacheManager {
  
  // 装未包装缓存和包装成Transaction缓存的map映射
  private Map<Cache, TransactionalCache> transactionalCaches = new HashMap<Cache, TransactionalCache>();
  // 操作缓存多了一个Cache参数,实际上是调用Transaction的对应方法
  public void clear(Cache cache) {
    getTransactionalCache(cache).clear();
  }

  public Object getObject(Cache cache, CacheKey key) {
    return getTransactionalCache(cache).getObject(key);
  }
  
  public void putObject(Cache cache, CacheKey key, Object value) {
    getTransactionalCache(cache).putObject(key, value);
  }
  
  // 全部缓存commit
  public void commit() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.commit();
    }
  }
  // 全部缓存rollback
  public void rollback() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.rollback();
    }
  }
  // 创建一个TransactionalCache,并把原cache为key放入map维护
  private TransactionalCache getTransactionalCache(Cache cache) {
    TransactionalCache txCache = transactionalCaches.get(cache);
    if (txCache == null) {
      txCache = new TransactionalCache(cache);
      transactionalCaches.put(cache, txCache);
    }
    return txCache;
  }

}

2. Logique de cache de niveau 2

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    Cache cache = ms.getCache(); // 获得二级缓存
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) { // 开启了缓存
        ensureNoOutParams(ms, parameterObject, boundSql);
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key); 
        if (list == null) { // 没有就查询
          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578. Query must be not synchronized to prevent deadlocks
        }
        return list; // 有就返回
      }
    }
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

Ce qui précède est une simple requête du cache de deuxième niveau d'abord, et retourne s'il n'y en a pas, puis vérifie la requête de BaseExcute (qui consiste à vérifier d'abord le cache de premier niveau, trouver le retour, ne pas vérifier la base de données, et mettre retournez-le dans le cache de premier niveau lorsque la base de données est vérifiée).

D'autres opérations telles que la mise à jour et la validation effaceront le cache de deuxième niveau (cela équivaut à une implémentation simple, ce qui signifie que s'il n'y a qu'une requête, le cache de deuxième niveau est toujours valide, et s'il y a une mise à jour, tout les caches de deuxième niveau doivent être effacés).

En outre:

Le cache du cache de second niveau, par défaut, chaque espace de noms partage un cache. L'implémentation de ce cache doit également être empaquetée avec LRUCache, etc. et SynchronizedCache.

Ceci est vérifié au point d'arrêt de Cache cache = ms.getCache () ;. Comment configurer l'empaquetage peut être analysé dans le processus approprié d'analyse de Mapper.xml.

 

Cinq: Résumé de l'utilisation du cache principal et secondaire

1. Configuration du cache de niveau 1:

Comme on peut le voir à partir du code, le cache de premier niveau est activé par défaut et il n'y a aucune instruction de réglage ou de jugement pour contrôler s'il faut effectuer la requête de cache de premier niveau ou ajouter l'opération. Par conséquent, le cache de premier niveau ne peut pas être désactivé.

2. Configuration du cache de niveau 2:

Il y a trois endroits dans la configuration du cache secondaire:

a, commutateur de cache global, mybatis-config.xml

<settings> <setting name = "cacheEnabled" value = "true" /> </settings>

b. L'instance de cache de second niveau mapper.xml sous chaque espace de noms

<cache /> ou reportez-vous au cache des autres espaces de noms <cache-ref namespace = "com.someone.application.data.SomeMapper" />

c. Configurez l'attribut useCache dans le nœud <select>

La valeur par défaut est true, lorsqu'elle est définie sur false, le cache secondaire ne prendra pas effet pour cette instruction select

3. Plage de cache

a, la portée du cache de premier niveau

La portée du cache de premier niveau est configurable:

<settings> <setting name = "localCacheScope" value = "STATEMENT" /> </settings>

Les options de plage sont: SESSION et STATEMENT, la valeur par défaut est SESSION

SESSION: Dans ce cas, toutes les requêtes exécutées dans une session (SqlSession) seront mises en cache

ÉNONCÉ: La session locale n'est utilisée que pour l'exécution des instructions et le cache de premier niveau sera effacé après avoir terminé une requête.

b, la portée du cache secondaire

La portée du cache secondaire est l'espace de noms, c'est-à-dire que plusieurs SqlSessions sous le même espace de noms partagent le même cache

 

4. Suggestions d'utilisation

Il n'est pas recommandé d'utiliser le cache de second niveau. Le cache de second niveau est partagé dans l'étendue de l'espace de noms. Bien que le cycle de vie soit sqlsesisonFacotry, toute mise à jour effacera tous les caches de second niveau. De plus, la seconde -level cache a également des problèmes lors de l'interrogation des tables, (par exemple, vous êtes connecté à une table qui n'est pas dans cet espace de noms, les données de cette table sont modifiées, le cache de deuxième niveau de cet espace de noms n'est pas connu), et d'autres problèmes, il n'est donc pas recommandé de l'utiliser.

Dans certains cas extrêmes, le cache de premier niveau peut contenir des données incorrectes. Une suggestion consiste à les remplacer par la plage STATEMENT et l'autre à ne pas utiliser une session SQL pour interroger à plusieurs reprises la même instruction dans la logique métier.

 

 

finir!

 

 

 

 

 

 

 

Je suppose que tu aimes

Origine blog.csdn.net/shuixiou1/article/details/113664750
conseillé
Classement