callback) {
+ Connection c = null;
+ try {
+ c = dataSource.getConnection();
+ return callback.execute(c);
+ } catch (SQLException ex) {
+ throw translate(ex);
+ } finally {
+ close(c);
+ }
+ }
+
+ /**
+ * Determine the column name to use. The column name is determined based on
+ * a lookup using ResultSetMetaData.
+ *
+ * This method implementation takes into account recent clarifications
+ * expressed in the JDBC 4.0 specification:
+ *
+ * columnLabel - the label for the column specified with the SQL AS
+ * clause. If the SQL AS clause was not specified, then the label is the
+ * name of the column.
+ *
+ * @return the column name to use
+ * @param resultSetMetaData
+ * the current meta data to use
+ * @param columnIndex
+ * the index of the column for the look up
+ * @throws SQLException
+ * in case of lookup failure
+ */
+ public static String lookupColumnName(ResultSetMetaData resultSetMetaData, int columnIndex)
+ throws SQLException {
+ String name = resultSetMetaData.getColumnLabel(columnIndex);
+ if (name == null || name.length() < 1) {
+ name = resultSetMetaData.getColumnName(columnIndex);
+ }
+ return name;
+ }
+
+ /**
+ * Retrieve a JDBC column value from a ResultSet, using the most appropriate
+ * value type. The returned value should be a detached value object, not
+ * having any ties to the active ResultSet: in particular, it should not be
+ * a Blob or Clob object but rather a byte array respectively String
+ * representation.
+ *
+ * Uses the getObject(index)
method, but includes additional
+ * "hacks" to get around Oracle 10g returning a non-standard object for its
+ * TIMESTAMP datatype and a java.sql.Date
for DATE columns
+ * leaving out the time portion: These columns will explicitly be extracted
+ * as standard java.sql.Timestamp
object.
+ *
+ * @param rs
+ * is the ResultSet holding the data
+ * @param index
+ * is the column index
+ * @return the value object
+ * @throws SQLException
+ * if thrown by the JDBC API
+ * @see java.sql.Blob
+ * @see java.sql.Clob
+ * @see java.sql.Timestamp
+ */
+ public static Object getResultSetValue(ResultSet rs, int index) throws SQLException {
+ Object obj = rs.getObject(index);
+ String className = null;
+ if (obj != null) {
+ className = obj.getClass().getName();
+ }
+ if (obj instanceof Blob) {
+ obj = rs.getBytes(index);
+ } else if (obj instanceof Clob) {
+ obj = rs.getString(index);
+ } else if (className != null
+ && ("oracle.sql.TIMESTAMP".equals(className) || "oracle.sql.TIMESTAMPTZ"
+ .equals(className))) {
+ obj = rs.getTimestamp(index);
+ } else if (className != null && className.startsWith("oracle.sql.DATE")) {
+ String metaDataClassName = rs.getMetaData().getColumnClassName(index);
+ if ("java.sql.Timestamp".equals(metaDataClassName)
+ || "oracle.sql.TIMESTAMP".equals(metaDataClassName)) {
+ obj = rs.getTimestamp(index);
+ } else {
+ obj = rs.getDate(index);
+ }
+ } else if (obj != null && obj instanceof java.sql.Date) {
+ if ("java.sql.Timestamp".equals(rs.getMetaData().getColumnClassName(index))) {
+ obj = rs.getTimestamp(index);
+ }
+ }
+ return obj;
+ }
+
+ public static void close(ResultSet rs) {
+ try {
+ if (rs != null) {
+ rs.close();
+ }
+ } catch (SQLException ex) {
+ }
+ }
+
+ public static void close(PreparedStatement ps) {
+ try {
+ if (ps != null) {
+ ps.close();
+ }
+ } catch (SQLException ex) {
+ }
+ }
+
+ public static void close(Statement stmt) {
+ try {
+ if (stmt != null) {
+ stmt.close();
+ }
+ } catch (SQLException ex) {
+ }
+ }
+
+ public static void close(boolean autoCommitValue, Connection c) {
+ try {
+ if (c != null) {
+ c.setAutoCommit(autoCommitValue);
+ }
+ } catch (SQLException ex) {
+ } finally {
+ close(c);
+ }
+ }
+
+ public static void close(Connection c) {
+ try {
+ if (c != null) {
+ c.close();
+ }
+ } catch (SQLException ex) {
+ }
+ }
+
+ public SqlException translate(Exception ex) {
+ return translate(ex.getMessage(), ex);
+ }
+
+ public SqlException translate(String message, Exception ex) {
+ // TODO
+ // if (getDbDialect().isDataIntegrityException(ex)) {
+ // return new DataIntegrityViolationException(message, ex);
+ // } else
+ if (ex instanceof SqlException) {
+ return (SqlException) ex;
+ } else {
+ return new SqlException(message, ex);
+ }
+ }
+
+ public int getDatabaseMajorVersion() {
+ return execute(new IConnectionCallback() {
+ public Integer execute(Connection con) throws SQLException {
+ return con.getMetaData().getDatabaseMajorVersion();
+ }
+ });
+ }
+
+ public int getDatabaseMinorVersion() {
+ return execute(new IConnectionCallback() {
+ public Integer execute(Connection con) throws SQLException {
+ return con.getMetaData().getDatabaseMinorVersion();
+ }
+ });
+ }
+
+ public String getDatabaseProductName() {
+ return execute(new IConnectionCallback() {
+ public String execute(Connection con) throws SQLException {
+ return con.getMetaData().getDatabaseProductName();
+ }
+ });
+ }
+
+}
diff --git a/symmetric/symmetric-ddl/src/main/java/org/jumpmind/symmetric/db/jdbc/JdbcSqlTransaction.java b/symmetric/symmetric-ddl/src/main/java/org/jumpmind/symmetric/db/jdbc/JdbcSqlTransaction.java
new file mode 100644
index 0000000000..3ab2d402aa
--- /dev/null
+++ b/symmetric/symmetric-ddl/src/main/java/org/jumpmind/symmetric/db/jdbc/JdbcSqlTransaction.java
@@ -0,0 +1,241 @@
+package org.jumpmind.symmetric.db.jdbc;
+
+import java.sql.BatchUpdateException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.jumpmind.symmetric.db.ISqlTransaction;
+
+/**
+ * TODO Support Oracle's non-standard way of batching
+ */
+public class JdbcSqlTransaction implements ISqlTransaction {
+
+ protected boolean inBatchMode = true;
+
+ protected Connection dbConnection;
+
+ protected String psql;
+
+ protected PreparedStatement pstmt;
+
+ protected JdbcSqlTemplate sqlConnection;
+
+ protected int numberOfRowsBeforeBatchFlush = 1000;
+
+ protected boolean oldAutoCommitValue;
+
+ protected List