2013-02-27 19 views
9

Nach meinem Verständnis der Verwendung von PreparedStatement in Java können wir es mehrmals verwenden. Aber ich habe etwas Verwirrung mit PreparedStatementCreator von Spring JDBC.Was ist der richtige Weg, PreparedStatementCreator von Spring JDBC zu verwenden?

Zum Beispiel folgenden Code betrachten,

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

Gemäß diesem Code, wenn ich springTest.getNameFromId (int id) Methode aufgerufen wird, gibt es Namen gegeben id, habe PreparedStatementCreator Hier habe ich für PreparedStatement Erstellung und PreparedStatementSetter zum Einstellen der Eingabeparameter und ich habe das Ergebnis von ResultSetExtractor erhalten. Aber Leistung ist sehr langsam.

Nach dem Debuggen und schauen was in PreparedStatementCreator und JdbcTemplate passiert, habe ich erfahren, dass PreparedStatementCreator jedes Mal neue PreparedStatement erstellt ... !!!

Jedes Mal, wenn ich Methoden jdbcTemplate.query (preparedStatementCreator, preparedStatementSetter, resultSetExtractor) aufruft, erstellt es neue PreparedStatement und dies verlangsamt die Leistung.

Ist dies der richtige Weg PreparedStatementCreator zu verwenden? Weil ich PreparedStatement in diesem Code nicht wiederverwenden kann. Und wenn dies der richtige Weg ist, um PreparedStatementCreator zu verwenden, als wie man von der Wiederverwendbarkeit von PreparedStatement profitieren kann? ...

Antwort

2

Vorbereitete Anweisungen werden normalerweise vom zugrunde liegenden Verbindungspool zwischengespeichert, sodass Sie sich keine Gedanken darüber machen müssen, ob Sie jedes Mal ein neues erstellen.

Also ich denke, dass Ihre tatsächliche Verwendung korrekt ist.

JdbcTemplate schließt die Erklärung ab, nachdem er ausgeführt wird, so dass, wenn Sie wirklich wollen, die gleiche vorbereitete Anweisung erneut zu verwenden Sie die Anweisung Proxy konnte und abfangen die Close-Methode in der Anweisung Schöpfer

Zum Beispiel (nicht getestet, nur als Beispiel):

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

Nach Debuggen und Blick in die Inneren PreparedStatementCreator und JdbcTemplate passiert, ich habe zu wissen, dass PreparedStatementCreator jedes Mal neues PreparedStatement schafft !!!

bin ich nicht sicher, warum das so schockierend ist, da es Ihr eigener Code ist, die durch den Aufruf connection.prepareStatement(query); ein neues PreparedStatement jedes Mal erstellt. Wenn Sie denselben verwenden möchten, sollten Sie keinen neuen erstellen.

+0

ich auch mit der Rückkehr gleiche PreparedStatement versuchte, dann Fehler „Closed Connection“ ich habe, und als ich versuchte gleiches Verbindungsobjekt zu verwenden, als ich Fehler „Closed Statement“ bekam –

+0

Wenn Sie im Gespräch sind Ihr Beispiel oben, das liegt daran, dass Sie eine ordnungsgemäße Transaktionsverwaltung durchführen. Bei einer laufenden Transaktion würde alles gegen die gleiche Verbindung ausgeführt werden, und die Verwendung der gleichen Anweisung sollte kein Problem darstellen. –

+0

Sorry, Tippfehler über ... machen, dass "Sie * nicht * richtige Transaktionsverwaltung machen". –

3

Sie sind auf dem richtigen Weg, PreparedStatementCreator zu verwenden.

  1. In jeder neuen Transaktion sollten Sie brandneue PreparedStatement Instanz erstellen, es ist auf jeden Fall richtig. PreparedStatementCreator wird hauptsächlich entwickelt, um den Code-Block wickeln PreparedStatement Instanz leicht zu sagen, nicht zu schaffen, dass Sie die neue Instanz jedes itme Resue sollten.
  2. PreparedStatement wurde hauptsächlich entwickelt, um das vorlaeufige und vorkompilierte SQL-Statement-DBMS zu senden, das etwas vorkompilierte Zeit für die SQL-Ausführung spart.

Zusammenfassend, was Sie getan haben, ist richtig. Verwenden Sie PreparedStatement wird eine bessere Leistung als Statement haben.