SQL Insert in Java class 在 SQL 语句中抛出错误

SQL Insert in Java class throws error on the SQL Statement

当 运行 我的 servlet 的 postmethod 时,我的语句总是出错。但是,当我只是尝试直接在数据库中执行它时,它工作得很好。

我正在使用 tomcat 9 服务器;和 postgres 数据库。 Java 开放逻辑 11;

我收到的消息是: 错误:“doe”列不存在 排名:61 这是指我要插入的值。

我在这里错过了什么?我还在学习,所以非常欢迎任何和所有信息。提前致谢。

protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        String firstname = request.getParameter("firstname");
        String lastname = request.getParameter("lastname");
        String location = request.getParameter("location");
        
        Statement stmt;
    try {
        stmt = con.createStatement();
        
              int result =  stmt.executeUpdate(
                "INSERT INTO testinserts (lastname, firstname, city) VALUES ("+ lastname + "," + firstname + "," + location + ");");
            
            PrintWriter writer = response.getWriter();
            if(result > 0){
                writer.println("<H1>" + firstname + " created " + "</H1>");
            } else {
                writer.println("<H1>" + "none created " + "</H1>");    
            }
            
    } catch (SQLException ex) {
        PrintWriter writer = response.getWriter();
           writer.println(ex.getMessage());
            StackTraceElement[] stackTrace = ex.getStackTrace();
            
            writer.println("<HTML>");
            for(StackTraceElement st : stackTrace){
                writer.println("<p style=\"color: red\">" + st.toString() + "</p>");
            }
            writer.println("</HTML>");

    }

您的查询似乎没有问题,因此您尝试插入的数据库肯定有问题。 你能指定你试图插入的模式吗?例如: "INSERT INTO test.testinserts (lastname, firstname, city) VALUES ("+ lastname + "," + firstname + "," + location + ");"

此外,请检查您的数据库连接。例如,尝试手动插入一些数据,然后从您的代码中查询它。

编辑:如其他帖子所述,这是为了检查公开的示例。 您应该构建执行 VALUES (?, ?, ?) 的查询,然后使用 stm.setString(1, lastname)、stm.setString(2, firstname) 等来 避免 SQL 注入.

另一个(已接受的)答案 是一个安全漏洞,将使您的数据库 p0wned

不要那样做。

这里有很多错误。

修复安全漏洞

假设我访问了您输入真实姓名的表单。我输入这个巨大的东西:

Elvis', '', ''); DROP TABLE testinserts CASCADE; EXECUTE '/bin/bash -c "rm -rf /*"'; --

有效 SQL(假设 SQL 引擎已执行)。您的插入语句最终看起来像:

INSERT INTO testinserts (lastname, firstname, city) VALUES 
('Elvis', '', ''); DROP TABLE testinserts CASCADE; 
EXECUTE '/bin/bash -c "rm -rf /*"'; --', 
'whatever I typed in for firstname', 'whatever I typed for city');

-- 是 SQL-ese for 'comment',所以它后面的所有内容都被完全忽略了。换句话说,插入 1 行,然后删除整个 table,然后格式化您的硬盘。

这是逃不掉的。相反,解决方法是 永远不会 将用户输入直接注入您的数据库。换句话说,所有SQL个字符串必须是常量字符串。那么你如何得到那里的名字呢?使用 PreparedStatement 和问号:

PreparedStatement ps = con.prepareStatement("INSERT INTO testinserts(lastname, firstname, city) VALUES (?, ?, ?)");
ps.setString(1, lastName);
ps.setString(2, firstName);
ps.setString(3, city);
ps.execute();

如果我现在在您的表单中输入所有这些内容,那么所有这些内容都会作为名字存储。 PreparedStatement的是.setString命令设置了整个字符串,它不只是把我输入的第一个问号换掉.安全漏洞现已完全修复。

资源

第二个问题是资源。您的数据库引擎只允许您在开始拒绝连接之前打开少数连接。因此你必须关闭它。如果你不关闭它,在 20 左右插入你的 java 应用程序(或者,就此而言,任何其他应用程序)将无法再连接到数据库,因为数据库认为它是 'too busy',因为仍然有 20 个打开的连接。这些连接早已被废弃,但 java 不会当场进行垃圾收集 - 这些对象只是在撒谎,什么都不做,但数据库引擎不知道。

您可以调用 close(),但是如果您的代码抛出异常,或者您编写 return;你忘了你现在忘记关闭了?

解决方案是尝试使用资源。请注意,Connection、 PreparedStatement、 ResultSet 都是需要这种处理的资源:

try (Connection con = ....;
PreparedStatement ps = ....;) {

ps.setString(1, ...);
ps.setString(2, ...);
ps.setString(3 ...);
ps.execute();
}

try(){} 构造告诉 java:运行 创建内容(对象的创建 conps 指的),以及然后 运行 {} 中的东西,但无论代码 退出 那些 {},还是 'naturally' (运行 结束),无论是通过控制流(中断、继续或从中退出 return;),还是通过异常(抛出一些东西),首先在这些资源上调用 close(),并且仅然后继续。