Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/hibernate/jdbc/AbstractBatcher.java


1   //$Id: AbstractBatcher.java,v 1.20 2005/04/25 07:33:12 oneovthafew Exp $
2   package org.hibernate.jdbc;
3   
4   import java.sql.CallableStatement;
5   import java.sql.Connection;
6   import java.sql.PreparedStatement;
7   import java.sql.ResultSet;
8   import java.sql.SQLException;
9   import java.util.HashSet;
10  import java.util.Iterator;
11  
12  import org.apache.commons.logging.Log;
13  import org.apache.commons.logging.LogFactory;
14  import org.hibernate.AssertionFailure;
15  import org.hibernate.HibernateException;
16  import org.hibernate.ScrollMode;
17  import org.hibernate.dialect.Dialect;
18  import org.hibernate.engine.SessionFactoryImplementor;
19  import org.hibernate.exception.JDBCExceptionHelper;
20  import org.hibernate.util.GetGeneratedKeysHelper;
21  import org.hibernate.util.JDBCExceptionReporter;
22  
23  /**
24   * Manages prepared statements and batching.
25   *
26   * @author Gavin King
27   */
28  public abstract class AbstractBatcher implements Batcher {
29  
30    private static int globalOpenPreparedStatementCount;
31    private static int globalOpenResultSetCount;
32  
33    private int openPreparedStatementCount;
34    private int openResultSetCount;
35  
36    protected static final Log log = LogFactory.getLog(AbstractBatcher.class);
37    protected static final Log SQL_LOG = LogFactory.getLog("org.hibernate.SQL");
38  
39    private final JDBCContext jdbcContext;
40    private final SessionFactoryImplementor factory;
41  
42    private PreparedStatement batchUpdate;
43    private String batchUpdateSQL;
44  
45    private HashSet statementsToClose = new HashSet();
46    private HashSet resultSetsToClose = new HashSet();
47    private PreparedStatement lastQuery;
48  
49    public AbstractBatcher(JDBCContext jdbcContext) {
50      this.jdbcContext = jdbcContext;
51      this.factory = jdbcContext.getFactory();
52    }
53  
54    protected PreparedStatement getStatement() {
55      return batchUpdate;
56    }
57  
58    public CallableStatement prepareCallableStatement(String sql) 
59    throws SQLException, HibernateException {
60      executeBatch();
61      logOpenPreparedStatement();
62      return getCallableStatement( jdbcContext.connection(), sql, false);
63    }
64  
65    public PreparedStatement prepareStatement(String sql) 
66    throws SQLException, HibernateException {
67      return prepareStatement(sql, false);
68    }
69  
70    public PreparedStatement prepareStatement(String sql, boolean getGeneratedKeys) 
71    throws SQLException, HibernateException {
72      executeBatch();
73      logOpenPreparedStatement();
74      return getPreparedStatement( jdbcContext.connection(), sql, false, getGeneratedKeys, null, false );
75    }
76  
77    public PreparedStatement prepareSelectStatement(String sql) 
78    throws SQLException, HibernateException {
79      logOpenPreparedStatement();
80      return getPreparedStatement( jdbcContext.connection(), sql, false, false, null, false );
81    }
82  
83    public PreparedStatement prepareQueryStatement(String sql, boolean scrollable, ScrollMode scrollMode) 
84    throws SQLException, HibernateException {
85      logOpenPreparedStatement();
86      PreparedStatement ps = getPreparedStatement( jdbcContext.connection(), sql, scrollable, scrollMode );
87      setStatementFetchSize(ps);
88      statementsToClose.add(ps);
89      lastQuery=ps;
90      return ps;
91    }
92  
93    public CallableStatement prepareCallableQueryStatement(String sql, boolean scrollable, ScrollMode scrollMode) 
94    throws SQLException, HibernateException {
95      logOpenPreparedStatement();
96      CallableStatement ps = (CallableStatement) getPreparedStatement(jdbcContext.connection(), sql, scrollable, false, scrollMode, true);
97      setStatementFetchSize(ps);
98      statementsToClose.add(ps);
99      lastQuery=ps;
100     return ps;
101   }
102 
103   public void abortBatch(SQLException sqle) {
104     try {
105       if (batchUpdate!=null) closeStatement(batchUpdate);
106     }
107     catch (SQLException e) {
108       //noncritical, swallow and let the other propagate!
109       JDBCExceptionReporter.logExceptions(e);
110     }
111     finally {
112       batchUpdate=null;
113       batchUpdateSQL=null;
114     }
115   }
116 
117   public ResultSet getResultSet(PreparedStatement ps) throws SQLException {
118     ResultSet rs = ps.executeQuery();
119     resultSetsToClose.add(rs);
120     logOpenResults();
121     return rs;
122   }
123 
124   public ResultSet getResultSet(CallableStatement ps, Dialect dialect) throws SQLException {
125     // TODO: maybe controlled by dialect...this works under oracle.
126     ResultSet rs = dialect.getResultSet(ps);
127     logOpenResults();
128     return rs;
129     
130   }
131   public void closeQueryStatement(PreparedStatement ps, ResultSet rs) throws SQLException {
132     statementsToClose.remove(ps);
133     if (rs!=null) resultSetsToClose.remove(rs);
134     try {
135       if (rs!=null) {
136         logCloseResults();
137         rs.close();
138       }
139     }
140     finally {
141       closeQueryStatement(ps);
142     }
143   }
144 
145   public PreparedStatement prepareBatchStatement(String sql) 
146   throws SQLException, HibernateException {
147     if ( !sql.equals(batchUpdateSQL) ) {
148       batchUpdate=prepareStatement(sql); // calls executeBatch()
149       batchUpdateSQL=sql;
150     }
151     else {
152       log.debug("reusing prepared statement");
153       log(sql);
154     }
155     return batchUpdate;
156   }
157 
158   public CallableStatement prepareBatchCallableStatement(String sql) 
159   throws SQLException, HibernateException {
160     if ( !sql.equals(batchUpdateSQL) ) { // TODO: what if batchUpdate is a callablestatement ?
161       batchUpdate=prepareCallableStatement(sql); // calls executeBatch()
162       batchUpdateSQL=sql;
163     }
164     return (CallableStatement)batchUpdate;
165   }
166 
167 
168   public void executeBatch() throws HibernateException {
169     if (batchUpdate!=null) {
170       try {
171         try {
172           doExecuteBatch(batchUpdate);
173         }
174         finally {
175           closeStatement(batchUpdate);
176         }
177       }
178       catch (SQLException sqle) {
179         throw JDBCExceptionHelper.convert(
180                 factory.getSQLExceptionConverter(),
181                 sqle,
182                 "Could not execute JDBC batch update",
183                 batchUpdateSQL
184         );
185       }
186       finally {
187         batchUpdate=null;
188         batchUpdateSQL=null;
189       }
190     }
191   }
192 
193   public void closeStatement(PreparedStatement ps) throws SQLException {
194     logClosePreparedStatement();
195     closePreparedStatement(ps);
196   }
197 
198   private void closeQueryStatement(PreparedStatement ps) throws SQLException {
199 
200     try {
201       //work around a bug in all known connection pools....
202       if ( ps.getMaxRows()!=0 ) ps.setMaxRows(0);
203       if ( ps.getQueryTimeout()!=0 ) ps.setQueryTimeout(0);
204     }
205     catch (Exception e) {
206       log.warn("exception clearing maxRows/queryTimeout", e);
207 //      ps.close(); //just close it; do NOT try to return it to the pool!
208       return; //NOTE: early exit!
209     }
210     finally {
211       closeStatement(ps);
212     }
213 
214     if ( lastQuery==ps ) lastQuery = null;
215     
216   }
217 
218   public void closeStatements() {
219     try {
220       if (batchUpdate!=null) batchUpdate.close();
221     }
222     catch (SQLException sqle) {
223       //no big deal
224       log.warn("Could not close a JDBC prepared statement", sqle);
225     }
226     batchUpdate=null;
227     batchUpdateSQL=null;
228 
229     Iterator iter = resultSetsToClose.iterator();
230     while ( iter.hasNext() ) {
231       try {
232         logCloseResults();
233         ( (ResultSet) iter.next() ).close();
234       }
235       catch (SQLException e) {
236         // no big deal
237         log.warn("Could not close a JDBC result set", e);
238       }
239     }
240     resultSetsToClose.clear();
241 
242     iter = statementsToClose.iterator();
243     while ( iter.hasNext() ) {
244       try {
245         closeQueryStatement( (PreparedStatement) iter.next() );
246       }
247       catch (SQLException e) {
248         // no big deal
249         log.warn("Could not close a JDBC statement", e);
250       }
251     }
252     statementsToClose.clear();
253   }
254 
255   protected abstract void doExecuteBatch(PreparedStatement ps) throws SQLException, HibernateException;
256   
257   private String preparedStatementCountsToString() {
258     return
259         " (open PreparedStatements: " + 
260         openPreparedStatementCount + 
261         ", globally: " +
262         globalOpenPreparedStatementCount +
263         ")";
264   }
265 
266   private String resultSetCountsToString() {
267     return
268         " (open ResultSets: " + 
269         openResultSetCount + 
270         ", globally: " +
271         globalOpenResultSetCount +
272         ")";
273   }
274 
275   private void logOpenPreparedStatement() {
276     if ( log.isDebugEnabled() ) {
277       log.debug( "about to open PreparedStatement" + preparedStatementCountsToString() );
278       openPreparedStatementCount++;
279       globalOpenPreparedStatementCount++;
280     }
281   }
282 
283   private void logClosePreparedStatement() {
284     if ( log.isDebugEnabled() ) {
285       log.debug( "about to close PreparedStatement" + preparedStatementCountsToString() );
286       openPreparedStatementCount--;
287       globalOpenPreparedStatementCount--;
288     }
289   }
290 
291   private void logOpenResults() {
292     if ( log.isDebugEnabled() ) {
293       log.debug( "about to open ResultSet" + resultSetCountsToString() );
294       openResultSetCount++;
295       globalOpenResultSetCount++;
296     }
297   }
298   private void logCloseResults() {
299     if ( log.isDebugEnabled() ) {
300       log.debug( "about to close ResultSet" + resultSetCountsToString() );
301       openResultSetCount--;
302       globalOpenResultSetCount--;
303     }
304   }
305 
306   protected SessionFactoryImplementor getFactory() {
307     return factory;
308   }
309   
310   private void log(String sql) {
311     SQL_LOG.debug(sql);
312     if ( factory.getSettings().isShowSqlEnabled() ) System.out.println("Hibernate: " + sql);
313   }
314 
315   private PreparedStatement getPreparedStatement(
316       final Connection conn, 
317       final String sql, 
318       final boolean scrollable, 
319       final ScrollMode scrollMode)
320   throws SQLException {
321     return getPreparedStatement(conn, sql, scrollable, false, scrollMode, false);
322   }
323 
324   private CallableStatement getCallableStatement( final Connection conn, 
325       final String sql, 
326       boolean scrollable)
327   throws SQLException {
328 
329     if ( scrollable && !factory.getSettings().isScrollableResultSetsEnabled() ) {
330       throw new AssertionFailure("scrollable result sets are not enabled");
331     }
332 
333     log(sql);
334     
335     log.trace("preparing callable statement");
336     if (scrollable) {
337       return conn.prepareCall(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
338     }
339     else {
340       return conn.prepareCall(sql);
341     }
342 
343   }
344 
345   private PreparedStatement getPreparedStatement(
346       final Connection conn, 
347       final String sql, 
348       boolean scrollable, 
349       final boolean useGetGeneratedKeys, 
350       final ScrollMode scrollMode,
351       final boolean callable)
352   throws SQLException {
353 
354     if ( scrollable && !factory.getSettings().isScrollableResultSetsEnabled() ) {
355       throw new AssertionFailure("scrollable result sets are not enabled");
356     }
357 
358     if ( useGetGeneratedKeys && !factory.getSettings().isGetGeneratedKeysEnabled() ) {
359       throw new AssertionFailure("getGeneratedKeys() support is not enabled");
360     }
361 
362     log(sql);
363 
364     try {
365       log.trace("preparing statement");
366       PreparedStatement result;
367       if (scrollable) {
368         if (callable) {
369           result = conn.prepareCall( sql, scrollMode.toResultSetType(), ResultSet.CONCUR_READ_ONLY );
370         } 
371         else {
372           result = conn.prepareStatement( sql, scrollMode.toResultSetType(), ResultSet.CONCUR_READ_ONLY );
373         }
374       }
375       else if (useGetGeneratedKeys) {
376         result = GetGeneratedKeysHelper.prepareStatement(conn, sql);
377       }
378       else {
379         if (callable) {
380           result = conn.prepareCall(sql);
381         } 
382         else {
383           result = conn.prepareStatement(sql);
384         }
385       }
386 
387       if ( factory.getStatistics().isStatisticsEnabled() ) {
388         factory.getStatisticsImplementor().prepareStatement();
389       }
390       
391       return result;
392     
393     }
394     catch (SQLException sqle) {
395       JDBCExceptionReporter.logExceptions(sqle);
396       throw sqle;
397     }
398 
399   }
400 
401   private void closePreparedStatement(PreparedStatement ps) throws SQLException {
402     try {
403       log.trace("closing statement");
404       ps.close();
405       if ( factory.getStatistics().isStatisticsEnabled() ) {
406         factory.getStatisticsImplementor().closeStatement();
407       }
408     }
409     finally {
410       if ( resultSetsToClose.size()==0 && statementsToClose.size()==0 ) {
411         jdbcContext.aggressiveRelease();
412       }
413     }
414   }
415 
416   private void setStatementFetchSize(PreparedStatement statement) throws SQLException {
417     Integer statementFetchSize = factory.getSettings().getJdbcFetchSize();
418     if (statementFetchSize!=null) statement.setFetchSize( statementFetchSize.intValue() );
419   }
420 
421   public Connection openConnection() throws HibernateException {
422     log.debug("opening JDBC connection");
423     try {
424       return factory.getConnectionProvider().getConnection();
425     }
426     catch (SQLException sqle) {
427       throw JDBCExceptionHelper.convert(
428           factory.getSQLExceptionConverter(),
429           sqle,
430           "Cannot open connection"
431       );
432     }
433   }
434 
435   public void closeConnection(Connection conn) throws HibernateException {
436     if ( log.isDebugEnabled() ) {
437       log.debug( 
438           "closing JDBC connection" + 
439           preparedStatementCountsToString() + 
440           resultSetCountsToString() 
441       );
442     }
443 
444     if ( conn == null ) {
445       return;
446     }
447 
448     try {
449       if ( !conn.isClosed() ) {
450         JDBCExceptionReporter.logAndClearWarnings(conn);
451       }
452       factory.getConnectionProvider().closeConnection(conn);
453     }
454     catch (SQLException sqle) {
455       throw JDBCExceptionHelper.convert(
456           factory.getSQLExceptionConverter(),
457           sqle,
458           "Cannot close connection"
459       );
460     }
461   }
462 
463   public void cancelLastQuery() throws HibernateException {
464     try {
465       if (lastQuery!=null) lastQuery.cancel();
466     }
467     catch (SQLException sqle) {
468       throw JDBCExceptionHelper.convert(
469           factory.getSQLExceptionConverter(),
470           sqle,
471           "Cannot cancel query"
472       );
473     }
474   }
475 
476 }
477 
478 
479 
480 
481 
482