Mybatisの1stレベルキャッシュ2
概要
途中でデータ登録や更新処理が入る場合、Mybatisは1stレベルキャッシュを使用しません。この場合のMybatisの1stレベルキャッシュの動きを確認します。
この記事は以下の記事の続きの内容となります。
Mybatisの1stレベルキャッシュ
確認環境
- Java 21
- Spring Boot 3.4.1
- MyBatis Spring Boot Starter 3.0.4
- PostgreSQL 16.4
確認方法
以下のようなフローのプログラムのログから、クエリが発行されることを確認します。
- selectを実行
- insert or updateを実行
- selectを実行
データベース
以下のテーブルとデータを用います。
テーブル定義
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
アプリケーション起動時に実行されるプログラムを作成して1stレベルキャッシュを確認します。以下のような構成になります。
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("selectを実行する");
print(exampleMapper.get("0001"));
log.info("insertを実行する");
exampleMapper.insert("0003", "new");
log.info("selectを実行する");
print(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);
void insert(@Param("id") String id, @Param("memo") String memo);
}
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>
<insert id="insert">
insert into example (id, memo) values(#{id}, #{memo});
</insert>
</mapper>
プログラムの実行結果
com.example.ExampleRepository : selectを実行する
org.mybatis.spring.SqlSessionUtils : Creating a new SqlSession
org.mybatis.spring.SqlSessionUtils : Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@74fab04a]
o.m.s.t.SpringManagedTransaction : JDBC Connection [HikariProxyConnection@378006273 wrapping org.postgresql.jdbc.PgConnection@3d4e405e] 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@74fab04a]
com.example.ExampleRepository : key: memo, value: example1
com.example.ExampleRepository : key: id, value: 0001
com.example.ExampleRepository : insertを実行する
org.mybatis.spring.SqlSessionUtils : Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@74fab04a] from current transaction
com.example.ExampleMapper.insert : ==> Preparing: insert into example (id, memo) values(?, ?);
com.example.ExampleMapper.insert : ==> Parameters: 0003(String), new(String)
com.example.ExampleMapper.insert : <== Updates: 1
org.mybatis.spring.SqlSessionUtils : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@74fab04a]
com.example.ExampleRepository : selectを実行する
org.mybatis.spring.SqlSessionUtils : Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@74fab04a] 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@74fab04a]
com.example.ExampleRepository : key: memo, value: example1
com.example.ExampleRepository : key: id, value: 0001
org.mybatis.spring.SqlSessionUtils : Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@74fab04a]
org.mybatis.spring.SqlSessionUtils : Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@74fab04a]
org.mybatis.spring.SqlSessionUtils : Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@74fab04a]
最初のselectが実行されるときにクエリが発行されています。その後のinsertも同様にクエリが発行されていることが分かります。また、最後のselectのときにもクエリが発行されています。
insertが行われた場合、1stレベルキャッシュは使用されずに改めてクエリが発行されることが分かりました。insertを行っている箇所をupdateやdeleteに置き換えても同様の結果が得られます。
上記の確認時はselect、insert共に同じ example
テーブルに対するものとなっています。ここからは、selectとinsertを別テーブルに行った際にどう動くかを確認します。
データベースの変更
以下のテーブル追加を行います。
テーブル追加
Table "public.example2"
Column | Type | Collation | Nullable | Default
--------+-----------------------+-----------+----------+---------
id | character varying(4) | | not null |
memo | character varying(50) | | |
確認プログラムの変更
確認プログラムの 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>
<insert id="insert">
insert into example2 (id, memo) values(#{id}, #{memo});
</insert>
</mapper>
変更後のプログラムの実行結果
com.example.ExampleRepository : selectを実行する
org.mybatis.spring.SqlSessionUtils : Creating a new SqlSession
org.mybatis.spring.SqlSessionUtils : Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@268cbb86]
o.m.s.t.SpringManagedTransaction : JDBC Connection [HikariProxyConnection@1492358500 wrapping org.postgresql.jdbc.PgConnection@7d44a19] 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@268cbb86]
com.example.ExampleRepository : key: memo, value: example1
com.example.ExampleRepository : key: id, value: 0001
com.example.ExampleRepository : insertを実行する
org.mybatis.spring.SqlSessionUtils : Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@268cbb86] from current transaction
com.example.ExampleMapper.insert : ==> Preparing: insert into example2 (id, memo) values(?, ?);
com.example.ExampleMapper.insert : ==> Parameters: 0003(String), new(String)
com.example.ExampleMapper.insert : <== Updates: 1
org.mybatis.spring.SqlSessionUtils : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@268cbb86]
com.example.ExampleRepository : selectを実行する
org.mybatis.spring.SqlSessionUtils : Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@268cbb86] 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@268cbb86]
com.example.ExampleRepository : key: memo, value: example1
com.example.ExampleRepository : key: id, value: 0001
org.mybatis.spring.SqlSessionUtils : Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@268cbb86]
org.mybatis.spring.SqlSessionUtils : Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@268cbb86]
org.mybatis.spring.SqlSessionUtils : Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@268cbb86]
insertの後のselectのときにもクエリが発行されています。別テーブルにinsertが行われた場合に1stレベルキャッシュが使用されないことが分かりました。
updateとdeleteでも同様の結果が得られます。
Discussion