Mybatisの1stレベルキャッシュを無効化する
概要
Mybatisの1stレベルキャッシュを完全に無効化する方法はありません。しかし、localCacheScope
を STATEMENT
にすることで無効化されたのと同様の挙動をさせることはできます(Mybatis.org (設定 - localCacheScope))。
localCacheScope
はデフォルトでは SESSION
に設定されています。この設定だと同じセッション内のすべてのクエリの結果がキャッシュされます。これを STATEMENT
に設定すると、1stレベルキャッシュはステートメントごとに適用されるようになり、クエリの結果が共有されないようになります。
確認環境
- Java 21
- Spring Boot 3.4.1
- MyBatis Spring Boot Starter 3.0.4
- PostgreSQL 16.4
確認方法
localCacheScope
を SESSION
に設定した状態で同じクエリを複数回実行しログを確認します。その後、localCacheScope
を STATEMENT
に設定した状態で同じクエリを複数回実行しログを確認します。
ログの出力内容の変化からlocalCacheScope
を STATEMENT
に設定したときのみ同じクエリがログに出力されていることを確認します。
依存関係
dependencies {
implementation("org.springframework.boot:spring-boot-starter:3.4.1")
implementation("org.springframework.boot:spring-boot-starter-jdbc:3.4.1")
implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.4")
implementation("org.postgresql:postgresql:42.7.4")
}
データベース
以下のテーブルとデータを用います。
テーブル定義
Table "public.example"
Column | Type | Collation | Nullable | Default
--------+-----------------------+-----------+----------+---------
id | character varying(4) | | not null |
memo | character varying(50) | | |
データ
id | memo
------+----------
0001 | example1
0002 | example2
確認プログラム
Spring Boot
アプリケーション起動時に実行されるプログラムを作成して動作を確認します。以下のような構成になります。
com.example
+- ExampleApplication.java
+- ExampleApplicationRunner.java
+- ExampleRepository.java
+- ExampleMapper.java
+- ExampleMapper.xml
ExampleApplication
はアプリケーションのエントリポイントです。
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
ExampleApplicationRunner
は ApplicationRunner
を実装します。このクラスがアプリケーション起動時に呼び出されます。
package com.example;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class ExampleApplicationRunner implements ApplicationRunner {
private ExampleRepository exampleRepository;
public ExampleApplicationRunner(ExampleRepository exampleRepository) {
this.exampleRepository = exampleRepository;
}
@Override
public void run(ApplicationArguments args) throws Exception {
exampleRepository.get();
}
}
ExampleRepository
はデータソースへアクセスするためのクラスです。
package com.example;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
public class ExampleRepository {
private final Logger log = LoggerFactory.getLogger(ExampleRepository.class);
private ExampleMapper exampleMapper;
public ExampleRepository(ExampleMapper exampleMapper) {
this.exampleMapper = exampleMapper;
}
@Transactional
public void get() {
log.info("1回目");
exampleMapper.get("0001");
log.info("2回目: 1回目と同じパラメータで同じステートメントを呼び出す");
exampleMapper.get("0001");
}
}
ExampleMapper
ではデータベース操作に対応するメソッドを宣言します。
package com.example;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface ExampleMapper {
List<Map<String, Object>> get(@Param("id") String id);
}
ExampleMapper.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.example.ExampleMapper">
<select id="get" resultType="hashmap">
select * from example where id = #{id};
</select>
</mapper>
localCacheScopeの設定
localCacheScope
を以下のように設定します。
mybatis.configuration.local-cache-scope=SESSION
プログラムの実行結果
com.example.ExampleRepository : 1回目
org.mybatis.spring.SqlSessionUtils : Creating a new SqlSession
org.mybatis.spring.SqlSessionUtils : Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@34780cd9]
o.m.s.t.SpringManagedTransaction : JDBC Connection [HikariProxyConnection@1636824514 wrapping org.postgresql.jdbc.PgConnection@3a082ff4] will be managed by Spring
com.example.ExampleMapper.get : ==> Preparing: select * from example where id = ?;
com.example.ExampleMapper.get : ==> Parameters: 0001(String)
com.example.ExampleMapper.get : <== Total: 1
org.mybatis.spring.SqlSessionUtils : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@34780cd9]
com.example.ExampleRepository : 2回目: 1回目と同じパラメータで同じステートメントを呼び出す
org.mybatis.spring.SqlSessionUtils : Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@34780cd9] from current transaction
1回目はクエリに関するログが出力されていますが、2回目はクエリに関するログが出力されていません。localCacheScope
に SESSION
を設定するとこちらの記事と同様の結果を得ることができました。
そのため、localCacheScope
を未設定のときと、SESSION
を指定する場合で挙動が同じことが分かります。
localCacheScopeの変更
localCacheScope
の値を STATEMENT
に変更します。
mybatis.configuration.local-cache-scope=STATEMENT
プログラムの再実行
com.example.ExampleRepository : 1回目
org.mybatis.spring.SqlSessionUtils : Creating a new SqlSession
org.mybatis.spring.SqlSessionUtils : Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7c8c70d6]
o.m.s.t.SpringManagedTransaction : JDBC Connection [HikariProxyConnection@797224183 wrapping org.postgresql.jdbc.PgConnection@3f0d6038] will be managed by Spring
com.example.ExampleMapper.get : ==> Preparing: select * from example where id = ?;
com.example.ExampleMapper.get : ==> Parameters: 0001(String)
com.example.ExampleMapper.get : <== Total: 1
org.mybatis.spring.SqlSessionUtils : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7c8c70d6]
com.example.ExampleRepository : 2回目: 1回目と同じパラメータで同じステートメントを呼び出す
org.mybatis.spring.SqlSessionUtils : Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7c8c70d6] from current transaction
com.example.ExampleMapper.get : ==> Preparing: select * from example where id = ?;
com.example.ExampleMapper.get : ==> Parameters: 0001(String)
com.example.ExampleMapper.get : <== Total: 1
org.mybatis.spring.SqlSessionUtils : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7c8c70d6]
2回目にクエリに関するログが出力されているため、1stレベルキャッシュを使用していないことが分かりました。
まとめ
-
localCacheScope
をSTATEMENT
にすることで1stレベルキャッシュを使用しません -
localCacheScope
をSESSION
にすると1stレベルキャッシュを使用します -
localCacheScope
のデフォルト値はSESSION
なので、未設定だと1stレベルキャッシュを使用します
Discussion