【问题标题】:SpringBoot Transaction Isolation LevelSpring Boot 事务隔离级别
【发布时间】:2020-06-07 22:23:15
【问题描述】:

我有这个项目试图检查事务隔离。我从 READ_UNCOMMITTED 级别开始,但它不起作用。 代码非常简单。

主类

@SpringBootApplication
@EnableTransactionManagement
public class HibernateTransactionsLocksTestApplication {

    public static void main(String[] args) {
        SpringApplication.run(HibernateTransactionsLocksTestApplication.class, args);
    }

}

控制器

    @RestController
    public class HomeController {

        private final AccountService accountService;

        public HomeController(AccountService accountService) {
            this.accountService = accountService;
        }

        @GetMapping("/updateAccount2RU")
        public String updateAccount2RU() throws InterruptedException {
            accountService.updateAccount2RU();
            return "done!";
        }

        @GetMapping("/update2Account2RU")
        public String update2Account2RU() throws InterruptedException {
            accountService.update2Account2RU();
            return "done!";
        }
}

服务

@Service
public class AccountService {

    private final AccountRepository accountRepository;

    public AccountService(AccountRepository accountRepository) {
        this.accountRepository = accountRepository;
    }

    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
    public void updateAccount2RU() throws InterruptedException {
        Account a = accountRepository.findById(2).get();
        System.out.println("Account amount: " + a.getAmount());
        a.setAmount(a.getAmount()+1);
        accountRepository.save(a);
        Thread.currentThread().sleep(5000);
    }

    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
    public void update2Account2RU() throws InterruptedException {
        Account a = accountRepository.findById(2).get();
        System.out.println("Account amount: " + a.getAmount());
        a.setAmount(a.getAmount()+1);
        Thread.currentThread().sleep(5000);
    }

}

Repository 是一个简单的 SpringData 存储库

@Repository
public interface AccountRepository extends JpaRepository<Account, Integer> {

    @Transactional(propagation = Propagation.MANDATORY, isolation = Isolation.READ_UNCOMMITTED)
    Account findByName(String name);
}

application.properties

server.contextPath=/

spring.datasource.url=jdbc:mysql://localhost:3306/trasactions-locks-tests
spring.datasource.username=
spring.datasource.password=
spring.jpa.properties.hibernate.dialect =org.hibernate.dialect.MySQL5Dialect

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
#spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

spring.jpa.open-in-view=false

#Check transactions behaviour
logging.level.org.springframework.transaction.interceptor=TRACE

基本上我在 Chrome 中打开 2 个选项卡并访问 updateAccount2RU(这应该读取帐户并增加金额,线程应该在醒来并提交事务之前休眠 5 秒)同时我访问第二种方法 update2Account2RU读取相同的帐户,并且从第一种方法读取的金额相同,而不是更新的方法。

【问题讨论】:

    标签: hibernate spring-boot transactions spring-data-jpa


    【解决方案1】:

    您很可能需要在第一种方法中使用accountRepository.save(a);

    SQL 更新通常由 Hibernate 缓冲,直到事务提交时,即当您的事务方法返回时。

    因此,在 T2 读取记录时,没有挂起的未提交数据库更改。

    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
    public void updateAccount2RU() throws InterruptedException {
        Account a = accountRepository.findById(2).get();
        System.out.println("Account amount: " + a.getAmount());
        a.setAmount(a.getAmount()+1);
    
        // force explicit flush so T2 should now see the uncommitted change
        accountRepository.saveAndFlush(a);
    
        Thread.currentThread().sleep(5000);
    }
    

    见:

    https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/objectstate-flushing.html

    Session 会不时执行所需的 SQL 语句 将 JDBC 连接的状态与对象的状态同步 保存在记忆中。这个过程,flush,默认发生在 以下几点

    • 在一些查询执行之前
    • 来自 org.hibernate.Transaction.commit()
    • 来自 Session.flush()

    【讨论】:

    • 不是 saveAndFlush 实际提交,因此当执行第二个方法时,它会读取一个提交的行(意味着 READ_UNCOMITTED 和 READ_COMITTED 之间没有区别)?我检查并使用 saveAndFlush READ_UNCOMITTED 和 READ_COMITTED 查看更改
    • 不,不是。当方法返回时,提交/回滚仍然发生。
    • 那为什么第二种方法即使使用 READ_COMITTED 也会读取更新的值?
    • 我认为您的原始问题已得到解答。您应该接受并提出新问题以了解更多问题。我可以保证 100% 刷新!= 提交。
    猜你喜欢
    • 2015-07-21
    • 2019-03-17
    • 2018-04-30
    • 1970-01-01
    • 2015-01-06
    • 2011-09-30
    • 2018-08-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多