Cache de nível 1
Mybatis suporta cache. Por padrão, o cache de primeiro nível está ativado. O cache de primeiro nível está no nível SqlSession , e o cache é o objeto SqlSession. Quando a instrução SQL e os parâmetros são os mesmos, quando usamos o mesmo objeto SqlSession para chamar o mesmo método Mapper, só precisamos executar SQL uma vez, porque após a primeira execução do método Mapper, Mybatis irá armazená-lo em cache e consultá-lo mais tarde Se os parâmetros e instruções SQL forem iguais, os dados em cache serão buscados diretamente em vez de consultar o banco de dados, o que melhora muito a eficiência da consulta.
Aqui estão os diagramas de cache de outras pessoas, que são claros e claros!
O ciclo de vida do cache de primeiro nível
- O cache de primeiro nível MyBatis é baseado em SQLSession.Quando MyBatis abre uma sessão de banco de dados, ele irá criar um novo objeto SqlSession, e haverá um novo objeto Executor no objeto SqlSession. O objeto Executor contém um novo objeto PerpetualCache; quando a sessão termina, o objeto SqlSession e seus objetos Executor internos e objetos PerpetualCache também são liberados.
- Se SqlSession chamar o método close (), o objeto PerpetualCache do cache de primeiro nível será liberado e o cache de primeiro nível ficará indisponível.
- Se SqlSession chamar clearCache (), os dados no objeto PerpetualCache serão limpos, mas o objeto ainda pode ser usado.
- Qualquer operação de atualização (update (), delete (), insert ()) realizada em SqlSession irá limpar os dados do objeto PerpetualCache, mas o objeto pode continuar a ser usado
Condição de disparo de cache de nível 1
- O statementId de entrada é o mesmo
- O intervalo de resultados no conjunto de resultados necessário para a consulta é o mesmo
- A string de instrução SqlPreparedstatement (boundSql.getSql ()) que é finalmente passada para JDBC java.sql. Produzida por esta consulta é a mesma
- Os valores dos parâmetros passados para java.sql.Statement a serem definidos são os mesmos
Casos de uso de cache de primeiro nível
Em primeiro lugar, a classe de entidade ( observação: precisa ser serializável, implementei a interface serializável aqui )
public class TtUser implements Serializable{
private Long id;
private String loginName;
private String password;
// 这里省略get、set方法
}
método dao
public interface TtUserMapper {
TtUser selectByPrimaryKey(Long id);
int updateByPrimaryKeySelective(TtUser record);
}
método mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xiateng.dao.user.TtUserMapper">
<!--<cache eviction="LRU" flushInterval="10000" size="1024" />-->
<resultMap id="BaseResultMap" type="com.xiateng.entity.TtUser">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="login_name" jdbcType="VARCHAR" property="loginName" />
<result column="password" jdbcType="VARCHAR" property="password" />
</resultMap>
<sql id="Base_Column_List">
id, login_name, password
</sql>
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from t_user
where id = #{id,jdbcType=BIGINT}
</select>
<update id="updateByPrimaryKeySelective" parameterType="com.xiateng.entity.TtUser">
update t_user
<set>
<if test="loginName != null">
login_name = #{loginName,jdbcType=VARCHAR},
</if>
<if test="password != null">
password = #{password,jdbcType=VARCHAR},
</if>
<if test="blong != null">
blong = #{blong,jdbcType=SMALLINT},
</if>
</set>
where id = #{id,jdbcType=BIGINT}
</update>
</mapper>
Chamada de controlador
@RequestMapping(value = "/a")
@ResponseBody
public String a(){
// 开启一个SqlSession(会话)
SqlSession sqlSession = sqlSessionFactory.openSession();
TtUserMapper mapper = sqlSession.getMapper(TtUserMapper.class);
TtUser ttUser = mapper.selectByPrimaryKey(1L);
System.out.println("-----------------------------------------"+ttUser);
System.out.println("-----------------------------------------第二次执行");
TtUser ttUser2 = mapper.selectByPrimaryKey(1L);
System.out.println("-----------------------------------------"+ttUser2);
// 关闭会话
sqlSession.close();
return "成功";
}
Impressão de console
Veremos que sql só é impresso uma vez, indicando que o banco de dados não foi consultado na segunda vez
Cache secundário
O cache de segundo nível é o cache do objeto SqlSessionFactory em Mybatis. A SqlSession criada pelo mesmo objeto SqlSessionFactory compartilha seu cache, mas os dados em cache não são o objeto, então o resultado da segunda consulta do cache de segundo nível é o mesmo que o
primeiro. Os objetos de entrada não são os mesmos. O cache de segundo nível é o cache de nível de namespace ,
Olha a foto
Preste atenção ao uso de cache secundário
- Todas as instruções de seleção no arquivo de instrução de mapeamento serão armazenadas em cache.
- Todas as instruções de inserção, atualização e exclusão no arquivo de instrução de mapeamento atualizarão o cache.
- O cache será recuperado usando o algoritmo menos usado recentemente (LRU) padrão.
- De acordo com a programação, como Sem intervalo de liberação (CNFI não tem intervalo de atualização), o cache não será liberado em nenhuma ordem cronológica.
- O cache armazenará 1024 referências para listar coleções ou objetos (não importa o que o método de consulta retorne)
- O cache será considerado como um cache de leitura / gravação (leitura / gravação), o que significa que a recuperação do objeto não é compartilhada e pode ser modificada com segurança pelo responsável pela chamada, sem interferir com possíveis modificações feitas por outros chamadores ou threads.
Mybatis não habilita o cache de segundo nível por padrão. Se você quiser habilitá-lo, você precisa configurá-lo, e o POJO retornado deve ser serializável (a classe de entidade implementa Serializable). Vamos dar um exemplo:
As entidades e métodos ainda são os anteriores, mas adicione a configuração <cache /> em mapper.xml como segue
<cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>
Significado da propriedade:
- despejo: Representa a estratégia de recuperação de cache.Atualmente MyBatis fornece as seguintes estratégias.
- LRU, o menos usado recentemente, um objeto que não é usado há mais tempo
- FIFO, primeiro a entrar, primeiro a sair, remove os objetos na ordem em que entram no cache
- SOFT, referência flexível, remove objetos com base no status do coletor de lixo e regras de referência flexível
- FRACA, referência fraca, remove objetos mais ativamente com base no status do coletor de lixo e regras de referência fraca. LRU é usado aqui para remover a imagem do par que não é usada por mais tempo. FlushInterval: tempo de intervalo de atualização, em milissegundos, aqui está configurado para atualizar em 100 segundos, se você não configurar, ele será atualizado quando SQL é executado o Cache.
- size: O número de referências, um número inteiro positivo, que representa o número máximo de objetos que o cache pode armazenar e não deve ser definido muito grande. Muita configuração causará estouro de memória. 1.024 objetos são configurados aqui
- readOnly: somente leitura, o que significa que os dados em cache só podem ser lidos, mas não modificados. A vantagem dessa configuração é que podemos ler o cache rapidamente. A desvantagem é que não temos como modificar o cache. Seu valor padrão é falso, o que não nos permite modificá-lo.
Em seguida, configure Mybatis para ativar o cache secundário, porque sou um projeto SpringBoot, então adicionei a seguinte configuração ao arquivo application.yml:
Finalmente teste o código do controlador
@RequestMapping(value = "/b")
@ResponseBody
public Map<String, Object> b(){
Map map = new HashMap();
TtUser ttUser = ttUserMapper.selectByPrimaryKey(1L);
map.put("ttUser",ttUser);
System.out.println("-----------------------------------------"+ttUser);
System.out.println("-----------------------------------------第二次执行");
TtUser ttUser2 = ttUserMapper.selectByPrimaryKey(1L);
map.put("ttUser2",ttUser2);
System.out.println("-----------------------------------------"+ttUser2);
System.out.println("-----------------------------------------第三次执行");
TtUser ttUser3 = ttUserMapper.selectByPrimaryKey(2L);
System.out.println("-----------------------------------------"+ttUser3);
map.put("ttUser3",ttUser3);
return map;
}
Resultado de impressão
A partir dos resultados de impressão, descobriremos que a primeira consulta imprimiu a instrução sql, ela consultou diretamente o banco de dados, a segunda consulta não imprimiu a instrução sql, porque os parâmetros da segunda vez, statementId, sql são os mesmos, atinge diretamente o cache secundário. Não há consulta de banco de dados e na terceira vez os parâmetros são diferentes, a instrução sql é impressa e o banco de dados é consultado diretamente
A diferença entre o cache de primeiro nível e o cache de segundo nível
Cache de nível 1 : habilitado por padrão, baseado em SQLSession, o objeto SQLSession precisa ser construído ao operar o banco de dados, há um HashMap no objeto para armazenar dados em cache e diferentes SQLSessions não são afetados diretamente. Adições, exclusões e alterações são executadas em uma SQLSession e enviadas ao banco de dados.O cache de primeiro nível será esvaziado para evitar leituras sujas.
Quando uma sqlsession é fechada, o cache de primeiro nível também desaparece.
Cache secundário : desativado por padrão. Com base no namespace, várias SQLSessions no mesmo namespace compartilharão um cache. O cache secundário também usa HashMap para armazenamento de dados. Comparado com o cache primário, o cache secundário tem um escopo maior e pode abranger vários SQLSessions.