Como posso cancelar uma consulta de longa duração usando Primavera e JDBCTemplate?

StackOverflow https://stackoverflow.com/questions/1054642

  •  20-08-2019
  •  | 
  •  

Pergunta

A classe java.sql.Statement JDBC tem um método cancel(). Isso pode ser chamado em outro segmento para cancelar uma instrução atualmente em execução.

Como posso conseguir isso usando Spring? Não consigo encontrar uma maneira de obter uma referência a uma declaração ao executar uma consulta. Também não posso encontrar uma cancelar-como método.

Aqui está um código de exemplo. Imagine isto leva até 10 segundos para executar, e às vezes a pedido do usuário, eu quero cancelá-lo:

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

Como eu poderia modificar isso para que eu tenha uma referência a um objeto java.sql.Statement?

Foi útil?

Solução

Deixe-me simplificar a resposta das oxbow_lakes: você pode usar a variante PreparedStatementCreator do método de consulta para ter acesso à instrução

.

Portanto, o seu código:

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

Se transformar em:

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

Agora, para cancelar você pode apenas chamar

stmt[0].cancel()

Você provavelmente vai querer dar uma referência para stmt para algum outro fio antes de realmente executar a consulta, ou simplesmente armazená-lo como uma variável de membro. Caso contrário, você não pode realmente cancelar qualquer coisa ...

Outras dicas

Você pode executar coisas através de métodos JdbcTemplate que permitem que você passar em um PreparedStatementCreator. Você pode sempre usar isso para invocações de interceptação (talvez usando um Proxy) que causou um cancel acontecer em um segmento separado por alguns cond tornou 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() { ... });
}        

Esta canceller pode-se ser cancelado após a conclusão; por exemplo

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

Agora o código que está respondendo ao cancelamento de um usuário ficaria assim:

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

Eu assumo pela Primavera você quer dizer o uso de JdbcDaoTemplate e / ou JdbcTemplate? Se assim for, isso realmente não ajuda ou atrapalha-lo a resolver o seu problema.

Eu vou assumir o seu caso de uso é que você está executando uma operação de DAO em um fio, e outro segmento chega e quer cancelar a operação do primeiro fio.

O primeiro problema que você tem para resolver é, como é que o segundo segmento saber qual cancelar? É este um GUI com um número fixo de threads, ou um servidor com vários?

Uma vez que você já resolveu essa parte, você precisa descobrir como cancelar a instrução no primeiro segmento. Uma abordagem simples para isso seria para armazenar PreparedStatement do primeiro segmento em um algum lugar campo (talvez em um campo simples, talvez em um mapa de ID thread para declarações), permitindo que o segundo segmento a entrar, para recuperar a chamada statwmentand cancelar () nele.

Tenha em mente que é possível que cancelar () pode apenas bloquear, dependendo do seu motorista e banco de dados JDBC. Além disso, certifique-se de pensar muito sobre sincronização aqui, são os seus tópicos estão indo para entrar em uma briga.

Você pode registrar um objeto callback do tipo StatementCallback em JdbcTemplate que serão executados com a declaração actualmente activa como um parâmetro. Neste callback então você pode cancelar a declaração:

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

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

        return null;
    }
});
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top