假设我们有以下数据库结构和附带的网页:
该数据库由两个表t1
和t2
组成。表t2
由两列组成,一列称为id_bar
,它只是一个唯一的id,另一列称为name
,用于存储ui使用的一些字符串。另一个表t1
有两列,一个名为id_foo
,只是一个唯一的ID,另一个名为id_bar
,必须指向t2
中的某一行(即这是外键)。
网页的相关部分是(伪代码):
Form form = new Form();
form.build_and_add_select_from_db_table("t2", "field1");
form.add_submit_button();
if (request.is_post() && form.is_valid(request.get_post_data())) {
Add a new row to t1, using the data from the request
response.redirect(<somewhere>);
}
print form.render(); // this will display validation errors as well
因此,Form
类将呈现form
由select
元素组成,其中t2
行为option
s,input
用于提交表单。
现在针对这个问题,进一步假设网站的其他部分允许用户从t2
删除行,那么可以使用以下内容:
t2
由行(1,“a”),(2,“b”)和(3,“c”)组成,其中例如: (1,“a”)表示id_bar
= 1且name
=“a”。select
元素if
语句的真正分支(但是,尚未将新行插入数据库)t2
删除所选行(即(1,“a”))t2
中的行所以似乎服务器脚本也必须处理这种情况;也就是说,重新呈现表单并通知用户A所选选项不再可用。
还存在由这种非原子性引起的其他类似情况,例如:如果我们有一个名为user
的表,其中每个用户都应该拥有唯一的用户名,通过与上述相同的推理,我们不能简单地说:
if (check if the username already exists in the database)
display_error();
else
insert_new_user_into_database();
一种解决方案(在两种情况下)都是乐观地尝试插入新行,然后以某种方式确定违反了什么约束,以及为什么,以及通过(我猜)解析错误消息和错误代码,通知用户出了什么问题。然而,这感觉非常混乱:
try {
if (request.is_post() && form.is_valid(request.get_post_data())) {
Add a new row to t1, using the data from the request
response.redirect(<somewhere>);
} catch (DatabaseException e) {
if (e.is_some_constraint_violation()) {
// Find out what field caused the violation
// and generate an appropriate error message,
// possibly also removing alternatives from some form fields,
// it will rethrow the exception or something if it can't
// figure out what happened.
form.handle_database_constraint_violation(e);
} else
throw;
}
另一种解决方案可能是某种锁定?
if (request.is_post())
lock_everything();
Build form ...
if (request.is_post() && form.is_valid(request.get_post_data())) {
Insert the new row into the database
unlock_everything();
response.redirect(<some other page>);
} else
unlock_everything();
这似乎是一个非常常见的问题(例如需要一个唯一的用户名),那么这里介绍的情况是否有一些众所周知的标准解决方案?
答案 0 :(得分:0)
这样做的一种方法是不从表中实际记录delete
,而是使用软删除将其标记为已删除,以便UI(和其他应用程序层)可以分辨他们不在那里。
ORM框架(例如Hibernate)通过使用版本字段来处理这种“并发修改”问题。然后,任何更新最终都会使where
子句的一部分看起来像where myVersion = dbVersion
,如果然后捕获异常并找出原因发生变化的原因。
这可以防止改变只是盲目地进行,并且还以原子方式进行,以便DB控制事务的原子性,而不是应用程序代码。 (锁定应用程序代码很乱)