View Javadoc

1   package org.apache.ojb.broker.platforms;
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.io.StringReader;
19  import java.sql.CallableStatement;
20  import java.sql.Connection;
21  import java.sql.DatabaseMetaData;
22  import java.sql.PreparedStatement;
23  import java.sql.ResultSet;
24  import java.sql.SQLException;
25  import java.sql.Statement;
26  import java.sql.Types;
27  import java.util.Properties;
28  
29  import org.apache.ojb.broker.PersistenceBrokerException;
30  import org.apache.ojb.broker.accesslayer.JoinSyntaxTypes;
31  import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
32  import org.apache.ojb.broker.query.LikeCriteria;
33  import org.apache.ojb.broker.util.logging.Logger;
34  import org.apache.ojb.broker.util.logging.LoggerFactory;
35  
36  /**
37   * This class is a concrete implementation of <code>Platform</code>. Provides default implementations for all
38   * methods declared in <code>Platform</code>.
39   * It is intended as a vanilla implementation and as baseclass for
40   * platform specific implementations.
41   *
42   * @version $Id: PlatformDefaultImpl.java,v 1.1 2007-08-24 22:17:35 ewestfal Exp $
43   * @author		Thomas Mahler
44   */
45  public class PlatformDefaultImpl implements Platform, JoinSyntaxTypes
46  {
47      protected Logger log = LoggerFactory.getLogger(PlatformDefaultImpl.class);
48      private static final String INITIALIZATION_CHECK_AUTOCOMMIT = "initializationCheck";
49      private static final String FALSE_STR = "false";
50  
51      protected boolean m_batchUpdatesChecked = false;
52      protected boolean m_supportsBatchUpdates = false;
53  
54      public boolean supportsBatchOperations()
55      {
56          return m_supportsBatchUpdates;
57      }
58  
59      /**
60       * Sets platform information for if the jdbc driver/db combo support
61       * batch operations. Will only be checked once, then have same batch
62       * support setting for the entire session.
63       *
64       * @param conn
65       */
66      protected void checkForBatchSupport(Connection conn)
67      {
68          if (!m_batchUpdatesChecked)
69          {
70              DatabaseMetaData meta;
71              try
72              {
73                  meta = conn.getMetaData();
74                  m_supportsBatchUpdates = meta.supportsBatchUpdates();
75              }
76              catch (Throwable th)
77              {
78                  log.info("Batch support check failed", th);
79                  m_supportsBatchUpdates = false;
80              }
81              finally
82              {
83                  m_batchUpdatesChecked = true;
84              }
85          }
86      }
87  
88      public void afterStatementCreate(Statement stmt) throws PlatformException
89      {
90          //noop
91      }
92  
93      public void beforeStatementClose(Statement stmt, ResultSet rs) throws PlatformException
94      {
95          if (rs != null)
96          {
97              try
98              {
99                  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 }