2012-08-16 61 views
2

我在我的應用程序這種形式的幾種功能:使用CallableStatements中的枚舉作爲一個白名單針對SQL注入

public long generatedCatId(String name,int age, CallableStatement statement) throws SQLException 
{ 
    statement.setString(1,name); 
    statement.setInt(2,age); 
    statement.registerOutParameter(3, Types.NUMERIC); 
    statement.execute(); 
    return statement.getLong(3); 
} 

我想這樣做是有一個包含所有允許CallableStatement對象爲Enum創建一個白名單,這樣我就可以在枚舉中的一組值上做一個簡單的包含。我遇到的問題是,如果沒有Connection對象,我無法創建CallableStatement對象。有沒有辦法解決這個限制?我不想傳遞連接對象,也不想重複編譯Statement,因爲這將作爲自動化過程的一部分,每天運行數十億次。最後我確實不會使用任何ORM工具。

UPDATE

任意語句的例子,我會跑:

CallableStatement statement = conn.prepareCallable("{Call insert_new_cat(?,?,?)}"; 
SQL: insert into cat(id,name,age) 
     values(cat_sequence.nextval,name,age) 
     returning id; 
CallableStatement statement = conn.prepareCallable("{Call update_cat(?,?)}"; 
SQL: update cat 
     set age = age 
     where id = id; 

第二次更新

似乎有左右我在做什麼了一些混亂。程序的流程如下:

進程A調用上面聲明的函數,提供編譯語句。示例如下:

Connection conn = DriverManager.getConnection("foo"); 
CallableStatement statement = conn.prepareCall("insert_new_cat");  
for(Cat currentCat : CatList) 
{ 
    generateCatId(currentCat.name(),currentCat.age(), statement); 
} 
conn.commit(); 

請注意以上是一個提交和一個編譯或準備語句。我希望該語句成爲枚舉或其他最終數據結構的一部分,然後我可以在generateCatId函數中對其進行比較。

+0

你可以舉兩個或兩個以上'允許'的例子嗎? – epoch 2012-08-16 13:40:50

+0

爲什麼不使用查詢字符串作爲白名單並在使用預處理語句之後?我不認爲你可以避免使用連接 – 2012-08-16 13:45:37

+0

@epoch在那裏添加了一些任意的語句來顯示我在做什麼。 – Woot4Moo 2012-08-16 13:48:37

回答

3

沒有辦法可以忽略Connection對象,連接也不是真的可以重用,除非你有連接池,但是仍然需要關閉池才能知道它們可以再次使用。但是如果你想封裝sql,你可以嘗試這樣的事:

enum CallableEnum { 
    CALLABLE_ONE("insert_new_cat", 3), 
    CALLABLE_TWO("update_cat", 2), 
    ; 

    private String sql; 
    private int parameterCount; 

    private CallableEnum(String sql, int params) { 
     this.sql = sql; 
     this.parameterCount = params; 
    } 

    public CallableStatement prepare(Connection connection) throws SQLException { 
     final StringBuilder builder = new StringBuilder("{CALL "); 
     builder.append(this.sql); 
     builder.append("("); 

     int count = this.parameterCount; 
     for (int i = 0; i < count; i++) { 
      builder.append("?"); 
      if (i != count - 1) { 
       builder.append(", "); 
      } 
     } 

     return connection.prepareCall(builder.append(")}").toString()); 
    } 
} 

,然後用它是這樣的:

Connection conn = DriverManager.getConnection("foo"); 
CallableStatement statement = CallableEnum.CALLABLE_ONE.prepare(conn); 
for (Cat currentCat : catList) { 
    generateCatId(currentCat.name(), currentCat.age(), statement); 
} 

conn.commit(); 
conn.close(); 

很明顯,你可以修改它來滿足您的需求,這是唯一的想法我可以從你的問題:)

UPDATE

好讓這似乎是瘋了,我沒有測試過,但我修改它封裝的一切:

enum CallableEnum { 
    CALLABLE_ONE("insert_new_cat", 3, new Executable<Long>() { 
     @Override 
     public Long apply(CallableStatement statement, Object... arguments) throws SQLException { 
      statement.setString(1, String.valueOf(arguments[0])); 
      statement.setInt(2, Integer.parseInt(String.valueOf(arguments[1]))); 
      statement.registerOutParameter(3, Types.NUMERIC); 
      statement.execute(); 
      return statement.getLong(3); 
     } 
    }), 
    ; 

    private String sql; 
    private Executable<?> executable; 
    private int parameterCount; 

    private CallableEnum(String sql, int params, Executable<?> todo) { 
     this.sql = sql; 
     this.parameterCount = params; 
     this.executable = todo; 
    } 

    public CallableStatement prepare(Connection connection) throws SQLException { 
     final StringBuilder builder = new StringBuilder("{CALL "); 
     builder.append(this.sql); 
     builder.append("("); 

     int count = this.parameterCount; 
     for (int i = 0; i < count; i++) { 
      builder.append("?"); 
      if (i != count - 1) { 
       builder.append(", "); 
      } 
     } 

     return connection.prepareCall(builder.append(")}").toString()); 
    } 

    public <T> T execute(Connection conn, Object... arguments) throws SQLException { 
     CallableStatement st = this.prepare(conn); 
     return (T) this.executable.apply(st, arguments); 
    } 

    private interface Executable<T> { 
     T apply(CallableStatement st, Object... arguments) throws SQLException; 
    } 
} 

現在可以像這樣使用:

Connection conn = DriverManager.getConnection("foo"); 
for (Cat currentCat : catList) { 
    CallableEnum.CALLABLE_ONE.execute(conn, currentCat.name(), currentCat.age()); 
} 
conn.commit(); 
conn.close(); 

我不知道這是你想要什麼,或者即使它會工作,但我會離開你:)

+0

是啊我知道這似乎是一個奇怪的問題,讓我試試看看我怎麼能進入它。 – Woot4Moo 2012-08-16 14:04:21

+0

當然,讓我知道如果還有其他東西:) – epoch 2012-08-16 14:08:11

+1

等一秒鐘的第二部分,如果這個作品正是我所期待的。一種任意命令不能對我的sql函數執行的方式。讓我測試一下。 – Woot4Moo 2012-08-16 15:00:44