001: package org.apache.ojb.broker.platforms;
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.io.StringReader;
019: import java.sql.CallableStatement;
020: import java.sql.Connection;
021: import java.sql.DatabaseMetaData;
022: import java.sql.PreparedStatement;
023: import java.sql.ResultSet;
024: import java.sql.SQLException;
025: import java.sql.Statement;
026: import java.sql.Types;
027: import java.util.Properties;
028:
029: import org.apache.ojb.broker.PersistenceBrokerException;
030: import org.apache.ojb.broker.accesslayer.JoinSyntaxTypes;
031: import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
032: import org.apache.ojb.broker.query.LikeCriteria;
033: import org.apache.ojb.broker.util.logging.Logger;
034: import org.apache.ojb.broker.util.logging.LoggerFactory;
035:
036: /**
037: * This class is a concrete implementation of <code>Platform</code>. Provides default implementations for all
038: * methods declared in <code>Platform</code>.
039: * It is intended as a vanilla implementation and as baseclass for
040: * platform specific implementations.
041: *
042: * @version $Id: PlatformDefaultImpl.java,v 1.27.2.7 2005/12/18 16:43:19 tomdz Exp $
043: * @author Thomas Mahler
044: */
045: public class PlatformDefaultImpl implements Platform, JoinSyntaxTypes {
046: protected Logger log = LoggerFactory
047: .getLogger(PlatformDefaultImpl.class);
048: private static final String INITIALIZATION_CHECK_AUTOCOMMIT = "initializationCheck";
049: private static final String FALSE_STR = "false";
050:
051: protected boolean m_batchUpdatesChecked = false;
052: protected boolean m_supportsBatchUpdates = false;
053:
054: public boolean supportsBatchOperations() {
055: return m_supportsBatchUpdates;
056: }
057:
058: /**
059: * Sets platform information for if the jdbc driver/db combo support
060: * batch operations. Will only be checked once, then have same batch
061: * support setting for the entire session.
062: *
063: * @param conn
064: */
065: protected void checkForBatchSupport(Connection conn) {
066: if (!m_batchUpdatesChecked) {
067: DatabaseMetaData meta;
068: try {
069: meta = conn.getMetaData();
070: m_supportsBatchUpdates = meta.supportsBatchUpdates();
071: } catch (Throwable th) {
072: log.info("Batch support check failed", th);
073: m_supportsBatchUpdates = false;
074: } finally {
075: m_batchUpdatesChecked = true;
076: }
077: }
078: }
079:
080: public void afterStatementCreate(Statement stmt)
081: throws PlatformException {
082: //noop
083: }
084:
085: public void beforeStatementClose(Statement stmt, ResultSet rs)
086: throws PlatformException {
087: if (rs != null) {
088: try {
089: rs.close();
090: } catch (SQLException e) {
091: throw new PlatformException("Resultset closing failed",
092: e);
093: }
094: }
095: }
096:
097: public void afterStatementClose(Statement stmt, ResultSet rs)
098: throws PlatformException {
099: //nothing
100: }
101:
102: public void beforeBatch(PreparedStatement stmt)
103: throws PlatformException {
104: // nothing
105: }
106:
107: public void addBatch(PreparedStatement stmt)
108: throws PlatformException {
109: try {
110: stmt.addBatch();
111: } catch (SQLException e) {
112: throw new PlatformException(
113: "Failure while calling 'addBatch' on given Statement object",
114: e);
115: }
116: }
117:
118: public int[] executeBatch(PreparedStatement stmt)
119: throws PlatformException {
120: try {
121: return stmt.executeBatch();
122: } catch (SQLException e) {
123: throw new PlatformException(
124: "Failure while calling 'executeBatch' on given Statement object",
125: e);
126: }
127: }
128:
129: /**
130: * @see Platform#initializeJdbcConnection
131: */
132: public void initializeJdbcConnection(JdbcConnectionDescriptor jcd,
133: Connection conn) throws PlatformException {
134: if (jcd.getBatchMode())
135: checkForBatchSupport(conn);
136:
137: switch (jcd.getUseAutoCommit()) {
138: case JdbcConnectionDescriptor.AUTO_COMMIT_IGNORE_STATE:
139: // nothing to do
140: break;
141: case JdbcConnectionDescriptor.AUTO_COMMIT_SET_TRUE_AND_TEMPORARY_FALSE:
142: try {
143: /*
144: arminw:
145: workaround to be backward compatible. In future releases we shouldn't change the autocommit
146: state of a connection at initializing by the ConnectionFactory. The autocommit state should
147: only be changed by the ConnectionManager. We have to separate this stuff.
148: */
149: if (!jcd.getAttribute(INITIALIZATION_CHECK_AUTOCOMMIT,
150: FALSE_STR).equalsIgnoreCase(FALSE_STR)
151: && !conn.getAutoCommit()) {
152: conn.setAutoCommit(true);
153: }
154: } catch (SQLException e) {
155: if (!jcd.isIgnoreAutoCommitExceptions()) {
156: throw new PlatformException(
157: "Connection initializing: setAutoCommit(true) failed",
158: e);
159: } else {
160: log
161: .info("Connection initializing: setAutoCommit jdbc-driver problems. "
162: + e.getMessage());
163: }
164: }
165: break;
166: case JdbcConnectionDescriptor.AUTO_COMMIT_SET_FALSE:
167: try {
168: if (conn.getAutoCommit())
169: conn.setAutoCommit(false);
170: } catch (SQLException e) {
171: if (!jcd.isIgnoreAutoCommitExceptions()) {
172: throw new PlatformException(
173: "Connection initializing: setAutoCommit(false) failed",
174: e);
175: } else {
176: log
177: .info("Connection initializing: setAutoCommit jdbc-driver problems. "
178: + e.getMessage());
179: }
180: }
181: break;
182: }
183: }
184:
185: public void changeAutoCommitState(JdbcConnectionDescriptor jcd,
186: Connection con, boolean newState) {
187: if (con == null) {
188: log
189: .error("Given m_connection was null, cannot prepare autoCommit state");
190: return;
191: }
192: if (JdbcConnectionDescriptor.AUTO_COMMIT_SET_TRUE_AND_TEMPORARY_FALSE == jcd
193: .getUseAutoCommit()) {
194: try {
195: con.setAutoCommit(newState);
196: } catch (SQLException e) {
197: if (jcd.isIgnoreAutoCommitExceptions()) {
198: log.info("Set autoCommit(" + newState
199: + ") failed: " + e.getMessage());
200: } else {
201: log.error(
202: "Set autoCommit(" + newState + ") failed",
203: e);
204: throw new PersistenceBrokerException(
205: "Set autoCommit(false) failed", e);
206: }
207: }
208: }
209: }
210:
211: /*
212: * @see Platform#setObject(PreparedStatement, int, Object, int)
213: */
214: public void setObjectForStatement(PreparedStatement ps, int index,
215: Object value, int sqlType) throws SQLException {
216: if ((sqlType == Types.LONGVARCHAR) && (value instanceof String)) {
217: String s = (String) value;
218: ps.setCharacterStream(index, new StringReader(s), s
219: .length());
220: }
221: /*
222: PATCH for BigDecimal truncation problem. Seems that several databases (e.g. DB2, Sybase)
223: has problem with BigDecimal fields if the sql-type was set. The problem was discussed here
224: http://nagoya.apache.org/eyebrowse/ReadMsg?listName=ojb-user@db.apache.org&msgNo=14113
225: A better option will be
226: <snip>
227: else if ((value instanceof BigDecimal) && (sqlType == Types.DECIMAL
228: || sqlType == Types.NUMERIC))
229: {
230: ps.setObject(index, value, sqlType,
231: ((BigDecimal) value).scale());
232: }
233: </snip>
234: But this way maxDB/sapDB does not work correct, so we use the most flexible solution
235: and let the jdbc-driver handle BigDecimal objects by itself.
236: */
237: else if (sqlType == Types.DECIMAL || sqlType == Types.NUMERIC) {
238: ps.setObject(index, value);
239: } else {
240: // arminw: this method call is done very, very often, so we can improve performance
241: // by comment out this section
242: // if (log.isDebugEnabled()) {
243: // log.debug("Default setObjectForStatement, sqlType=" + sqlType +
244: // ", value class=" + (value == null ? "NULL!" : value.getClass().getName())
245: // + ", value=" + value);
246: // }
247: ps.setObject(index, value, sqlType);
248: }
249: }
250:
251: /*
252: * @see Platform#setNullForStatement(PreparedStatement, int, int)
253: */
254: public void setNullForStatement(PreparedStatement ps, int index,
255: int sqlType) throws SQLException {
256: ps.setNull(index, sqlType);
257: }
258:
259: /**
260: * Get join syntax type for this RDBMS - one on of the constants from JoinSyntaxType interface
261: *
262: * @see Platform#getJoinSyntaxType
263: */
264: public byte getJoinSyntaxType() {
265: return SQL92_JOIN_SYNTAX;
266: }
267:
268: /**
269: * Override default ResultSet size determination (rs.last();rs.getRow())
270: * with select count(*) operation
271: *
272: * @see Platform#useCountForResultsetSize()
273: */
274: public boolean useCountForResultsetSize() {
275: return false;
276: }
277:
278: public String createSequenceQuery(String sequenceName,
279: Properties prop) {
280: return createSequenceQuery(sequenceName);
281: }
282:
283: /**
284: * Override this method to enable database based sequence generation
285: */
286: public String createSequenceQuery(String sequenceName) {
287: /*default implementation does not support this*/
288: throw new UnsupportedOperationException(
289: "This feature is not supported by this implementation");
290: }
291:
292: /**
293: * Override this method to enable database based sequence generation
294: */
295: public String nextSequenceQuery(String sequenceName) {
296: /*default implementation does not support this*/
297: throw new UnsupportedOperationException(
298: "This feature is not supported by this implementation");
299: }
300:
301: /**
302: * Override this method to enable database based sequence generation
303: */
304: public String dropSequenceQuery(String sequenceName) {
305: /*default implementation does not support this*/
306: throw new UnsupportedOperationException(
307: "This feature is not supported by this implementation");
308: }
309:
310: public CallableStatement prepareNextValProcedureStatement(
311: Connection con, String procedureName, String sequenceName)
312: throws PlatformException {
313: /*@todo implementation*/
314: throw new UnsupportedOperationException(
315: "Not supported by this implementation");
316: }
317:
318: public String getLastInsertIdentityQuery(String tableName) {
319: /*@todo implementation*/
320: throw new UnsupportedOperationException(
321: "This feature is not supported by this implementation");
322: }
323:
324: /**
325: * @see org.apache.ojb.broker.platforms.Platform#addPagingSql(java.lang.StringBuffer)
326: */
327: public void addPagingSql(StringBuffer anSqlString) {
328: // do nothing
329: }
330:
331: /**
332: * @see org.apache.ojb.broker.platforms.Platform#bindPagingParametersFirst()
333: */
334: public boolean bindPagingParametersFirst() {
335: return false;
336: }
337:
338: /**
339: * @see org.apache.ojb.broker.platforms.Platform#supportsPaging()
340: */
341: public boolean supportsPaging() {
342: return false;
343: }
344:
345: /**
346: * @see org.apache.ojb.broker.platforms.Platform#bindPagingParameters(java.sql.PreparedStatement, int, int, int)
347: */
348: public int bindPagingParameters(PreparedStatement ps, int index,
349: int startAt, int endAt) throws SQLException {
350: ps.setInt(index, startAt - 1); // zero based start
351: index++;
352: ps.setInt(index, endAt - (startAt - 1)); // number of rows to fetch
353: index++;
354: return index;
355: }
356:
357: /**
358: * Answer the Character for Concatenation
359: */
360: protected String getConcatenationCharacter() {
361: return "||";
362: }
363:
364: /**
365: * {@inheritDoc}
366: */
367: public boolean supportsMultiColumnCountDistinct() {
368: return true;
369: }
370:
371: /**
372: * @see org.apache.ojb.broker.platforms.Platform#concatenate(java.lang.String[])
373: */
374: public String concatenate(String[] theColumns) {
375: if (theColumns.length == 1) {
376: return theColumns[0];
377: }
378:
379: StringBuffer buf = new StringBuffer();
380: String concatChar = getConcatenationCharacter();
381:
382: for (int i = 0; i < theColumns.length; i++) {
383: if (i > 0) {
384: buf.append(" ").append(concatChar).append(" ");
385: }
386: buf.append(theColumns[i]);
387: }
388:
389: return buf.toString();
390: }
391:
392: /**
393: * @see org.apache.ojb.broker.platforms.Platform#getEscapeClause(org.apache.ojb.broker.query.LikeCriteria)
394: */
395: public String getEscapeClause(LikeCriteria aCriteria) {
396: String value = (String) aCriteria.getValue();
397: char escapeChar = LikeCriteria.getEscapeCharacter();
398:
399: if (value.indexOf(escapeChar) >= 0) {
400: return " ESCAPE '" + escapeChar + "'";
401: } else {
402: return "";
403: }
404: }
405:
406: /**
407: * @see org.apache.ojb.broker.platforms.Platform#registerOutResultSet(java.sql.CallableStatement, int)
408: */
409: public void registerOutResultSet(CallableStatement stmt,
410: int position) throws SQLException {
411: stmt.registerOutParameter(position, Types.OTHER);
412: }
413: }
|