我对原子操作的理解是,操作步骤不应该与任何其他操作的步骤交错 - 它应该作为一个单元执行。
我有一个创建数据库记录的方法,首先检查具有相同值的记录(也满足某些其他参数)是否已经存在,如果是,则不会创建记录。
在假码中:
public class FooDao implements IFooDao {
@Transactional
public void createFoo(String fooValue) {
if (!fooExists(fooValue)) {
// DB call to create foo
}
}
@Transactional
public boolean fooExists(String fooValue) {
// DB call to check if foo exists
}
}
但是我已经看到可以创建具有相同值的两个记录,这表明这些操作已经以某种方式交错。我知道使用Spring的事务代理,对象内部方法的自调用不会使用事务逻辑,但如果从对象外部调用createFoo(),那么我希望fooExists()仍然包含在事务。
我对交易原子性应该执行错误的期望是什么?我是否需要使用同步块来强制执行此操作?
答案 0 :(得分:1)
事务对数据库的真正意义取决于隔离级别。关于Isolation (database systems)的wikipdia文章解释得很清楚。
通常使用不太高的隔离级别,例如:Read committed
。这意味着在提交其他事务之前,可以从其他事务中读取数据。
在你的情况下这还不够,因为这与你想要的相反。 - 所以显而易见的解决方案是使用更严格和更慢的隔离级别:Repeatable reads
。
但说实话,我会用另一种方式:使相关列唯一(但不要删除if (!fooExists(fooValue))
- 检查)。因此99%的检查工作。在剩余的1%中,您将获得异常,因为您试图违反唯一约束。
答案 1 :(得分:0)
事务性意味着所有更新都在同一事务中发生,即所有更新/插入/删除都成功或全部回滚(例如,如果更新多个表)。
它不保证事务中查询的行为,这取决于RDBMS及其配置(数据库上隔离级别的配置)。
答案 2 :(得分:0)
@Transactional默认情况下不会使代码同步。两个单独的线程可以同时进入同一个块并导致插入。同步该方法并不是一个好的答案,因为这可能会极大地影响应用程序性能。如果您的问题是由两个不同的线程创建了两个相同的记录,您可能希望在数据库中添加一些具有唯一约束的索引,以便重复插入失败。