1. /*
  2. * Copyright 2002-2004 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.springframework.jdbc.support;
  17. import java.sql.Connection;
  18. import java.sql.ResultSet;
  19. import java.sql.SQLException;
  20. import java.sql.Statement;
  21. import java.sql.Types;
  22. import javax.sql.DataSource;
  23. import org.apache.commons.logging.Log;
  24. import org.apache.commons.logging.LogFactory;
  25. import org.springframework.dao.DataAccessException;
  26. import org.springframework.jdbc.CannotGetJdbcConnectionException;
  27. import org.springframework.jdbc.datasource.DataSourceUtils;
  28. /**
  29. * Utility methods for SQL statements.
  30. * @author Isabelle Muszynski
  31. * @author Thomas Risberg
  32. * @author Juergen Hoeller
  33. * @version $Id: JdbcUtils.java,v 1.9 2004/04/22 07:45:02 jhoeller Exp $
  34. */
  35. public class JdbcUtils {
  36. private static final Log logger = LogFactory.getLog(JdbcUtils.class);
  37. /**
  38. * Close the given JDBC Statement and ignore any thrown exception.
  39. * This is useful for typical finally blocks in manual JDBC code.
  40. * @param stmt the JDBC Statement to close
  41. */
  42. public static void closeStatement(Statement stmt) {
  43. if (stmt != null) {
  44. try {
  45. stmt.close();
  46. }
  47. catch (SQLException ex) {
  48. logger.warn("Could not close JDBC Statement", ex);
  49. }
  50. }
  51. }
  52. /**
  53. * Close the given JDBC ResultSet and ignore any thrown exception.
  54. * This is useful for typical finally blocks in manual JDBC code.
  55. * @param rs the JDBC ResultSet to close
  56. */
  57. public static void closeResultSet(ResultSet rs) {
  58. if (rs != null) {
  59. try {
  60. rs.close();
  61. }
  62. catch (SQLException ex) {
  63. logger.warn("Could not close JDBC ResultSet", ex);
  64. }
  65. }
  66. }
  67. /**
  68. * Extract database meta data.
  69. * <p>This method will open a connection to the database and retrieve the database metadata.
  70. * Since this method is called before the exception translation feature is configured for
  71. * a datasource, this method can not rely on the SQLException translation functionality.
  72. * <p>Any exceptions will be wrapped in a MetaDataAccessException. This is a checked exception
  73. * and any calling code should catch and handle this exception. You can just log the
  74. * error and hope for the best, but there is probably a more serious error that will
  75. * reappear when you try to access the database again.
  76. * @param dataSource the DataSource to use
  77. * @param action callback that will do the actual work
  78. * @return object containing the extracted information
  79. */
  80. public static Object extractDatabaseMetaData(DataSource dataSource, DatabaseMetaDataCallback action)
  81. throws MetaDataAccessException {
  82. Connection con = null;
  83. try {
  84. con = DataSourceUtils.getConnection(dataSource);
  85. if (con != null) {
  86. return action.processMetaData(con.getMetaData());
  87. }
  88. else {
  89. throw new MetaDataAccessException("Error while getting connection");
  90. }
  91. }
  92. catch (CannotGetJdbcConnectionException ex) {
  93. //throw checked exception - we don't want this to be fatal?
  94. throw new MetaDataAccessException("Error while getting connection",ex);
  95. }
  96. catch (DataAccessException ex) {
  97. //throw checked exception - we don't want this to be fatal?
  98. throw new MetaDataAccessException("Error while extracting DatabaseMetaData",ex);
  99. }
  100. catch (SQLException ex) {
  101. //throw checked exception - we don't want this to be fatal?
  102. throw new MetaDataAccessException("Error while extracting DatabaseMetaData",ex);
  103. }
  104. finally {
  105. DataSourceUtils.closeConnectionIfNecessary(con, dataSource);
  106. }
  107. }
  108. /**
  109. * Count the occurrences of the character <code>placeholder</code> in an SQL string
  110. * <code>str</code>. The character <code>placeholder</code> is not counted if it
  111. * appears within a literal as determined by the <code>delim</code> that is passed in.
  112. * <p>Examples: If the delimiter is the single quote, and the character to count the
  113. * occurrences of is the question mark, then:
  114. * <p><code>The big ? 'bad wolf?'</code> gives a count of one.<br>
  115. * <code>The big ?? bad wolf</code> gives a count of two.<br>
  116. * <code>The big 'ba''ad?' ? wolf</code> gives a count of one.
  117. * <p>The grammar of the string passed in should obey the rules of the JDBC spec
  118. * which is close to no rules at all: one placeholder per parameter, and it should
  119. * be valid SQL for the target database.
  120. * @param str string to search in. Returns 0 if this is null
  121. * @param placeholder the character to search for and count.
  122. * @param delim the delimiter for character literals.
  123. */
  124. public static int countParameterPlaceholders(String str, char placeholder, char delim) {
  125. int count = 0;
  126. boolean insideLiteral = false;
  127. for (int i = 0; str != null && i < str.length(); i++) {
  128. if (str.charAt(i) == placeholder) {
  129. if (!insideLiteral)
  130. count++;
  131. }
  132. else {
  133. if (str.charAt(i) == delim) {
  134. insideLiteral = insideLiteral ^ true;
  135. }
  136. }
  137. }
  138. return count;
  139. }
  140. /**
  141. * Check that a SQL type is numeric.
  142. * @param sqlType the SQL type to be checked
  143. * @return if the type is numeric
  144. */
  145. public static boolean isNumeric(int sqlType) {
  146. return Types.BIT == sqlType || Types.BIGINT == sqlType || Types.DECIMAL == sqlType ||
  147. Types.DOUBLE == sqlType || Types.FLOAT == sqlType || Types.INTEGER == sqlType ||
  148. Types.NUMERIC == sqlType || Types.REAL == sqlType || Types.SMALLINT == sqlType ||
  149. Types.TINYINT == sqlType;
  150. }
  151. /**
  152. * Translate a SQL type into one of a few values:
  153. * All integer types are translated to Integer.
  154. * All real types are translated to Double.
  155. * All string types are translated to String.
  156. * All other types are left untouched.
  157. * @param sqlType the type to be translated into a simpler type
  158. * @return the new SQL type
  159. */
  160. public static int translateType(int sqlType) {
  161. int retType = sqlType;
  162. if (Types.BIT == sqlType || Types.TINYINT == sqlType || Types.SMALLINT == sqlType ||
  163. Types.INTEGER == sqlType) {
  164. retType = Types.INTEGER;
  165. }
  166. else if (Types.CHAR == sqlType || Types.VARCHAR == sqlType) {
  167. retType = Types.VARCHAR;
  168. }
  169. else if (Types.DECIMAL == sqlType || Types.DOUBLE == sqlType || Types.FLOAT == sqlType ||
  170. Types.NUMERIC == sqlType || Types.REAL == sqlType) {
  171. retType = Types.NUMERIC;
  172. }
  173. return retType;
  174. }
  175. }