Spring 中的交易

Transaction in Spring

我是 Spring 的新手,对交易有疑问。

我知道对于每个 http 请求都有一个 servlet 线程,它有自己的堆栈。据我所知,所有局部变量和方法都驻留在堆栈中。所以如果我有一个方法 public void A(); 那么 servlet 线程 A 和线程 B 在它们的堆栈中都有一个函数的副本。

现在如果我用@Transactional(propagation=Propagation.REQUIRED ,timeout=1,isolation=Isolation.READ_COMMITTED)注释了一个方法那么我想知道以下几点:

  1. 是否每个线程A和线程B都有自己的栈,独立工作?

  2. 如果线程 A 正在更新某些内容而线程 B 正在读取某些内容,因为在不同的堆栈中,隔离是否可行?或者 B 将读取数据而没有关于线程 A 的任何信息?

我想通过图表了解这一点,以便了解端到端的工作原理?

线程 B 只会在线程 A 的事务完成后看到线程 A 对数据库所做的修改。

仍然有可能线程 B 从数据库中读取某些内容,然后线程 A 更新数据库中的内容并提交,然后线程 B 覆盖线程 A 写入的内容。

为避免这种情况,您需要在数据库上使用某种类型的锁定,或使用更严格的隔离级别(但这会带来高性能损失,因为数据库通常需要执行大量锁定才能实现更严格的隔离级别隔离级别)

每个线程都有自己的堆栈,你就在这里。但是他们没有方法的副本。方法只是一系列操作。但是他们在这个方法中有变量的副本(局部变量)。

说到隔离级别,其实跟线程和栈无关。它指的是 数据库隔离级别 概念。 Spring 向您的数据库询问要用于数据库事务的级别。 PostreSQL 对事务隔离有很好的 doc page 解释。

所以在这里问线程如何相互查看是不太正确的,因为从数据的角度来看,它们看到的是从数据库中获取的内容。并且数据库 returns 数据相应地符合当前事务隔离级别。每个线程启动自己的事务,即它创建一个新的数据库连接并告诉它启动一个新的事务。

例子

为了感受幕后发生的事情,这里有一个例子。假设你有这样的方法:

@Transactional
public Person getPerson(int id) {
    Person person = em.find(Person.class, id);
    return person;
}

以下是每行 Spring 引擎盖下发生的事情:

@Transactional
public Person getPerson(int id) {
// SQL sent to the database:
// Begin transaction

Person person = em.find(Person.class, id);
// SQL sent to the database:
// select p from person p where p.id = id
// the data from the database then gets converted to Java Person class

return person;
}
// after end of the method Spring automatically commits the transaction (not always, it depends on the `propagation` setting)
// SQL sent to the database:
// commit

请阅读深入解释事务隔离的 PostgreSQL 文档。 Java 线程只相应地接收数据。

简单地说:

  1. B 将在没有任何关于线程 A 的信息的情况下读取数据

现在为什么会这样?在 servlet 环境中,您通常有一个或多个 servlet,并且所有数据都在 class 级别,因此在 doHttp 方法之外共享相同的数据,servlet 不是线程安全的。

当然,方法中的数据是线程安全的,因为每个方法调用都是一个调用函数,并且将有一个线程来为方法请求提供服务,并且它有自己的堆栈。

但是,如果您有两个并发方法调用,并且出于这个原因,在两个单独的线程中,事务将不相同,因为线程本地不相同并且不共享数据库的相同图像它如果您考虑 JPA,那么 persistanceContext 是存储在 threadlocal 中的一级缓存是很自然的。我的建议是使用更高级别的事务隔离来缓解可能出现的问题

希望对您有所帮助