【问题标题】:PreparedStatement: How to insert data into multiple tables using JDBCPreparedStatement:如何使用 JDBC 将数据插入到多个表中
【发布时间】:2014-03-26 21:24:34
【问题描述】:

有人能告诉我以下 JDBC 代码中是否需要第一个 stmt.close(); 来针对两个不同的表执行两个不同的 SQL 查询?

public class MyService {
    private Connection connection = null;

    public void save(Book book) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "password"); 

            PreparedStatement stmt = connection.prepareStatement("INSERT INTO PUBLISHER (CODE, PUBLISHER_NAME) VALUES (?, ?)");
            stmt.setString(1, book.getPublisher().getCode());   
            stmt.setString(2, book.getPublisher().getName());           
            stmt.executeUpdate();

            stmt.close(); //1

            stmt = connection.prepareStatement("INSERT INTO BOOK (ISBN, BOOK_NAME, PUBLISHER_CODE) VALUES (?, ?, ?)");
            stmt.setString(1, book.getIsbn());  
            stmt.setString(2, book.getName());
            stmt.setString(3, book.getPublisher().getCode());
            stmt.executeUpdate();

            stmt.close(); //2       
        } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } 
        finally { connection.close(); }         
    }
}

【问题讨论】:

  • 我记得close()是用来释放JDBC资源的
  • 每个PreparedStatement在使用后都需要关闭,所以,是的,你需要两个#close,就像你有两个PreparedStatements一样。不过,最好在 finally 块中关闭它们,就像使用 Connection 一样。
  • 是的,否则您不会关闭第一个准备好的语句对象。您还应该考虑使用 try-with-resources 以避免在发生异常时不关闭语句。

标签: java sql jdbc prepared-statement


【解决方案1】:
 try{
     String insertIntoCust = "insert into NORTHWIND_CUSTOMER(CUSTOMER_ID,FIRST_NAME,LAST_NAME,ADDRESS,CITY,STATE,POSTAL_CODE) values(?,?,?,?,?,?)";
     pst = connect.prepareStatement(insertIntoCust);
     pst.setString(1, txtCustomerId.getText());
     pst.setString(2, txtFirstName.getText());
     pst.setString(3, txtLastName.getText());
     pst.setString(4, txtAddress2.getText());
     pst.setString(5, txtCity.getText());
     pst.setString(6, txtState.getText());
     pst.setString(7, txtPostalCode.getText());

     pst.execute();

     String insertIntoOrder = "insert into NORTHWIND_ORDER(ORDER_ID,ORDER_DATE) values(?,?)";
     pst = connect.prepareStatement(insertIntoOrder);
     pst.setString(1, txtOrderId.getText());
     pst.setString(2, txtOrderDate.getText());

     pst.execute();
     JOptionPane.showMessageDialog(null, "Saved");
 }catch(Exception e){
     JOptionPane.showMessageDialog(null, e);
 }

使用上面的代码,我可以通过一个按钮将数据插入到多个表中。 pst.execute 需要插入到两个查询中;如果不是,则具有 pst.execute 的查询就是将接收数据的表。

【讨论】:

    【解决方案2】:

    对于关闭它们的语句会释放所有准备它们构建的语句,这是一个常见的误解。这是错误的。导致准备语句的优化由数据库执行。然后由数据库存储/缓存,通常在下次准备语句时重新使用。

    因此,准备好的语句可以根据需要随时关闭和准备 - 数据库将在下一次识别相同的语句并恢复它上次所做的缓存准备 - 如果它愿意的话。

    总而言之 - ,语句应该结束 - 而,这不会降低查询的有效性。

    【讨论】:

      【解决方案3】:

      在我的书中,我总是建议关闭已打开的资源以避免可能的泄漏。

      一种更现代的方式是使用try-with-resources

      try (Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "password")) {
      
          try (PreparedStatement stmt = connection.prepareStatement("INSERT INTO PUBLISHER (CODE, PUBLISHER_NAME) VALUES (?, ?)")) {
              stmt.setString(1, book.getPublisher().getCode());   
              stmt.setString(2, book.getPublisher().getName());           
              stmt.executeUpdate();
          }
          // stmt is auto closed here, even if SQLException is thrown
      
          try (PreparedStatement stmt = connection.prepareStatement("INSERT INTO BOOK (ISBN, BOOK_NAME, PUBLISHER_CODE) VALUES (?, ?, ?)");
              stmt.setString(1, book.getIsbn());  
              stmt.setString(2, book.getName());
              stmt.setString(3, book.getPublisher().getCode());
              stmt.executeUpdate();
          }
          // stmt is auto closed here, even if SQLException is thrown
      }
      // connection is auto closed here, even if SQLException is thrown
      

      【讨论】:

      • 如何添加一个相同但成批的例子,假设我们正在循环一组书籍和出版商,准备一批在循环外执行。
      • 那将是一个不同的问题。
      • 需要 Java 1.7 或更高版本。
      【解决方案4】:

      是的,stmt.close() 方法都是必需的。 您应该始终显式关闭 Statement 或 PreparedStatement 对象以确保正确清理。

      【讨论】:

      【解决方案5】:

      最好关闭语句句柄,因为它会释放 JDBC 和数据库资源。你可以阅读更多关于 stmt.close() here

      我想指出,最好在 finally 块中关闭您的 Statement 对象,以便即使发生异常也可以释放 DB 资源。

      【讨论】:

        【解决方案6】:

        这不是必需的,但建议这样做。 http://docs.oracle.com/javase/7/docs/api/java/sql/Statement.html#close()

        代码中第一个close() 之后的下一行为引用stmt 分配一个新值,因此用于执行第一个插入的对象将被GC 处理并最终关闭。当你知道你已经完成它时,继续关闭它是一个很好的做法。这会立即释放 JDBC 资源。

        【讨论】:

        • 但是,如果由于某种原因 GC 终结器未能正确关闭资源,并且由于连接池,资源与未关闭的连接保持关联,那么语句资源会不断增加。我并不是说这一定会发生,只是我不会对它不会发生取决于 jdbc 驱动程序的实现感到惊讶,所以我个人希望看到所有资源都关闭并考虑不这样做作为可能的泄漏/错误。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-08-11
        • 2013-11-28
        • 2014-10-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-28
        相关资源
        最近更新 更多