Sử dụng lại một Câu lệnh chuẩn bị nhiều lần


96

trong trường hợp sử dụng PreparedStatement với một kết nối chung duy nhất mà không có bất kỳ nhóm nào, tôi có thể tạo lại một phiên bản cho mọi thao tác dml / sql dựa trên sức mạnh của các câu lệnh đã chuẩn bị không?

Ý tôi là:

for (int i=0; i<1000; i++) {
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    preparedStatement.setObject(1, someValue);
    preparedStatement.executeQuery();
    preparedStatement.close();
}

thay vì:

PreparedStatement preparedStatement = connection.prepareStatement(sql);
for (int i=0; i<1000; i++) {
    preparedStatement.clearParameters();
    preparedStatement.setObject(1, someValue);
    preparedStatement.executeQuery();
}
preparedStatement.close();

câu hỏi của tôi nảy sinh bởi thực tế là tôi muốn đặt mã này vào một môi trường đa luồng, bạn có thể cho tôi một số lời khuyên được không? cảm ơn


vì vậy truy vấn của bạn, sqlkhông thay đổi với trong vòng lặp? nếu truy vấn đó không thay đổi cho mỗi lần lặp lại của vòng lặp, thì tại sao bạn lại tạo một truy vấn mới PreparedStatementcho mỗi lần lặp (trong đoạn mã đầu tiên)? Có lý do gì để làm như vậy không?
Sabir Khan

cho phép nói nếu truy vấn đang thay đổi, thì cách tiếp cận thứ hai vẫn tốt hơn phải không? Bất kỳ nhược điểm?
Stunner

Câu trả lời:


144

Cách thứ hai hiệu quả hơn một chút, nhưng cách tốt hơn nhiều là thực thi chúng theo lô:

public void executeBatch(List<Entity> entities) throws SQLException { 
    try (
        Connection connection = dataSource.getConnection();
        PreparedStatement statement = connection.prepareStatement(SQL);
    ) {
        for (Entity entity : entities) {
            statement.setObject(1, entity.getSomeProperty());
            // ...

            statement.addBatch();
        }

        statement.executeBatch();
    }
}

Tuy nhiên, bạn phụ thuộc vào việc triển khai trình điều khiển JDBC bao nhiêu lô bạn có thể thực thi cùng một lúc. Ví dụ, bạn có thể muốn thực thi chúng sau mỗi 1000 lô:

public void executeBatch(List<Entity> entities) throws SQLException { 
    try (
        Connection connection = dataSource.getConnection();
        PreparedStatement statement = connection.prepareStatement(SQL);
    ) {
        int i = 0;

        for (Entity entity : entities) {
            statement.setObject(1, entity.getSomeProperty());
            // ...

            statement.addBatch();
            i++;

            if (i % 1000 == 0 || i == entities.size()) {
                statement.executeBatch(); // Execute every 1000 items.
            }
        }
    }
}

Đối với môi trường đa luồng, bạn không cần phải lo lắng về điều này nếu bạn có và đóng kết nối và câu lệnh trong phạm vi ngắn nhất có thể bên trong cùng một khối phương thức theo thành ngữ JDBC thông thường sử dụng câu lệnh try-with-resources như được hiển thị trong các đoạn trên.

Nếu các lô đó là giao dịch, thì bạn muốn tắt tính năng tự động gửi kết nối và chỉ thực hiện giao dịch khi tất cả các lô kết thúc. Nếu không, nó có thể dẫn đến một cơ sở dữ liệu bẩn khi loạt lô đầu tiên thành công và sau đó thì không.

public void executeBatch(List<Entity> entities) throws SQLException { 
    try (Connection connection = dataSource.getConnection()) {
        connection.setAutoCommit(false);

        try (PreparedStatement statement = connection.prepareStatement(SQL)) {
            // ...

            try {
                connection.commit();
            } catch (SQLException e) {
                connection.rollback();
                throw e;
            }
        }
    }
}

inside the same method block- ý bạn là mọi luồng sẽ có ngăn xếp riêng của nó và kết nối và các câu lệnh này nằm trong ngăn xếp từ một phía và từ một nguồn dữ liệu khác sẽ cung cấp cho mọi phiên bản kết nối mới của executeFunction (== mọi luồng). Tôi hiểu bạn đúng không? "
Pavel_K

Tôi đang thực hiện phương pháp đầu tiên nhưng trong khi theo dõi trong trình biên dịch SQL, tôi thấy nhiều câu lệnh đã chuẩn bị lặp lại thay vì một câu lệnh. Tôi không thể tìm ra lý do tại sao nó hiển thị nhiều câu lệnh. cần hỗ trợ.
chàng trai lừa đảo

Câu trả lời của bạn là tốt miễn là truy vấn không thay đổi trong vòng lặp .. điều gì sẽ xảy ra nếu truy vấn thay đổi, ví dụ như trong trường hợp của tôi khi truy vấn được thay đổi .. tôi cho rằng vẫn còn cách tiếp cận thứ hai tốt hơn. vui lòng xác nhận
Stunner

13

Vòng lặp trong mã của bạn chỉ là một ví dụ quá đơn giản, phải không?

Sẽ tốt hơn nếu bạn tạo PreparedStatementmột lần duy nhất và sử dụng lại nó nhiều lần trong vòng lặp.

Trong những trường hợp không thể thực hiện được (vì nó làm phức tạp dòng chương trình quá nhiều), vẫn có lợi khi sử dụng a PreparedStatement, ngay cả khi bạn chỉ sử dụng nó một lần, vì phía máy chủ của công việc (phân tích cú pháp SQL và bộ nhớ đệm thực thi kế hoạch), vẫn sẽ được giảm.

Để giải quyết tình huống bạn muốn sử dụng lại phía Java PreparedStatement, một số trình điều khiển JDBC (chẳng hạn như Oracle) có tính năng lưu vào bộ nhớ đệm: Nếu bạn tạo PreparedStatementcho cùng một SQL trên cùng một kết nối, nó sẽ cung cấp cho bạn cùng một ) ví dụ.

Về đa luồng: Dù sao thì tôi không nghĩ rằng các kết nối JDBC có thể được chia sẻ trên nhiều luồng (tức là được sử dụng đồng thời bởi nhiều luồng). Mỗi luồng sẽ nhận được kết nối của riêng mình từ nhóm, sử dụng nó và đưa nó trở lại nhóm.


1
Trên thực tế, kết nối có luồng độc quyền của nó và mọi câu lệnh đều được thực thi trong đó, nhưng tôi truy cập thông qua một chồng các câu lệnh được chuẩn bị sẵn cho luồng đó. Vì vậy, chủ đề đồng thời khác bước đầu vượt qua chỉ params cần thiết để xây dựng tất cả các tuyên bố chuẩn bị, nhưng sau đó họ có thể sửa đổi params đồng thời
Thép Plume
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.