Análisis del código fuente de Mybaits (5) La explicación más detallada de la caché de primer nivel y la caché de segundo nivel

Análisis del código fuente de Mybaits (5) Explicación detallada de la caché de primer nivel y la caché de segundo nivel

Prólogo: El artículo anterior explicaba la fuente de datos mybaits, este artículo explicaba el uso básico de la caché de primer nivel de mybaits, la caché de segundo nivel y la implementación principal.

Este artículo se divide principalmente en las siguientes partes:

El uso y prueba de la caché de primer nivel y la caché de segundo nivel

Introducción a las clases relacionadas con mybaits cache

Caché de nivel 1 detallado

Caché detallado de segundo nivel

1. El uso y prueba de la caché de primer nivel y la caché de segundo nivel

En mybaits, la memoria caché de primer nivel está habilitada de forma predeterminada, el ciclo de vida de la memoria caché está en el nivel de sesión SQL y la configuración global de la memoria caché de segundo nivel está habilitada de forma predeterminada, pero también debe habilitarla en el espacio de nombres para usar el caché de segundo nivel, el caché de segundo nivel El ciclo de vida es sqlsessionFactory, y el alcance de la operación de caché es que cada asignador corresponde a un caché (es por eso que debe habilitarse en el espacio de nombres configurado por el asignador para que surta efecto)

1. Agregue la siguiente configuración en SqlMapper, cacheEnabled es responsable de activar el caché de segundo nivel, logImpl es responsable de imprimir sql (podemos probar si el caché se ha ido según si imprimimos el sql real)

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

2. Prueba

	
	/**
	 * 一级缓存测试 :
	 * 	测试前,需要加下面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);
	}
	

A partir de los resultados de la prueba, se puede ver que cuando mybaits no abre la caché de segundo nivel, se ejecuta nuevamente una consulta con la misma sesión sql.Si no hay cierre, se verificará la caché de primer nivel. la caché de nivel está activada, si la caché de segundo nivel está activada, si la consulta está cerrada, la caché aún se puede usar (se usa la caché de segundo nivel), el mecanismo de caché es: la primera consulta, el segundo- La caché de nivel se comprobará primero, la caché de primer nivel no se encuentra y la base de datos no se vuelve a comprobar, y la base de datos se encuentra primero en la caché de primer nivel, y luego se coloca en la caché de segundo nivel. segunda consulta, la caché de segundo nivel se comprobará primero, y si la caché de segundo nivel está disponible, se devolverá.

Dos, la introducción de clases relacionadas con la caché mybaits.

Las interfaces de nivel superior de la caché mybaits de nivel 1 y la caché de nivel 2 son las mismas, y ambas son clases de caché. De forma predeterminada, la implementación de la caché de mybaits es un paquete hashMap y hay muchos paquetes que implementan la clase de caché .

1. Echemos un vistazo a la clase Cache y la estructura del paquete de la interfaz de nivel superior.

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();

}

A excepción de PerpetualCache , otras implementaciones de caché utilizan el modo decorador. El PerpetualCache subyacente completa el caché real y agrega otras funciones sobre esta base.

SynchronizedCache : al bloquear el método get / put, se garantiza que solo un subproceso opera el caché

FifoCache , LruCache : cuando la caché alcanza el límite superior, elimine la caché a través de la estrategia FIFO o LRU (acceso más temprano a la memoria) ( las dos se pueden usar cuando la estrategia de invalidación de caché está configurada cuando el espacio de nombres comienza <caché> )

ScheduledCache: antes de realizar operaciones como get / put / remove / getSize, determine si el tiempo de caché ha excedido el tiempo máximo de caché establecido (el valor predeterminado es una hora), si es así, borre el caché, es decir, borre el caché de vez en cuando

SoftCache / WeakCache: La caché se implementa a través de referencias suaves y referencias débiles de JVM. Cuando la memoria de la JVM es insuficiente, estas cachés se limpiarán automáticamente.

TranscationCache: el empaquetado transaccional, es decir, una operación en esta caché no se actualizará inmediatamente en la caché delegada. La operación se dividirá en mantenimiento de eliminación y adición al contenedor de mapas. Una vez que se llama al método de confirmación, la operación real se almacena en caché. .

2. La lógica de implementación principal de la clase de implementación de caché

1) PerpetualCache 

public class PerpetualCache implements Cache {

  private String id;

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

PerpetualCache es una caché de mapa. Esta es la implementación de caché predeterminada de mybaits. La caché de primer nivel usa esta clase. Si la caché de segundo nivel quiere usar una caché de terceros, hay paquetes jar listos para usar que se pueden usar, que no se describe aquí.

2) FifoCache  

FifoCache es un paquete de almacenamiento en caché que implementa el almacenamiento en caché primero en entrar, primero en salir. Su principio de implementación es utilizar el mecanismo LinkedList primero en entrar, primero en salir.

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 es un paquete de caché que implementa el mecanismo de eliminación de LRU, su principio fundamental es utilizar la estructura de tres parámetros de LinkList.

new LinkedHashMap <Object, View> (size, 0.75f, true), la función del tercer parámetro accessOrder es si agregar el elemento al final de la lista vinculada si se accede al elemento. Combinado con un método removeEldestEntry protegido de LinkedHashMap, se puede implementar LRU (es decir, la eliminación a la que no se ha accedido durante más tiempo).

 	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) 、Caché sincronizado

SynchronizedCache es un paquete de bloqueo síncrono, esto es simple, es decir, todos los métodos de implementación que dependen de los cambios de estado de la caché están bloqueados.

@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

El paquete de TranscationCache es mantener dos mapas internamente. Un mapa instala la operación de eliminación de caché y el otro mapa instala la operación de colocación de caché. Cuando se confirma, atraviesa los dos mapas y ejecuta la operación de almacenamiento en caché. Si se restablece, se borrará Estos dos mapas temporales. Las siguientes variables miembro de cahce y dos clases internas (estas dos clases internas son para envolver la operación de caché).

	/**
	 * 对需要添加元素的包装,并且传入了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;

La siguiente es la implementación principal

// 添加的时候,操作的是二个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. Implementación de la clave de caché

La clave almacenada en caché por mybaits se implementa en función de una clase CacheKey, y su mecanismo principal es el siguiente:

 * El mecanismo de implementación de la clave de caché : a través del método de actualización, calcule el código hash y agréguelo a la colección de la lista interna para determinar si es la misma, también en función de que los elementos de la lista interna sean todos iguales.
 * Mecanismo para crear CacheKey de mybaits : misma declaración, desplazamiento de parámetro de página interno, límite de parámetro de página interno, declaración sql precompilada, mapeo de parámetros.

/**
	 * 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); 
	}

Tres, la explicación detallada del caché de primer nivel.

Ya hemos entendido la interfaz de la clase de caché y su implementación anterior, y ahora podemos explorar cómo se usa el siguiente nivel de caché en mybaits.

Antes de hablar sobre la caché de primer nivel, revisemos que sqlsession realmente llama a Executor para operar, y BaseExecutor es la implementación básica de Executor. Tiene varias otras implementaciones. Usamos SimpleExecutor y otra

CachingExecutor implementa la caché de segundo nivel. Comencemos mirando la creación de la caché al crear una sesión, y luego BaseExecutor comenzará a buscar en la caché de primer nivel.

1. Proceso de creación de caché (la creación de caché es cuando se crea SqlSession, miramos directamente el 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;
  }

 Aunque lo anterior es el proceso de creación del Ejecutor, en realidad es el proceso de creación del caché de primer nivel El caché de primer nivel es una variable miembro de un Caché en el Ejecutor. La caché de segundo nivel se realiza por CachingExecutor packaging 's de Ejecutor, que será analizado en detalle más adelante.

2. Consulta la entrada BaseExecutor.query para iniciar el análisis

Todas las consultas de SqlSession se implementan mediante el método de consulta del llamado Executor. El código de la clase de implementación BaseExecutor es el siguiente:

  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);
 }

Esta consulta es principalmente para crear una clave de caché (la lógica de creación se ha discutido anteriormente) y para recuperar sql. Mire el método de consulta sobrecargado llamado por query.

 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; // 取到返回
  }

En este método de consulta, primero saque el caché local localCache (esto es PerpetualCache), y regrese si lo encuentra, y verifique el método de base de datos queryFromDatabase si no lo encuentra. Además, la caché de primer nivel se puede configurar como un rango de instrucciones, es decir, la caché local se borrará para cada consulta. Veamos nuevamente el método 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;
  }

Además, si se realizan operaciones como la actualización, se eliminará la caché local.

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);
  }

Cuarto, la realización del caché de segundo nivel.

Como se mencionó anteriormente, la caché de segundo nivel se basa principalmente en el empaquetado de CachingExecutor, por lo que podemos comprender la caché de segundo nivel analizando directamente esta clase.

1. Explicación detallada de los miembros de CachingExecutor y TransactionalCacheManager

public class CachingExecutor implements Executor {

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

TransactionalCacheManager : se utiliza para administrar el objeto de caché de segundo nivel utilizado por CachingExecutor, solo se define un campo transaccionalCaches

Mapa final privado <Caché, TransactionalCache > transactionalCaches = new HashMap <Cache, TransactionalCache> ();

Su clave es el objeto de caché de segundo nivel utilizado por CachingExecutor, y el valor es el objeto TransactionalCache correspondiente. Veamos su implementación a continuación.

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. Lógica de caché de caché de nivel 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);
  }

Lo anterior es una consulta simple de la caché de segundo nivel primero, y regresa si no hay, luego verifica la doquery de BaseExcute (que es verificar primero la caché de primer nivel, encontrar la devolución, no verificar la base de datos y poner vuelve a la caché de primer nivel cuando se comprueba la base de datos).

Otras operaciones como actualizar y confirmar borrarán la caché de segundo nivel (esto equivale a una implementación simple, lo que significa que si solo hay una consulta, la caché de segundo nivel siempre es válida, y si hay una actualización, todas las cachés de segundo nivel deben borrarse).

Además:

La caché de la caché de segundo nivel, por defecto, cada espacio de nombres comparte una caché La implementación de esta caché también debe estar empaquetada con LRUCache, etc. y SynchronizedCache.

Esto se verifica en el punto de interrupción de Cache cache = ms.getCache (); La forma de configurar el paquete se puede analizar en el proceso relevante de análisis de Mapper.xml.

 

Cinco: Resumen del uso de la caché primaria y secundaria

1. Configuración de caché de nivel 1:

Como se puede ver en el código, la caché de primer nivel está activada de forma predeterminada y no hay una configuración o una declaración de juicio para controlar si se realiza la consulta de la caché de primer nivel o se agrega la operación. Por lo tanto, la caché de primer nivel no se puede desactivar.

2. Configuración de caché de nivel 2:

Hay tres lugares en la configuración de la caché secundaria:

a, conmutador de caché global, mybatis-config.xml

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

b. La instancia de caché de segundo nivel mapper.xml debajo de cada espacio de nombres

<cache /> o consulte la caché de otros espacios de nombres <cache-ref namespace = "com.someone.application.data.SomeMapper" />

c. Configure el atributo useCache en el nodo <select>

El valor predeterminado es verdadero, cuando se establece en falso, la caché secundaria no tendrá efecto para esta instrucción de selección

3. Rango de caché

a, el alcance de la caché de primer nivel

El alcance de la caché de primer nivel es configurable:

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

Las opciones de rango son: SESIÓN y DECLARACIÓN, el valor predeterminado es SESIÓN

SESIÓN: En este caso, todas las consultas ejecutadas en una sesión (SqlSession) se almacenarán en caché.

DECLARACIÓN: La sesión local solo se utiliza para la ejecución de declaraciones, y la caché de primer nivel se borrará después de completar una consulta.

b, el alcance de la caché secundaria

El alcance de la caché secundaria es el espacio de nombres, es decir, varias SqlSessions bajo el mismo espacio de nombres comparten la misma caché

 

4. Sugerencias de uso

No se recomienda utilizar la caché de segundo nivel. La caché de segundo nivel se comparte en el ámbito del espacio de nombres. Aunque el ciclo de vida es sqlsesisonFacotry, cualquier actualización borrará todas las cachés de segundo nivel. Además, la segunda El caché de nivel también tiene problemas al consultar tablas (por ejemplo, se conectó a una tabla que no está en este espacio de nombres, los datos de esa tabla se cambiaron, el caché de segundo nivel de este espacio de nombres no se conoce) y algunos otros problemas, por lo que no se recomienda su uso.

En algunos casos extremos, la caché de primer nivel puede tener datos sucios. Una sugerencia es cambiarla al rango STATEMENT, y la otra es no usar una sqlsession para consultar repetidamente la misma declaración en la lógica empresarial.

 

 

¡fin!

 

 

 

 

 

 

 

Supongo que te gusta

Origin blog.csdn.net/shuixiou1/article/details/113664750
Recomendado
Clasificación