Spring Boot の Webアプリで、データベース操作時のトランザクションを有効にする方法を書いていきます。
バージョン
動作確認で使用した製品のバージョンは以下の通りです。
- SpringBoot 3.0.2
- Lombok 1.18.24
- Java 17
- Maven 3.8.6
- PostgreSQL 15.1
- pgJDBC 42.5.1(JDBC Driver)
1. サービスクラスの作成
アノテーション @Transactional
を使って、トランザクションを有効にします。
src/main/java/org/example/service/TaskService.java
package org.example.service; import org.example.model.Task; import org.example.repository.TaskRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import jakarta.transaction.Transactional; @Service public class TaskService { @Autowired TaskRepository repository; @Transactional public Task save(Task task) { // ロールバックされて、データは保存されません。 repository.save(task); throw new RuntimeException("意図的な例外"); } public Task saveWithoutTransaction(Task task) { // ロールバックされず、データが保存されます。 repository.save(task); throw new RuntimeException("意図的な例外"); } }
意図的に非検査例外をスローして、ロールバックの確認ができるようにしています。
※ 検査例外は、デフォルトだとロールバックされません。
2. リポジトリの作成
Spring Boot Data JPA のリポジトリを作成します。
src/main/java/org/example/repository/TaskRepository.java
package org.example.repository; import org.example.model.Task; import org.springframework.data.repository.CrudRepository; public interface TaskRepository extends CrudRepository<Task, Long> { }
3. コントローラーの作成
以下のコントローラーを作成します。
src/main/java/org/example/controller/TaskController.java
package org.example.controller; import org.example.model.Task; import org.example.service.TaskService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/task") public class TaskController { @Autowired TaskService service; @GetMapping("/create") public Task create() { return service.save( new Task("トランザクション有り") ); } @GetMapping("/create-without-transaction") public Task createWithoutTransaction() { return service.saveWithoutTransaction( new Task("トランザクション無し") ); } }
動作確認のしやすさを優先しています。
4. モデルの作成
以下のクラスを作成します。
src/main/java/org/example/model/Task.java
package org.example.model; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @Entity @Getter @Setter @NoArgsConstructor public class Task { @Id @GeneratedValue(strategy= GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String title; public Task(String title) { this.title = title; } }
5. 起動クラスの作成
アプリを起動するクラスを作成します。
src/main/java/org/example/SpringApp.java
package org.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringApp { public static void main(String[] args) { SpringApplication.run(SpringApp.class, args); } }
6. 設定ファイルの作成
PostgreSQL に接続するために、以下のプロパティファイルを作成します。
src/main/resources/application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/test spring.datasource.username=usr spring.datasource.password=pass
ローカルの test データベースに接続します。
7. テーブルの作成
上のデータベースに接続して、以下のテーブルを作成しておきます。
CREATE TABLE task ( id bigserial PRIMARY KEY, title varchar(128) NOT NULL );
8. ビルドファイルの作成
Maven のビルドファイルは以下の通りです。
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>org.example</groupId> <artifactId>spring-transaction</artifactId> <version>1.0.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
9. 動作確認
アプリを起動して以下のURLにアクセスすると、DBにデータが作成されます。
http://localhost:8080/task/create-without-transaction
以下のURLにアクセスすると、トランザクションがロールバックされるため、DBにデータが作成されません。
http://localhost:8080/task/create