001 package org.apache.ojb.broker.accesslayer;
002
003 /* Copyright 2002-2005 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 import java.sql.Connection;
019 import java.sql.PreparedStatement;
020 import java.sql.ResultSet;
021 import java.sql.SQLException;
022 import java.sql.Statement;
023
024 import org.apache.ojb.broker.PersistenceBrokerSQLException;
025 import org.apache.ojb.broker.accesslayer.sql.SqlGenerator;
026 import org.apache.ojb.broker.accesslayer.sql.SqlGeneratorFactory;
027 import org.apache.ojb.broker.core.proxy.ProxyHelper;
028 import org.apache.ojb.broker.metadata.ClassDescriptor;
029 import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
030 import org.apache.ojb.broker.metadata.ConnectionPoolDescriptor;
031 import org.apache.ojb.broker.platforms.Platform;
032 import org.apache.ojb.broker.platforms.PlatformException;
033 import org.apache.ojb.broker.platforms.PlatformFactory;
034 import org.apache.ojb.broker.query.Query;
035 import org.apache.ojb.broker.util.logging.Logger;
036 import org.apache.ojb.broker.util.logging.LoggerFactory;
037 import org.apache.ojb.broker.util.ExceptionHelper;
038
039 /**
040 * This class serves as a cache for Statements that are
041 * used for persistence operations on a given class.
042 * @author Thomas Mahler
043 * @version $Id: StatementsForClassImpl.java,v 1.1 2007-08-24 22:17:30 ewestfal Exp $
044 */
045 public class StatementsForClassImpl implements StatementsForClassIF
046 {
047 private Logger log = LoggerFactory.getLogger(StatementsForClassImpl.class);
048
049 /**
050 * sets the escape processing mode
051 */
052 // protected static boolean ESCAPEPROCESSING = false;
053
054 protected final ClassDescriptor classDescriptor;
055 protected final SqlGenerator sqlGenerator;
056 protected final Platform platform;
057 protected final Class clazz;
058 protected final int fetchSize;
059 private String deleteSql;
060 private String insertSql;
061 private String updateSql;
062 private String selectByPKSql;
063
064 /**
065 * force use of JDBC 1.0 statement creation
066 */
067 protected boolean FORCEJDBC1_0 = false;
068
069 public StatementsForClassImpl(
070 final JdbcConnectionDescriptor jcd,
071 final ClassDescriptor classDescriptor)
072 {
073 this.classDescriptor = classDescriptor;
074 clazz = classDescriptor.getClassOfObject();
075 platform = PlatformFactory.getPlatformFor(jcd);
076 sqlGenerator = SqlGeneratorFactory.getInstance().createSqlGenerator(platform);
077
078 final ConnectionPoolDescriptor cpd = jcd.getConnectionPoolDescriptor();
079 fetchSize = cpd.getFetchSize();
080
081 // detect JDBC level
082 double level = jcd.getJdbcLevel();
083 FORCEJDBC1_0 = level == 1.0;
084 }
085
086 /**
087 * Answer true if a PreparedStatement has to be used
088 * <br>false for a CallableStatement
089 */
090 protected boolean usePreparedDeleteStatement()
091 {
092 return !(classDescriptor.getDeleteProcedure() != null &&
093 classDescriptor.getDeleteProcedure().hasReturnValues());
094 }
095
096 public PreparedStatement getDeleteStmt(Connection con) throws SQLException
097 {
098 if (deleteSql == null)
099 {
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 }