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