假设我有一些假设的表格:用户,项目和销售。
CREATE TABLE User (
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL
);
CREATE TABLE Item (
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
upc VARCHAR(255) NOT NULL,
description VARCHAR(255) NOT NULL,
price DECIMAL(5,2) NOT NULL
userId INT NOT NULL,
FOREIGN KEY(userId) REFERENCES User(id)
);
CREATE TABLE Sale (
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
quantity INT NOT NULL,
total DECIMAL(5,2) NOT NULL,
itemId INT NOT NULL,
FOREIGN KEY(itemId) REFERENCES Item(id)
);
每个用户可以添加多个项目,并且可以销售每个项目的多个数量。每笔销售的记录将进入Sale表,但我必须确保输入到Sale表中的Item ID实际上由“创建”条目的用户拥有。
我已经考虑过从应用层(例如JDBC)执行此操作的几种方法。
执行SELECT
以确保用户拥有该项目。
SELECT id FROM Item WHERE id = ? AND userId = ?
如果存在匹配(即返回的行),则用户拥有该项并可以插入该项的销售记录。然而,这种方法效率似乎有点低,因为我必须执行多个单独的查询才能完成一项任务。拥有一个连接池(因此为每个查询重用相同的连接)将有助于提高性能,但我不确定多少。
通过INSERT
:
INSERT ... SELECT
”
INSERT INTO Sale(quantity, total, itemId)
SELECT 4, 5.00, 3 FROM Dual
WHERE EXISTS(SELECT id FROM Item WHERE id = ? AND userId = ?);
我非常喜欢这个选项的想法,但是有一个我无法解决的突出问题:
查询本身将从应用程序完成。 insert语句的部分是原始值,直到最后一秒才知道。并且由于执行“条件插入”的唯一方法是从某个表(虚拟或其他)中获取SELECT
数据,因此不能将?
占位符用于列名的预准备语句。
换句话说,上面语句中的4,5.00和3是原始值,我知道将它们放入SQL字符串的唯一方法是进行连接:
String sql = "INSERT INTO Sale(quantity, total, itemId) SELECT "
+ quantity + ", " + total + ...;
这使得可能的SQL注入攻击敞开大门。如果Java变量quantity
和total
是数字数据类型(即,不能有引号),这样做有点棘手,但它仍然是一个我不想打开的漏洞。
有没有一种好方法可以在一个SQL语句中有效地完成我想要做的事情?或者是上面选项(1)的最佳方式?