Java中查询中的SQL变量

时间:2014-04-14 15:26:33

标签: java mysql jdbc

我想多次使用相同的值。

如果我在dbForge中使用MySQL下一个查询,

SET @v1 = 123, @v2='2014-04-11', @v3 = 'user1', @v4='title1';
INSERT INTO test_table (TID, CREATED, OWNER, TITLE)
VALUES (@v1,@v2,@v3,@v4)
ON DUPLICATE KEY UPDATE
CREATED=@v2, OWNER=@v3, TITLE=@v4

它正确执行,但在Java中,当我使用代码时

final String dbQuerry = "SET @v1 = %s, @v2='%s', @v3 = '%s', @v4='%s';\n"+
                          "INSERT INTO test_table (TID, CREATED, OWNER, TITLE)\n" + 
                          "VALUES (@v1,@v2,@v3,@v4)\n" +
                          "ON DUPLICATE KEY UPDATE\n" + 
                          "CREATED=@v2, OWNER=@v3, TITLE=@v4";

String currentQuerry = String.format(dbQuerry, t.getParam("ID"), 
                                               t.getParam("Date"),
                                               t.getParam("User"),
                                               t.getParam("Title"));
mDBStatement.execute(currentQuerry);

我有一个例外

  

SQL异常:SQL语法中有错误;检查手册   对应于您的MySQL服务器版本,以获得正确的语法   在'INSERT INTO test_table(TID,CREATED,OWNER,TITLE)值附近使用   (@ v1,@ v2,@ v3,@ v4)ON'在第2行

我可以使用这样的东西

final String dbQuerry = "INSERT INTO test_table (TID, CREATED, OWNER, TITLE)\n" + 
                        "VALUES (?,?,?,?)\n" +
                        "ON DUPLICATE KEY UPDATE\n" + 
                        "CREATED=?, OWNER=?, TITLE=?";

  PreparedStatement st = mDBConnection.prepareStatement(dbQuerry);
  st.setInt(1, Integer.valueOf(t.getParam("ID")));
  st.setString(2, t.getParam("Date"));
  st.setString(5, t.getParam("Date"));
  st.setString(3, t.getParam("User"));
  st.setString(6, t.getParam("User"));
  st.setString(4, t.getParam("Title"));
  st.setString(7, t.getParam("Title"));

但它看起来很难看。

有没有办法解决这个问题?

2 个答案:

答案 0 :(得分:1)

一种选择是使用特殊的 VALUES() 函数来引用插入到列中的值,如果INSERT成功的话,就像这样:

...
ON DUPLICATE KEY
UPDATE CREATED = VALUES(CREATED)
     , OWNER   = VALUES(ONWER)
     , TITLE   = VALUES(TITLE)

首选示例中的后一种形式,使用占位符作为绑定变量。什么是丑陋的,必须两次提供相同的价值。

我建议这样的事情:

final String dbQuerry = "INSERT INTO test_table (TID,CREATED,OWNER,TITLE)\n" + 
        " VALUES (?,?,?, ?)\n" +
        " ON DUPLICATE KEY UPDATE\n" + 
        " CREATED=VALUES(CREATED), OWNER=VALUES(OWNER), TITLE=VALUES(TITLE)";

PreparedStatement st = mDBConnection.prepareStatement(dbQuerry);
st.setInt(1, Integer.valueOf(t.getParam("ID")));
st.setString(2, t.getParam("Date"));
st.setString(3, t.getParam("User"));
st.setString(4, t.getParam("Title"));

那并不难看。这是规范模式。


使用特殊的 VALUES() 函数特别有用,如果我们使用VALUES子句插入多行,例如

INSERT INTO fee (fi, fo, fum)
VALUES
(1,'doo','dah'),(2,'menom','menah'),(3,'buhdeep','uhdeepee')
ON DUPLICATE KEY
UPDATE fo  = VALUES(fo)
     , fum = VALUES(fum)

或者,使用INSERT ... SELECT表单:

INSERT INTO fee (fi, fo, fum)
SELECT t.ay, t.bee, t.cee FROM sometable t
ON DUPLICATE KEY
UPDATE fo  = VALUES(fo)
     , fum = VALUES(fum)

顺便说一句......从第一个表单返回的错误是我们在连接字符串中不包含 allowMultiQueries=true 时所期望的错误类型。请注意,每次执行启用多个查询会有效禁用安全功能。

仔细考虑将使用一些精心设计的值生成并发送到数据库的SQL文本:

val = "foo'; DROP TABLE students; --"

使用预准备语句(使用带有占位符的静态SQL文本作为绑定变量,如上例所示)可以防止这种SQL注入模式。并且在一次执行中不允许多个语句是另一种阻止SQL注入攻击的方法。

答案 1 :(得分:0)

我认为@变量仅用于存储过程...

您可以定义存储过程,也可以使用第二个选项:

final String dbQuerry = "INSERT INTO test_table (TID, CREATED, OWNER, TITLE)\n" + 
                    "VALUES (?,?,?,?)\n" +
                    "ON DUPLICATE KEY UPDATE\n" + 
                    "CREATED=?, OWNER=?, TITLE=?";

  PreparedStatement st = mDBConnection.prepareStatement(dbQuerry);
st.setInt(1, Integer.valueOf(t.getParam("ID")));
st.setString(2, t.getParam("Date"));
st.setString(5, t.getParam("Date"));
st.setString(3, t.getParam("User"));
st.setString(6, t.getParam("User"));
st.setString(4, t.getParam("Title"));
st.setString(7, t.getParam("Title"));