当两个线程同时访问带有@Transactional 的方法时,我们会得到多少个事务?

How many transactions do we get when two threads visit a method with @Transactional at the same time?

假设我们有这段代码:

@Transactional
public String getUser(long id) {

    User user = userDao.getUserById(id);
    if(user == null) {
        user = new User();
        user.setXXX(XXX);
        userDao.insert(user);
    }   
}

假设数据源是mysql5

如果两个线程同时访问getUser()方法,我们会得到多少事务?如果答案是两笔,那么两笔交易之间是什么关系?

截至 Spring 文档:

The getTransaction(..) method returns a TransactionStatus object, depending on a TransactionDefinition parameter. The returned TransactionStatus might represent a new transaction, or can represent an existing transaction if a matching transaction exists in the current call stack. The implication in this latter case is that, as with Java EE transaction contexts, a TransactionStatus is associated with a thread of execution.

这意味着对于每个线程 Spring 都将创建一个新事务,除非该线程已经存在一个事务。

当两个线程进入这个方法时,会创建两个单独的事务,它们之间没有任何关系。这里没有嵌套或任何东西(那是不同的场景)。我能想到的唯一存在任何关系的情况是使用 Propagation.REQUIRES_NEW 时,但这里不是这种情况。

How many transactions do we get if two threads visit getUser() method at the same time?

这个问题的答案取决于连接工厂和数据库类型。如果您使用连接池或以其他方式创建两个连接,则每个线程将获得与数据库的单独事务。此处的详细信息在很大程度上取决于您的连接池和数据库设置。

If the answer is two, so what is the relationship between the two transactions?

这取决于数据库对这些事务的处理方式。重要的是要认识到事务与互斥无关,而是与数据库一致性有关。引用自wikipedia

A transaction symbolizes a unit of work performed within a database management system (or similar system) against a database, and treated in a coherent and reliable way independent of other transactions. A transaction generally represents any change in database.

事务如何交互在很大程度上取决于创建什么类型的事务以及在其中执行什么。如果您询问是否有可能让 2 个线程同时查找具有相同 ID 的 User,然后尝试创建相同的 User 两次,那么答案肯定是肯定的。

一个问题是,两个 userDao.insert(...) 调用都可能成功,但第二次提交其事务(竞争条件警报)可能会抛出某种唯一约束异常,但这可能在 AOP 级别而不是在你的代码中。