2014-04-14 82 views
0

我想多次使用相同的值。Java中的查詢中的SQL變量

如果我在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 
[email protected], [email protected], [email protected] 

它正確執行,但在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" + 
          "[email protected], [email protected], [email protected]"; 

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

我有一個例外

SQL異常:你的SQL語法有錯誤;在'INSERT INTO test_table(TID,CREATED,OWNER,TITLE)VALUES (@ v1,@ v2,@ v3,@ v4)ON'處檢查與您的MySQL服務器版本對應的手冊 正確的語法爲 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")); 

但它看起來醜陋。

有沒有辦法解決這個問題?

回答

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()功能,如果我們upserting多行,無論是與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) 

BTW ...正在從第一形式返回的錯誤是我們預計,如果錯誤的類型allowMultiQueries=true不包含在連接字符串中。請注意,每次執行時啓用多個查詢會有效地禁用安全功能。

仔細考慮將要生成,並與一些精心製作的值發送給數據庫的SQL文本:

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

使用事先準備好的聲明與佔位符(靜態SQL文本,綁定變量,如示例上面)阻止這種SQL注入模式。並且在一次執行中禁止多個語句是阻止SQL注入攻擊的另一種方式。

+0

這似乎是最好的辦法。另一種方式(這是不安全的)就像是使用replaceAll(「@ v1」,t.getParam(「ID」))等 – Woody

+0

Thx。它真的更好。 在我的例子中st.setString(int,String);我不喜歡,我會添加列,在這種情況下,我每次都必須重新檢查索引。 – Esperan

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")); 
+0

注意:**'@ var' **用戶定義的變量可用於常規SQL以及存儲程序。使用特殊的'VALUES(foo)'函數作爲引用給INSERT中列的值的引用(即,如果插入成功將會插入的值)是我們避免必須傳遞相同值的常規方式;這在插入多行時特別有用。 – spencer7593

+0

感謝Spencer7593的信息。 –