MySQL条件INSERT基于用户提供的数据和现有数据

时间:2018-03-03 05:23:28

标签: mysql jdbc insert prepared-statement

假设我有一些假设的表格:用户,项目和销售。

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)执行此操作的几种方法。

  1. 执行SELECT以确保用户拥有该项目。

    SELECT id FROM Item WHERE id = ? AND userId = ?
    

    如果存在匹配(即返回的行),则用户拥有该项并可以插入该项的销售记录。然而,这种方法效率似乎有点低,因为我必须执行多个单独的查询才能完成一项任务。拥有一个连接池(因此为每个查询重用相同的连接)将有助于提高性能,但我不确定多少。

  2. 通过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 = ?);
    

    我非常喜欢这个选项的想法,但是有一个我无法解决的突出问题:

  3. 查询本身将从应用程序完成。 insert语句的部分是原始值,直到最后一秒才知道。并且由于执行“条件插入”的唯一方法是从某个表(虚拟或其他)中获取SELECT数据,因此不能将?占位符用于列名的预准备语句。

    换句话说,上面语句中的4,5.00和3是原始值,我知道将它们放入SQL字符串的唯一方法是进行连接:

    String sql = "INSERT INTO Sale(quantity, total, itemId) SELECT "
        + quantity + ", " + total + ...;
    

    这使得可能的SQL注入攻击敞开大门。如果Java变量quantitytotal是数字数据类型(即,不能有引号),这样做有点棘手,但它仍然是一个我不想打开的漏洞。

    有没有一种好方法可以在一个SQL语句中有效地完成我想要做的事情?或者是上面选项(1)的最佳方式?

0 个答案:

没有答案