Skip to content

Add Transactional Support to the @Modifying Annotation #3733

Closed as not planned
@petro-zhylenko

Description

@petro-zhylenko

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:

  1. When the method is invoked within a readOnly transaction, errors can occur (e.g., cannot execute statement in read-only transaction).
  2. Developers must explicitly add the @Transactional annotation to every method, resulting in repeated boilerplate code.
  3. 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:

  1. 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)");
    }
  2. Integration with @Transactional:
    When transactional = 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;
    }
  3. Atomicity Enforcement:
    Even for "atomic" operations (e.g., single UPDATE or DELETE), 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

  1. Ensures Safe Transactional Execution:
    Avoids situations where a method with @Modifying is invoked within a readOnly transaction, leading to errors or incorrect behavior.

  2. Reduces Boilerplate Code:
    Developers will no longer need to manually add @Transactional(readOnly = false) to every method using @Modifying.

  3. Improves Standards:
    By default, transactions will be active since the cases requiring transactions significantly outnumber those where they are not needed.

  4. Flexibility:
    The additional transactional = 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

  1. Reduced risk of errors related to readOnly transactions.
  2. Simplified code with automatic transactional context handling.
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: declinedA suggestion or change that we don't feel we should currently apply

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions