Description
Problem
The @Modifying
annotation is used for INSERT
, UPDATE
, DELETE
, and table structure modification operations. In most cases, such operations require transactional support to ensure:
- Data consistency.
- Protection against incorrect execution when the method is invoked within a
readOnly
transaction. - Safe execution of operations in cases of errors or exceptions.
Currently, @Modifying
does not automatically enforce a transactional context, leading to several issues:
- When the method is invoked within a
readOnly
transaction, errors can occur (e.g.,cannot execute statement in read-only transaction
). - Developers must explicitly add the
@Transactional
annotation to every method, resulting in repeated boilerplate code. - In large projects, there is a risk of forgetting to add transactional support, which can cause unexpected behavior or be difficult to debug.
Proposal
Add transactional support to the @Modifying
annotation by introducing the following changes:
-
New
transactional
Parameter in@Modifying
:- Type:
boolean
- Default value:
true
(i.e., transactional support enabled by default).
This allows developers to explicitly specify when a transaction is not needed, while keeping the default behavior transactional for most cases.
Example:
@Modifying(transactional = true) public void updateEntity(Entity entity) { // Update data } @Modifying(transactional = false) public void createIndex() { jdbcTemplate.execute("CREATE INDEX idx_name ON table_name (column_name)"); }
- Type:
-
Integration with
@Transactional
:
Whentransactional = true
, the@Modifying
annotation should implicitly add behavior equivalent to@Transactional(readOnly = false)
.Implementation Example:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Transactional(readOnly = false) public @interface Modifying { boolean transactional() default true; }
-
Atomicity Enforcement:
Even for "atomic" operations (e.g., singleUPDATE
orDELETE
), transactional support is beneficial as it:- Provides protection against potential conflicts or errors.
- Standardizes behavior and makes the codebase more predictable.
Reasons for the Change
-
Ensures Safe Transactional Execution:
Avoids situations where a method with@Modifying
is invoked within areadOnly
transaction, leading to errors or incorrect behavior. -
Reduces Boilerplate Code:
Developers will no longer need to manually add@Transactional(readOnly = false)
to every method using@Modifying
. -
Improves Standards:
By default, transactions will be active since the cases requiring transactions significantly outnumber those where they are not needed. -
Flexibility:
The additionaltransactional = false
parameter allows for handling rare cases where transactional support is unnecessary (e.g., DDL operations or externally managed transactions).
Examples of Use
1. Default Transactional Method:
@Modifying
public void saveEntity(Entity entity) {
repository.save(entity);
}
2. Non-Transactional Method (DDL Operation):
@Modifying(transactional = false)
public void alterTable() {
jdbcTemplate.execute("ALTER TABLE table_name ADD COLUMN new_column INT");
}
3. Invocation Within a Read-Only Transaction:
@Transactional(readOnly = true)
public void processEntity(Entity entity) {
updateEntity(entity); // Guaranteed to run with transactional = true
}
@Modifying
public void updateEntity(Entity entity) {
repository.save(entity);
}
Expected Outcome
- Reduced risk of errors related to
readOnly
transactions. - Simplified code with automatic transactional context handling.
- Flexibility for cases where transactions are not needed.
Additional Steps
Update the documentation for @Modifying
to include the new transactional
parameter and provide guidance on when to use it.