A database write needs a transaction if the change is to be committed. In Quarkus, `@Transactional` marks a CDI method to run inside a transaction. The annotation comes from JTA (Java/Jakarta Transactions API). By default, a transaction starts before the method if one is not already active; when the method finishes, it is committed or rolled back depending on specified exceptions. section 7.2.2 section 7.5.1
Place `@Transactional` on a method that persists or changes entities. A reservation endpoint can call `reservation.persist()` inside an annotated `make` method. With a repository instead of an active-record entity, GraphQL mutations can annotate `register` and `remove` while calling repository `persist` and `delete`. Read methods in these examples are not annotated. section 7.1.3 section 7.2.2
Listing 7.17 The GraphQLInventoryService modified code
@GraphQLApi
public class GraphQLInventoryService {
@Inject
CarRepository carRepository;
@Query
public List<Car> cars() {
return carRepository.listAll();
}
@Transactional
@Mutation
public Car register(Car car) {
carRepository.persist(car);
Log.info("Persisting " + car);
return car;
}
@Transactional
@Mutation
public boolean remove(String licensePlateNumber) {
Optional<Car> toBeRemoved = carRepository
.findByLicensePlateNumberOptional(licensePlateNumber);
if(toBeRemoved.isPresent()) {
carRepository.delete(toBeRemoved.get());
return true;
} else {
return false;
}
}
}
Compare the annotated write mutations with the unannotated `cars` query in the GraphQL inventory service. section 7.2.2
`Transactional.TxType` controls what happens when a method is called with or without an existing transaction:
- `REQUIRED` (the default) joins an incoming transaction or starts one if none exists. section 7.5.1
- `REQUIRES_NEW` starts a new transaction and suspends any existing one; `MANDATORY` requires an existing transaction or throws an exception. section 7.5.1
- `SUPPORTS` joins an existing transaction but otherwise runs without one. `NOT_SUPPORTED` runs without one, suspending an incoming transaction. section 7.5.1
- `NEVER` throws an exception if the method is called within a transaction. section 7.5.1
The annotation can also go on a class, applying to its methods unless an individual method overrides it. Choose the type for the operation: a reservation-statistics endpoint may not need a transaction, so `SUPPORTS` or `NOT_SUPPORTED` may fit better. Starting or suspending transactions adds invocation overhead. section 7.5.1
An annotation is not the only way to establish a boundary. When a gRPC inventory service persists cars inside a lambda outside the CDI bean invocation, it wraps each `persist(car)` call in `QuarkusTransaction.requiringNew().run(...)`. Each call then runs in its own transaction. section 7.2.2

