001: package liquibase.util;
002:
003: import liquibase.exception.JDBCException;
004:
005: import java.sql.*;
006: import java.util.Collection;
007:
008: public abstract class JdbcUtils {
009:
010: /**
011: * Constant that indicates an unknown (or unspecified) SQL type.
012: *
013: * @see java.sql.Types
014: */
015: public static final int TYPE_UNKNOWN = Integer.MIN_VALUE;
016:
017: /**
018: * Close the given JDBC Statement and ignore any thrown exception.
019: * This is useful for typical finally blocks in manual JDBC code.
020: *
021: * @param stmt the JDBC Statement to close (may be <code>null</code>)
022: */
023: public static void closeStatement(Statement stmt) {
024: if (stmt != null) {
025: try {
026: stmt.close();
027: } catch (SQLException ex) {
028: // logger.debug("Could not close JDBC Statement", ex);
029: } catch (Throwable ex) {
030: // We don't trust the JDBC driver: It might throw RuntimeException or Error.
031: // logger.debug("Unexpected exception on closing JDBC Statement", ex);
032: }
033: }
034: }
035:
036: /**
037: * Close the given JDBC ResultSet and ignore any thrown exception.
038: * This is useful for typical finally blocks in manual JDBC code.
039: *
040: * @param rs the JDBC ResultSet to close (may be <code>null</code>)
041: */
042: public static void closeResultSet(ResultSet rs) {
043: if (rs != null) {
044: try {
045: rs.close();
046: } catch (SQLException ex) {
047: // logger.debug("Could not close JDBC ResultSet", ex);
048: } catch (Throwable ex) {
049: // We don't trust the JDBC driver: It might throw RuntimeException or Error.
050: // logger.debug("Unexpected exception on closing JDBC ResultSet", ex);
051: }
052: }
053: }
054:
055: /**
056: * Retrieve a JDBC column value from a ResultSet, using the most appropriate
057: * value type. The returned value should be a detached value object, not having
058: * any ties to the active ResultSet: in particular, it should not be a Blob or
059: * Clob object but rather a byte array respectively String representation.
060: * <p>Uses the <code>getObject(index)</code> method, but includes additional "hacks"
061: * to get around Oracle 10g returning a non-standard object for its TIMESTAMP
062: * datatype and a <code>java.sql.Date</code> for DATE columns leaving out the
063: * time portion: These columns will explicitly be extracted as standard
064: * <code>java.sql.Timestamp</code> object.
065: *
066: * @param rs is the ResultSet holding the data
067: * @param index is the column index
068: * @return the value object
069: * @throws SQLException if thrown by the JDBC API
070: * @see java.sql.Blob
071: * @see java.sql.Clob
072: * @see java.sql.Timestamp
073: */
074: public static Object getResultSetValue(ResultSet rs, int index)
075: throws SQLException {
076: Object obj = rs.getObject(index);
077: if (obj instanceof Blob) {
078: obj = rs.getBytes(index);
079: } else if (obj instanceof Clob) {
080: obj = rs.getString(index);
081: } else if (obj != null
082: && obj.getClass().getName().startsWith(
083: "oracle.sql.TIMESTAMP")) {
084: obj = rs.getTimestamp(index);
085: } else if (obj != null
086: && obj.getClass().getName().startsWith(
087: "oracle.sql.DATE")) {
088: String metaDataClassName = rs.getMetaData()
089: .getColumnClassName(index);
090: if ("java.sql.Timestamp".equals(metaDataClassName)
091: || "oracle.sql.TIMESTAMP".equals(metaDataClassName)) {
092: obj = rs.getTimestamp(index);
093: } else {
094: obj = rs.getDate(index);
095: }
096: } else if (obj != null && obj instanceof java.sql.Date) {
097: if ("java.sql.Timestamp".equals(rs.getMetaData()
098: .getColumnClassName(index))) {
099: obj = rs.getTimestamp(index);
100: }
101: }
102: return obj;
103: }
104:
105: /**
106: * Check whether the given SQL type is numeric.
107: *
108: * @param sqlType the SQL type to be checked
109: * @return whether the type is numeric
110: */
111: public static boolean isNumeric(int sqlType) {
112: return Types.BIT == sqlType || Types.BIGINT == sqlType
113: || Types.DECIMAL == sqlType || Types.DOUBLE == sqlType
114: || Types.FLOAT == sqlType || Types.INTEGER == sqlType
115: || Types.NUMERIC == sqlType || Types.REAL == sqlType
116: || Types.SMALLINT == sqlType
117: || Types.TINYINT == sqlType;
118: }
119:
120: /**
121: * Return a single result object from the given Collection.
122: * <p>Throws an exception if 0 or more than 1 element found.
123: * @param results the result Collection (can be <code>null</code>)
124: * @return the single result object
125: */
126: public static Object requiredSingleResult(Collection results)
127: throws JDBCException {
128: int size = (results != null ? results.size() : 0);
129: if (size == 0) {
130: throw new JDBCException(
131: "Empty result set, expected one row");
132: }
133: if (results.size() > 1) {
134: throw new JDBCException("Result set larger than one row");
135: }
136: return results.iterator().next();
137: }
138:
139: }
|