Web系開発メモ

Java, C#, HTML, CSS, JavaScript のことなどを書いてます。

SpringBoot トランザクションを有効にする方法

Spring Boot の Webアプリで、データベース操作時のトランザクションを有効にする方法を書いていきます。

バージョン

動作確認で使用した製品のバージョンは以下の通りです。

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