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 the
DataSource 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