首页 > 解决方案 > 是否有必要开始事务以在数据库中查找对象?

问题描述

我知道与数据库中的对象交互需要以下内容:

db.getTransaction().begin();
//Code to modify objects in the data base
db.getTransaction().commit();

但是当我使用函数 find(object.class, object.id) 时有必要吗?

标签: javadatabaseobjectpersist

解决方案


是否有必要开始事务以在数据库中查找对象?

取决于您所说的“交易”一词的含义。

数据库在事务中做所有事情。时期。因此,简单地说,“是的”,开始事务是必要的,因为不这样做就无法与数据库交互。

但是,数据库具有“便利”功能,您可以在其中像不存在事务一样工作。这是个谎言; 它仅意味着您发送到数据库的每条语句都被默默地包装在“开始交易”中;和“提交;” 来电。这被称为“自动提交”模式(而不是“无事务模式”),因为这是它的正确词:它在每个事务之后自动提交。

因此,在基本 JDBC(Java 中所有基于 SQL 的 DB 交互通常建立在其上的基础层,但请注意,您正在使用构建在它之上的某种库!)中,您可以只运行一个选择查询,通过使用自动提交模式,无需“打扰”事务。

但是,自动提交模式通常是一个坏主意。

事务是数据库设计的基础。即使您只是进行“读取”查询,事务仍然很重要。它们保证一致性和原子性。这是一个例子:

假设您想知道 Jane 的银行余额和 Jack 的银行余额,例如检查余额是否足以出租房屋或诸如此类。

看起来很简单:

SELECT balance FROM bankaccounts WHERE user = 'Jack';
SELECT balance FROM bankaccounts WHERE user = 'Jane';

把它们加起来,对吧?

错了

如果您在自动提交模式下运行它,那么可能 Jack 有 50k,Jane 有 100k,但总和似乎是 200k。这是通过杰克设法在两条语句之间将他的 50k 转移给 jane 来完成的,给你一个 50k+150k = 200k 的总和,即使这是一个谎言。

因此事务也适用于只读会话;假设您使用正确的隔离级别(SERIALIZABLE 是合理的级别),它们甚至会导致重试。通过交易,您每次都会得到 150k 的正确答案,无论简和杰克何时或多久来回转移资金以试图欺骗您。

JDBC 本身没有“getTransaction()”或“开始”,通常这不是“执行”事务的正确方法。因此,不清楚您在这里使用的是哪个第三方库。

一个适当的数据库抽象看起来像:

int sum = dbAccess.executeInt(db -> {
  // query or modify things here:
  return
  db.select("SELECT balance FROM bankaccount WHERE user = ?", "Jack").singleInt() +
  db.select("SELECT balance FROM bankaccount WHERE user = ?", "Jane").singleInt();
};

它需要在 lambda 中处理重试,并且与与数据库的所有交互都是事务性的事实很好地吻合。

您可能想多阅读一些文档;这种(使用 lambdas 的)策略可能晚于你的 DB 库的开始(lambdas 是在 java8 中添加的;诚然,那是十年前的事了)。


推荐阅读