如何在 java 中的全局 DbConnection 中使用准备好的 sql 语句?

How to use a prepared sql statement in a global DbConnection in java?

对于学校,我有一个 Java 项目,其中包含几个 Maven 库。

对于我们的数据库连接,我制作了一个“全局”连接文件,在这个文件中我有一些基本的select、更新和删除方法,您可以从其他文件调用这些方法,例如:select(query). 我从这个问题中得到了这段代码:

现在的问题;我们需要一个准备好的语句,我该如何实现它,同时保持 select 方法像现在一样全局可用。

这里是 DbConnection class:

public ArrayList<ArrayList<Object>> Select(String query){

    try{
        Statement statement = this.connection.createStatement();
        ResultSet rs = statement.executeQuery(query);

        ResultSetMetaData metaData = rs.getMetaData();

        ArrayList<ArrayList<Object>> result = new ArrayList<>();

        while (rs.next()){

            ArrayList<Object> row = new ArrayList<Object>();

            for (int i = 1; i <= metaData.getColumnCount(); i++){
                String strColumnName = metaData.getColumnName(i);
                Object columnValue = rs.getObject(i);
                row.add(columnValue);

            }
            result.add(row);
        }
        return result;

    }catch (SQLException e){
        e.printStackTrace();
    }

    return null;
}

正如某些评论中所建议的那样,您的方法违反了 Java 安全准则,原因有很多,最重要的原因有:

  • 它不执行并检查输入
  • 它为 SQL 注入攻击敞开了大门,因为它无法控制对数据库执行的查询类型。

但是,如果这只是一项学校作业,我可以使用 PreparedStatemnt 来帮助您实现它。
这个想法是您假设调用者知道输入查询中包含的参数数量并为其提供确切的值数量。
基于此,我能想到的最简单的实现如下:

public ArrayList<Map<String, Object>> performGenericParameterizedQuery(String queryWithParameters, 
    Object ... values) {
    
    ArrayList<Map<String, Object>> result = new ArrayList<>();
    
    try(PreparedStatement ps = this.connection.preparedStatement(queryWithParameters)) {
        
        for(int i=0; i<values.length; i++) {
            ps.setObject(i+1, values[i]);
        }
        
        ResultSet rs = ps.executeQuery();
        
        ResultSetMetaData metaData = rs.getMetaData();

        while (rs.next()){

            Map<String, Object> row = new HashMap<>();

            for (int i = 1; i <= metaData.getColumnCount(); i++){
                String strColumnName = metaData.getColumnName(i);
                Object columnValue = rs.getObject(i);
                row.put(columnValue, strColumnName);
            }
            result.add(row);
        }
    
    }catch (SQLException e){
        e.printStackTrace();
    }

    return result;
}

老实说,我还没有实际编译或测试过那个方法。但我只是想告诉您如何“一般地”使用 PreparedStatement。我假设您的 class 中有可用的 connection 实例(您可以将其作为 Singleton 或只是在 class 的特定 init 方法中准备),另外您应该添加所有必需的导入。
你可以注意到我使用了一个地图列表,而不是你选择的结构,在我看来,它更清晰,更适合这个目的。
此外,正如我所做的那样,我总是建议将您的语句包装在一个 try-with-resource 块中,这样一旦执行从块中退出,它就会自动关闭(连同 ResultSet)不会有任何资源泄漏。