如果我误用了任何 OOP 术语,请原谅我,因为我仍然对这个主题感到困惑。
我一直在阅读面向对象编程 (OOP) - 专门针对 Web 应用程序。我一直在讨论数据访问对象 (DAO) 的概念。 DAO 负责 CRUD(创建、读取、更新和删除)方法并将应用程序的服务(业务逻辑)层连接到数据库。
我的问题特别与 DAO 中的 Update() 方法有关。在我读过的示例中,开发人员通常将 bean 对象传递给 DAO Update() 方法作为其主要参数 updateCustomer(customerBean) 该方法然后执行一些 SQL,该 SQL 根据 bean 中的数据更新所有列。
我看到这个逻辑的问题是 Update() 方法根据 bean 的数据更新数据库中的所有列,理论上可能导致它覆盖另一个用户或系统可能需要同时更新的列。
一个简化的例子可能是:
-
用户 1 更新 bean 中的字段 A
-
用户 2 更新 bean 中的字段 B
-
用户 2 将 bean 传递给 DAO,DAO 更新所有字段。
-
用户 1 将 bean 传递给 DAO,DAO 更新所有字段。
-
用户 2 的更改已丢失!
我读过关于乐观锁定和悲观锁定作为一次只允许一个更新的可能解决方案,但我可以想到许多情况,应用程序需要允许同时编辑记录的不同部分,而无需锁定或抛出错误。
例如,假设管理员在客户登录网站的同时更新客户的 lastName,登录系统需要更新 dateLastLoggedIn 列,同时计划任务需要更新更新 lastPaymentReminderDate。在这个疯狂的示例中,如果您将一个 bean 对象传递给 Update() 方法,并且每次都保存整个数据记录,那么无论哪个进程最后运行 Update() 方法都会覆盖所有数据。
肯定有办法解决这个问题。根据我的研究,我提出了一些可能性,但我很想知道实现这一目标的正确/最佳方法。
可能的解决方案 1:DAO Update() 方法不接受 Bean 作为参数
如果 Update() 方法接受的数据结构包含数据库中需要更新的所有列而不是 bean 对象,那么您可以使您的 SQL 语句足够智能以仅更新传递给该方法的字段。例如,参数可能如下所示:
{
customerID: 1,
firstName: 'John'
}
这基本上会告诉 Update() 方法仅根据 customerID 更新列 firstName,1。这将使您的 DAO 极其灵活,并使服务层能够与数据库动态交互.我有一种直觉,这违反了 OOP 的一些"黄金法则",但我不确定是哪个。我也从未在网上看到任何这样的 DAO 示例。
可能的解决方案 2:向您的 DAO 添加其他 Update() 方法。
您也可以通过向您的 DAO 添加更具体的 Update() 方法来解决此问题。例如,您可能有一个用于 dateLastLoggedIn()' and 'dateLastPaymentReminderDate()。这样,理论上每个需要更新记录的服务都可以同时进行。如果需要,可以对每个特定的更新方法进行任何锁定。
这种方法的主要缺点是,你的 DAO 将开始变得非常混乱,因为各种更新语句,我已经看到很多博客文章都在写 DAO 会很快变得混乱。
假设您需要允许同时更新记录数据的子集,您将如何使用 DAO 对象解决此类难题?您会坚持将 bean 传递给 DAO,还是有其他我没有考虑过的解决方案?
如果您执行返回 bean 的 DAO.read() 操作,然后使用用户的新值更新 bean,然后将该 bean 传递给 DAO.update(bean) 方法,那么您应该没有问题除非两个用户操作彼此在几毫秒内发生。您的问题意味着 bean 在传递给 update() 方法之前被存储在会话范围内或类似的范围内。如果这就是您正在做的事情,请不要这样做,原因正是您所描述的。您不希望您的 bean 与 db 记录不同步。为了更好的安全性,围绕读取和更新操作package一个事务,那么即使用户 2 与用户 1 在完全相同的时间提交他的更改,两个用户也不会互相踩到对方的脚趾。
Read(),设置值,update() 是要走的路,我想。保持豆子新鲜。没有人想要不新鲜的豆子。
- 感谢@Dave Anderson 的回复。当两个用户(或一个用户和一个服务)需要同时更新记录时,我的问题实际上与解决 DAO.update(bean) 问题有关。该示例应该演示一个典型的网站场景。该 bean 仅在请求范围内存在,并且应在用户将数据 POST 回服务器时保存。请求可能发生在不同的时间,但理论上两个 POST 可以同时进行,因此一个用户可以覆盖另一个用户。那有意义吗?
-
是的,我想我解决了这个问题。如果您围绕读取/设置属性/更新事件序列package事务,那么您将不会遇到问题。如果两个用户同时更新一条记录,事务会阻止你描述的场景,阻止脏记录被更新。交易{读取();设置属性;更新();犯罪(); }
-
谢谢你。这就说得通了。事务/锁将有助于防止将脏数据添加到数据库中。但是,如果您确实希望两个用户或系统能够同时更新数据库怎么办?例如,用户 1 正在更新 A 列,而用户 2 正在更新 B 列?如果我使用事务/锁,我将无法同时更新记录,对吗?
-
正确的。该表被"锁定"直到事务完成,然后执行下一个事务。如果事务包括读取和更新,那么您是安全的。
-
嗨@Dave L,在技术OO术语中,您不应该要求用户1和用户2分别同时更新A列和B列。这些列对应于定义对象状态的属性。在用户 1 更新列 A 之后,从 bean 生成的对象具有状态 A,比如说。当用户 2 更新 B 列时,状态发生了变化。因此,事务/锁是必要的。