001: package liquibase.database.template;
002:
003: import liquibase.database.Database;
004: import liquibase.database.DatabaseConnection;
005: import liquibase.database.sql.CallableSqlStatement;
006: import liquibase.database.sql.SqlStatement;
007: import liquibase.exception.JDBCException;
008: import liquibase.log.LogFactory;
009: import liquibase.util.JdbcUtils;
010:
011: import java.sql.CallableStatement;
012: import java.sql.ResultSet;
013: import java.sql.SQLException;
014: import java.sql.Statement;
015: import java.util.ArrayList;
016: import java.util.HashMap;
017: import java.util.List;
018: import java.util.Map;
019:
020: /**
021: * Class to simplify execution of SqlStatements. Based heavily on <a href="http://static.springframework.org/spring/docs/2.0.x/reference/jdbc.html">Spring's JdbcTemplate</a>.
022: * <br><br>
023: * <b>Note: This class is currently intended for LiquiBase-internal use only and may change without notice in the future</b>
024: */
025: @SuppressWarnings({"unchecked"})
026: public class JdbcTemplate {
027:
028: protected Database database;
029:
030: public JdbcTemplate(Database database) {
031: this .database = database;
032: }
033:
034: public boolean executesStatements() {
035: return true;
036: }
037:
038: //-------------------------------------------------------------------------
039: // Methods dealing with static SQL (java.sql.Statement)
040: //-------------------------------------------------------------------------
041:
042: public Object execute(StatementCallback action)
043: throws JDBCException {
044: DatabaseConnection con = database.getConnection();
045: Statement stmt = null;
046: try {
047: stmt = con.createStatement();
048: Statement stmtToUse = stmt;
049:
050: return action.doInStatement(stmtToUse);
051: } catch (SQLException ex) {
052: // Release Connection early, to avoid potential connection pool deadlock
053: // in the case when the exception translator hasn't been initialized yet.
054: JdbcUtils.closeStatement(stmt);
055: stmt = null;
056: throw new JDBCException("Error executing SQL "
057: + action.getStatement().getSqlStatement(database),
058: ex);
059: } finally {
060: JdbcUtils.closeStatement(stmt);
061: }
062: }
063:
064: public void execute(final SqlStatement sql) throws JDBCException {
065: if (sql instanceof CallableSqlStatement) {
066: call(((CallableSqlStatement) sql), new ArrayList());
067: return;
068: }
069:
070: class ExecuteStatementCallback implements StatementCallback {
071: public Object doInStatement(Statement stmt)
072: throws SQLException, JDBCException {
073: stmt.execute(sql.getSqlStatement(database));
074: return null;
075: }
076:
077: public SqlStatement getStatement() {
078: return sql;
079: }
080: }
081: execute(new ExecuteStatementCallback());
082: }
083:
084: public Object query(final SqlStatement sql,
085: final ResultSetExtractor rse) throws JDBCException {
086: if (sql instanceof CallableSqlStatement) {
087: throw new JDBCException(
088: "Direct query using CallableSqlStatement not currently implemented");
089: }
090:
091: class QueryStatementCallback implements StatementCallback {
092: public Object doInStatement(Statement stmt)
093: throws SQLException, JDBCException {
094: ResultSet rs = null;
095: try {
096: rs = stmt.executeQuery(sql
097: .getSqlStatement(database));
098: ResultSet rsToUse = rs;
099: return rse.extractData(rsToUse);
100: } finally {
101: JdbcUtils.closeResultSet(rs);
102: }
103: }
104:
105: public SqlStatement getStatement() {
106: return sql;
107: }
108: }
109: return execute(new QueryStatementCallback());
110: }
111:
112: public List query(SqlStatement sql, RowMapper rowMapper)
113: throws JDBCException {
114: return (List) query(sql, new RowMapperResultSetExtractor(
115: rowMapper));
116: }
117:
118: public Object queryForObject(SqlStatement sql, RowMapper rowMapper)
119: throws JDBCException {
120: List results = query(sql, rowMapper);
121: return JdbcUtils.requiredSingleResult(results);
122: }
123:
124: public Object queryForObject(SqlStatement sql, Class requiredType)
125: throws JDBCException {
126: return queryForObject(sql,
127: getSingleColumnRowMapper(requiredType));
128: }
129:
130: public long queryForLong(SqlStatement sql) throws JDBCException {
131: Number number = (Number) queryForObject(sql, Long.class);
132: return (number != null ? number.longValue() : 0);
133: }
134:
135: public int queryForInt(SqlStatement sql) throws JDBCException {
136: Number number = (Number) queryForObject(sql, Integer.class);
137: return (number != null ? number.intValue() : 0);
138: }
139:
140: public List queryForList(SqlStatement sql, Class elementType)
141: throws JDBCException {
142: return query(sql, getSingleColumnRowMapper(elementType));
143: }
144:
145: public List<Map> queryForList(SqlStatement sql)
146: throws JDBCException {
147: //noinspection unchecked
148: return (List<Map>) query(sql, getColumnMapRowMapper());
149: }
150:
151: public int update(final SqlStatement sql) throws JDBCException {
152: if (sql instanceof CallableSqlStatement) {
153: throw new JDBCException(
154: "Direct update using CallableSqlStatement not currently implemented");
155: }
156:
157: class UpdateStatementCallback implements StatementCallback {
158: public Object doInStatement(Statement stmt)
159: throws SQLException, JDBCException {
160: return stmt
161: .executeUpdate(sql.getSqlStatement(database));
162: }
163:
164: public SqlStatement getStatement() {
165: return sql;
166: }
167: }
168: return (Integer) execute(new UpdateStatementCallback());
169: }
170:
171: //-------------------------------------------------------------------------
172: // Methods dealing with callable statements
173: //-------------------------------------------------------------------------
174:
175: public Object execute(CallableSqlStatement csc,
176: CallableStatementCallback action) throws JDBCException {
177: CallableStatement cs = null;
178: try {
179: cs = csc.createCallableStatement(database);
180: CallableStatement csToUse = cs;
181: return action.doInCallableStatement(csToUse);
182: } catch (SQLException ex) {
183: throw new JDBCException(
184: "Error executing callable statement", ex);
185: } finally {
186: JdbcUtils.closeStatement(cs);
187: }
188:
189: }
190:
191: public Map call(CallableSqlStatement csc,
192: final List declaredParameters) throws JDBCException {
193: return (Map) execute(csc, new CallableStatementCallback() {
194: public Object doInCallableStatement(CallableStatement cs)
195: throws SQLException {
196: //not currently doing anything with returned results
197: // boolean retVal = cs.execute();
198: // int updateCount = cs.getUpdateCount();
199: // Map returnedResults = new HashMap();
200: // if (retVal || updateCount != -1) {
201: // returnedResults.putAll(extractReturnedResultSets(cs, declaredParameters, updateCount));
202: // }
203: // returnedResults.putAll(extractOutputParameters(cs, declaredParameters));
204: cs.execute();
205: return new HashMap();
206: }
207: });
208: }
209:
210: /**
211: * Create a new RowMapper for reading columns as key-value pairs.
212: *
213: * @return the RowMapper to use
214: * @see ColumnMapRowMapper
215: */
216: protected RowMapper getColumnMapRowMapper() {
217: return new ColumnMapRowMapper();
218: }
219:
220: /**
221: * Create a new RowMapper for reading result objects from a single column.
222: *
223: * @param requiredType the type that each result object is expected to match
224: * @return the RowMapper to use
225: * @see SingleColumnRowMapper
226: */
227: protected RowMapper getSingleColumnRowMapper(Class requiredType) {
228: return new SingleColumnRowMapper(requiredType);
229: }
230:
231: /**
232: * Adds a comment to the database. Currently does nothing but is over-ridden in the output JDBC template
233: * @param message
234: * @throws JDBCException
235: */
236: public void comment(String message) throws JDBCException {
237: LogFactory.getLogger().info(message);
238: }
239:
240: /**
241: * Adapter to enable use of a RowCallbackHandler inside a ResultSetExtractor.
242: * <p>Uses a regular ResultSet, so we have to be careful when using it:
243: * We don't use it for navigating since this could lead to unpredictable consequences.
244: */
245: private static class RowCallbackHandlerResultSetExtractor implements
246: ResultSetExtractor {
247:
248: private final RowCallbackHandler rch;
249:
250: public RowCallbackHandlerResultSetExtractor(
251: RowCallbackHandler rch) {
252: this .rch = rch;
253: }
254:
255: public Object extractData(ResultSet rs) throws SQLException {
256: while (rs.next()) {
257: this.rch.processRow(rs);
258: }
259: return null;
260: }
261: }
262: }
|