Mybatis
Artigo Diretório
- Mybatis
-
- processo de carregamento mybatis
- Cache de primeiro nível Mybatis
-
- Cache de nível 1
-
- Introdução
- Análise de código fonte
-
- 1. Insira a implementação do método () de clearCache DefaultSqlSession
- 2. Entre na interface do Executor
- 3. Insira a implementação do método clearLocalCache () no Executor
- 4. Insira o método localCache.clear ():
- 5.cache.clear (), qual é o objeto cache?
- 1. O método no Executor, o método para criar um cache é o método createCacheKey ()
- Cache secundário
processo de carregamento mybatis
-
O arquivo de configuração global Config.xml é analisado e colocado no objeto de configuração. A configuração de anotação e os arquivos de configuração SQL em Java são encapsulados no objeto de instrução e armazenados na memória, que também é mantida pelo objeto de configuração
-
Carregamento do arquivo de mapeamento: o objeto mappedStatement analisa o sql, e cada objeto sql corresponde a um statementID e armazena o sql analisado no mapa, a chave é o statementId e o valor é o sql analisado, que é armazenado no sqlsource
-
sqlSource armazena informações de sql e também fornece funções de análise de sql
dynamicsqlSource: $ {} analise este tipo de sql dinâmico
RawsqlSource: # {} Analise este tipo de sql dinâmico
StaticsqlSource: Depois de analisar os dois tipos de sql acima, armazene-o em sqlSource, por meio deste sqlSource pode obter BoundSql
combinado com o código-fonte staticSqlSource, será mais fácil de entender
public class StaticSqlSource implements SqlSource {
private final String sql;
private final List<ParameterMapping> parameterMappings;
private final Configuration configuration;
public StaticSqlSource(Configuration configuration, String sql) {
this(configuration, sql, null);
}
public StaticSqlSource(Configuration configuration, String sql, List<ParameterMapping> parameterMappings) {
this.sql = sql;
this.parameterMappings = parameterMappings;
this.configuration = configuration;
}
@Override
public BoundSql getBoundSql(Object parameterObject) {
return new BoundSql(configuration, sql, parameterMappings, parameterObject);
}
}
-
BoundSql:
String sql; sql analisado
Listar mapeamentos de parâmetros; correspondente aos parâmetros de entrada
sqlNode: estrutura em árvore
sqlSource armazena informações de sql, que é uma coleção de sqlNode
Cache de primeiro nível Mybatis
Mybatis tem um cache de primeiro nível e um cache de segundo nível
Cache de nível 1
Introdução
O cache de primeiro nível é baseado em sqlSession
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
sqlSession = sqlSessionFactory.openSession();
DemoMapper demoMapper = sqlSession.getMapper(DemoMapper.class);
// 第一次查询
Demo demo1 = demoMapper.findById(1);
// 第二次查询
Demo demo2 = demoMapper.findById(1);
O código acima usa o mesmo demoMapper para duas consultas consecutivas. A primeira consulta realmente conectará e consultará o banco de dados e, na segunda vez, irá para o cache de primeiro nível de mybatis para buscá-lo. Ou seja, após a primeira consulta, o resultado da consulta será armazenado no cache de primeiro nível e, em seguida, a consulta será pesquisada no cache de primeiro nível novamente após a consulta e será retornado se corresponder, e será consultado no banco de dados se não corresponder.
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
sqlSession = sqlSessionFactory.openSession();
DemoMapper demoMapper = sqlSession.getMapper(DemoMapper.class);
// 第一次查询
Demo demo1 = demoMapper.findById(1);
//更新
Demo demo = new User();
demo.setId(1);
demo.setName("test");
demoMapper.updateDemo(demo);
// 第二次查询
Demo demo2 = demoMapper.findById(1);
No código acima, após a primeira consulta, os dados são atualizados novamente e, em seguida, a segunda consulta é realizada.
Processo :
- A primeira consulta se conecta ao banco de dados e armazena os resultados da consulta no cache de primeiro nível.
- Quando os dados são atualizados, a operação de atualização limpa o cache de primeiro nível, de forma que o cache correspondente no cache de primeiro nível seja esvaziado neste momento.
- Na segunda consulta, ele não será obtido no cache de primeiro nível, portanto, vá até o banco de dados para consultar.
Análise de código fonte
Encontramos a interface SqlSesion. Aquela que parece ter um efeito no cache de primeiro nível é um método clearCache ()
1. Insira a implementação do método () de clearCache DefaultSqlSession
O código-fonte é o seguinte: Para facilitar a visualização, não vou tirar uma captura de tela e colar o código-fonte diretamente
public void clearCache() {
this.executor.clearLocalCache();
}
2. Entre na interface do Executor
3. Insira a implementação do método clearLocalCache () no Executor
O código-fonte implementado na classe BaseExecutor é o seguinte
public void clearLocalCache() {
if (!this.closed) {
this.localCache.clear();
this.localOutputParameterCache.clear();
}
}
4. Insira o método localCache.clear ():
O código-fonte da classe PerpetualCache é o seguinte
public void clear() {
this.cache.clear();
}
5.cache.clear (), qual é o objeto cache?
Olhando para o código-fonte abaixo, o objeto original que executa clear é apenas um objeto de mapa simples. A partir daqui, podemos saber que o cache de primeiro nível de mybatis é armazenado usando o Mapa.
-------------------------------------------------- --------------------Linha divisória---------------------------- ------------------------------------------------
O código-fonte apresentado acima é a limpeza do cache de primeiro nível. E quanto à criação?
Vamos revisar o processo do código-fonte acima
SqlSession – DefaultSqlSession – Executor – BaseExecutor – PerpetualCache
O que é Executor? O executor, sabemos que o executor é utilizado para executar requisições SQL, já que o esvaziamento do cache é executado no exctor, a operação de armazenar os resultados e armazenar os resultados após a execução da requisição SQL também deve ser executada aqui.
1. O método no Executor, o método para criar um cache é o método createCacheKey ()
2. Vá para a classe de implementação BaseExecutor para ver a implementação específica
O código-fonte é o seguinte:
@Override
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);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
Observe que esta linha de código CacheKey key = createCacheKey (ms, parameter, rowBounds, boundSql);
O código-fonte do método createCacheKey () aqui: O conteúdo é gravado diretamente no comentário do seguinte código
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
CacheKey cacheKey = new CacheKey();
// boundSQL的ID,应该就是那个statementID,类似路径包名+类名+sql这样
cacheKey.update(ms.getId());
// 就是SQL中使用的offset,控制查询范围的那种
cacheKey.update(rowBounds.getOffset());
// SQL中有没有limit限制查询数量
cacheKey.update(rowBounds.getLimit());
// 这里就是具体的SQL语句了
cacheKey.update(boundSql.getSql());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 这里value是什么,可以去回顾下加载过程,ParameterMapping存储的是SQL中的参数,所以这里就是存储的参数信息
cacheKey.update(value);
}
}
if (configuration.getEnvironment() != null) {
// issue #176
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}
Resumindo: o cache de primeiro nível é criado aqui. O que está armazenado nele foi comentado no comentário acima, e irei listá-lo abaixo
CacheKey cacheKey = new CacheKey();
// boundSQL的ID,应该就是那个statementID,类似路径包名+类名+sql这样
cacheKey.update(ms.getId());
// 就是SQL中使用的offset,控制查询范围的那种
cacheKey.update(rowBounds.getOffset());
// SQL中有没有limit限制查询数量
cacheKey.update(rowBounds.getLimit());
// 这里就是具体的SQL语句了
cacheKey.update(boundSql.getSql());
// 这里value是什么,可以去回顾下加载过程,ParameterMapping存储的是SQL中的参数,所以这里就是存储的参数信息
cacheKey.update(value);
Cache secundário
Introdução
O cache secundário também armazena os resultados da consulta e o SQL correspondente. Após a consulta ser concluída, ela é colocada no cache e a consulta pode ser recuperada dele novamente.
Então, qual é a diferença entre ele e o cache de primeiro nível
O cache de primeiro nível foi introduzido acima e é baseado em SQLSession, ou seja, a mesma sqlSession.
O cache de segundo nível é baseado no nameSpace do mapeador, que é o cache de segundo nível de um mapeador (ou vários mapeadores com o mesmo nameSpace). Pode haver várias sqlSessions.
E o cache de segundo nível precisa ser ativado manualmente
usar
Adicione o seguinte código no arquivo de configuração global
Em seguida, adicione o seguinte código ao mapeador específico que você precisa usar
A classe de implementação usada por mybatis por padrão é a classe de cache PerpetualCache rastreada do código-fonte acima, que implementa a interface de cache. Portanto, aqui também podemos definir a classe de implementação do cache de segundo nível e implementar a interface do cache.
Cache secundário distribuído
Como o mybatis implementa o cache distribuído? Se você operar de acordo com o cache acima, será apenas um único cache.
Cache distribuído: vários servidores podem acessar o cache de operação
Aqui podemos usar Redis combinado com mybatis para implementar o cache distribuído
O método de uso é o seguinte:
1. Dependência do aumento do arquivo Pom
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-redis</artifactId>
<version>1.0.0-beta2</version>
</dependency>
2. Especifique a classe de implementação de cache a ser usada no mapeador usado
<cache type="org.mybatis.caches.redis.RedisCache"></cache>
3. Configure as informações do redis, etc.
Análise de código fonte
Vamos dar uma olhada no cache secundário implementado por redisCache
1. Método de construção
Pelo código aqui, podemos ver que o cache secundário implementado pelo Redis usa jedis para operar.
public RedisCache(final String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
this.id = id;
RedisConfig redisConfig = RedisConfigurationBuilder.getInstance().parseConfiguration();
pool = new JedisPool(redisConfig, redisConfig.getHost(), redisConfig.getPort(),
redisConfig.getConnectionTimeout(), redisConfig.getSoTimeout(), redisConfig.getPassword(),
redisConfig.getDatabase(), redisConfig.getClientName());
}
2. Put get get.A partir daqui, qual é a estrutura de armazenamento do cache secundário?
A partir do comando hset, é fácil saber que a estrutura hash é usada para armazenar dados em cache
@Override
public void putObject(final Object key, final Object value) {
execute(new RedisCallback() {
@Override
public Object doWithRedis(Jedis jedis) {
jedis.hset(id.toString().getBytes(), key.toString().getBytes(), SerializeUtil.serialize(value));
return null;
}
});
}
@Override
public Object getObject(final Object key) {
return execute(new RedisCallback() {
@Override
public Object doWithRedis(Jedis jedis) {
return SerializeUtil.unserialize(jedis.hget(id.toString().getBytes(), key.toString().getBytes()));
}
});
}