2012-08-16 27 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