Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support to propagate a savepoint operation via TransactionSynchronization #30509

Closed
kazuki43zoo opened this issue May 20, 2023 · 0 comments
Closed
Assignees
Labels
in: data Issues in data modules (jdbc, orm, oxm, tx) type: enhancement A general enhancement
Milestone

Comments

@kazuki43zoo
Copy link
Contributor

kazuki43zoo commented May 20, 2023

I propose to support to propagate a savepoint operation via TransactionSynchronization.

Background

I'm the MyBatis's developer. Now, If application developer use the savepoint(=PROPAGATION_NESTED) feature together with MyBatis, If perform select statement after rollback a savepoint, an application may get the rolled back data from MyBatis's local cache.
I want to the opportunity for clearing local cache that hold on MyBatis module at savepoint operations(at roll backed mainly) for resolving potential problem.

Related Issues

Example Testing Code

package com.example;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionOperations;
import org.springframework.transaction.support.TransactionTemplate;

@SpringBootTest
class MybatisSpringGh785ApplicationTests {

  @Autowired
  MessageMapper mapper;

  @Autowired
  PlatformTransactionManager transactionManager;

  @Test
  void contextLoads() {

    // TransactionOperations for standard JDBC Transaction
    DefaultTransactionDefinition txDef = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRED);
    TransactionOperations txOperations = new TransactionTemplate(transactionManager, txDef);

    // TransactionOperations for JDBC savepoint Transaction
    DefaultTransactionDefinition savepointTxDef = new DefaultTransactionDefinition(
        TransactionDefinition.PROPAGATION_NESTED);
    TransactionOperations savepointTxOperations = new TransactionTemplate(transactionManager, savepointTxDef);

    // Insert test data
    txOperations.executeWithoutResult(s -> {
      mapper.insert(new Message(1, "Hello!"));
    });

    // Do Testing
    txOperations.executeWithoutResult(txStatus -> {

      // Select latest message and save to local cache
      Message messageBefore = mapper.select(1);

      // Execute processing on savepoint transaction
      savepointTxOperations.executeWithoutResult(savepointTxStatus -> {

        // Update and clear local cache
        mapper.update(new Message(1, "Hello World!!"));

        // Select latest message and save to local cache
        mapper.select(1);

        // Mark to rollback savepoint
        savepointTxStatus.setRollbackOnly();
      });

      // Select latest message but does not return latest message from database because does not clear local cache when savepoint rollback
      Message messageAfter = mapper.select(1);

      Assertions.assertThat(messageAfter.message()).isEqualTo(messageBefore.message()); // Failed assertion 

    });
  }

  record Message(int id, String message) {

  }

  @Mapper
  interface MessageMapper {
    @Select("SELECT id, message FROM messages WHERE id = #{id}")
    Message select(int id);

    @Insert("INSERT INTO messages (id, message) VALUES(#{id}, #{message})")
    void insert(Message message);

    @Update("UPDATE messages SET message = #{message} WHERE id = #{id}")
    void update(Message message);

  }

}
CREATE TABLE messages
(
    id      bigint primary key,
    message varchar(256) not null
);
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label May 20, 2023
@jhoeller jhoeller added in: data Issues in data modules (jdbc, orm, oxm, tx) type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Nov 24, 2023
@jhoeller jhoeller self-assigned this Nov 24, 2023
@jhoeller jhoeller added this to the 6.2.x milestone Nov 24, 2023
@jhoeller jhoeller changed the title Support to propagate a savepoint operations via TransactionSynchronization Support to propagate a savepoint operation via TransactionSynchronization Feb 15, 2024
@jhoeller jhoeller modified the milestones: 6.2.x, 6.2.0-M1 Mar 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: data Issues in data modules (jdbc, orm, oxm, tx) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

3 participants