Good day everyone!
Well, the end of the month is always intensive, and here we have only one day left before the start of the second stream of the course
“Developer on the Spring Framework” - a wonderful and interesting course that leads a no less beautiful and evil
Yuri (as some students call him for the level of requirements in DZ), so let's consider another material that we have prepared for you.
Go.
')
IntroductionMost of the time, developers do not attach importance to transaction management. As a result, either most of the code is rewritten later, or the developer implements transaction management without knowing how it should actually work or what aspects should be used specifically in their case.
An important aspect of transaction management is determining the correct transaction boundaries, when the transaction should begin and when to end, when data should be added to the database and when it should be rolled back (in case of an exception).

The most important aspect for developers is to understand how to implement transaction management in the application in the best possible way. Therefore, let's consider various options.
Transaction Management MethodsTransactions can be managed in the following ways:
1. Software control by writing custom codeThis is the old way to manage transactions.
EntityManagerFactory factory = Persistence.createEntityManagerFactory("PERSISTENCE_UNIT_NAME"); EntityManager entityManager = entityManagerFactory.createEntityManager(); Transaction transaction = entityManager.getTransaction() try { transaction.begin(); someBusinessCode(); transaction.commit(); } catch(Exception ex) { transaction.rollback(); throw ex; }
Pros :
- Borders of transaction are obvious in the code.
Cons :
- It is repetitive and error prone.
- Any mistake can have a very big impact.
- You need to write many templates, also, if you want to call another method from this method, you need to manage it again from the code.
2. Using Spring to manage transactionsSpring supports two types of transaction management
1. Software Transaction Management : You must manage transactions through programming. This method is quite flexible, but it is difficult to maintain.
2. Declarative transaction management : You separate transaction management from business logic. You use only annotations in an XML-based configuration for transaction management.
We strongly recommend using declarative transactions. If you want to know the reasons, then read on, otherwise go directly to the section Declarative Transaction Management, if you want to implement this option.Now let's look at each approach in detail.
2.1. Software transaction management:The Spring framework provides two tools for programmatic transaction management.
a. Using
TransactionTemplate
(recommended by the Spring team):
Let's look at how you can implement this type using the example code below (taken from the Spring documentation with some modifications)
Note that the code snippets are from Spring Docs.Context Xml file:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/TEST"/> <property name="username" value="root"/> <property name="password" value="password"/> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <bean id="serviceImpl" class="com.service.ServiceImpl"> <constructor-arg ref="transactionManager"/> </bean>
Service
class:
public class ServiceImpl implements Service { private final TransactionTemplate transactionTemplate;
If there is no return value, use the convenience class
TransactionCallbackWithoutResult
with an anonymous class, as shown below:
transactionTemplate.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { updateOperation1(); updateOperation2(); } });
- Instances of the
TransactionTemplate
class are thread-safe, so not all dialog states are supported. TransactionTemplate
instances nevertheless maintain a configuration state, so if a class needs to use a TransactionTemplate with different settings (for example, a different isolation level), then you need to create two different TransactionTemplate instances, although in some classes one TransactionTemplate instance can be used.
b. Using the
PlatformTransactionManager
implementation directly:
Let's look at this option again in the code.
<!-- Initialization for data source --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/TEST"/> <property name="username" value="root"/> <property name="password" value="password"/> </bean> <!-- Initialization for TransactionManager --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> public class ServiceImpl implements Service { private PlatformTransactionManager transactionManager; public void setTransactionManager( PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } DefaultTransactionDefinition def = new DefaultTransactionDefinition();
Now, before moving on to the next method of managing transactions, let's see how to decide which type of transaction management to choose.
Choosing between
Software and
Declarative Transaction Management :
- Software transaction management is a good choice only if you have a small number of transactional transactions. (In most cases, this is not a transaction.)
- The transaction name can be explicitly set only in Software Transaction Management.
- Software transaction management should be used when you want to explicitly monitor transaction management.
- On the other hand, if your application contains numerous transactional operations, it is worth using declarative control.
- Declarative management does not allow managing transactions in business logic and is easy to configure.
2.2. Declarative transactions (Usually used in almost all scenarios of any web application)Step 1 : Define the transaction manager in the context xml file of your spring application.
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"/> <tx:annotation-driven transaction-manager="txManager"/>
Step 2 : Enable annotation support by adding an entry in the context xml file of your spring application.
OR add
@EnableTransactionManagement
to your configuration file, as shown below:
@Configuration @EnableTransactionManagement public class AppConfig { ... }
Spring recommends annotating only specific classes (and methods of specific classes) with the @Transactional
annotation versus annotating interfaces.The reason for this is that you place the annotation at the interface level, and if you use proxy classes (
proxy-target-class = «true»
) or intertwining aspect (
mode = «aspectj»
), then the transaction parameters are not recognized by the proxy infrastructure and plexuses, for example Transactional behavior will not apply.
Step 3 : Add the
@Transactional
annotation to a class (class method) or interface (interface method).
<tx:annotation-driven proxy-target-class="true">
The default configuration is:
proxy-target-class="false"
- The
@Transactional
may be placed before an interface definition, interface method, class definition, or public class method. - If you want some class methods (marked with
@Transactional
annotation) to have different attribute settings, such as isolation level or distribution level, place an annotation at the method level to override the class-level attribute settings. - In the proxy mode (which is set by default), only “external” method calls that go through the proxy can be intercepted. This means that a “stand-alone call”, for example, a method in the target object that calls any other method of the target object, will not result in an actual transaction at run time, even if the method being called is marked with
@Transactional
.
Now let's
@Transactional
difference between the attributes of the
@Transactional
annotation
@Transactional (isolation=Isolation.READ_COMMITTED)
Isolation.DEFAULT
set by default.- In most cases, you will use the default settings until you have special requirements.
- Tells the transaction manager (
tx
) that the next isolation level should be used for the current tx
. Must be installed at the point where tx
starts from, because we cannot change the isolation level after running tx.
DEFAULT : Use the default isolation level in the underlying database.
READ_COMMITTED (fixed data read): Constant, indicating that dirty read is prevented; non-repetitive reading and phantom reading may occur.
READ_UNCOMMITTED (read uncommitted data): This isolation level indicates that a transaction can read data that has not yet been deleted by other transactions.
REPEATABLE_READ (repeatability of reading): Constant, indicating that “dirty” reading and non-repeatable reading are prevented; phantom reading may appear.
SERIALIZABLE (orderability): Permanent, indicating that “dirty” reading, non-repeatable reading and phantom reading are prevented.
What do these slang mean: “dirty” reading, phantom reading or repeated reading?
- Dirty Read : Transaction “A” writes. Meanwhile, transaction “B” reads the same record before completing transaction A. Later, transaction A decides to roll back, and now we have changes in transaction B that are incompatible. This is a dirty read. Transaction B worked at the isolation level READ_UNCOMMITTED, so it could read the changes made by transaction A before the transaction was completed.
- Non-Repeatable Read : Transaction "A" reads some records. Then transaction “B” records this record and fixes it. Later, transaction A reads the same record again and may receive different values, because transaction B made changes to this record and fixed them. This is a non-repetitive reading.
- Phantom Read (Phantom Read) : Transaction "A" reads a series of records. Meanwhile, transaction “B” inserts a new record in the same row as transaction A. Later, transaction A reads the same range again and also gets the record that transaction B has just inserted. This is a phantom read: the transaction has extracted a number of records several times from the database and received different result sets (containing phantom records).
@Transactional(timeout=60)
The default is the default timeout for the underlying transaction system.
Tells the tx manager about the length of time to wait for the tx downtime before deciding whether to roll back non-responding transactions.
@Transactional(propagation=Propagation.REQUIRED)
If not specified, the propagating default behavior is
REQUIRED
.
Other options:
REQUIRES_NEW, MANDATORY, SUPPORTS, NOT_SUPPORTED, NEVER
and
NESTED
.
REQUIREDIndicates that the target method cannot work without active tx. If tx is already running before calling this method, then it will continue in the same tx, or the new tx will start shortly after calling this method.
REQUIRES_NEW- Indicates that a new tx should be launched each time the target method is called. If the tx is already coming, it will be suspended before starting a new one.
MANDATORY- Indicates that the target method requires active tx. If tx does not continue, it will not work, throwing an exception.
SUPPORTS- Indicates that the target method can be run independently of tx. If tx works, he will participate in the same tx. If executed without tx, it will still be executed if there are no errors.
- Methods that retrieve data are the best candidates for this option.
NOT_SUPPORTED- Indicates that the target method does not require distribution of the transaction context.
- Basically, the methods that are executed in a transaction, but perform operations with RAM, are the best candidates for this option.
NEVER- Indicates that the target method will throw an exception if executed in a transactional process.
- This option is in most cases not used in projects.
@Transactional (rollbackFor=Exception.class)
Default value:
rollbackFor=RunTimeException.class
In Spring, all API classes throw a RuntimeException, which means that if a method fails, the container always rolls back the current transaction.
The problem is only proven exceptions. Thus, this parameter can be used for declarative rollback of a transaction if a
Checked Exception
occurs.
@Transactional (noRollbackFor=IllegalStateException.class)
Indicates that a rollback should not occur if the target method raises this exception.
Now the final, but most important step in transaction management is the placement of the
@Transactiona
l annotation. In most cases, confusion arises where the abstract should be placed: at the service level or at the DAO level?
@Transactional
: Service or DAO level?Service is the best place to place
@Transactional
, the service level should contain the behavior of the use case at the level of detail for user interaction, which logically goes into the transaction.
There are many CRUD applications that do not have significant business logic, have a service level, which simply transfers data between controllers and data access objects, which is not useful. In these cases, we can place the transaction annotation at the DAO level.
Therefore, in practice, you can put them anywhere, it is up to you.
In addition, if you place
@Transactional
at the DAO level and if your DAO level is reused by different services, then it will be difficult to place it at the DAO level, since different services may have different requirements.
If your service level retrieves objects using Hibernate, and let's say you have lazy initializations in the definition of a domain object, then you need to open a transaction at the service level, otherwise you will have to face LazyInitializationException thrown by the ORM.
Consider another example where your service level can call two different DAO methods to perform database operations. If your first DAO operation fails, the other two can be transferred, and you end the inconsistent database state. Annotation at the service level can save you from such situations.
Hope this article has helped you.
THE END
It is always interesting to see your comments or questions.