Quarkus Extension for Spring Transaction API
While users are encouraged to use Jakarta @Transactional annotations directly, Quarkus provides a compatibility layer for Spring @Transactional in the form of the spring-tx extension.
This guide explains how a Quarkus application can leverage the Spring @Transactional annotation to manage transactions.
前提条件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 17+がインストールされ、
JAVA_HOMEが適切に設定されていること -
Apache Maven 3.9.16
-
使用したい場合は、 Quarkus CLI
-
ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること
-
Spring DI エクステンションにある程度慣れている
Maven プロジェクトの作成
まず、新しいプロジェクトが必要です。以下のコマンドで新しいプロジェクトを作成します。
Windowsユーザーの場合:
-
cmdを使用する場合、(バックスラッシュ
\を使用せず、すべてを同じ行に書かないでください)。 -
Powershellを使用する場合は、
-Dパラメータを二重引用符で囲んでください。例:"-DprojectArtifactId=spring-tx-quickstart"
This command generates a project which imports the spring-tx, spring-di, spring-web, and spring-data-jpa extensions.
If you already have your Quarkus project configured, you can add the spring-tx extension
to your project by running the following command in your project base directory:
quarkus extension add spring-tx
./mvnw quarkus:add-extension -Dextensions='spring-tx'
./gradlew addExtension --extensions='spring-tx'
これにより、ビルドファイルに以下が追加されます:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-spring-tx</artifactId>
</dependency>
implementation("io.quarkus:quarkus-spring-tx")
Define the entity
このガイドでは、以下のJPA Entityを使用しています。
package org.acme.spring.tx;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
@Entity
public class Fruit {
@Id
@GeneratedValue
private Long id;
private String name;
public Fruit() {
}
public Fruit(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
リポジトリの定義
In a typical Spring Data fashion, create a repository for Fruit:
package org.acme.spring.tx;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
public interface FruitRepository extends CrudRepository<Fruit, Long> {
List<Fruit> findByName(String name);
}
Creating a transactional service
Now let’s create a service that uses Spring’s @Transactional annotation to manage transactions.
Create src/main/java/org/acme/spring/tx/FruitService.java with the following content:
package org.acme.spring.tx;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class FruitService {
@Autowired
FruitRepository fruitRepository;
@Transactional (1)
public Fruit addFruit(String name) {
return fruitRepository.save(new Fruit(name));
}
@Transactional(propagation = org.springframework.transaction.annotation.Propagation.REQUIRED) (2)
public void addMultipleFruits(List<String> names) {
for (String name : names) {
fruitRepository.save(new Fruit(name));
}
}
}
| 1 | The Spring @Transactional annotation is automatically transformed into a Jakarta @Transactional annotation at build time. |
| 2 | You can specify propagation attributes just like in Spring. REQUIRED is the default. |
You can also place @Transactional at the class level to make all public methods transactional:
package org.acme.spring.tx;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional (1)
public class TransactionalService {
public void doSomething() {
// this method is transactional
}
public void doSomethingElse() {
// this method is also transactional
}
}
| 1 | All public methods of this class are transactional. |
Controlling rollback behavior
Spring’s rollbackFor and noRollbackFor attributes are supported:
package org.acme.spring.tx;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class RollbackService {
@Transactional(rollbackFor = BusinessException.class) (1)
public void performBusinessOperation() throws BusinessException {
// if BusinessException is thrown, the transaction rolls back
}
@Transactional(noRollbackFor = NonCriticalException.class) (2)
public void performTolerantOperation() throws NonCriticalException {
// if NonCriticalException is thrown, the transaction still commits
}
}
| 1 | The transaction will roll back if BusinessException (or a subclass) is thrown. |
| 2 | The transaction will NOT roll back if NonCriticalException is thrown. |
Jakarta RESTリソースの更新
With the repository and service in place, create the Jakarta REST resource that will use the FruitService.
Create src/main/java/org/acme/spring/tx/FruitResource.java with the following content:
package org.acme.spring.tx;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class FruitResource {
@Autowired
FruitService fruitService;
@Autowired
FruitRepository fruitRepository;
@GetMapping("/fruits")
public List<Fruit> list() {
return (List<Fruit>) fruitRepository.findAll();
}
@PostMapping("/fruits")
public Fruit add(@RequestBody Fruit fruit) {
return fruitService.addFruit(fruit.getName());
}
}
アプリケーションをパッケージ化して実行する
| Quarkus Dev Services will automatically start a PostgreSQL container in dev and test modes, so no manual database configuration is needed. |
次のコマンドでアプリケーションを実行します。
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
In another terminal, add a fruit:
curl -X POST -H "Content-Type: application/json" -d '{"name": "Apple"}' http://localhost:8080/fruits
Then list all fruits:
curl http://localhost:8080/fruits
いつものように、アプリケーションは以下の方法でパッケージ化されます。
quarkus build
./mvnw install
./gradlew build
そして、 java -jar target/quarkus-app/quarkus-run.jar を使って実行します。
次のようにネイティブ実行可能ファイルを生成することもできます。
quarkus build --native
./mvnw install -Dnative
./gradlew build -Dquarkus.native.enabled=true
Supported Spring @Transactional features
The table below summarizes the supported attributes:
| 属性 | ステータス | Comments |
|---|---|---|
|
Supported |
All values except |
|
Supported |
|
|
Supported |
|
|
Supported |
|
|
Supported |
|
|
Unsupported |
Ignored with a warning |
|
Unsupported |
Ignored with a warning |
|
Unsupported |
Only |
|
Unsupported |
Quarkus manages the transaction manager |
|
Unsupported |
Ignored with a warning |
重要な技術的注意点
Quarkus での Spring サポートは、Spring Application Context を開始せず、Spring インフラストラクチャークラスも実行しないことに注意してください。Spring クラスとアノテーションは、メタデータの読み取りにのみ使用されるか、ユーザーコードメソッドの戻り値の型またはパラメーター型として使用されます。エンドユーザーにとってそれが意味することは、任意の Spring ライブラリーを追加しても効果がないということです。さらに、Spring インフラストラクチャークラス (たとえば、 org.springframework.beans.factory.config.BeanPostProcessor など) は実行されません。