2009-06-28 12 views
11

Lớp JDBC java.sql.Statement có phương thức cancel(). Điều này có thể được gọi trong một chủ đề khác để hủy bỏ một câu lệnh đang chạy.Làm cách nào để hủy truy vấn chạy dài bằng Spring và JDBCTemplate?

Làm cách nào để đạt được điều này bằng Spring? Tôi không thể tìm thấy một cách để có được một tham chiếu đến một tuyên bố khi chạy một truy vấn. Tôi cũng không thể tìm thấy một phương pháp giống như hủy bỏ.

Dưới đây là một số mã mẫu. Hãy tưởng tượng này phải mất đến 10 giây để thực hiện, và đôi khi có yêu cầu của người sử dụng, tôi muốn hủy nó:

final int i = simpleJdbcTemplate.queryForInt("select max(gameid) from game"); 

Làm thế nào sẽ để tôi sửa đổi này vì vậy tôi có một tham chiếu đến một đối tượng java.sql.Statement?

Trả lời

11

Để tôi đơn giản hóa câu trả lời của oxbow_lakes: bạn có thể sử dụng biến thể PreparedStatementCreator của phương pháp truy vấn để truy cập vào câu lệnh.

Vì vậy, mã của bạn:

final int i = simpleJdbcTemplate.queryForInt("select max(gameid) from game"); 

nên biến thành:

final PreparedStatement[] stmt = new PreparedStatement[1]; 
final int i = (Integer)getJdbcTemplate().query(new PreparedStatementCreator() { 
    public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { 
     stmt[0] = connection.prepareStatement("select max(gameid) from game"); 
     return stmt[0]; 
    } 
}, new ResultSetExtractor() { 
    public Object extractData(ResultSet resultSet) throws SQLException, DataAccessException { 
     return resultSet.getString(1); 
    } 
}); 

Bây giờ để hủy bỏ bạn chỉ có thể gọi

stmt[0].cancel() 

Bạn có thể muốn cung cấp cho một tham chiếu đến stmt để một số chủ đề khác trước khi thực sự chạy truy vấn hoặc đơn giản là lưu trữ nó dưới dạng biến tố thành viên e. Nếu không, bạn không thể thực sự hủy bất kỳ điều gì ...

1

Bạn có thể thực hiện các công cụ qua các phương thức JdbcTemplate cho phép bạn chuyển vào một số PreparedStatementCreator. Bạn luôn có thể sử dụng điều này để chặn các lời gọi (có thể sử dụng một số Proxy) gây ra một số cancel xảy ra trên một sợi riêng biệt bởi một số cond trở thành true.

public Results respondToUseRequest(Request req) { 
    final AtomicBoolean cond = new AtomicBoolean(false); 
    requestRegister.put(req, cond); 
    return jdbcTemplate.query(new PreparedStatementCreator() { 
      public PreparedStatement createPreparedStatement(Connection conn) { 
       PreparedStatement stmt = conn.prepareStatement(); 
       return proxyPreparedStatement(stmt, cond); 
      } 
     }, 
     new ResultSetExtractor() { ... }); 
}   

canceller có thể tự hủy khi hoàn tất thành công; ví dụ

private final static ScheduledExecutorService scheduler = 
       Executors.newSingleThreadedScheduledExecutor(); 

PreparedStatement proxyPreparedStatement(final PreparedStatement s, AtomicBoolean cond) { 
    //InvocationHandler delegates invocations to the underlying statement 
    //but intercepts a query 
    InvocationHandler h = new InvocationHandler() { 

     public Object invoke(Object proxy, Method m, Object[] args) { 
      if (m.getName().equals("executeQuery") { 
       Runnable cancel = new Runnable() { 
        public void run() { 
         try { 
          synchronized (cond) { 
           while (!cond.get()) cond.wait(); 
           s.cancel(); 
          } 
         } catch (InterruptedException e) { } 
        } 
       } 
       Future<?> f = scheduler.submit(cancel); 
       try { 
        return m.invoke(s, args); 
       } finally { 
        //cancel the canceller upon succesful completion 
        if (!f.isDone()) f.cancel(true); //will cause interrupt 
       } 
      } 
      else { 
       return m.invoke(s, args); 
      } 
     } 

    } 

    return (PreparedStatement) Proxy.newProxyInstance(
       getClass().getClassLoader(), 
       new Class[]{PreparedStatement.class}, 
       h); 

Vì vậy, bây giờ là mã được đáp ứng để hủy của người dùng sẽ trông như thế:

cond.set(true); 
synchronized (cond) { cond.notifyAll(); } 
+0

Ví dụ thú vị và kỳ lạ, không hoàn toàn chắc chắn về cách có thể áp dụng cho sự cố trong tầm tay. Rõ ràng là tự động hủy 10 giây sẽ cần thay thế bằng thứ gì đó được kích hoạt bên ngoài. – skaffman

+0

Tại sao nó phải được kích hoạt bên ngoài? OP đã không đề cập đến bất cứ điều gì về nó, nói, người dùng định nghĩa –

+0

OP ở đây: thực sự, tôi muốn hủy bỏ được kích hoạt để đáp ứng với hành động của người dùng. –

0

tôi giả bởi Spring bạn có nghĩa là việc sử dụng các JdbcDaoTemplate và/hoặc JdbcTemplate? Nếu vậy, điều này không thực sự giúp đỡ hoặc cản trở bạn trong việc giải quyết vấn đề của bạn.

Tôi giả sử trường hợp sử dụng của bạn là bạn đang thực hiện thao tác DAO trong một chuỗi và một chuỗi khác đến và muốn hủy thao tác của chuỗi đầu tiên.

Vấn đề đầu tiên bạn phải giải quyết là, luồng thứ hai biết cái nào cần hủy? Đây có phải là GUI với số lượng chủ đề cố định hoặc một máy chủ có nhiều chủ đề không?

Khi bạn đã giải quyết được phần đó, bạn cần tìm ra cách hủy câu lệnh trong chuỗi đầu tiên. Một cách tiếp cận đơn giản để làm điều này là lưu trữ PreparedStatement của thread đầu tiên ở một nơi nào đó (có lẽ trong một trường đơn giản, có lẽ trong một bản đồ của ID luồng tới các câu lệnh), cho phép chuỗi thứ hai đi vào, lấy lại lệnh hủy bỏ statwmentand() trên đó.

Hãy nhớ rằng có thể hủy() chỉ có thể chặn, tùy thuộc vào trình điều khiển và cơ sở dữ liệu JDBC của bạn. Ngoài ra, hãy chắc chắn rằng bạn nghĩ rằng khó khăn về đồng bộ hóa ở đây, là chủ đề của bạn sẽ nhận được vào một cuộc chiến.

+0

Thông tin tuyệt vời và chi tiết, nhưng câu hỏi tôi đang tìm kiếm trả lời là: Làm thế nào tôi có thể nhận được một tham chiếu đến một tuyên bố khi chạy một truy vấn thông qua JdbcTemplate? –

+0

Vâng, bạn nên đã nói như vậy :) Như @oxbow nói, bạn có thể sử dụng một thể hiện tùy chỉnh của PreparedStatementCreator để có được quyền kiểm soát tạo ra tuyên bố, và sau đó vượt qua đó để JdbcTemplate. – skaffman

0

Bạn có thể đăng ký đối tượng gọi lại kiểu StatementCallback trên JdbcTemplate sẽ được thực hiện với câu lệnh hiện đang hoạt động làm tham số. Trong cuộc gọi lại này, bạn có thể hủy tuyên bố sau:

simpleJdbcTemplate.getJdbcOperations().execute(new StatementCallback() { 

    @Override 
    public Object doInStatement(final Statement statement) throws SQLException, DataAccessException { 
     if (!statement.isClosed()) { 
      statement.cancel(); 
     } 

     return null; 
    } 
});