2013-02-27 12 views
9

Secondo quanto ho capito, l'uso di PreparedStatement in Java è possibile utilizzarlo più volte. Ma ho un po 'di confusione con PreparedStatementCreator di Spring JDBC.Qual è il modo corretto di utilizzare PreparedStatementCreator of Spring JDBC?

Ad esempio si consideri codice seguente,

public class SpringTest { 

    JdbcTemplate jdbcTemplate; 
    PreparedStatementCreator preparedStatementCreator; 
    ResultSetExtractor<String> resultSetExtractor; 

    public SpringTest() throws SQLException { 

     jdbcTemplate = new JdbcTemplate(OracleUtil.getDataSource()); 

     preparedStatementCreator = new PreparedStatementCreator() { 
      String query = "select NAME from TABLE1 where ID=?"; 
      public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { 
       return connection.prepareStatement(query); 
      } 
     }; 

     resultSetExtractor = new ResultSetExtractor<String>() { 
      public String extractData(ResultSet resultSet) throws SQLException, 
      DataAccessException { 
       if (resultSet.next()) { 
        return resultSet.getString(1); 
       } 
       return null; 
      } 
     }; 
    } 
    public String getNameFromId(int id){ 
     return jdbcTemplate.query(preparedStatementCreator, new Table1Setter(id), resultSetExtractor); 
    } 

    private static class Table1Setter implements PreparedStatementSetter{ 

     private int id; 
     public Table1Setter(int id) { 
      this.id =id; 
     } 
     @Override 
     public void setValues(PreparedStatement preparedStatement) throws SQLException { 
      preparedStatement.setInt(1, id); 
     } 
    } 
    public static void main(String[] args) { 
     try { 
      SpringTest springTest = new SpringTest(); 

      for(int i=0;i<10;i++){ 
       System.out.println(springTest.getNameFromId(i)); 
      } 
     } catch (SQLException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

Secondo questo codice, quando ho chiamato springTest.getNameFromId (int id) metodo, restituisce il nome dal dato id, Qui ho usato per la creazione di PreparedStatementCreator PreparedStatement e PreparedStatementSetter per l'impostazione dei parametri di input e ho ottenuto il risultato da ResultSetExtractor. Ma le prestazioni sono molto lente.

Dopo aver eseguito il debug e aver esaminato cosa succede all'interno di PreparedStatementCreator e JdbcTemplate, ho avuto modo di sapere che PreparedStatementCreator crea sempre nuove PreparedStatement ... !!!

Ogni volta che sono chiamata metodo jdbcTemplate.query (preparedStatementCreator, preparedStatementSetter, resultSetExtractor), crea un nuovo PreparedStatement e questo rallenta le prestazioni.

È questo il modo corretto di utilizzare PreparedStatementCreator? Perché in questo codice non riesco a riutilizzare PreparedStatement. E se questo è il modo giusto per usare PreparedStatementCreator di come trarre beneficio dalla riutilizzabilità di PreparedStatement?

risposta

2

Le istruzioni preparate vengono in genere memorizzate nella cache dal pool di connessione sottostante, quindi non è necessario preoccuparsi di crearne una nuova ogni volta o meno.

Quindi penso che l'utilizzo effettivo sia corretto.

JdbcTemplate chiude la dichiarazione dopo l'esecuzione, quindi se si vuole veramente per riutilizzare la stessa dichiarazione preparata si potrebbe procura la dichiarazione e intercettare il metodo close nella dichiarazione creatore

Per esempio (non testato, solo come esempio):

public abstract class ReusablePreparedStatementCreator implements PreparedStatementCreator { 

    private PreparedStatement statement; 

    public PreparedStatement createPreparedStatement(Connection conn) throws SQLException { 
     if (statement != null) 
      return statement; 

     PreparedStatement ps = doPreparedStatement(conn); 

     ProxyFactory pf = new ProxyFactory(ps); 
     MethodInterceptor closeMethodInterceptor = new MethodInterceptor() { 

      @Override 
      public Object invoke(MethodInvocation invocation) throws Throwable { 
       return null; // don't close statement 
      } 
     }; 

     NameMatchMethodPointcutAdvisor closeAdvisor = new NameMatchMethodPointcutAdvisor(); 
     closeAdvisor.setMappedName("close"); 
     closeAdvisor.setAdvice(closeMethodInterceptor); 
     pf.addAdvisor(closeAdvisor); 

     statement = (PreparedStatement) pf.getProxy(); 

     return statement;  
    } 

    public abstract PreparedStatement doPreparedStatement(Connection conn) throws SQLException; 

    public void close() { 
     try { 
      PreparedStatement ps = (PreparedStatement) ((Advised) statement).getTargetSource().getTarget(); 
      ps.close(); 
     } catch (Exception e) { 
      // handle exception 
     } 
    } 

} 
1

Dopo il debug e la ricerca in ciò che accade all'interno PreparedStatementCreator e JdbcTemplate ho avuto modo di sapere che PreparedStatementCreator crea ogni volta nuovo PreparedStatement ... !!!

Io non so perché è così scioccante dal momento che è il proprio codice che crea un nuovo PreparedStatement ogni momento chiamando connection.prepareStatement(query);. Se vuoi riutilizzare lo stesso, non dovresti crearne uno nuovo.

+0

ho provato anche con il ritorno stesso PreparedStatement, poi ho ottenuto l'errore "connessione chiusa", e quando ho cercato di usare lo stesso oggetto di connessione che ho ottenuto l'errore "Dichiarazione Closed" –

+0

Se stai parlando in il tuo esempio sopra, sarebbe perché stai facendo una corretta gestione delle transazioni. Con una transazione in corso, tutto verrebbe eseguito contro la stessa connessione e il riutilizzo della stessa istruzione non dovrebbe essere un problema. –

+0

Spiacenti, digita sopra ... fai in modo che "non * stai * facendo una corretta gestione delle transazioni". –

3

Sei sulla strada giusta per utilizzare PreparedStatementCreator.

  1. In ogni nuova transazione, è necessario creare nuovo PreparedStatement esempio, è sicuramente corretto. PreparedStatementCreator è stato progettato principalmente per avvolgere il blocco di codice per creare PreparedStatement esempio facilmente, senza dire che si dovrebbe resue la nuova istanza ogni itme.
  2. PreparedStatement è progettato principalmente per inviare il DBMS di istruzioni SQL precompilato e precompilato che consente di risparmiare tempo precompilato per l'esecuzione SQL.

Per riassumere, ciò che hai fatto è corretto. utilizzare PreparedStatement avrà prestazioni migliori rispetto a Dichiarazione.

Problemi correlati