Here are some points about the transaction function that is indispensable for database access.
This article assumes Doma 2.44.0.
Please also read Introduction to Doma for an introduction to other features of Doma.
The first thing to consider regarding transactions. That is whether the framework you are using (such as Spring Framework) provides transactional functionality. If you do, please consider using it first.
Spring Framework provides transactional functionality.
The getDataSource method of the implementation class of ʻorg.seasar.doma.jdbc.Config should be set to return the DataSource wrapped using ʻorg.springframework.jdbc.datasource.TransactionAwareDataSourceProxy. ** This is very important **.
After taking the above measures, refer to this guide and add @Transactional to the class or method of Spring component to execute transaction. available.
If you use doma-spring-boot, the above DataSource wrapping will be done automatically. Spring-boot-jpetstore is a sample application that uses the transaction function of Spring Framework using doma-spring-boot.
Quarkus provides transactional capabilities.
ʻThe getDataSource method of the implementation class of org.seasar.doma.jdbc.Configis theDataSource managed by Agroal which is the connection pool implementation of Quarkus. Please return `.
After taking the above measures, you can use transactions by adding @Transactional to the classes and methods of CDI components as described in this document.
If you use Quarkus Extension for Doma, the above DataSource setting will be done automatically. Quarkus-sample is a sample application that uses Quarkus' transaction function using Quarkus Extension for Doma.
Consider using Doma's local transactions.
The characteristics of Doma's local transactions are as follows.
ThreadLocal to manage connections on a per-thread basis(not a declarative API like@ Transactional`)Usage is described in the next section.
The working code is in getting-started, but here's an excerpt of the important part.
Main.java
public class Main {
public static void main(String[] args) {
var config = createConfig();
var tm = config.getTransactionManager();
// setup database
var appDao = new AppDaoImpl(config);
tm.required(appDao::create);
// read and update
//④ Pass the lambda expression to the transaction manager method
tm.required(
() -> {
var repository = new EmployeeRepository(config);
var employee = repository.selectById(1);
employee.age += 1;
repository.update(employee);
});
}
private static Config createConfig() {
var dialect = new H2Dialect();
//① Create a transaction-enabled data source
var dataSource =
new LocalTransactionDataSource("jdbc:h2:mem:tutorial;DB_CLOSE_DELAY=-1", "sa", null);
var jdbcLogger = new Slf4jJdbcLogger();
//② Create a transaction manager
var transactionManager = new LocalTransactionManager(dataSource, jdbcLogger);
//③ Make it possible to return the instance created in ① and ② above from the implementation class of Config.
return new DbConfig(dialect, dataSource, jdbcLogger, transactionManager);
}
}
Instantiate LocalTransactionDataSource.
In this example, the connection URL etc. is received by the constructor, but it also has a constructor that receives the DataSource instance.
Pass the instance of LocalTransactionDataSource created in ① above to the constructor to instantiate LocalTransactionManager.
Pass the instance created in ① and ② to the constructor of DbConfig, which is the implementation class of Config, and instantiate it.
Tm here is an instance of LocalTransactionManager created in ②.
You can execute a transaction by passing the process you want to handle in the transaction to the required method of tm as a lambda expression.
The required method is a method that starts if the transaction has not been started yet, and there are other methods such as the requiresNew method that always starts a new transaction and the notSupported method that temporarily stops the transaction. These methods can be nested and used.
The transaction is rolled back when you throw an exception from the lambda expression or call the setRollbackOnly method. Otherwise it will be committed.
One caveat is that if you have set up a local transaction, in principle all database access by Doma should be done via TransactionManager. Otherwise, you will get an exception.
I introduced the points of using transactions in Doma.
If you're using Doma and you think your transactions aren't working, you can refer to this article as well as the linked articles and samples.
Recommended Posts