想象一下,你有下表(注意:这是一个人为/简化的例子):
CREATE TABLE foo (
book_id number,
page number,
-- [a bunch of other columns describing a single page in a book]
);
ALTER TABLE foo
ADD (CONSTRAINT foo_pk PRIMARY KEY(book_id, page));
虽然(book_id,page)对是唯一的,但书籍之间将重复相同的页码(许多书籍将有第1页)。因此,如果SQL查询未指定book_id,则可以选择/更新/删除错误的页面。我们所有的查询一次只能处理一本书,但我看到了几个错误,其中book_id参数被意外省略。
是否有一种编程方式来强制执行每个select,insert,update等查询在where子句中指定book_id?
我们动态生成查询的SQL代码,并使用Spring的JdbcTemplate执行它们。数据库是Oracle。使用自动化测试来检查许多可能的查询(以及将来添加的新查询!)不会因重复的page_ids而被绊倒。我可以覆盖JdbcTemplate代码以确保sql查询始终包含book_id参数,但这涉及手动解析SQL代码(尤其是子查询很棘手)并且看起来很糟糕。是否有更强大的解决方案来执行此操作?一些触发器,存储过程,约束?
答案 0 :(得分:3)
保护数据库免受程序员错误的常见方法是要求应用程序使用存储过程。 (有时这可以使用权限来完成。)
与特殊查询相比,检查您的合规性更容易。
答案 1 :(得分:2)
您可以使用函数或存储过程而不是直接使用UPDATE。该过程需要2个参数,如果其中任何一个为空,则抛出错误。
另一个选项是确保您生成的查询始终具有book_id约束。我希望您不是将整个SQL语句创建为String并且您正在使用参数化查询。如果不是,则使用参数化查询是确保始终传递book_id的好方法(如果未设置参数,则查询将不会运行)。此外,如果在使用参数化查询时未对输入进行消毒,则不会有风险。
答案 2 :(得分:1)
首先,这确实是一个测试问题 - 不是用户会犯错,而是开发人员,他们的错误应该在之前应用程序上线。
话虽如此,您可以通过触发器的组合来捕获此类更新:
一个简单的例子:
SQL> create table t1 (id int, col2 int);
Table created.
SQL> insert into t1 values(1, null);
1 row created.
SQL> insert into t1 values(2, null);
1 row created.
SQL> create package p1 is g_id integer; end;
2 /
Package created.
SQL> create trigger t1_bus
2 before update on t1
3 begin
4 p1.g_id := null;
5* end;
SQL> /
Trigger created.
SQL> create trigger t1_bir
2 before update on t1
3 for each row
4 begin
5 if :new.id != p1.g_id then
6 raise_application_error(-20000,'You can only update 1 ID at a time');
7 end if;
8 p1.g_id := :new.id;
9 end;
10 /
Trigger created.
SQL> update t1 set col2=1 where id=1;
1 row updated.
SQL> update t1 set col2=2 where id=2;
1 row updated.
SQL> update t1 set col2=3; -- ID not specified
update t1 set col2=3
*
ERROR at line 1:
ORA-20000: You can only update 1 ID at a time
答案 3 :(得分:0)
我能想到的唯一方法是将表中的book_id和page列替换为存储两条信息的单个列 - 如果您想要整数列或者类似(book_id * 10000 + page) “book_id-page”表示字符串列。
从正确的角度来看这是一个坏主意(两个属性存储在一列中),但会强制程序员使用这两个属性与表进行交互。如果这对你来说足够重要,你可能会考虑它。