🎉
JpaRepository で saveAndFlush しても commit されないですよね?
結論
されません。
背景
SpringBoot
アプリケーションで @Transactional
をつけたメソッド内で JpaRepository
の saveAndFlush()
を呼び出すような実装をした。
コードレビューをしていただいた際に 「saveAndFlush()
って変更を commit
しないですかね?(ロールバックできなくなったりしないか心配)」 とのコメントをもらった。
flush
は commit
はしないだろうと思っていたものの、JPA
関連のドキュメント等を眺めて見ると「flush
は commit
しません」みたいなことはどこにも明言されておらず[1]、なんだか不安になってしまったので動作確認ベースで一応調査しておこう、となった。
動作確認
@Transactional
をつけたメソッド内で JpaRepository
の saveAndFlush()
を呼び出し、その後非検査例外を発生させ、非検査例外発生前に行った変更がロールバックされるか確認する。
よくあるユーザー追加のユースケースを例に考える。
動作確認で使用したコードはすべて以下のリポジトリに置いてあります。
主要なアプリケーションコードと設定ファイルたち
# UserControler.java
リクエスト(AddUserRequest: firstName と lastName) を受け取ってユーザー追加処理 (userService.add) を呼び出すだけ
@RestController
@RequiredArgsConstructor
class UserControler {
private final UserService userService;
@RequestMapping("/users", RequestMethod.POST)
public void save(@RequestBody AddUserRequest request) {
userService.add(request.getFirstName(), request.getLastName());
}
}
# AddUserRequest.java
@Value
class AddUserRequest {
String firstName;
String lastName;
}
# UserService.java
ユーザー追加処理の実装
ここが動作確認のメインとなる処理
@Service
@RequiredArgsConstructor
class UserService {
private final UserRepositpry userRepositpry;
@Transactional
public void add(String firstName, String lastName) {
User user = new User(firstName, lastName)
userRepositpry.saveAndFlush(user);
// ここで非検査例外発生
throw new RuntimeException();
}
}
# User.java
@Entity
@Table("users")
public class User {
@GeneratedValue
Long id;
String firstName;
String lastName;
}
# UserRepositpry.java
public interface UserRepositpry implements JpaRepository<Long, User> {}
# docker-compose.yml
version: "3.8"
services:
mysql-container:
container_name: mysql-container
image: mysql:8.0.22
environment:
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
MYSQL_DATABASE: "sample"
MYSQL_USER: "user"
MYSQL_PASSWORD: "password"
networks:
- default
volumes:
- ./docker/volumes/mysql/my.cnf:/etc/mysql/conf.d/my.cnf
- ./docker/volumes/mysql/initdb.d:/docker-entrypoint-initdb.d
ports:
- "3306:3306"
# create_tables.sql
CREATE TABLE users
(
id BIGINT AUTO_INCREMENT,
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL,
PRIMARY KEY (id)
);
手順
- DB のコンテナを立ち上げる。
$ docker compose up -d
- SpringBoot アプリケーションを実行する。
IntelliJ のスタートボタンポチしました。 - 以下の CURL コマンドを実行して
UserService#add
を呼び出す。
$ curl -X POST -H "Content-Type: application/json" -d '{"firstName":"Daniel", "lastName": "Caesar"}' localhost:8080/users
- 最後に以下のコマンドで DB の中身を確認する。
ロールバックされていれば DB は空のはずである。
$ docker mysql-container mysql -uuser -ppassword `select count(*) from users;`
結果
$ docker mysql-container mysql -uuser -ppassword `select * from users;`
0
レコードが 0 件なので、ちゃんとロールバックされることが確認できました。
めでたしめでたし。
参考資料
- https://www.baeldung.com/spring-data-jpa-save-saveandflush
- https://stackoverflow.com/questions/14581865/hibernate-flush-and-commit#:~:text=You need to flush in,changes to your persistence layer.
-
見落としているのかもしれない...。stackoverflow には 「
flush
はcommit
しないよ」 とのコメントがちらほら。 ↩︎
Discussion