View Javadoc

1   package org.apache.ojb.broker.accesslayer;
2   
3   /* Copyright 2002-2005 The Apache Software Foundation
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  import java.sql.Connection;
19  import java.sql.PreparedStatement;
20  import java.sql.ResultSet;
21  import java.sql.SQLException;
22  import java.sql.Statement;
23  
24  import org.apache.ojb.broker.PersistenceBrokerSQLException;
25  import org.apache.ojb.broker.accesslayer.sql.SqlGenerator;
26  import org.apache.ojb.broker.accesslayer.sql.SqlGeneratorFactory;
27  import org.apache.ojb.broker.core.proxy.ProxyHelper;
28  import org.apache.ojb.broker.metadata.ClassDescriptor;
29  import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
30  import org.apache.ojb.broker.metadata.ConnectionPoolDescriptor;
31  import org.apache.ojb.broker.platforms.Platform;
32  import org.apache.ojb.broker.platforms.PlatformException;
33  import org.apache.ojb.broker.platforms.PlatformFactory;
34  import org.apache.ojb.broker.query.Query;
35  import org.apache.ojb.broker.util.logging.Logger;
36  import org.apache.ojb.broker.util.logging.LoggerFactory;
37  import org.apache.ojb.broker.util.ExceptionHelper;
38  
39  /**
40   * This class serves as a cache for Statements that are
41   * used for persistence operations on a given class.
42   * @author Thomas Mahler
43   * @version $Id: StatementsForClassImpl.java,v 1.1 2007-08-24 22:17:30 ewestfal Exp $
44   */
45  public class StatementsForClassImpl implements StatementsForClassIF
46  {
47      private Logger log = LoggerFactory.getLogger(StatementsForClassImpl.class);
48  
49      /**
50       * sets the escape processing mode
51       */
52      // protected static boolean ESCAPEPROCESSING = false;
53  
54      protected final ClassDescriptor classDescriptor;
55      protected final SqlGenerator sqlGenerator;
56      protected final Platform platform;
57      protected final Class clazz;
58      protected final int fetchSize;
59      private String deleteSql;
60      private String insertSql;
61      private String updateSql;
62      private String selectByPKSql;
63  
64      /**
65       * force use of JDBC 1.0 statement creation
66       */
67      protected boolean FORCEJDBC1_0 = false;
68  
69      public StatementsForClassImpl(
70          final JdbcConnectionDescriptor jcd,
71          final ClassDescriptor classDescriptor)
72      {
73          this.classDescriptor = classDescriptor;
74          clazz = classDescriptor.getClassOfObject();
75          platform = PlatformFactory.getPlatformFor(jcd);
76          sqlGenerator = SqlGeneratorFactory.getInstance().createSqlGenerator(platform);
77  
78          final ConnectionPoolDescriptor cpd = jcd.getConnectionPoolDescriptor();
79          fetchSize = cpd.getFetchSize();
80  
81          // detect JDBC level
82          double level = jcd.getJdbcLevel();
83          FORCEJDBC1_0 = level == 1.0;
84      }
85  
86      /**
87       * Answer true if a PreparedStatement has to be used
88       * <br>false for a CallableStatement
89       */
90      protected boolean usePreparedDeleteStatement()
91      {
92          return !(classDescriptor.getDeleteProcedure() != null &&
93                  classDescriptor.getDeleteProcedure().hasReturnValues());
94      }
95  
96      public PreparedStatement getDeleteStmt(Connection con) throws SQLException
97      {
98          if (deleteSql == null)
99          {
100             deleteSql = sqlGenerator.getPreparedDeleteStatement(classDescriptor).getStatement();
101         }
102         try
103         {
104             return prepareStatement(con,
105                     deleteSql,
106                     Query.NOT_SCROLLABLE,
107                     usePreparedDeleteStatement(),
108                     StatementManagerIF.FETCH_SIZE_NOT_APPLICABLE);
109         }
110         catch (SQLException ex)
111         {
112             log.error("Can't prepare delete statement: " + deleteSql, ex);
113             throw ex;
114         }
115     }
116 
117     public Statement getGenericStmt(Connection con, boolean scrollable)
118         throws PersistenceBrokerSQLException
119     {
120         Statement stmt;
121         try
122         {
123             stmt = createStatement(con, scrollable,
124                     StatementManagerIF.FETCH_SIZE_NOT_EXPLICITLY_SET);
125         }
126         catch (SQLException ex)
127         {
128             throw ExceptionHelper.generateException("Can't prepare statement:", ex, null, log);
129         }
130         return stmt;
131     }
132 
133     /**
134      * Answer true if a PreparedStatement has to be used
135      * <br>false for a CallableStatement
136      */
137     protected boolean usePreparedInsertStatement()
138     {
139         return !(classDescriptor.getInsertProcedure() != null &&
140                 classDescriptor.getInsertProcedure().hasReturnValues());
141     }
142 
143     public PreparedStatement getInsertStmt(Connection con) throws SQLException
144     {
145         if (insertSql == null)
146         {
147             insertSql = sqlGenerator.getPreparedInsertStatement(classDescriptor).getStatement();
148         }
149         try
150         {
151             return prepareStatement(con,
152                     insertSql,
153                     Query.NOT_SCROLLABLE,
154                     usePreparedInsertStatement(),
155                     StatementManagerIF.FETCH_SIZE_NOT_APPLICABLE);
156         }
157         catch (SQLException ex)
158         {
159             log.error("Can't prepare insert statement: " + insertSql, ex);
160             throw ex;
161         }
162     }
163 
164     public PreparedStatement getPreparedStmt(Connection con, String sql,
165                                              boolean scrollable, int explicitFetchSizeHint, boolean callableStmt)
166         throws PersistenceBrokerSQLException
167     {
168         PreparedStatement stmt;
169         try
170         {
171             stmt = prepareStatement(con, sql, scrollable,
172                     !callableStmt, explicitFetchSizeHint);
173         }
174         catch (SQLException ex)
175         {
176             throw ExceptionHelper.generateException("Can't prepare statement:", ex, sql, log);
177         }
178         return stmt;
179     }
180 
181     public PreparedStatement getSelectByPKStmt(Connection con) throws SQLException
182     {
183         if (selectByPKSql == null)
184         {
185             selectByPKSql = sqlGenerator.getPreparedSelectByPkStatement(classDescriptor).getStatement();
186         }
187         try
188         {
189             return prepareStatement(con, selectByPKSql, Query.NOT_SCROLLABLE, true, 1);
190         }
191         catch (SQLException ex)
192         {
193             log.error(ex);
194             throw ex;
195         }
196     }
197 
198     /**
199      * Answer true if a PreparedStatement has to be used
200      * <br>false for a CallableStatement
201      */
202     protected boolean usePreparedUpdateStatement()
203     {
204         return !(classDescriptor.getUpdateProcedure() != null &&
205                 classDescriptor.getUpdateProcedure().hasReturnValues());
206     }
207 
208     public PreparedStatement getUpdateStmt(Connection con) throws SQLException
209     {
210         if (updateSql == null)
211         {
212             updateSql = sqlGenerator.getPreparedUpdateStatement(classDescriptor).getStatement();
213         }
214         try
215         {
216             return prepareStatement(con,
217                     updateSql,
218                     Query.NOT_SCROLLABLE,
219                     usePreparedUpdateStatement(),
220                     StatementManagerIF.FETCH_SIZE_NOT_APPLICABLE);
221         }
222         catch (SQLException ex)
223         {
224             log.error("Can't prepare update statement: " + updateSql, ex);
225             throw ex;
226         }
227     }
228 
229     /**
230      * Prepares a statement with parameters that should work with most RDBMS.
231      *
232      * @param con the connection to utilize
233      * @param sql the sql syntax to use when creating the statement.
234      * @param scrollable determines if the statement will be scrollable.
235      * @param createPreparedStatement if <code>true</code>, then a
236      * {@link PreparedStatement} will be created. If <code>false</code>, then
237      * a {@link java.sql.CallableStatement} will be created.
238      * @param explicitFetchSizeHint will be used as fetchSize hint
239      * (if applicable) if > 0
240      *
241      * @return a statement that can be used to execute the syntax contained in
242      * the <code>sql</code> argument.
243      */
244     protected PreparedStatement prepareStatement(Connection con,
245                                                  String sql,
246                                                  boolean scrollable,
247                                                  boolean createPreparedStatement,
248                                                  int explicitFetchSizeHint)
249         throws SQLException
250     {
251         PreparedStatement result;
252 
253         // if a JDBC1.0 driver is used the signature
254         // prepareStatement(String, int, int) is  not defined.
255         // we then call the JDBC1.0 variant prepareStatement(String)
256         try
257         {
258             // if necessary use JDB1.0 methods
259             if (!FORCEJDBC1_0)
260             {
261                 if (createPreparedStatement)
262                 {
263                     result =
264                         con.prepareStatement(
265                             sql,
266                             scrollable
267                                 ? ResultSet.TYPE_SCROLL_INSENSITIVE
268                                 : ResultSet.TYPE_FORWARD_ONLY,
269                             ResultSet.CONCUR_READ_ONLY);
270                     afterJdbc2CapableStatementCreate(result, explicitFetchSizeHint);
271                 }
272                 else
273                 {
274                     result =
275                         con.prepareCall(
276                             sql,
277                             scrollable
278                                 ? ResultSet.TYPE_SCROLL_INSENSITIVE
279                                 : ResultSet.TYPE_FORWARD_ONLY,
280                             ResultSet.CONCUR_READ_ONLY);
281                 }
282             }
283             else
284             {
285                 if (createPreparedStatement)
286                 {
287                     result = con.prepareStatement(sql);
288                 }
289                 else
290                 {
291                     result = con.prepareCall(sql);
292                 }
293             }
294         }
295         catch (AbstractMethodError err)
296         {
297             // this exception is raised if Driver is not JDBC 2.0 compliant
298             log.warn("Used driver seems not JDBC 2.0 compatible, use the JDBC 1.0 mode", err);
299             if (createPreparedStatement)
300             {
301                 result = con.prepareStatement(sql);
302             }
303             else
304             {
305                 result = con.prepareCall(sql);
306             }
307             FORCEJDBC1_0 = true;
308         }
309         catch (SQLException eSql)
310         {
311             // there are JDBC Driver that nominally implement JDBC 2.0, but
312             // throw DriverNotCapableExceptions. If we catch one of these
313             // we force usage of JDBC 1.0
314             if (eSql
315                 .getClass()
316                 .getName()
317                 .equals("interbase.interclient.DriverNotCapableException"))
318             {
319                 log.warn("JDBC 2.0 problems with this interbase driver, we use the JDBC 1.0 mode");
320                 if (createPreparedStatement)
321                 {
322                     result = con.prepareStatement(sql);
323                 }
324                 else
325                 {
326                     result = con.prepareCall(sql);
327                 }
328                 FORCEJDBC1_0 = true;
329             }
330             else
331             {
332                 throw eSql;
333             }
334         }
335         try
336         {
337             if (!ProxyHelper.isNormalOjbProxy(result))  // tomdz: What about VirtualProxy
338             {
339                 platform.afterStatementCreate(result);
340             }
341         }
342         catch (PlatformException e)
343         {
344             log.error("Platform dependend failure", e);
345         }
346         return result;
347     }
348 
349     /**
350      * Creates a statement with parameters that should work with most RDBMS.
351      */
352     private Statement createStatement(Connection con, boolean scrollable, int explicitFetchSizeHint)
353         throws java.sql.SQLException
354     {
355         Statement result;
356         try
357         {
358             // if necessary use JDBC1.0 methods
359             if (!FORCEJDBC1_0)
360             {
361                 result =
362                     con.createStatement(
363                         scrollable
364                             ? ResultSet.TYPE_SCROLL_INSENSITIVE
365                             : ResultSet.TYPE_FORWARD_ONLY,
366                         ResultSet.CONCUR_READ_ONLY);
367                 afterJdbc2CapableStatementCreate(result, explicitFetchSizeHint);
368             }
369             else
370             {
371                 result = con.createStatement();
372             }
373         }
374         catch (AbstractMethodError err)
375         {
376             // if a JDBC1.0 driver is used, the signature
377             // createStatement(int, int) is  not defined.
378             // we then call the JDBC1.0 variant createStatement()
379             log.warn("Used driver seems not JDBC 2.0 compatible, use the JDBC 1.0 mode", err);
380             result = con.createStatement();
381             FORCEJDBC1_0 = true;
382         }
383         catch (SQLException eSql)
384         {
385             // there are JDBC Driver that nominally implement JDBC 2.0, but
386             // throw DriverNotCapableExceptions. If we catch one of these
387             // we force usage of JDBC 1.0
388             if (eSql.getClass().getName()
389                 .equals("interbase.interclient.DriverNotCapableException"))
390             {
391                 log.warn("JDBC 2.0 problems with this interbase driver, we use the JDBC 1.0 mode");
392                 FORCEJDBC1_0 = true;
393                 result = con.createStatement();
394             }
395             else
396             {
397                 throw eSql;
398             }
399         }
400         try
401         {
402             platform.afterStatementCreate(result);
403         }
404         catch (PlatformException e)
405         {
406             log.error("Platform dependend failure", e);
407         }
408         return result;
409     }
410 
411     private void afterJdbc2CapableStatementCreate(Statement stmt, int explicitFetchSizeHint)
412             throws SQLException
413     {
414         if (stmt != null)
415         {
416             final int fetchSizeHint;
417             if (explicitFetchSizeHint == StatementManagerIF.FETCH_SIZE_NOT_APPLICABLE)
418             {
419                 fetchSizeHint = StatementManagerIF.FETCH_SIZE_NOT_APPLICABLE;
420             }
421             else if (explicitFetchSizeHint != StatementManagerIF.FETCH_SIZE_NOT_EXPLICITLY_SET)
422             {
423                 fetchSizeHint = explicitFetchSizeHint; // specific for this Statement
424             }
425             else
426             {
427                 fetchSizeHint = fetchSize; // connection pool default
428             }
429             if (fetchSizeHint > 0)
430             {
431                 stmt.setFetchSize(fetchSize);
432             }
433         }
434     }
435 
436 }