2009-06-28 10 views
11

Klasa JDBC java.sql.Statement ma metodę cancel(). Można go wywołać w innym wątku, aby anulować aktualnie działającą instrukcję.Jak mogę anulować długo działającą kwerendę za pomocą Spring i JDBCTemplate?

Jak mogę to osiągnąć za pomocą Spring? Nie mogę znaleźć sposobu, aby uzyskać odniesienie do wyciągu podczas uruchamiania zapytania. Nie mogę też znaleźć metody anulowania.

Oto przykładowy kod. Wyobraź sobie ten trwa do 10 sekund, aby wykonać, a czasami na życzenie użytkownika, chcę, aby go anulować:

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

Jak bym zmodyfikować to więc mam odniesienie do java.sql.Statement obiektu?

Odpowiedz

11

Pozwól mi uprościć odpowiedź oxbow_lakes: możesz użyć wersji PreparedStatementCreator, aby uzyskać dostęp do wyciągu.

Więc kod:

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

powinien przekształcić:

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); 
    } 
}); 

teraz, aby anulować można po prostu zadzwonić

stmt[0].cancel() 

Prawdopodobnie chcesz dać odniesienie do stmt do inny wątek przed uruchomieniem zapytania lub po prostu zapisać go jako zmienną członkowską mi. W przeciwnym razie nie można naprawdę niczego anulować ...

1

Można wykonywać różne rzeczy za pomocą metod JdbcTemplate, które umożliwiają przekazanie PreparedStatementCreator. Zawsze można użyć tego do przechwytywania wywołań (być może przy użyciu Proxy), które spowodowały, że cancel stało się w osobnym wątku przez jakieś cond, które stało się .

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() { ... }); 
}   

Ten canceller może zostać anulowany po pomyślnym zakończeniu; na przykład

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); 

więc teraz kodu, który reaguje na anulowanie użytkownika wyglądałby następująco:

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

Ciekawym przykładem i dziwne, nie do końca pewien, w jaki sposób można zastosować do problemu pod ręką. Oczywiście, to 10-sekundowe auto-anulowanie wymagałoby zastąpienia czymś zewnętrznie wywołanym. – skaffman

+0

Dlaczego musiałby być uruchamiany z zewnątrz? OP nie wspomniał nic o tym, że jest, powiedzmy, zdefiniowanym przez użytkownika –

+0

OP tutaj: faktycznie, chcę, aby anulować zostanie wywołany w odpowiedzi na działanie użytkownika. –

0

Zakładam przez Wiosna na myśli wykorzystanie JdbcDaoTemplate i/lub JdbcTemplate? Jeśli tak, to tak naprawdę nie pomaga lub nie przeszkadza ci w rozwiązaniu problemu.

Założę, że twój przypadek użycia jest taki, że wykonujesz operację DAO w jednym wątku, a inny wątek przychodzi i chce anulować działanie pierwszego wątku.

Pierwszy problem, który musisz rozwiązać, to w jaki sposób drugi wątek wie, który z nich anulować? Czy jest to GUI z ustaloną liczbą wątków, czy serwer z kilkoma?

Po rozwiązaniu tej części należy dowiedzieć się, jak anulować instrukcję w pierwszym wątku. Jednym prostym podejściem do tego byłoby przechowywanie w pierwszym polu PreparedStatement w pierwszym wątku (być może w prostym polu, być może na mapie z identyfikatorem wątku do wyciągów), pozwalając na wejście drugiego wątku, pobranie statwmentand anulowanie połączenia() na tym.

Należy pamiętać, że funkcja cancel() może po prostu zablokować, w zależności od sterownika JDBC i bazy danych. Upewnij się też, czy nie myślisz o synchronizacji tutaj, czy twoje wątki będą walczyć.

+0

To jest świetne i szczegółowe informacje, ale pytanie, które chcę być odpowiedź brzmi następująco: Jak mogę uzyskać odwołanie do instrukcji przy uruchamianiu zapytania przez JdbcTemplate? –

+0

Cóż, powinieneś to powiedzieć :) Jak powiedział @oxbow, możesz użyć niestandardowego wystąpienia PreparedStatementCreator, aby uzyskać kontrolę nad tworzeniem instrukcji, a następnie przekazać to do JdbcTemplate. – skaffman

0

Można zarejestrować obiekt wywołania zwrotnego typu StatementCallback na JdbcTemplate, który zostanie wykonany z aktywną bieżącą instrukcją jako parametrem. W tym zwrotnego można następnie odwołać oświadczenie:

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

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

     return null; 
    } 
}); 
Powiązane problemy