Java &引用;选择“更新”选项;并使用悲观锁定进行更新
我正试图使用selectforupdate实现悲观锁定,因为我希望其他线程等待所选行上的锁被释放。 我所理解的部分是在经历了多个线程和各种类似的线程之后,如果select和update发生在同一个方法中,那么这是可以实现的,因此它们是同一事务的一部分 在我的例子中,问题是我有一个用于DAO功能的JAR,其中在Java &引用;选择“更新”选项;并使用悲观锁定进行更新,java,spring,jdbc,pessimistic-locking,Java,Spring,Jdbc,Pessimistic Locking,我正试图使用selectforupdate实现悲观锁定,因为我希望其他线程等待所选行上的锁被释放。 我所理解的部分是在经历了多个线程和各种类似的线程之后,如果select和update发生在同一个方法中,那么这是可以实现的,因此它们是同一事务的一部分 在我的例子中,问题是我有一个用于DAO功能的JAR,其中在selectforUpdate方法中可用,并且有一个单独的更新方法可用,这两个方法都有一个finally块,其中包含 resultSet.close(); statement.close()
selectforUpdate
方法中可用,并且有一个单独的更新方法可用,这两个方法都有一个finally块,其中包含
resultSet.close();
statement.close();
connection.close();
现在我正在努力寻找一种方法,可以从JAR外部使用这两种方法,也许可以通过
@Transactional
注释来注释我的方法,并以某种方式使其工作。因此,只有在执行更新方法后,锁才会被释放。您犯了一个错误。在工作中使用错误的工具。事务级别和更新的目的是确保数据完整性。时期它不是为控制流量而设计的,如果你使用它,它迟早会咬你的屁股
让我试着解释一下SELECT FOR UPDATE
的作用,这样,当我稍后告诉您,它绝对不是为了您试图用它做什么时,就更容易理解了
想象一家银行。很简单。银行有一些自动取款机和一个网站,在那里你可以看到你的交易并把钱转到其他账户
想象一下,你(ABC)和我(雷尼尔)正试图从银行骗取一些钱。这是我们的计划:我们设立它,让你有1000欧元,在你的帐户,我没有
然后,你从手机登录网站,开始转账,将1000欧元转账到我的账户。但是,当你这样做的时候,正好在中间,你从ATM机取出10欧元。
如果银行把他们的交易搞砸了,你很可能会得到990欧元,在你的账户里,我有1000欧元,在我的账户里,然后我们抢劫了银行。这就是为什么会发生这种情况(如果在示例进行到一半时,您认为:我已经知道这些东西,我知道更新的作用!-我不确定您是否知道,请仔细阅读)
ATM代码
怎么会出错呢
出错的方式是,如果balanceTo
和balanceFrom
被“锁定”,则ATM取款通过,然后网站交易中的更新SQL语句通过(这有效地消除了ATM取款,无论ATM吐出什么都是免费的),或者如果ATM的余额检查锁定,然后转账通过,然后ATM的更新通过(这会给收件人,即我他们的1000欧元),并确保ATM代码的更新,将您的余额设置为990,是最后发生的事情,给我们990欧元的免费资金
那么修复方法是什么呢?提示:不适用于更新
修复是考虑事务<强>意味着< /强>。事务的目的是将操作转换成原子概念。无论是您的帐户都被转移量减少,并且我的帐户是相同的,或者什么都没有发生。 对于改变事情(更新和插入)的语句来说,这是很明显的。当我们谈论读取数据时,这有点不确定。这些读取应该被视为事务的一部分吗
一种方法是说:不,除非在最后添加更新的,在这种情况下,是-即只有在应用更新的时锁定这些行,直到事务结束
但这不是确保数据完整性的唯一方法
乐观地等待救援,或者更确切地说,等待你的厄运
一种更常见的方法称为MVCC(多版本并发控制),它的速度要快得多。MVCC(也称为乐观锁定)背后的思想是假设不会发生冲突。没有任何东西会被锁定。相反,[A]在您提交之前,事务中所做的所有更改对任何其他事务中运行的事物都是完全不可见的,并且[B]当您提交一个事务时,数据库会检查您在此事务范围内所做的一切是否仍然“有效”——例如,如果您在该事务中更新了一行,该行也被另一个已提交的事务修改过,则在提交时会出错,而不是在运行UPDATE stateme时新界
在此框架中,我们仍然可以讨论SELECT偶数的含义。在java/JDBC中,这称为事务隔离级别,可在DB连接上配置。银行为避免此问题而应使用的最佳级别称为事务级别。可序列化
。可序列化有效地表示所有内容污染其他所有内容:如果在事务期间读取一些数据,并且在提交时,由于其他事务修改了某些内容,相同的SELECT语句会产生不同的结果,那么提交就会失败
它们以所谓的“RetryException”失败。这意味着它字面上的意思是:只需从头开始您的交易。如果您考虑银行的例子,这是有意义的:如果银行做得正确并设置可序列化的交易隔离级别,会发生什么,是ATM机的交易还是Transferr交易将获得retryexception。假设银行编写了正确的代码,并且他们实际执行了异常告诉您的操作(重新开始),那么他们将重新开始,包括重新读取余额。现在不会发生银行欺诈行为
至关重要的是,在可序列化的
模型中,永远不会发生锁定,更新的根本没有任何意义
因此,通常情况下,对于更新来说,什么都不做,一个完全不做的操作,dependi
startTransaction();
int currentBalance = sql("SELECT balance FROM account WHERE user = ?", abc);
if (currentBalance < requestedWithdrawal) throw new InsufficientFundsEx();
sql("UPDATE account SET balance = ? WHERE user = ?", currentBalance - requestedWithdrawal, abc);
commit();
moneyHopper.spitOut(requestedWithdrawal();
startTransaction();
int balanceTo = sql("SELECT balance FROM account WHERE user = ?", reinier);
int balanceFrom = sql("SELECT balance FROM account WHERE user = ?", abc);
if (transfer > balanceFrom) throw new InsufficientFundsEx();
sql("UPDATE account SET balance = ? WHERE user = ?", balanceTo + transfer, reinier);
sql("UPDATE account SET balance = ? WHERE user = ?", balanceFrom - transfer, abc);
commit();
controller.notifyTransferSucceeded();
dbAccess.run(db -> {
int balance = db.sql("SELECT balance FROM account WHERE user =?", abc);
if (balance < requested) throw new InsufficientBalanceEx();
db.update("UPDATE account SET balance = ? WHERE user = ?", balance - requested, abc);
return requested;
};