【问题标题】:How does Java's PreparedStatement work?Java 的 PreparedStatement 是如何工作的?
【发布时间】:2010-09-29 23:03:35
【问题描述】:

我打算用PreparedStatement 对象替换重复执行的Statement 对象以提高性能。我正在使用 MySQL 函数 now() 和字符串变量等参数。

我见过的大多数PreparedStatement 查询都包含常量值(例如10,以及类似"New York" 的字符串)作为查询中? 的参数。我将如何使用 now() 之类的函数和变量作为参数?是否有必要在查询中使用?s 而不是实际值?我很困惑。

【问题讨论】:

  • 您是在问是否可以使用字符串值函数代替字符串文字?您是在问是否可以使用 int 值函数代替文字整数?可以给个代码sn-p吗?

标签: java jdbc prepared-statement


【解决方案1】:

我开发了一个函数,允许您在 SQL 查询中使用命名参数:

private PreparedStatement generatePreparedStatement(String query, Map<String, Object> parameters) throws DatabaseException
    {
        String paramKey = "";
        Object paramValue = null;
        PreparedStatement statement = null;
        Pattern paramRegex = null; 
        Matcher paramMatcher = null;
        int paramIndex = 1;

        try
        {
            //Create the condition
            paramRegex = Pattern.compile("(:[\\d\\w_-]+)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
            paramMatcher = paramRegex.matcher(query);
            statement = this.m_Connection.prepareStatement(paramMatcher.replaceAll("?"),
                                ResultSet.TYPE_FORWARD_ONLY,
                                ResultSet.CONCUR_READ_ONLY, 
                                ResultSet.HOLD_CURSORS_OVER_COMMIT);

            //Check if there are parameters
            paramMatcher = paramRegex.matcher(query);
            while (paramMatcher.find()) 
            {
                paramKey = paramMatcher.group().substring(1);
                if(parameters != null && parameters.containsKey(paramKey))
                {
                    //Add the parameter 
                    paramValue = parameters.get(paramKey);
                    if (paramValue instanceof Date) 
                    {
                        statement.setDate(paramIndex, (java.sql.Date)paramValue);                 
                    } 
                    else if (paramValue instanceof Double) 
                    {
                        statement.setDouble(paramIndex, (Double)paramValue);                  
                    } 
                    else if (paramValue instanceof Long) 
                    {
                        statement.setLong(paramIndex, (Long)paramValue);                  
                    } 
                    else if (paramValue instanceof Integer) 
                    {
                        statement.setInt(paramIndex, (Integer)paramValue);                
                    } 
                    else if (paramValue instanceof Boolean) 
                    {
                        statement.setBoolean(paramIndex, (Boolean)paramValue);                
                    } 
                    else 
                    {
                        statement.setString(paramIndex, paramValue.toString());     
                    }
                }
                else
                {
                    throw new DatabaseException("The parameter '" + paramKey + "' doesn't exists in the filter '" + query + "'");
                }

                paramIndex++;
            }
        }
        catch (SQLException  l_ex) 
        {
            throw new DatabaseException(tag.lib.common.ExceptionUtils.getFullMessage(l_ex));
        }

        return statement;
    }

你可以这样使用它:

Map<String, Object> pars = new HashMap<>();
pars.put("name", "O'Really");
String sql = "SELECT * FROM TABLE WHERE NAME = :name";

【讨论】:

    【解决方案2】:

    如果您要调用 SQL 服务器的内置函数,请使用 PreparedStatement

    如果您正在调用已加载到 SQL 服务器上的存储过程,请使用 CallableStatement

    使用问号作为您传递的函数/过程参数和接收的函数返回值的占位符。

    【讨论】:

      【解决方案3】:

      您不必在 PreparedStatement 中使用占位符。比如:

      PreparedStatement stmt = con.prepareStatement("select sysdate from dual");
      

      会工作得很好。但是,您不能使用占位符然后将函数调用绑定到它。这样的东西不能用来调用 sysdate 函数:

      PreparedStatement stmt = con.prepareStatement("select ? from dual");
      stmt.setSomethingOrOther(1, "sysdate");
      

      【讨论】:

        【解决方案4】:

        如果您有一个来自用户输入的变量,那么您必须使用 ?而不是连接字符串。用户可能会恶意输入字符串,如果您将字符串直接放入 SQL 中,它可能会运行您不想要的命令。

        我意识到这个被过度使用了,但它完美地说明了这一点:

        【讨论】:

        • 我相信,你的意思是 SQL 注入。
        【解决方案5】:

        如果您有变量,请使用“?”

        int temp = 75;
        PreparedStatement pstmt = con.prepareStatement(
            "UPDATE test SET num = ?, due = now() ");
        pstmt.setInt(1, temp); 
        pstmt.executeUpdate():
        

        生成如下所示的 sql 语句:

        UPDATE test SET num = 75, due = now();
        

        【讨论】:

        • 这对于大多数数据库系统是不正确的。大多数数据库系统将准备查询文本,然后在执行时发送参数值。这永远不会产生一个字面上包含参数值的语句。
        猜你喜欢
        • 2017-06-05
        • 1970-01-01
        • 2012-08-21
        • 2013-08-28
        • 1970-01-01
        • 1970-01-01
        • 2012-09-19
        • 2016-04-13
        • 2015-08-30
        相关资源
        最近更新 更多