Aprendizagem MyBatis (1) — Etapas simples de uso do cache de segundo nível MyBatis

1. Uso de cache de segundo nível

No MyBatis , o cache de segundo nível não está habilitado por padrão. Se quiser usá-lo, você precisa habilitá-lo manualmente. mybatis-config.xmlDefinir no arquivo de configuraçãocacheEnabled = verdadeiro, a configuração é a seguinte:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <!--对应的设置都在 Configuration 配置类保存着,直接点进源码查看即可-->
  <settings>
    <!--开启二级缓存-->
    <setting name="cacheEnabled" value="true"/>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="multipleResultSetsEnabled" value="true"/>
    <setting name="useColumnLabel" value="true"/>
    <setting name="useGeneratedKeys" value="false"/>
    <setting name="autoMappingBehavior" value="PARTIAL"/>

    <!--修改默认 SQL 执行器,默认是 SIMPLE,但是修改成 REUSE 会好一点点,这也是数据库优化的一个关键之处-->
    <setting name="defaultExecutorType" value="REUSE"/>
    <setting name="defaultStatementTimeout" value="25"/>
    <setting name="safeRowBoundsEnabled" value="false"/>
    <setting name="mapUnderscoreToCamelCase" value="false"/>

    <!--修改作用域,查看 LocalCacheScope 枚举值,修改成 STATEMENT 之后一级缓存失效,
    但是不一定没用一级缓存,只是作用域缩小了而已,后面的嵌套、子查询就用到了一级缓存-->
    <setting name="localCacheScope" value="SESSION"/>
    <setting name="jdbcTypeForNull" value="OTHER"/>
    <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
  </settings>
  <plugins>
    <plugin interceptor="org.apache.ibatis.gwmtest.plugin.MyPageInterceptor"></plugin>
  </plugins>

  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/gwmdb?useSSL=false"/>
        <property name="username" value="root"/>
        <property name="password" value="itsme999"/>
      </dataSource>
    </environment>
  </environments>


  <mappers>
    <mapper resource="mapper/PersonMapper.xml"/>
  </mappers>

</configuration>

Ou modifique-o através do método de configuração Configuration e chameconfiguração.setCacheEnabled (verdadeiro)código mostrado abaixo:

public class MyBatisCacheTest {
    
    

  private static SqlSessionFactory sqlSessionFactory;
  private static Configuration configuration;
  private static JdbcTransaction jdbcTransaction;
  private static Connection connection;
  private static final String resource = "/mybatis-config.xml";
  private static MappedStatement mappedStatement;
  private static SqlSession sqlSession;
  private static SqlSession sqlSession2;


  static {
    
    
    try {
    
    
      InputStream inputStream = MyBatisCacheTest.class.getResourceAsStream(resource);
      sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
      configuration = sqlSessionFactory.getConfiguration();
      configuration.setCacheEnabled(true);
      connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/gwmdb?useSSL=false&allowPublicKeyRetrieval=true", "root", "itsme999");
      jdbcTransaction = new JdbcTransaction(connection);
      String statement = "org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson";
      mappedStatement = configuration.getMappedStatement( statement);
      // 注意这里设置了自动提交
      sqlSession = sqlSessionFactory.openSession(true);
      sqlSession2 = sqlSessionFactory.openSession(true);
    } catch (Exception e) {
    
    
      e.printStackTrace();
    }
  }
}

Lembra como usar o cache de segundo nível depois de ligar o switch? Existem duas maneiras: anotação , configuração xml e combinação de anotação + configuração . Prepare dois arquivos com antecedência: PersonMapper.xml e interface PersonMapper .

1. Use o cache de anotação—@CacheNamespace

Adicione anotações diretamente à interface PersonMapper @CacheNamespacee @CacheNamespacedefina o namespace do cache. Uma vez adicionada esta anotação, é equivalente a preparar um cache de segundo nível no arquivo de interface PersonMapper . O código é o seguinte:

@CacheNamespace
public interface PersonMapper {
    
    

  @Select("select id,user_name as userName,age as age from test_user where id = #{myId}")
  Person getPerson(@Param("myId") Integer id);
}

O código de uso é o seguinte:

  public static void main(String[] args) throws Exception {
    
    
    PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);

    Person person = mapper.getPerson(1);
    sqlSession.commit();
    Person person1 = mapper.getPerson(1);

    System.out.println("person==person1 = " + (person == person1));
  }

Deve-se notar aqui que o cache de segundo nível deve ser enviado antes de ser atingido. Caso contrário, por que a taxa de acerto do cache L2 é 0,0 ? O código-fonte será analisado posteriormente e o resultado final será o seguinte:

2023-02-16 14:31:37,201 DEBUG [main] -org.apache.ibatis.transaction.jdbc.JdbcTransaction Opening JDBC Connection
2023-02-16 14:31:37,216 DEBUG [main] -org.apache.ibatis.datasource.pooled.PooledDataSource Created connection 19986569.
2023-02-16 14:31:37,222 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson ==>  Preparing: select id,user_name as userName,age as age from test_user where id = ?
2023-02-16 14:31:37,276 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson ==> Parameters: 1(Integer)
2023-02-16 14:31:37,350 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson <==      Total: 1
2023-02-16 14:31:37,364  WARN [main] -org.apache.ibatis.io.SerialFilterChecker As you are using functionality that deserializes object streams, it is recommended to define the JEP-290 serial filter. Please refer to https://docs.oracle.com/pls/topic/lookup?ctx=javase15&id=GUID-8296D8E8-2B93-4B9A-856E-0A65AF9B8C66
2023-02-16 14:31:37,369 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper Cache Hit Ratio [org.apache.ibatis.gwmtest.dao.PersonMapper]: 0.5
person==person1 = false

veráTaxa de acertos do cacheA taxa de acerto é 0,5, indicando que o cache de segundo nível foi atingido. Mas no final descobri: person==person1 = falseisso ocorre porque a desserialização regenera uma instância do tipo Person. Definitivamente duas instâncias completamente diferentes. Por exemplo, ao transmitir Pessoa pela rede, a Pessoa gerada pela extremidade receptora pode ser a mesma que a instância Pessoa na extremidade emissora.

Observe que a anotação @CacheNamespace é adicionada ao PersonMapper neste momento , o que equivale ao cache de segundo nível que está sendo preparado na interface PersonMapper. E a consulta também usa @Selecto método de anotação durante o teste, e o teste usa o mesmo SqlSession. Agora vou fazer dois SqlSession diferentes e testá-los. O código é o seguinte:

  public static void main(String[] args) throws Exception {
    
    
    PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);

    PersonMapper mapper1 = sqlSession2.getMapper(PersonMapper.class);
    Person person = mapper.getPerson(1);
    sqlSession.commit();
    Person person1 = mapper1.getPerson(1);
    mapper.getPerson(1);

    System.out.println("person==person1 = " + (person == person1));
  }

resultado da operação:

2023-02-16 15:11:27,441 DEBUG [main] -org.apache.ibatis.transaction.jdbc.JdbcTransaction Opening JDBC Connection
2023-02-16 15:11:27,453 DEBUG [main] -org.apache.ibatis.datasource.pooled.PooledDataSource Created connection 19986569.
2023-02-16 15:11:27,460 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson ==>  Preparing: select id,user_name as userName,age as age from test_user where id = ?
2023-02-16 15:11:27,511 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson ==> Parameters: 1(Integer)
2023-02-16 15:11:27,572 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson <==      Total: 1
2023-02-16 15:11:27,586  WARN [main] -org.apache.ibatis.io.SerialFilterChecker As you are using functionality that deserializes object streams, it is recommended to define the JEP-290 serial filter. Please refer to https://docs.oracle.com/pls/topic/lookup?ctx=javase15&id=GUID-8296D8E8-2B93-4B9A-856E-0A65AF9B8C66
2023-02-16 15:11:27,592 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper Cache Hit Ratio [org.apache.ibatis.gwmtest.dao.PersonMapper]: 0.5
2023-02-16 15:11:27,592 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper Cache Hit Ratio [org.apache.ibatis.gwmtest.dao.PersonMapper]: 0.6666666666666666
person==person1 = false

Verifica-se que o cache secundário ainda pode ser atingido, o que permite que diferentes SqlSession acessem o cache secundário ao mesmo tempo em multithreads para melhorar a eficiência da consulta.

Agora vamos adicionar outro método de consulta getPerson2() à interface PesonMapper. Este método não usa mais o @Selectmétodo. O código é o seguinte:

@CacheNamespace
public interface PersonMapper {
    
    
  
  @Select("select id,user_name as userName,age as age from test_user where id = #{666}")
  Person getPerson(Integer id);

  Person getPerson2(Integer id);
}

Em seguida, implemente este método no arquivo de configuração PesonMapper.xml , a configuração é a seguinte:

<?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="org.apache.ibatis.gwmtest.dao.PersonMapper">
  
  <select id="getPerson2" resultType="org.apache.ibatis.gwmtest.entity.Person">
    select id,user_name as userName,age as age from test_user where id = #{666}
  </select>
</mapper>

Então o código de teste é o seguinte:

  public static void ma333in(String[] args) throws Exception {
    
    
    PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);

    PersonMapper mapper1 = sqlSession2.getMapper(PersonMapper.class);
    Person person = mapper.getPerson2(1);
    sqlSession.commit();
    Person person1 = mapper1.getPerson2(1);

    System.out.println("person==person1 = " + (person == person1));
  }

Resultado de saída:

2023-02-16 15:22:07,088 DEBUG [main] -org.apache.ibatis.transaction.jdbc.JdbcTransaction Opening JDBC Connection
2023-02-16 15:22:07,102 DEBUG [main] -org.apache.ibatis.datasource.pooled.PooledDataSource Created connection 1487470647.
2023-02-16 15:22:07,108 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson2 ==>  Preparing: select id,user_name as userName,age as age from test_user where id = ?
2023-02-16 15:22:07,159 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson2 ==> Parameters: 1(Integer)
2023-02-16 15:22:07,200 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson2 <==      Total: 1
2023-02-16 15:22:07,202 DEBUG [main] -org.apache.ibatis.transaction.jdbc.JdbcTransaction Opening JDBC Connection
2023-02-16 15:22:07,210 DEBUG [main] -org.apache.ibatis.datasource.pooled.PooledDataSource Created connection 1796371666.
2023-02-16 15:22:07,210 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson2 ==>  Preparing: select id,user_name as userName,age as age from test_user where id = ?
2023-02-16 15:22:07,211 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson2 ==> Parameters: 1(Integer)
2023-02-16 15:22:07,212 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson2 <==      Total: 1
person==person1 = false

Verificou-se que o cache de segundo nível não foi atingido. Como o SqlSession era diferente, o cache de primeiro nível também não foi atingido. Por que o cache L2 não foi atingido?O cache L2 está obviamente habilitado, mas você precisa saber onde o cache L2 está habilitado? Está na interface PersonMapper. Observe que no arquivo de interface, o cache de segundo nível não está habilitado no arquivo de recursos PersonMapper.xml, então você definitivamente não será capaz de acessar o cache de segundo nível usando o método em a configuração PersonMapper.xml. Então, como podemos permitir que ele use o cache de segundo nível na interface PersonMapper? Você pode usar a tag <cache-ref> , o código é o seguinte:

<?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="org.apache.ibatis.gwmtest.dao.PersonMapper">

  <cache-ref namespace="org.apache.ibatis.gwmtest.dao.PersonMapper"/>
  
  <select id="getPerson2" resultType="org.apache.ibatis.gwmtest.entity.Person">
    select id,user_name as userName,age as age from test_user where id = #{666}
  </select>
</mapper>

O atributo namespace na tag <cache-ref> pode ser referenciado para configurar o cache de segundo nível na interface PersonMapper. O teste final pode atingir o cache de segundo nível.

Acima estão as etapas para usar o cache de segundo nível por meio da anotação @CacheNamespace . Observe que a interface do Mapper e o cache de segundo nível preparado no arquivo de configuração do Mapper não são iguais. Para usar o mesmo, você precisa se apresentar. A tag <cache-ref> é usada para introduzir o cache de segundo nível configurado na interface no arquivo de configuração.

2. Use xml para configurar o cache

Adicione-o diretamente à configuração do PersonMapper.xml<cache/>Tags são equivalentes a preparar um cache de segundo nível no PersonMapper .

<?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="org.apache.ibatis.gwmtest.dao.PersonMapper">
  
  <select id="getPerson2" resultType="org.apache.ibatis.gwmtest.entity.Person">
    select id,user_name as userName,age as age from test_user where id = #{666}
  </select>
</mapper>

O código da interface PersonMapper correspondente é o seguinte:


public interface PersonMapper {
    
    
  
  @Select("select id,user_name as userName,age as age from test_user where id = #{666}")
  Person getPerson(Integer id);

  Person getPerson2(Integer id);
}

Cuidado para não usar a anotação @CacheNamespace neste momento, pois ela já foi passada em xml<cache/>A tag está pronta para o cache de segundo nível, portanto você não pode construir um cache de segundo nível aqui, caso contrário ocorrerá um conflito e um erro será relatado. O erro é o seguinte:

Caused by: java.lang.IllegalArgumentException: Caches collection already contains value for org.apache.ibatis.gwmtest.dao.PersonMapper
	at org.apache.ibatis.session.Configuration$StrictMap.put(Configuration.java:1021)
	at org.apache.ibatis.session.Configuration$StrictMap.put(Configuration.java:977)
	at org.apache.ibatis.session.Configuration.addCache(Configuration.java:713)
	at org.apache.ibatis.builder.MapperBuilderAssistant.useNewCache(MapperBuilderAssistant.java:140)
	at org.apache.ibatis.builder.annotation.MapperAnnotationBuilder.parseCache(MapperAnnotationBuilder.java:190)
	at org.apache.ibatis.builder.annotation.MapperAnnotationBuilder.parse(MapperAnnotationBuilder.java:121)
	at org.apache.ibatis.binding.MapperRegistry.addMapper(MapperRegistry.java:72)
	at org.apache.ibatis.session.Configuration.addMapper(Configuration.java:848)
	at org.apache.ibatis.builder.xml.XMLMapperBuilder.bindMapperForNamespace(XMLMapperBuilder.java:432)
	at org.apache.ibatis.builder.xml.XMLMapperBuilder.parse(XMLMapperBuilder.java:97)
	at org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement(XMLConfigBuilder.java:378)
	at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:120)

Ele lembra que o cache já existe. Você pode referenciá-lo diretamente, mas não precisa prepará-lo novamente. Você pode usar a anotação @CacheNamespaceRef para apresentá-lo. Antes de fazer isso, teste se ele pode atingir o cache de segundo nível .

Depois de definir a interface PersonMapper e PersonMapper.xml, comece o teste. Primeiro teste o método getPerson(). Observe que este método está na interface PersonMapper. Você deve saber que a interface PersonMapper testada não é decorada com anotações @CacheNamespace e @CacheNamespaceRef. O código de teste é o seguinte:

  public static void main(String[] args) throws Exception {
    
    
    PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);

    PersonMapper mapper1 = sqlSession2.getMapper(PersonMapper.class);
    Person person = mapper.getPerson(1);
    sqlSession.commit();
    Person person1 = mapper1.getPerson(1);
    mapper.getPerson(1);

    System.out.println("person==person1 = " + (person == person1));
  }

Resultado de saída:

2023-02-16 15:43:42,638 DEBUG [main] -org.apache.ibatis.transaction.jdbc.JdbcTransaction Opening JDBC Connection
2023-02-16 15:43:42,652 DEBUG [main] -org.apache.ibatis.datasource.pooled.PooledDataSource Created connection 255944888.
2023-02-16 15:43:42,658 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson ==>  Preparing: select id,user_name as userName,age as age from test_user where id = ?
2023-02-16 15:43:42,703 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson ==> Parameters: 1(Integer)
2023-02-16 15:43:42,760 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson <==      Total: 1
2023-02-16 15:43:42,763 DEBUG [main] -org.apache.ibatis.transaction.jdbc.JdbcTransaction Opening JDBC Connection
2023-02-16 15:43:42,770 DEBUG [main] -org.apache.ibatis.datasource.pooled.PooledDataSource Created connection 1935972447.
2023-02-16 15:43:42,770 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson ==>  Preparing: select id,user_name as userName,age as age from test_user where id = ?
2023-02-16 15:43:42,771 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson ==> Parameters: 1(Integer)
2023-02-16 15:43:42,772 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson <==      Total: 1
person==person1 = false

Verificou-se que o cache de segundo nível não foi utilizado, pois o cache de segundo nível não foi definido na interface PersonMapper e foi conectado diretamente ao banco de dados para duas consultas. Então, como podemos fazer com que ele use o segundo cache de nível e acertá-lo? Conforme mencionado acima, há um conflito direto no uso da anotação @CacheNamespace para preparar o cache, portanto a anotação CacheNamespaceRef deve ser usada aqui para fazer referência ao cache de segundo nível configurado em PersonMapper.xml. código mostrado abaixo:

@CacheNamespaceRef(PersonMapper.class)
public interface PersonMapper {
    
    
  
  @Select("select id,user_name as userName,age as age from test_user where id = #{666}")
  Person getPerson(Integer id);

  Person getPerson2(Integer id);
}

Resultado dos testes:

2023-02-16 15:49:48,135 DEBUG [main] -org.apache.ibatis.transaction.jdbc.JdbcTransaction Opening JDBC Connection
2023-02-16 15:49:48,146 DEBUG [main] -org.apache.ibatis.datasource.pooled.PooledDataSource Created connection 294184992.
2023-02-16 15:49:48,153 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson ==>  Preparing: select id,user_name as userName,age as age from test_user where id = ?
2023-02-16 15:49:48,212 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson ==> Parameters: 1(Integer)
2023-02-16 15:49:48,264 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper.getPerson <==      Total: 1
2023-02-16 15:49:48,281  WARN [main] -org.apache.ibatis.io.SerialFilterChecker As you are using functionality that deserializes object streams, it is recommended to define the JEP-290 serial filter. Please refer to https://docs.oracle.com/pls/topic/lookup?ctx=javase15&id=GUID-8296D8E8-2B93-4B9A-856E-0A65AF9B8C66
2023-02-16 15:49:48,285 DEBUG [main] -org.apache.ibatis.gwmtest.dao.PersonMapper Cache Hit Ratio [org.apache.ibatis.gwmtest.dao.PersonMapper]: 0.5
person==person1 = false

Verificou-se que o cache de segundo nível pode ser atingido novamente. Então, ao testar o método getPerson2(), ele pode finalmente atingir o cache de segundo nível.

Acima estão as etapas específicas para usar o cache de segundo nível Mybatis.

Acho que você gosta

Origin blog.csdn.net/qq_35971258/article/details/129059557
Recomendado
Clasificación