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.22.2.7 2005/10/27 17:33:23 arminw Exp $
044: */
045: public class StatementsForClassImpl implements StatementsForClassIF {
046: private Logger log = LoggerFactory
047: .getLogger(StatementsForClassImpl.class);
048:
049: /**
050: * sets the escape processing mode
051: */
052: // protected static boolean ESCAPEPROCESSING = false;
053: protected final ClassDescriptor classDescriptor;
054: protected final SqlGenerator sqlGenerator;
055: protected final Platform platform;
056: protected final Class clazz;
057: protected final int fetchSize;
058: private String deleteSql;
059: private String insertSql;
060: private String updateSql;
061: private String selectByPKSql;
062:
063: /**
064: * force use of JDBC 1.0 statement creation
065: */
066: protected boolean FORCEJDBC1_0 = false;
067:
068: public StatementsForClassImpl(final JdbcConnectionDescriptor jcd,
069: final ClassDescriptor classDescriptor) {
070: this .classDescriptor = classDescriptor;
071: clazz = classDescriptor.getClassOfObject();
072: platform = PlatformFactory.getPlatformFor(jcd);
073: sqlGenerator = SqlGeneratorFactory.getInstance()
074: .createSqlGenerator(platform);
075:
076: final ConnectionPoolDescriptor cpd = jcd
077: .getConnectionPoolDescriptor();
078: fetchSize = cpd.getFetchSize();
079:
080: // detect JDBC level
081: double level = jcd.getJdbcLevel();
082: FORCEJDBC1_0 = level == 1.0;
083: }
084:
085: /**
086: * Answer true if a PreparedStatement has to be used
087: * <br>false for a CallableStatement
088: */
089: protected boolean usePreparedDeleteStatement() {
090: return !(classDescriptor.getDeleteProcedure() != null && classDescriptor
091: .getDeleteProcedure().hasReturnValues());
092: }
093:
094: public PreparedStatement getDeleteStmt(Connection con)
095: throws SQLException {
096: if (deleteSql == null) {
097: deleteSql = sqlGenerator.getPreparedDeleteStatement(
098: classDescriptor).getStatement();
099: }
100: try {
101: return prepareStatement(con, deleteSql,
102: Query.NOT_SCROLLABLE, usePreparedDeleteStatement(),
103: StatementManagerIF.FETCH_SIZE_NOT_APPLICABLE);
104: } catch (SQLException ex) {
105: log.error("Can't prepare delete statement: " + deleteSql,
106: ex);
107: throw ex;
108: }
109: }
110:
111: public Statement getGenericStmt(Connection con, boolean scrollable)
112: throws PersistenceBrokerSQLException {
113: Statement stmt;
114: try {
115: stmt = createStatement(con, scrollable,
116: StatementManagerIF.FETCH_SIZE_NOT_EXPLICITLY_SET);
117: } catch (SQLException ex) {
118: throw ExceptionHelper.generateException(
119: "Can't prepare statement:", ex, null, log);
120: }
121: return stmt;
122: }
123:
124: /**
125: * Answer true if a PreparedStatement has to be used
126: * <br>false for a CallableStatement
127: */
128: protected boolean usePreparedInsertStatement() {
129: return !(classDescriptor.getInsertProcedure() != null && classDescriptor
130: .getInsertProcedure().hasReturnValues());
131: }
132:
133: public PreparedStatement getInsertStmt(Connection con)
134: throws SQLException {
135: if (insertSql == null) {
136: insertSql = sqlGenerator.getPreparedInsertStatement(
137: classDescriptor).getStatement();
138: }
139: try {
140: return prepareStatement(con, insertSql,
141: Query.NOT_SCROLLABLE, usePreparedInsertStatement(),
142: StatementManagerIF.FETCH_SIZE_NOT_APPLICABLE);
143: } catch (SQLException ex) {
144: log.error("Can't prepare insert statement: " + insertSql,
145: ex);
146: throw ex;
147: }
148: }
149:
150: public PreparedStatement getPreparedStmt(Connection con,
151: String sql, boolean scrollable, int explicitFetchSizeHint,
152: boolean callableStmt) throws PersistenceBrokerSQLException {
153: PreparedStatement stmt;
154: try {
155: stmt = prepareStatement(con, sql, scrollable,
156: !callableStmt, explicitFetchSizeHint);
157: } catch (SQLException ex) {
158: throw ExceptionHelper.generateException(
159: "Can't prepare statement:", ex, sql, log);
160: }
161: return stmt;
162: }
163:
164: public PreparedStatement getSelectByPKStmt(Connection con)
165: throws SQLException {
166: if (selectByPKSql == null) {
167: selectByPKSql = sqlGenerator
168: .getPreparedSelectByPkStatement(classDescriptor)
169: .getStatement();
170: }
171: try {
172: return prepareStatement(con, selectByPKSql,
173: Query.NOT_SCROLLABLE, true, 1);
174: } catch (SQLException ex) {
175: log.error(ex);
176: throw ex;
177: }
178: }
179:
180: /**
181: * Answer true if a PreparedStatement has to be used
182: * <br>false for a CallableStatement
183: */
184: protected boolean usePreparedUpdateStatement() {
185: return !(classDescriptor.getUpdateProcedure() != null && classDescriptor
186: .getUpdateProcedure().hasReturnValues());
187: }
188:
189: public PreparedStatement getUpdateStmt(Connection con)
190: throws SQLException {
191: if (updateSql == null) {
192: updateSql = sqlGenerator.getPreparedUpdateStatement(
193: classDescriptor).getStatement();
194: }
195: try {
196: return prepareStatement(con, updateSql,
197: Query.NOT_SCROLLABLE, usePreparedUpdateStatement(),
198: StatementManagerIF.FETCH_SIZE_NOT_APPLICABLE);
199: } catch (SQLException ex) {
200: log.error("Can't prepare update statement: " + updateSql,
201: ex);
202: throw ex;
203: }
204: }
205:
206: /**
207: * Prepares a statement with parameters that should work with most RDBMS.
208: *
209: * @param con the connection to utilize
210: * @param sql the sql syntax to use when creating the statement.
211: * @param scrollable determines if the statement will be scrollable.
212: * @param createPreparedStatement if <code>true</code>, then a
213: * {@link PreparedStatement} will be created. If <code>false</code>, then
214: * a {@link java.sql.CallableStatement} will be created.
215: * @param explicitFetchSizeHint will be used as fetchSize hint
216: * (if applicable) if > 0
217: *
218: * @return a statement that can be used to execute the syntax contained in
219: * the <code>sql</code> argument.
220: */
221: protected PreparedStatement prepareStatement(Connection con,
222: String sql, boolean scrollable,
223: boolean createPreparedStatement, int explicitFetchSizeHint)
224: throws SQLException {
225: PreparedStatement result;
226:
227: // if a JDBC1.0 driver is used the signature
228: // prepareStatement(String, int, int) is not defined.
229: // we then call the JDBC1.0 variant prepareStatement(String)
230: try {
231: // if necessary use JDB1.0 methods
232: if (!FORCEJDBC1_0) {
233: if (createPreparedStatement) {
234: result = con
235: .prepareStatement(
236: sql,
237: scrollable ? ResultSet.TYPE_SCROLL_INSENSITIVE
238: : ResultSet.TYPE_FORWARD_ONLY,
239: ResultSet.CONCUR_READ_ONLY);
240: afterJdbc2CapableStatementCreate(result,
241: explicitFetchSizeHint);
242: } else {
243: result = con
244: .prepareCall(
245: sql,
246: scrollable ? ResultSet.TYPE_SCROLL_INSENSITIVE
247: : ResultSet.TYPE_FORWARD_ONLY,
248: ResultSet.CONCUR_READ_ONLY);
249: }
250: } else {
251: if (createPreparedStatement) {
252: result = con.prepareStatement(sql);
253: } else {
254: result = con.prepareCall(sql);
255: }
256: }
257: } catch (AbstractMethodError err) {
258: // this exception is raised if Driver is not JDBC 2.0 compliant
259: log
260: .warn(
261: "Used driver seems not JDBC 2.0 compatible, use the JDBC 1.0 mode",
262: err);
263: if (createPreparedStatement) {
264: result = con.prepareStatement(sql);
265: } else {
266: result = con.prepareCall(sql);
267: }
268: FORCEJDBC1_0 = true;
269: } catch (SQLException eSql) {
270: // there are JDBC Driver that nominally implement JDBC 2.0, but
271: // throw DriverNotCapableExceptions. If we catch one of these
272: // we force usage of JDBC 1.0
273: if (eSql.getClass().getName().equals(
274: "interbase.interclient.DriverNotCapableException")) {
275: log
276: .warn("JDBC 2.0 problems with this interbase driver, we use the JDBC 1.0 mode");
277: if (createPreparedStatement) {
278: result = con.prepareStatement(sql);
279: } else {
280: result = con.prepareCall(sql);
281: }
282: FORCEJDBC1_0 = true;
283: } else {
284: throw eSql;
285: }
286: }
287: try {
288: if (!ProxyHelper.isNormalOjbProxy(result)) // tomdz: What about VirtualProxy
289: {
290: platform.afterStatementCreate(result);
291: }
292: } catch (PlatformException e) {
293: log.error("Platform dependend failure", e);
294: }
295: return result;
296: }
297:
298: /**
299: * Creates a statement with parameters that should work with most RDBMS.
300: */
301: private Statement createStatement(Connection con,
302: boolean scrollable, int explicitFetchSizeHint)
303: throws java.sql.SQLException {
304: Statement result;
305: try {
306: // if necessary use JDBC1.0 methods
307: if (!FORCEJDBC1_0) {
308: result = con.createStatement(
309: scrollable ? ResultSet.TYPE_SCROLL_INSENSITIVE
310: : ResultSet.TYPE_FORWARD_ONLY,
311: ResultSet.CONCUR_READ_ONLY);
312: afterJdbc2CapableStatementCreate(result,
313: explicitFetchSizeHint);
314: } else {
315: result = con.createStatement();
316: }
317: } catch (AbstractMethodError err) {
318: // if a JDBC1.0 driver is used, the signature
319: // createStatement(int, int) is not defined.
320: // we then call the JDBC1.0 variant createStatement()
321: log
322: .warn(
323: "Used driver seems not JDBC 2.0 compatible, use the JDBC 1.0 mode",
324: err);
325: result = con.createStatement();
326: FORCEJDBC1_0 = true;
327: } catch (SQLException eSql) {
328: // there are JDBC Driver that nominally implement JDBC 2.0, but
329: // throw DriverNotCapableExceptions. If we catch one of these
330: // we force usage of JDBC 1.0
331: if (eSql.getClass().getName().equals(
332: "interbase.interclient.DriverNotCapableException")) {
333: log
334: .warn("JDBC 2.0 problems with this interbase driver, we use the JDBC 1.0 mode");
335: FORCEJDBC1_0 = true;
336: result = con.createStatement();
337: } else {
338: throw eSql;
339: }
340: }
341: try {
342: platform.afterStatementCreate(result);
343: } catch (PlatformException e) {
344: log.error("Platform dependend failure", e);
345: }
346: return result;
347: }
348:
349: private void afterJdbc2CapableStatementCreate(Statement stmt,
350: int explicitFetchSizeHint) throws SQLException {
351: if (stmt != null) {
352: final int fetchSizeHint;
353: if (explicitFetchSizeHint == StatementManagerIF.FETCH_SIZE_NOT_APPLICABLE) {
354: fetchSizeHint = StatementManagerIF.FETCH_SIZE_NOT_APPLICABLE;
355: } else if (explicitFetchSizeHint != StatementManagerIF.FETCH_SIZE_NOT_EXPLICITLY_SET) {
356: fetchSizeHint = explicitFetchSizeHint; // specific for this Statement
357: } else {
358: fetchSizeHint = fetchSize; // connection pool default
359: }
360: if (fetchSizeHint > 0) {
361: stmt.setFetchSize(fetchSize);
362: }
363: }
364: }
365:
366: }
|