数据库事务将一系列修改组合在一起,以一种方式要么完成所有修改,要么一个都不修
改。经典使用事实的例子是两个银行账户间的现金交易。基本逻辑很简单。
account1.deposit(100)
account2.withdraw(100)
在Active Record 中,我们使用transaction()方法来执行一个块,这是一个特殊的数
据库事务处理的上下关联的段落。这个块的结尾,事务处理被提交,更新数据库,除非块中
有异常抛出,一旦出现异常,所有的改变将被回滚,数据库的状态还是没有改变。因为事务
处理位于数据库连接的上下文环境中。我们必须用一个Active Record 类做为一个被调来来
调用它们。像这样写
Account.transaction do
account1.deposit(100)
account2.withdraw(100)
end
让我们来试试事务处理。先创建一个新的数据库表。因为我们使用MySQL,我们就用
InnoDB 的存储引擎来创建表,这是支持事务处理的。
create table accounts (
id int not null auto_increment,
number varchar(10) not null,
balance decimal(10,2) default 0.0,
primary key (id)
) type=InnoDB;
接着,我们将定义一个简单的银行账户类。个类定义实例方法desposit()存钱,
withdraw()方法取钱。它也提供一些基本的确认—对这个特定账户类型,balance 不可能为
负数。
class Account < ActiveRecord::Base
def withdraw(amount)
adjust_balance_and_save(-amount)
end
def deposit(amount)
adjust_balance_and_save(amount)
end
private
def adjust_balance_and_save(amount)
self.balance += amount
save!
end
def validate
errors.add(:balance, "is negative") if balance < 0
end
end
让我们看看helper 方法,adjust_ablance_and_save()。第一行简单地更新balance 字
段。然后这个方法试着使用save!方法保存model。(记住如果对象不能被保存,save!会引发
一个异常。我们使用这个异常来通知事务是否发生错误。)
现在让我们写在两个账户转账代码。它很直白。
peter = Account.create(:balance => 100, :number => "12345")
paul = Account.create(:balance => 200, :number => "54321")
Account.transaction do
paul.deposit(10)
peter.withdraw(10)
end
我们检查数据库,的确,现金被交易成功了。
mysql> select * from accounts;
+----+--------+---------+
| id | number | balance |
+----+--------+---------+
| 5 | 12345 | 90.00 |
| 6 | 54321 | 210.00 |
+----+--------+---------+
我们再向前一步。试着转账350 美元,确认有效性的规则就会起作用。让我们试试。
peter = Account.create(:balance => 100, :number => "12345")
paul = Account.create(:balance => 200, :number => "54321")
Account.transaction do
paul.deposit(350)
peter.withdraw(350)
end
当我们运行它时,我们会在控制台得到一个异常报告。
validations.rb:652:in ‘save!': ActiveRecord::RecordInvalid
from transactions.rb:36:in ‘adjust_balance_and_save'
from transactions.rb:25:in ‘withdraw'
: :
from transactions.rb:71
看看数据库,我们可看到数据没有被更改。
mysql> select * from accounts;
+----+--------+---------+
| id | number | balance |
+----+--------+---------+
| 7 | 12345 | 100.00 |
| 8 | 54321 | 200.00 |
+----+--------+---------+
但是这里有个陷阱在等着你。事务处理可以避免数据库发生不一致的情况,但是model
对象又该如何呢?让我们看看对于它们会发生什么,我们必须截获异常来允许程序继续处理。
peter = Account.create(:balance => 100, :number => "12345")
paul = Account.create(:balance => 200, :number => "54321")
begin
Account.transaction do
paul.deposit(350)
peter.withdraw(350)
end
rescue
puts "Transfer aborted"
end
puts "Paul has #{paul.balance}"
puts "Peter has #{peter.balance}"
结果让人惊讶。
Transfer aborted
Paul has 550.0
Peter has -250.0
虽然数据库仍保持原样,但我们的model 对象却更新了。这是因为Active Record 不能
跟踪不同对象的更新前后的状态--事实上它也不可能,因为没有简单的办法知道哪个model
是在事务处理当中。我们可以调整这个,通过明白的告诉transaction()方法,把model 作
为一个参数输入才行。
peter = Account.create(:balance => 100, :number => "12345")
paul = Account.create(:balance => 200, :number => "54321")
begin
Account.transaction(peter, paul) do
paul.deposit(350)
peter.withdraw(350)
end
rescue
puts "Transfer aborted"
end
puts "Paul has #{paul.balance}"
puts "Peter has #{peter.balance}"
这次我们看到models 最终都没有改变。
Transfer aborted
Paul has 200.0
Peter has 100.0
我们可通过将转账功能移到Account 类中来整理一下这个代码。因为一个转账包含两个
单独的帐户,它们彼此不能互相驱动,我们要让它成为一个类方法,以接受两个account 对
象作为参数。注意在类方法内我们怎样来简化调用transaction()方法。
class Account < ActiveRecord::Base
def self.transfer(from, to, amount)
transaction(from, to) do
from.withdraw(amount)
to.deposit(amount)
end
end
end
用这个被定义的方法,我们的程序也干净多了。
peter = Account.create(:balance => 100, :number => "12345")
paul = Account.create(:balance => 200, :number => "54321")
Account.transfer(peter, paul, 350) rescue puts "Transfer aborted"
puts "Paul has #{paul.balance}"
puts "Peter has #{peter.balance}"
Transfer aborted
Paul has 200.0
Peter has 100.0
但是让事务处理代码自动恢复对象的状态有一个不足--你不能获得在确认期间得到任何
被添加的错误信息。无效的对象将不会被保存,事务处理将回滚所有的东西。但没有简单的
访法知道曾经发生了什么错误。
内建的事务处理
当我们讨论父表和子表时,我们说当你要保存一个父表的记录时,Active Record 会注
意存储所有依赖的子表记录。这会有多个SQL 语句执行(一个是为父表,而每个子表都有sql
语句)。很显然,这种改变也是原子性的,但直到目前为止,我们都没有使用事务来保存这
样的相关对象。
Active Record 已经做得不错,它在一个事务处理中把所有的更新和插入操作包装到
save()(删除是destroy())。这些操作要么写成功,要么什么数据都不写入数据库。只有
在你管理多个SQL 语句时,你才需要显式的事务处理。
多数据库事务处理
在rails 中,怎样跨越多个不同数据库同步事务处理?
当前的回答是你不能。Rails 不支持分布的两阶段提交。
但是你可能通过嵌套事务来模仿这种效果。记住那个事务与数据库连接相关,并且连接
与model 相关。所以如果acconts 表在一个数据库中,并且用户在另一个数据库中,你可以
将同样的事情做两次来模仿些事务。例如,
User.transaction(user) do
Account.transaction(account) do
account.calculate_fees
user.date_fees_last_calculated = Time.now
user.save
account.save
end
end
这也只是一种尽可能解决办法。It is possible that the commit in the users database
might fail (perhaps the disk is full), but by then the commit in the accounts
database has completed and the table has been updated. This would leave the
overall transaction in an inconsistent state. It is possible (if not pleasant)
to code around these issues for each individual set of circumstances, but for
now, you probably shouldn’t be relying on Active Record if you are writing
applications that update multiple databases concurrently.
分享到:
相关推荐
PostgreSQL 事务处理技术内幕深度探索 PostgreSQL 是一种开放源代码的关系数据库管理系统(RDBMS),它具有高度的可靠性、稳定性和安全性,被广泛应用于各种企业级应用和云服务。PostgreSQL 的事务处理机制是...
事务处理广泛地应用于数据库和操作系统领域,并在现代计算机系统中监控、控制和更新信息。本书向读者展示了大型的、分布的、异构的计算机系统是如何进行可靠工作的。作者使用事务作为基本概念,说明了在有限的资金和...
### 联机事务处理系统(OLTP)深入解析 #### OLTP系统概览 联机事务处理系统(OLTP)是数据库管理的核心组件之一,主要用于处理大量的事务性操作,如银行交易、订单处理和机票预订等。这些系统旨在提供高并发、低...
在IT领域,尤其是在数据库编程中,事务处理是一个至关重要的概念,尤其在使用C#进行开发时。事务确保了数据的一致性和完整性,是数据库操作的基本单位。本篇将深入探讨C#中的事务处理,以及如何在实践中应用这些知识...
本压缩包文件"易语言学习进阶事务处理"是针对易语言使用者提供的一份进阶学习资料,主要涵盖了事务处理的相关知识。 事务处理在计算机科学中是指在数据库管理系统中执行的一系列操作,这些操作被视为一个单一的工作...
Java事务处理详解 Java事务处理是指在Java应用程序中对事务的管理和控制。事务是指一系列的操作,Either all succeed or all fail。Java事务处理的目的是为了确保数据的一致性和完整性。 Spring是Java事务处理的...
### Socket_UDP多事务处理程序框架的关键知识点 #### 第一部分:Socket通信事务处理中的常见问题描述 **1.1 Socket通信基础** Socket通信是基于互联网协议(IP)进行数据传输的标准方式,提供了网络上的进程间...
### 分布式事务处理在大规模SOA系统中的挑战与解决方案 #### 一、背景与历史:山穷水尽 在介绍分布式事务处理之前,我们首先回顾一下事务处理的历史背景及其面临的挑战。传统的事务处理通常是在单个应用系统内部...
数据库事务处理是数据库管理系统中的核心概念,用于确保数据的一致性和完整性。事务是数据库操作的基本单元,它包含一组逻辑操作,这些操作要么全部执行,要么全部不执行,以确保数据的原子性。事务处理主要关注两个...
事务处理 概念与技术
在IT领域,事务处理是数据库管理系统中的核心概念,它确保了数据的一致性和完整性。本文将深入探讨事务的原理和实现方法。 1. **什么是事务** 事务是一系列数据库操作的逻辑单元,这些操作要么全部成功执行,要么...
在易语言的学习过程中,进阶事务处理是提升技能的重要环节。事务处理通常涉及到数据库操作、多线程、错误处理和程序流程控制等复杂技术,对于软件的稳定性和效率有着至关重要的作用。 在这个“易语言学习进阶事务...
### ASP.NET中的事务处理与异常处理 在ASP.NET开发中,事务处理与异常处理是确保应用程序稳定性和数据一致性的重要组成部分。本文将详细探讨这两个概念及其具体应用方式。 #### 一、事务处理 事务处理主要用于...
数据库事务处理是数据库管理系统中的核心概念,用于确保数据的一致性和完整性。在“数据库事务处理基础——设计与实现”这个主题中,我们将深入探讨数据库事务的各个方面,包括其定义、特性、类型以及如何在实际应用...
在VB.NET 2008中,事务处理是确保数据库操作一致性的重要机制。它允许一组数据库操作要么全部成功,要么全部失败,从而避免了数据不一致性的风险。本示例涵盖了如何在VB.NET环境中利用ADO.NET进行事务管理,特别是在...
### Java事务处理详细介绍 #### 一、什么是Java事务 在软件开发领域,特别是涉及数据库操作的应用中,**事务处理**是非常关键的一个概念。通常人们认为事务处理与数据库操作紧密相关,其实事务的概念远不止于此。...
在SQL Server中,事务处理是数据库操作的核心组成部分,它确保数据的一致性和完整性。事务是一组逻辑操作,这些操作被视为单个单元,要么全部完成,要么全部回滚,以维护数据库的ACID(原子性、一致性、隔离性和持久...
本文将探讨分布式系统中事务处理的常用方法,并重点分析数据分区和数据镜像这两种扩展数据服务的手段,以及它们对事务处理带来的挑战。 首先,我们需要了解单台服务器在处理大量网络请求时所面临的性能瓶颈和服务的...
### JavaBean中使用JDBC方式进行事务处理 #### 一、引言 在现代软件开发过程中,数据一致性是非常关键的一个方面,特别是在涉及到多个数据库操作时。本文将详细介绍如何在JavaBean中利用JDBC(Java Database ...