http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-server/src/main/java/org/apache/cayenne/dba/h2/H2PkGenerator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/h2/H2PkGenerator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/h2/H2PkGenerator.java index a6264aa..a7d7788 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/h2/H2PkGenerator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/h2/H2PkGenerator.java @@ -41,83 +41,68 @@ import org.apache.cayenne.map.DbKeyGenerator; */ public class H2PkGenerator extends OraclePkGenerator { - protected H2PkGenerator(JdbcAdapter adapter) { - super(adapter); - } - - @Override - protected String createSequenceString(DbEntity ent) { - return "CREATE SEQUENCE " + sequenceName(ent) + " START WITH " + pkStartValue - + " INCREMENT BY " + pkCacheSize(ent) + " CACHE 1"; - } - - @Override - protected long longPkFromDatabase(DataNode node, DbEntity entity) throws Exception { - - DbKeyGenerator pkGenerator = entity.getPrimaryKeyGenerator(); - String pkGeneratingSequenceName; - if (pkGenerator != null && DbKeyGenerator.ORACLE_TYPE.equals(pkGenerator.getGeneratorType()) - && pkGenerator.getGeneratorName() != null) { - pkGeneratingSequenceName = pkGenerator.getGeneratorName(); - } else { - pkGeneratingSequenceName = sequenceName(entity); - } - - Connection con = node.getDataSource().getConnection(); - try { - Statement st = con.createStatement(); - try { - String sql = "SELECT NEXT VALUE FOR " + pkGeneratingSequenceName; - adapter.getJdbcEventLogger().logQuery(sql, Collections.EMPTY_LIST); - ResultSet rs = st.executeQuery(sql); - try { - // Object pk = null; - if (!rs.next()) { - throw new CayenneRuntimeException("Error generating pk for DbEntity " + entity.getName()); - } - return rs.getLong(1); - } finally { - rs.close(); - } - } finally { - st.close(); - } - } finally { - con.close(); - } - - } - - /** - * Fetches a list of existing sequences that might match Cayenne generated - * ones. - */ - @Override - protected List<String> getExistingSequences(DataNode node) throws SQLException { - - // check existing sequences - Connection con = node.getDataSource().getConnection(); - - try { - Statement sel = con.createStatement(); - try { - String sql = "SELECT LOWER(sequence_name) FROM Information_Schema.Sequences"; - adapter.getJdbcEventLogger().logQuery(sql, Collections.EMPTY_LIST); - ResultSet rs = sel.executeQuery(sql); - try { - List<String> sequenceList = new ArrayList<String>(); - while (rs.next()) { - sequenceList.add(rs.getString(1)); - } - return sequenceList; - } finally { - rs.close(); - } - } finally { - sel.close(); - } - } finally { - con.close(); - } - } + protected H2PkGenerator(JdbcAdapter adapter) { + super(adapter); + } + + @Override + protected String createSequenceString(DbEntity ent) { + return "CREATE SEQUENCE " + sequenceName(ent) + " START WITH " + pkStartValue + " INCREMENT BY " + + pkCacheSize(ent) + " CACHE 1"; + } + + @Override + protected long longPkFromDatabase(DataNode node, DbEntity entity) throws Exception { + + DbKeyGenerator pkGenerator = entity.getPrimaryKeyGenerator(); + String pkGeneratingSequenceName; + if (pkGenerator != null && DbKeyGenerator.ORACLE_TYPE.equals(pkGenerator.getGeneratorType()) + && pkGenerator.getGeneratorName() != null) { + pkGeneratingSequenceName = pkGenerator.getGeneratorName(); + } else { + pkGeneratingSequenceName = sequenceName(entity); + } + + try (Connection con = node.getDataSource().getConnection();) { + + try (Statement st = con.createStatement();) { + String sql = "SELECT NEXT VALUE FOR " + pkGeneratingSequenceName; + adapter.getJdbcEventLogger().logQuery(sql, Collections.EMPTY_LIST); + + try (ResultSet rs = st.executeQuery(sql);) { + // Object pk = null; + if (!rs.next()) { + throw new CayenneRuntimeException("Error generating pk for DbEntity " + entity.getName()); + } + return rs.getLong(1); + } + } + } + } + + /** + * Fetches a list of existing sequences that might match Cayenne generated + * ones. + */ + @Override + protected List<String> getExistingSequences(DataNode node) throws SQLException { + + // check existing sequences + + try (Connection con = node.getDataSource().getConnection();) { + + try (Statement sel = con.createStatement();) { + String sql = "SELECT LOWER(sequence_name) FROM Information_Schema.Sequences"; + adapter.getJdbcEventLogger().logQuery(sql, Collections.EMPTY_LIST); + + try (ResultSet rs = sel.executeQuery(sql);) { + List<String> sequenceList = new ArrayList<String>(); + while (rs.next()) { + sequenceList.add(rs.getString(1)); + } + return sequenceList; + } + } + } + } }
http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresPkGenerator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresPkGenerator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresPkGenerator.java index b53a648..91c8db2 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresPkGenerator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresPkGenerator.java @@ -41,83 +41,61 @@ import org.apache.cayenne.map.DbKeyGenerator; */ public class IngresPkGenerator extends OraclePkGenerator { - protected IngresPkGenerator(JdbcAdapter adapter) { - super(adapter); - } + protected IngresPkGenerator(JdbcAdapter adapter) { + super(adapter); + } - @Override - protected long longPkFromDatabase(DataNode node, DbEntity entity) throws Exception { + @Override + protected long longPkFromDatabase(DataNode node, DbEntity entity) throws Exception { - DbKeyGenerator pkGenerator = entity.getPrimaryKeyGenerator(); - String pkGeneratingSequenceName; - if (pkGenerator != null - && DbKeyGenerator.ORACLE_TYPE.equals(pkGenerator.getGeneratorType()) - && pkGenerator.getGeneratorName() != null) { - pkGeneratingSequenceName = pkGenerator.getGeneratorName(); - } else { - pkGeneratingSequenceName = sequenceName(entity); - } + DbKeyGenerator pkGenerator = entity.getPrimaryKeyGenerator(); + String pkGeneratingSequenceName; + if (pkGenerator != null && DbKeyGenerator.ORACLE_TYPE.equals(pkGenerator.getGeneratorType()) + && pkGenerator.getGeneratorName() != null) { + pkGeneratingSequenceName = pkGenerator.getGeneratorName(); + } else { + pkGeneratingSequenceName = sequenceName(entity); + } - Connection con = node.getDataSource().getConnection(); - try { - Statement st = con.createStatement(); - try { - String sql = "SELECT " + pkGeneratingSequenceName + ".nextval"; - adapter.getJdbcEventLogger().logQuery(sql, Collections.EMPTY_LIST); - ResultSet rs = st.executeQuery(sql); - try { - // Object pk = null; - if (!rs.next()) { - throw new CayenneRuntimeException( - "Error generating pk for DbEntity " + entity.getName()); - } - return rs.getLong(1); - } - finally { - rs.close(); - } - } - finally { - st.close(); - } - } - finally { - con.close(); - } - } + try (Connection con = node.getDataSource().getConnection();) { - @Override - protected List<String> getExistingSequences(DataNode node) throws SQLException { + try (Statement st = con.createStatement();) { + String sql = "SELECT " + pkGeneratingSequenceName + ".nextval"; + adapter.getJdbcEventLogger().logQuery(sql, Collections.EMPTY_LIST); - // check existing sequences - Connection connection = node.getDataSource().getConnection(); + try (ResultSet rs = st.executeQuery(sql);) { + // Object pk = null; + if (!rs.next()) { + throw new CayenneRuntimeException("Error generating pk for DbEntity " + entity.getName()); + } + return rs.getLong(1); + } + } + } + } - try { - Statement select = connection.createStatement(); - try { - String sql = "select seq_name from iisequences where seq_owner != 'DBA'"; - adapter.getJdbcEventLogger().logQuery(sql, Collections.EMPTY_LIST); - ResultSet rs = select.executeQuery(sql); - try { - List<String> sequenceList = new ArrayList<String>(); - while (rs.next()) { - String name = rs.getString(1); - if (name != null) { - sequenceList.add(name.trim()); - } - } - return sequenceList; - } - finally { - rs.close(); - } - } - finally { - select.close(); - } - } - finally { - connection.close(); - } - } + @Override + protected List<String> getExistingSequences(DataNode node) throws SQLException { + + // check existing sequences + + try (Connection connection = node.getDataSource().getConnection();) { + + try (Statement select = connection.createStatement();) { + String sql = "select seq_name from iisequences where seq_owner != 'DBA'"; + adapter.getJdbcEventLogger().logQuery(sql, Collections.EMPTY_LIST); + + try (ResultSet rs = select.executeQuery(sql);) { + List<String> sequenceList = new ArrayList<String>(); + while (rs.next()) { + String name = rs.getString(1); + if (name != null) { + sequenceList.add(name.trim()); + } + } + return sequenceList; + } + } + } + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLPkGenerator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLPkGenerator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLPkGenerator.java index 7ad240a..180a4c2 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLPkGenerator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLPkGenerator.java @@ -34,171 +34,149 @@ import org.apache.cayenne.map.DbEntity; */ public class MySQLPkGenerator extends JdbcPkGenerator { - MySQLPkGenerator(JdbcAdapter adapter) { - super(adapter); - } - - @Override - protected String dropAutoPkString() { - return "DROP TABLE IF EXISTS AUTO_PK_SUPPORT"; - } - - /** - * Overrides superclass's implementation to perform locking of the primary key lookup - * table. - * - * @since 3.0 - */ - @Override - protected long longPkFromDatabase(DataNode node, DbEntity entity) throws Exception { - - // must work directly with JDBC connection, since we - // must unlock the AUTO_PK_SUPPORT table in case of - // failures.... ah..JDBC is fun... - - // chained SQL exception - SQLException exception = null; - long pk = -1l; - - Connection con = node.getDataSource().getConnection(); - try { - - if (con.getAutoCommit()) { - con.setAutoCommit(false); - } - - Statement st = con.createStatement(); - - try { - pk = getLongPrimaryKey(st, entity.getName()); - con.commit(); - } - catch (SQLException pkEx) { - - try { - con.rollback(); - } - catch (SQLException e) { - - } - - exception = processSQLException(pkEx, exception); - } - finally { - // UNLOCK! - // THIS MUST BE EXECUTED NO MATTER WHAT, OR WE WILL LOCK THE PRIMARY KEY - // TABLE!! - try { - String unlockString = "UNLOCK TABLES"; - adapter.getJdbcEventLogger().logQuery(unlockString, Collections.EMPTY_LIST); - st.execute(unlockString); - } - catch (SQLException unlockEx) { - exception = processSQLException(unlockEx, exception); - } - finally { - // close statement - try { - st.close(); - } - catch (SQLException stClosingEx) { - // ignoring... - } - } - } - } - catch (SQLException otherEx) { - exception = processSQLException(otherEx, exception); - } - finally { - try { - con.close(); - } - catch (SQLException closingEx) { - // ignoring - } - } - - // check errors - if (exception != null) { - throw exception; - } - - return pk; - - } - - /** - * Appends a new SQLException to the chain. If parent is null, uses the exception as - * the chain root. - */ - protected SQLException processSQLException(SQLException exception, SQLException parent) { - if (parent == null) { - return exception; - } - - parent.setNextException(exception); - return parent; - } - - @Override - protected String pkTableCreateString() { - StringBuilder buf = new StringBuilder(); - buf.append("CREATE TABLE IF NOT EXISTS AUTO_PK_SUPPORT (").append( - " TABLE_NAME CHAR(100) NOT NULL,").append( - " NEXT_ID BIGINT NOT NULL, UNIQUE (TABLE_NAME)").append(")"); - - return buf.toString(); - } - - /** - * @since 3.0 - */ - protected long getLongPrimaryKey(Statement statement, String entityName) - throws SQLException { - // lock - String lockString = "LOCK TABLES AUTO_PK_SUPPORT WRITE"; - adapter.getJdbcEventLogger().logQuery(lockString, Collections.EMPTY_LIST); - statement.execute(lockString); - - // select - - String selectString = super.pkSelectString(entityName); - adapter.getJdbcEventLogger().logQuery(selectString, Collections.EMPTY_LIST); - ResultSet rs = statement.executeQuery(selectString); - long pk = -1; - try { - if (!rs.next()) { - throw new SQLException("No rows for '" + entityName + "'"); - } - - pk = rs.getLong(1); - - if (rs.next()) { - throw new SQLException("More than one row for '" + entityName + "'"); - } - } - finally { - try { - rs.close(); - } - catch (Exception ex) { - // ignoring... - } - } - - // update - String updateString = super.pkUpdateString(entityName) + " AND NEXT_ID = " + pk; - adapter.getJdbcEventLogger().logQuery(updateString, Collections.EMPTY_LIST); - int updated = statement.executeUpdate(updateString); - // optimistic lock failure... - if (updated != 1) { - throw new SQLException("Error updating PK count '" - + entityName - + "': " - + updated); - } - - return pk; - } + MySQLPkGenerator(JdbcAdapter adapter) { + super(adapter); + } + + @Override + protected String dropAutoPkString() { + return "DROP TABLE IF EXISTS AUTO_PK_SUPPORT"; + } + + /** + * Overrides superclass's implementation to perform locking of the primary + * key lookup table. + * + * @since 3.0 + */ + @Override + protected long longPkFromDatabase(DataNode node, DbEntity entity) throws Exception { + + // must work directly with JDBC connection, since we + // must unlock the AUTO_PK_SUPPORT table in case of + // failures.... ah..JDBC is fun... + + // chained SQL exception + SQLException exception = null; + long pk = -1l; + + try (Connection con = node.getDataSource().getConnection();) { + + if (con.getAutoCommit()) { + con.setAutoCommit(false); + } + + Statement st = con.createStatement(); + + try { + pk = getLongPrimaryKey(st, entity.getName()); + con.commit(); + } catch (SQLException pkEx) { + + try { + con.rollback(); + } catch (SQLException e) { + + } + + exception = processSQLException(pkEx, exception); + } finally { + // UNLOCK! + // THIS MUST BE EXECUTED NO MATTER WHAT, OR WE WILL LOCK THE + // PRIMARY KEY + // TABLE!! + try { + String unlockString = "UNLOCK TABLES"; + adapter.getJdbcEventLogger().logQuery(unlockString, Collections.EMPTY_LIST); + st.execute(unlockString); + } catch (SQLException unlockEx) { + exception = processSQLException(unlockEx, exception); + } finally { + // close statement + try { + st.close(); + } catch (SQLException stClosingEx) { + // ignoring... + } + } + } + } catch (SQLException otherEx) { + exception = processSQLException(otherEx, exception); + } + + // check errors + if (exception != null) { + throw exception; + } + + return pk; + + } + + /** + * Appends a new SQLException to the chain. If parent is null, uses the + * exception as the chain root. + */ + protected SQLException processSQLException(SQLException exception, SQLException parent) { + if (parent == null) { + return exception; + } + + parent.setNextException(exception); + return parent; + } + + @Override + protected String pkTableCreateString() { + StringBuilder buf = new StringBuilder(); + buf.append("CREATE TABLE IF NOT EXISTS AUTO_PK_SUPPORT (").append(" TABLE_NAME CHAR(100) NOT NULL,") + .append(" NEXT_ID BIGINT NOT NULL, UNIQUE (TABLE_NAME)").append(")"); + + return buf.toString(); + } + + /** + * @since 3.0 + */ + protected long getLongPrimaryKey(Statement statement, String entityName) throws SQLException { + // lock + String lockString = "LOCK TABLES AUTO_PK_SUPPORT WRITE"; + adapter.getJdbcEventLogger().logQuery(lockString, Collections.EMPTY_LIST); + statement.execute(lockString); + + // select + + String selectString = super.pkSelectString(entityName); + adapter.getJdbcEventLogger().logQuery(selectString, Collections.EMPTY_LIST); + ResultSet rs = statement.executeQuery(selectString); + long pk = -1; + try { + if (!rs.next()) { + throw new SQLException("No rows for '" + entityName + "'"); + } + + pk = rs.getLong(1); + + if (rs.next()) { + throw new SQLException("More than one row for '" + entityName + "'"); + } + } finally { + try { + rs.close(); + } catch (Exception ex) { + // ignoring... + } + } + + // update + String updateString = super.pkUpdateString(entityName) + " AND NEXT_ID = " + pk; + adapter.getJdbcEventLogger().logQuery(updateString, Collections.EMPTY_LIST); + int updated = statement.executeUpdate(updateString); + // optimistic lock failure... + if (updated != 1) { + throw new SQLException("Error updating PK count '" + entityName + "': " + updated); + } + + return pk; + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLProcedureAction.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLProcedureAction.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLProcedureAction.java index 5c5b199..ac189ab 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLProcedureAction.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLProcedureAction.java @@ -35,115 +35,102 @@ import org.apache.cayenne.query.ProcedureQuery; */ class MySQLProcedureAction extends ProcedureAction { - public MySQLProcedureAction(ProcedureQuery query, DataNode dataNode) { - super(query, dataNode); - } - - @Override - public void performAction(Connection connection, OperationObserver observer) - throws SQLException, Exception { - - processedResultSets = 0; - - ProcedureTranslator transl = createTranslator(connection); - CallableStatement statement = (CallableStatement) transl.createStatement(); - - try { - - // this is one difference with super - we need to read the first result set - // without calling 'getMoreResults' - which may actually be a good default - // strategy? - boolean firstResult = statement.execute(); - - // read out parameters - readProcedureOutParameters(statement, observer); - - // read first result - if (firstResult) { - processResultSet(statement, observer); - } - else if (!processUpdate(statement, observer)) { - return; - } - - // read the rest of the query - while (true) { - if (statement.getMoreResults()) { - processResultSet(statement, observer); - } - else if (!processUpdate(statement, observer)) { - break; - } - } - } - finally { - try { - statement.close(); - } - catch (SQLException ex) { - - } - } - } - - private void processResultSet(CallableStatement statement, OperationObserver observer) - throws Exception { - ResultSet rs = statement.getResultSet(); - - try { - RowDescriptor descriptor = describeResultSet(rs, processedResultSets++); - readResultSet(rs, descriptor, query, observer); - } - finally { - try { - rs.close(); - } - catch (SQLException ex) { - } - } - } - - private boolean processUpdate(CallableStatement statement, OperationObserver observer) - throws Exception { - int updateCount = statement.getUpdateCount(); - if (updateCount == -1) { - return false; - } - dataNode.getJdbcEventLogger().logUpdateCount(updateCount); - observer.nextCount(query, updateCount); - - return true; - } - - /** - * Creates a translator that adds parenthesis to no-param queries. - */ - // see CAY-750 for the problem description - @Override - protected ProcedureTranslator createTranslator(Connection connection) { - ProcedureTranslator translator = new MySQLProcedureTranslator(); - translator.setAdapter(dataNode.getAdapter()); - translator.setQuery(query); - translator.setEntityResolver(dataNode.getEntityResolver()); - translator.setConnection(connection); - translator.setJdbcEventLogger(dataNode.getJdbcEventLogger()); - return translator; - } - - // same as postgres translator - should we make this the default? - static class MySQLProcedureTranslator extends ProcedureTranslator { - - @Override - protected String createSqlString() { - - String sql = super.createSqlString(); - - // add empty parameter parenthesis - if (sql.endsWith("}") && !sql.endsWith(")}")) { - sql = sql.substring(0, sql.length() - 1) + "()}"; - } - - return sql; - } - } + public MySQLProcedureAction(ProcedureQuery query, DataNode dataNode) { + super(query, dataNode); + } + + @Override + public void performAction(Connection connection, OperationObserver observer) throws SQLException, Exception { + + processedResultSets = 0; + + ProcedureTranslator transl = createTranslator(connection); + + try (CallableStatement statement = (CallableStatement) transl.createStatement();) { + + // this is one difference with super - we need to read the first + // result set + // without calling 'getMoreResults' - which may actually be a good + // default + // strategy? + boolean firstResult = statement.execute(); + + // read out parameters + readProcedureOutParameters(statement, observer); + + // read first result + if (firstResult) { + processResultSet(statement, observer); + } else if (!processUpdate(statement, observer)) { + return; + } + + // read the rest of the query + while (true) { + if (statement.getMoreResults()) { + processResultSet(statement, observer); + } else if (!processUpdate(statement, observer)) { + break; + } + } + } + + } + + private void processResultSet(CallableStatement statement, OperationObserver observer) throws Exception { + ResultSet rs = statement.getResultSet(); + + try { + RowDescriptor descriptor = describeResultSet(rs, processedResultSets++); + readResultSet(rs, descriptor, query, observer); + } finally { + try { + rs.close(); + } catch (SQLException ex) { + } + } + } + + private boolean processUpdate(CallableStatement statement, OperationObserver observer) throws Exception { + int updateCount = statement.getUpdateCount(); + if (updateCount == -1) { + return false; + } + dataNode.getJdbcEventLogger().logUpdateCount(updateCount); + observer.nextCount(query, updateCount); + + return true; + } + + /** + * Creates a translator that adds parenthesis to no-param queries. + */ + // see CAY-750 for the problem description + @Override + protected ProcedureTranslator createTranslator(Connection connection) { + ProcedureTranslator translator = new MySQLProcedureTranslator(); + translator.setAdapter(dataNode.getAdapter()); + translator.setQuery(query); + translator.setEntityResolver(dataNode.getEntityResolver()); + translator.setConnection(connection); + translator.setJdbcEventLogger(dataNode.getJdbcEventLogger()); + return translator; + } + + // same as postgres translator - should we make this the default? + static class MySQLProcedureTranslator extends ProcedureTranslator { + + @Override + protected String createSqlString() { + + String sql = super.createSqlString(); + + // add empty parameter parenthesis + if (sql.endsWith("}") && !sql.endsWith(")}")) { + sql = sql.substring(0, sql.length() - 1) + "()}"; + } + + return sql; + } + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSniffer.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSniffer.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSniffer.java index a7c7457..2917fa2 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSniffer.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSniffer.java @@ -36,52 +36,46 @@ import org.apache.cayenne.di.Inject; */ public class MySQLSniffer implements DbAdapterDetector { - protected AdhocObjectFactory objectFactory; + protected AdhocObjectFactory objectFactory; - public MySQLSniffer(@Inject AdhocObjectFactory objectFactory) { - this.objectFactory = objectFactory; - } + public MySQLSniffer(@Inject AdhocObjectFactory objectFactory) { + this.objectFactory = objectFactory; + } - @Override - public DbAdapter createAdapter(DatabaseMetaData md) throws SQLException { - String dbName = md.getDatabaseProductName(); - if (dbName == null || !dbName.toUpperCase().contains("MYSQL")) { - return null; - } + @Override + public DbAdapter createAdapter(DatabaseMetaData md) throws SQLException { + String dbName = md.getDatabaseProductName(); + if (dbName == null || !dbName.toUpperCase().contains("MYSQL")) { + return null; + } - // if InnoDB is used as a default engine, allow PK - Statement statement = md.getConnection().createStatement(); - boolean supportFK = false; - String adapterStorageEngine = MySQLAdapter.DEFAULT_STORAGE_ENGINE; + // if InnoDB is used as a default engine, allow PK - try { - // http://dev.mysql.com/doc/refman/5.0/en/storage-engines.html - // per link above "table type" concept is deprecated in favor of "storage - // engine". Not sure if we should check "storage_engine" variable and in what - // version of MySQL it got introduced... - ResultSet rs = statement.executeQuery("SHOW VARIABLES LIKE 'table_type'"); - try { - if (rs.next()) { - String storageEngine = rs.getString(2); - if (storageEngine != null) { - adapterStorageEngine = storageEngine; - supportFK = storageEngine.toUpperCase().equals("INNODB"); - } - } - } - finally { - rs.close(); - } - } - finally { - statement.close(); - } + boolean supportFK = false; + String adapterStorageEngine = MySQLAdapter.DEFAULT_STORAGE_ENGINE; - MySQLAdapter adapter = objectFactory.newInstance( - MySQLAdapter.class, - MySQLAdapter.class.getName()); - adapter.setSupportsFkConstraints(supportFK); - adapter.setStorageEngine(adapterStorageEngine); - return adapter; - } + try (Statement statement = md.getConnection().createStatement();) { + // http://dev.mysql.com/doc/refman/5.0/en/storage-engines.html + // per link above "table type" concept is deprecated in favor of + // "storage + // engine". Not sure if we should check "storage_engine" variable + // and in what + // version of MySQL it got introduced... + + try (ResultSet rs = statement.executeQuery("SHOW VARIABLES LIKE 'table_type'");) { + if (rs.next()) { + String storageEngine = rs.getString(2); + if (storageEngine != null) { + adapterStorageEngine = storageEngine; + supportFK = storageEngine.toUpperCase().equals("INNODB"); + } + } + } + } + + MySQLAdapter adapter = objectFactory.newInstance(MySQLAdapter.class, MySQLAdapter.class.getName()); + adapter.setSupportsFkConstraints(supportFK); + adapter.setStorageEngine(adapterStorageEngine); + return adapter; + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBasePkGenerator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBasePkGenerator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBasePkGenerator.java index 3b9e9d7..bb3068f 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBasePkGenerator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBasePkGenerator.java @@ -43,262 +43,245 @@ import org.apache.cayenne.util.IDUtil; */ public class OpenBasePkGenerator extends JdbcPkGenerator { - protected OpenBasePkGenerator(JdbcAdapter adapter) { - super(adapter); - } - - /** - * Returns a non-repeating primary key for a given PK attribute. Since - * OpenBase-specific mechanism is used, key caching is disabled. Instead a database - * operation is performed on every call. + protected OpenBasePkGenerator(JdbcAdapter adapter) { + super(adapter); + } + + /** + * Returns a non-repeating primary key for a given PK attribute. Since + * OpenBase-specific mechanism is used, key caching is disabled. Instead a + * database operation is performed on every call. + * + * @since 3.0 + */ + @Override + public Object generatePk(DataNode node, DbAttribute pk) throws Exception { + + DbEntity entity = (DbEntity) pk.getEntity(); + + switch (pk.getType()) { + case Types.BINARY: + case Types.VARBINARY: + return IDUtil.pseudoUniqueSecureByteSequence(pk.getMaxLength()); + } + + long value = longPkFromDatabase(node, entity); + + if (pk.getType() == Types.BIGINT) { + return Long.valueOf(value); + } else { + // leaving it up to the user to ensure that PK does not exceed max + // int... + return Integer.valueOf((int) value); + } + } + + /** + * Generates new (unique and non-repeating) primary key for specified + * DbEntity. Executed SQL looks like this: + * + * <pre> + * NEWID FOR Table Column + * </pre> + * + * COLUMN must be marked as UNIQUE in order for this to work properly. + * + * @since 3.0 + */ + @Override + protected long longPkFromDatabase(DataNode node, DbEntity entity) throws Exception { + + String sql = newIDString(entity); + adapter.getJdbcEventLogger().logQuery(sql, Collections.EMPTY_LIST); + + try (Connection con = node.getDataSource().getConnection();) { + + try (Statement st = con.createStatement();) { + + try (ResultSet rs = st.executeQuery(sql);) { + // Object pk = null; + if (!rs.next()) { + throw new CayenneRuntimeException("Error generating pk for DbEntity " + entity.getName()); + } + return rs.getLong(1); + } + } + } + } + + /** + * Returns SQL string that can generate new (unique and non-repeating) + * primary key for specified DbEntity. No actual database operations are + * performed. + * + * @since 1.2 + */ + protected String newIDString(DbEntity ent) { + if (ent.getPrimaryKeys() == null || ent.getPrimaryKeys().size() != 1) { + throw new CayenneRuntimeException("Error generating pk for DbEntity " + ent.getName() + + ": pk must be single attribute"); + } + DbAttribute primaryKeyAttribute = ent.getPrimaryKeys().iterator().next(); + + return "NEWID FOR " + ent.getName() + ' ' + primaryKeyAttribute.getName(); + } + + @Override + public void createAutoPk(DataNode node, List dbEntities) throws Exception { + // looks like generating a PK on top of an existing one does not + // result in errors... + + // create needed sequences + Iterator<?> it = dbEntities.iterator(); + while (it.hasNext()) { + DbEntity entity = (DbEntity) it.next(); + + // the caller must take care of giving us the right entities + // but lets check anyway + if (!canCreatePK(entity)) { + continue; + } + + runUpdate(node, createPKString(entity)); + runUpdate(node, createUniquePKIndexString(entity)); + } + } + + /** * - * @since 3.0 */ - @Override - public Object generatePk(DataNode node, DbAttribute pk) throws Exception { - - DbEntity entity = (DbEntity) pk.getEntity(); - - switch (pk.getType()) { - case Types.BINARY: - case Types.VARBINARY: - return IDUtil.pseudoUniqueSecureByteSequence(pk.getMaxLength()); - } - - long value = longPkFromDatabase(node, entity); - - if (pk.getType() == Types.BIGINT) { - return Long.valueOf(value); - } - else { - // leaving it up to the user to ensure that PK does not exceed max int... - return Integer.valueOf((int) value); - } - } - - /** - * Generates new (unique and non-repeating) primary key for specified DbEntity. - * Executed SQL looks like this: + @Override + public List createAutoPkStatements(List dbEntities) { + List<String> list = new ArrayList<String>(2 * dbEntities.size()); + Iterator<?> it = dbEntities.iterator(); + while (it.hasNext()) { + DbEntity entity = (DbEntity) it.next(); + + // the caller must take care of giving us the right entities + // but lets check anyway + if (!canCreatePK(entity)) { + continue; + } + + list.add(createPKString(entity)); + list.add(createUniquePKIndexString(entity)); + } + + return list; + } + + protected boolean canCreatePK(DbEntity entity) { + return entity.getPrimaryKeys().size() > 0; + } + + /** * - * <pre> - * NEWID FOR Table Column - * </pre> - * - * COLUMN must be marked as UNIQUE in order for this to work properly. - * - * @since 3.0 - */ - @Override - protected long longPkFromDatabase(DataNode node, DbEntity entity) throws Exception { - - String sql = newIDString(entity); - adapter.getJdbcEventLogger().logQuery(sql, Collections.EMPTY_LIST); - - Connection con = node.getDataSource().getConnection(); - try { - Statement st = con.createStatement(); - try { - - ResultSet rs = st.executeQuery(sql); - try { - // Object pk = null; - if (!rs.next()) { - throw new CayenneRuntimeException( - "Error generating pk for DbEntity " + entity.getName()); - } - return rs.getLong(1); - } - finally { - rs.close(); - } - } - finally { - st.close(); - } - } - finally { - con.close(); - } - - } - - /** - * Returns SQL string that can generate new (unique and non-repeating) primary key for - * specified DbEntity. No actual database operations are performed. - * - * @since 1.2 - */ - protected String newIDString(DbEntity ent) { - if (ent.getPrimaryKeys() == null || ent.getPrimaryKeys().size() != 1) { - throw new CayenneRuntimeException("Error generating pk for DbEntity " - + ent.getName() - + ": pk must be single attribute"); - } - DbAttribute primaryKeyAttribute = ent.getPrimaryKeys().iterator().next(); - - return "NEWID FOR " + ent.getName() + ' ' + primaryKeyAttribute.getName(); - } - - @Override - public void createAutoPk(DataNode node, List dbEntities) throws Exception { - // looks like generating a PK on top of an existing one does not - // result in errors... - - // create needed sequences - Iterator<?> it = dbEntities.iterator(); - while (it.hasNext()) { - DbEntity entity = (DbEntity) it.next(); - - // the caller must take care of giving us the right entities - // but lets check anyway - if (!canCreatePK(entity)) { - continue; - } - - runUpdate(node, createPKString(entity)); - runUpdate(node, createUniquePKIndexString(entity)); - } - } - - /** - * - */ - @Override - public List createAutoPkStatements(List dbEntities) { - List<String> list = new ArrayList<String>(2 * dbEntities.size()); - Iterator<?> it = dbEntities.iterator(); - while (it.hasNext()) { - DbEntity entity = (DbEntity) it.next(); - - // the caller must take care of giving us the right entities - // but lets check anyway - if (!canCreatePK(entity)) { - continue; - } - - list.add(createPKString(entity)); - list.add(createUniquePKIndexString(entity)); - } - - return list; - } - - protected boolean canCreatePK(DbEntity entity) { - return entity.getPrimaryKeys().size() > 0; - } - - /** - * - */ - @Override - public void dropAutoPk(DataNode node, List dbEntities) throws Exception { - // there is no simple way to do that... probably requires - // editing metadata tables... - // Good thing is that it doesn't matter, since PK support - // is attached to the table itself, so if a table is dropped, - // it will be dropped as well - } - - /** - * Returns an empty list, since OpenBase doesn't support this operation. - */ - @Override - public List dropAutoPkStatements(List dbEntities) { - return Collections.EMPTY_LIST; - } - - /** - * Returns a String to create PK support for an entity. - */ - protected String createPKString(DbEntity entity) { - Collection<DbAttribute> pk = entity.getPrimaryKeys(); - - if (pk == null || pk.size() == 0) { - throw new CayenneRuntimeException("Entity '" - + entity.getName() - + "' has no PK defined."); - } - - StringBuilder buffer = new StringBuilder(); - buffer.append("CREATE PRIMARY KEY "); - - QuotingStrategy context = getAdapter().getQuotingStrategy(); - - buffer.append(context.quotedIdentifier(entity, entity.getName())); - - buffer.append(" ("); - - Iterator<DbAttribute> it = pk.iterator(); - - // at this point we know that there is at least on PK column - DbAttribute firstColumn = it.next(); - buffer.append(context.quotedName(firstColumn)); - - while (it.hasNext()) { - DbAttribute column = it.next(); - buffer.append(", "); - buffer.append(context.quotedName(column)); - } - - buffer.append(")"); - return buffer.toString(); - } - - /** - * Returns a String to create a unique index on table primary key columns per OpenBase - * recommendations. - */ - protected String createUniquePKIndexString(DbEntity entity) { - Collection<DbAttribute> pk = entity.getPrimaryKeys(); - - QuotingStrategy context = getAdapter().getQuotingStrategy(); - if (pk == null || pk.size() == 0) { - throw new CayenneRuntimeException("Entity '" - + entity.getName() - + "' has no PK defined."); - } - - StringBuilder buffer = new StringBuilder(); - - // compound PK doesn't work well with UNIQUE index... - // create a regular one in this case - buffer.append(pk.size() == 1 ? "CREATE UNIQUE INDEX " : "CREATE INDEX "); - - buffer.append(context.quotedIdentifier(entity, entity.getName())); - buffer.append(" ("); - - Iterator<DbAttribute> it = pk.iterator(); - - // at this point we know that there is at least on PK column - DbAttribute firstColumn = it.next(); - buffer.append(context.quotedName(firstColumn)); - - while (it.hasNext()) { - DbAttribute column = it.next(); - buffer.append(", "); - buffer.append(context.quotedName(column)); - } - buffer.append(")"); - return buffer.toString(); - } - - @Override - public void reset() { - // noop - } - - /** - * Returns zero, since PK caching is not feasible with OpenBase PK generation - * mechanism. */ - @Override - public int getPkCacheSize() { - return 0; - } - - @Override - public void setPkCacheSize(int pkCacheSize) { - // noop, no PK caching - } + @Override + public void dropAutoPk(DataNode node, List dbEntities) throws Exception { + // there is no simple way to do that... probably requires + // editing metadata tables... + // Good thing is that it doesn't matter, since PK support + // is attached to the table itself, so if a table is dropped, + // it will be dropped as well + } + + /** + * Returns an empty list, since OpenBase doesn't support this operation. + */ + @Override + public List dropAutoPkStatements(List dbEntities) { + return Collections.EMPTY_LIST; + } + + /** + * Returns a String to create PK support for an entity. + */ + protected String createPKString(DbEntity entity) { + Collection<DbAttribute> pk = entity.getPrimaryKeys(); + + if (pk == null || pk.size() == 0) { + throw new CayenneRuntimeException("Entity '" + entity.getName() + "' has no PK defined."); + } + + StringBuilder buffer = new StringBuilder(); + buffer.append("CREATE PRIMARY KEY "); + + QuotingStrategy context = getAdapter().getQuotingStrategy(); + + buffer.append(context.quotedIdentifier(entity, entity.getName())); + + buffer.append(" ("); + + Iterator<DbAttribute> it = pk.iterator(); + + // at this point we know that there is at least on PK column + DbAttribute firstColumn = it.next(); + buffer.append(context.quotedName(firstColumn)); + + while (it.hasNext()) { + DbAttribute column = it.next(); + buffer.append(", "); + buffer.append(context.quotedName(column)); + } + + buffer.append(")"); + return buffer.toString(); + } + + /** + * Returns a String to create a unique index on table primary key columns + * per OpenBase recommendations. + */ + protected String createUniquePKIndexString(DbEntity entity) { + Collection<DbAttribute> pk = entity.getPrimaryKeys(); + + QuotingStrategy context = getAdapter().getQuotingStrategy(); + if (pk == null || pk.size() == 0) { + throw new CayenneRuntimeException("Entity '" + entity.getName() + "' has no PK defined."); + } + + StringBuilder buffer = new StringBuilder(); + + // compound PK doesn't work well with UNIQUE index... + // create a regular one in this case + buffer.append(pk.size() == 1 ? "CREATE UNIQUE INDEX " : "CREATE INDEX "); + + buffer.append(context.quotedIdentifier(entity, entity.getName())); + buffer.append(" ("); + + Iterator<DbAttribute> it = pk.iterator(); + + // at this point we know that there is at least on PK column + DbAttribute firstColumn = it.next(); + buffer.append(context.quotedName(firstColumn)); + + while (it.hasNext()) { + DbAttribute column = it.next(); + buffer.append(", "); + buffer.append(context.quotedName(column)); + } + buffer.append(")"); + return buffer.toString(); + } + + @Override + public void reset() { + // noop + } + + /** + * Returns zero, since PK caching is not feasible with OpenBase PK + * generation mechanism. + */ + @Override + public int getPkCacheSize() { + return 0; + } + + @Override + public void setPkCacheSize(int pkCacheSize) { + // noop, no PK caching + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchAction.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchAction.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchAction.java index 59766cb..8c6a7fb 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchAction.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchAction.java @@ -51,223 +51,201 @@ import org.apache.cayenne.util.Util; */ class Oracle8LOBBatchAction implements SQLAction { - private BatchQuery query; - private DbAdapter adapter; - private JdbcEventLogger logger; - - private static void bind(DbAdapter adapter, PreparedStatement statement, ParameterBinding[] bindings) - throws SQLException, Exception { + private BatchQuery query; + private DbAdapter adapter; + private JdbcEventLogger logger; - for (ParameterBinding b : bindings) { - adapter.bindParameter(statement, b.getValue(), b.getStatementPosition(), b.getAttribute().getType(), b - .getAttribute().getScale()); - } - } + private static void bind(DbAdapter adapter, PreparedStatement statement, ParameterBinding[] bindings) + throws SQLException, Exception { - Oracle8LOBBatchAction(BatchQuery query, DbAdapter adapter, JdbcEventLogger logger) { - this.adapter = adapter; - this.query = query; - this.logger = logger; - } + for (ParameterBinding b : bindings) { + adapter.bindParameter(statement, b.getValue(), b.getStatementPosition(), b.getAttribute().getType(), b + .getAttribute().getScale()); + } + } - @Override - public void performAction(Connection connection, OperationObserver observer) throws SQLException, Exception { + Oracle8LOBBatchAction(BatchQuery query, DbAdapter adapter, JdbcEventLogger logger) { + this.adapter = adapter; + this.query = query; + this.logger = logger; + } - Oracle8LOBBatchTranslator translator; - if (query instanceof InsertBatchQuery) { - translator = new Oracle8LOBInsertBatchTranslator((InsertBatchQuery) query, adapter, OracleAdapter.TRIM_FUNCTION); - } else if (query instanceof UpdateBatchQuery) { - translator = new Oracle8LOBUpdateBatchTranslator((UpdateBatchQuery) query, adapter, OracleAdapter.TRIM_FUNCTION); - } else { - throw new CayenneException("Unsupported batch type for special LOB processing: " + query); - } + @Override + public void performAction(Connection connection, OperationObserver observer) throws SQLException, Exception { - translator.setNewBlobFunction(OracleAdapter.NEW_BLOB_FUNCTION); - translator.setNewClobFunction(OracleAdapter.NEW_CLOB_FUNCTION); - - // no batching is done, queries are translated - // for each batch set, since prepared statements - // may be different depending on whether LOBs are NULL or not.. - - Oracle8LOBBatchQueryWrapper selectQuery = new Oracle8LOBBatchQueryWrapper(query); - List<DbAttribute> qualifierAttributes = selectQuery.getDbAttributesForLOBSelectQualifier(); - - for (BatchQueryRow row : query.getRows()) { - - selectQuery.indexLOBAttributes(row); - - int updated = 0; - String updateStr = translator.createSql(row); - - // 1. run row update - logger.logQuery(updateStr, Collections.EMPTY_LIST); - PreparedStatement statement = connection.prepareStatement(updateStr); - try { - - ParameterBinding[] bindings = translator.updateBindings(row); - logger.logQueryParameters("bind", bindings); - - bind(adapter, statement, bindings); - - updated = statement.executeUpdate(); - logger.logUpdateCount(updated); - } finally { - try { - statement.close(); - } catch (Exception e) { - } - } - - // 2. run row LOB update (SELECT...FOR UPDATE and writing out LOBs) - processLOBRow(connection, translator, selectQuery, qualifierAttributes, row); - - // finally, notify delegate that the row was updated - observer.nextCount(query, updated); - } - } - - void processLOBRow(Connection con, Oracle8LOBBatchTranslator queryBuilder, Oracle8LOBBatchQueryWrapper selectQuery, - List<DbAttribute> qualifierAttributes, BatchQueryRow row) throws SQLException, Exception { - - List<DbAttribute> lobAttributes = selectQuery.getDbAttributesForUpdatedLOBColumns(); - if (lobAttributes.size() == 0) { - return; - } - - boolean isLoggable = logger.isLoggable(); - - List<Object> qualifierValues = selectQuery.getValuesForLOBSelectQualifier(row); - List<Object> lobValues = selectQuery.getValuesForUpdatedLOBColumns(); - int parametersSize = qualifierValues.size(); - int lobSize = lobAttributes.size(); - - String selectStr = queryBuilder.createLOBSelectString(lobAttributes, qualifierAttributes); - - if (isLoggable) { - logger.logQuery(selectStr, qualifierValues); - logger.logQueryParameters("write LOB", null, lobValues, false); - } - - PreparedStatement selectStatement = con.prepareStatement(selectStr); - try { - for (int i = 0; i < parametersSize; i++) { - Object value = qualifierValues.get(i); - DbAttribute attribute = qualifierAttributes.get(i); - - adapter.bindParameter(selectStatement, value, i + 1, attribute.getType(), attribute.getScale()); - } - - ResultSet result = selectStatement.executeQuery(); - - try { - if (!result.next()) { - throw new CayenneRuntimeException("Missing LOB row."); - } - - // read the only expected row - - for (int i = 0; i < lobSize; i++) { - DbAttribute attribute = lobAttributes.get(i); - int type = attribute.getType(); - - if (type == Types.CLOB) { - Clob clob = result.getClob(i + 1); - Object clobVal = lobValues.get(i); - - if (clobVal instanceof char[]) { - writeClob(clob, (char[]) clobVal); - } else { - writeClob(clob, clobVal.toString()); - } - } else if (type == Types.BLOB) { - Blob blob = result.getBlob(i + 1); - - Object blobVal = lobValues.get(i); - if (blobVal instanceof byte[]) { - writeBlob(blob, (byte[]) blobVal); - } else { - String className = (blobVal != null) ? blobVal.getClass().getName() : null; - throw new CayenneRuntimeException("Unsupported class of BLOB value: " + className); - } - } else { - throw new CayenneRuntimeException("Only BLOB or CLOB is expected here, got: " + type); - } - } - - if (result.next()) { - throw new CayenneRuntimeException("More than one LOB row found."); - } - } finally { - try { - result.close(); - } catch (Exception e) { - } - } - } finally { - try { - selectStatement.close(); - } catch (Exception e) { - } - } - } - - /** - * Override the Oracle writeBlob() method to be compatible with Oracle8 - * drivers. - */ - protected void writeBlob(Blob blob, byte[] value) { - // Fix for CAY-1307. For Oracle8, get the method found by reflection in - // OracleAdapter. (Code taken from Cayenne 2.) - Method getBinaryStreamMethod = Oracle8Adapter.getOutputStreamFromBlobMethod(); - try { - OutputStream out = (OutputStream) getBinaryStreamMethod.invoke(blob, (Object[]) null); - try { - out.write(value); - out.flush(); - } finally { - out.close(); - } - } catch (Exception e) { - throw new CayenneRuntimeException("Error processing BLOB.", Util.unwindException(e)); - } - } - - /** - * Override the Oracle writeClob() method to be compatible with Oracle8 - * drivers. - */ - protected void writeClob(Clob clob, char[] value) { - Method getWriterMethod = Oracle8Adapter.getWriterFromClobMethod(); - try { - Writer out = (Writer) getWriterMethod.invoke(clob, (Object[]) null); - try { - out.write(value); - out.flush(); - } finally { - out.close(); - } - - } catch (Exception e) { - throw new CayenneRuntimeException("Error processing CLOB.", Util.unwindException(e)); - } - } - - /** - * Override the Oracle writeClob() method to be compatible with Oracle8 - * drivers. - */ - protected void writeClob(Clob clob, String value) { - Method getWriterMethod = Oracle8Adapter.getWriterFromClobMethod(); - try { - Writer out = (Writer) getWriterMethod.invoke(clob, (Object[]) null); - try { - out.write(value); - out.flush(); - } finally { - out.close(); - } - } catch (Exception e) { - throw new CayenneRuntimeException("Error processing CLOB.", Util.unwindException(e)); - } - } + Oracle8LOBBatchTranslator translator; + if (query instanceof InsertBatchQuery) { + translator = new Oracle8LOBInsertBatchTranslator((InsertBatchQuery) query, adapter, + OracleAdapter.TRIM_FUNCTION); + } else if (query instanceof UpdateBatchQuery) { + translator = new Oracle8LOBUpdateBatchTranslator((UpdateBatchQuery) query, adapter, + OracleAdapter.TRIM_FUNCTION); + } else { + throw new CayenneException("Unsupported batch type for special LOB processing: " + query); + } + + translator.setNewBlobFunction(OracleAdapter.NEW_BLOB_FUNCTION); + translator.setNewClobFunction(OracleAdapter.NEW_CLOB_FUNCTION); + + // no batching is done, queries are translated + // for each batch set, since prepared statements + // may be different depending on whether LOBs are NULL or not.. + + Oracle8LOBBatchQueryWrapper selectQuery = new Oracle8LOBBatchQueryWrapper(query); + List<DbAttribute> qualifierAttributes = selectQuery.getDbAttributesForLOBSelectQualifier(); + + for (BatchQueryRow row : query.getRows()) { + + selectQuery.indexLOBAttributes(row); + + int updated = 0; + String updateStr = translator.createSql(row); + + // 1. run row update + logger.logQuery(updateStr, Collections.EMPTY_LIST); + + try (PreparedStatement statement = connection.prepareStatement(updateStr);) { + + ParameterBinding[] bindings = translator.updateBindings(row); + logger.logQueryParameters("bind", bindings); + + bind(adapter, statement, bindings); + + updated = statement.executeUpdate(); + logger.logUpdateCount(updated); + } + + // 2. run row LOB update (SELECT...FOR UPDATE and writing out LOBs) + processLOBRow(connection, translator, selectQuery, qualifierAttributes, row); + + // finally, notify delegate that the row was updated + observer.nextCount(query, updated); + } + } + + void processLOBRow(Connection con, Oracle8LOBBatchTranslator queryBuilder, Oracle8LOBBatchQueryWrapper selectQuery, + List<DbAttribute> qualifierAttributes, BatchQueryRow row) throws SQLException, Exception { + + List<DbAttribute> lobAttributes = selectQuery.getDbAttributesForUpdatedLOBColumns(); + if (lobAttributes.size() == 0) { + return; + } + + boolean isLoggable = logger.isLoggable(); + + List<Object> qualifierValues = selectQuery.getValuesForLOBSelectQualifier(row); + List<Object> lobValues = selectQuery.getValuesForUpdatedLOBColumns(); + int parametersSize = qualifierValues.size(); + int lobSize = lobAttributes.size(); + + String selectStr = queryBuilder.createLOBSelectString(lobAttributes, qualifierAttributes); + + if (isLoggable) { + logger.logQuery(selectStr, qualifierValues); + logger.logQueryParameters("write LOB", null, lobValues, false); + } + + try (PreparedStatement selectStatement = con.prepareStatement(selectStr);) { + for (int i = 0; i < parametersSize; i++) { + Object value = qualifierValues.get(i); + DbAttribute attribute = qualifierAttributes.get(i); + + adapter.bindParameter(selectStatement, value, i + 1, attribute.getType(), attribute.getScale()); + } + + try (ResultSet result = selectStatement.executeQuery();) { + if (!result.next()) { + throw new CayenneRuntimeException("Missing LOB row."); + } + + // read the only expected row + + for (int i = 0; i < lobSize; i++) { + DbAttribute attribute = lobAttributes.get(i); + int type = attribute.getType(); + + if (type == Types.CLOB) { + Clob clob = result.getClob(i + 1); + Object clobVal = lobValues.get(i); + + if (clobVal instanceof char[]) { + writeClob(clob, (char[]) clobVal); + } else { + writeClob(clob, clobVal.toString()); + } + } else if (type == Types.BLOB) { + Blob blob = result.getBlob(i + 1); + + Object blobVal = lobValues.get(i); + if (blobVal instanceof byte[]) { + writeBlob(blob, (byte[]) blobVal); + } else { + String className = (blobVal != null) ? blobVal.getClass().getName() : null; + throw new CayenneRuntimeException("Unsupported class of BLOB value: " + className); + } + } else { + throw new CayenneRuntimeException("Only BLOB or CLOB is expected here, got: " + type); + } + } + + if (result.next()) { + throw new CayenneRuntimeException("More than one LOB row found."); + } + } + } + } + + /** + * Override the Oracle writeBlob() method to be compatible with Oracle8 + * drivers. + */ + protected void writeBlob(Blob blob, byte[] value) { + // Fix for CAY-1307. For Oracle8, get the method found by reflection in + // OracleAdapter. (Code taken from Cayenne 2.) + Method getBinaryStreamMethod = Oracle8Adapter.getOutputStreamFromBlobMethod(); + try { + + try (OutputStream out = (OutputStream) getBinaryStreamMethod.invoke(blob, (Object[]) null);) { + out.write(value); + out.flush(); + } + } catch (Exception e) { + throw new CayenneRuntimeException("Error processing BLOB.", Util.unwindException(e)); + } + } + + /** + * Override the Oracle writeClob() method to be compatible with Oracle8 + * drivers. + */ + protected void writeClob(Clob clob, char[] value) { + Method getWriterMethod = Oracle8Adapter.getWriterFromClobMethod(); + try { + + try (Writer out = (Writer) getWriterMethod.invoke(clob, (Object[]) null);) { + out.write(value); + out.flush(); + } + + } catch (Exception e) { + throw new CayenneRuntimeException("Error processing CLOB.", Util.unwindException(e)); + } + } + + /** + * Override the Oracle writeClob() method to be compatible with Oracle8 + * drivers. + */ + protected void writeClob(Clob clob, String value) { + Method getWriterMethod = Oracle8Adapter.getWriterFromClobMethod(); + try { + + try (Writer out = (Writer) getWriterMethod.invoke(clob, (Object[]) null);) { + out.write(value); + out.flush(); + } + } catch (Exception e) { + throw new CayenneRuntimeException("Error processing CLOB.", Util.unwindException(e)); + } + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchQueryWrapper.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchQueryWrapper.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchQueryWrapper.java index 5632e73..4cfee1b 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchQueryWrapper.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchQueryWrapper.java @@ -39,168 +39,166 @@ import org.apache.cayenne.query.BatchQueryRow; */ class Oracle8LOBBatchQueryWrapper { - protected BatchQuery query; - - protected List<DbAttribute> dbAttributes; - - // attribute list decoders - protected boolean[] qualifierAttributes; - protected boolean[] allLOBAttributes; - protected Object[] updatedLOBAttributes; - - Oracle8LOBBatchQueryWrapper(BatchQuery query) { - this.query = query; - this.dbAttributes = query.getDbAttributes(); - - int len = dbAttributes.size(); - this.qualifierAttributes = new boolean[len]; - this.allLOBAttributes = new boolean[len]; - this.updatedLOBAttributes = new Object[len]; - - indexQualifierAttributes(); - } - - /** - * Indexes attributes - */ - protected void indexQualifierAttributes() { - int len = this.dbAttributes.size(); - for (int i = 0; i < len; i++) { - DbAttribute attribute = this.dbAttributes.get(i); - int type = attribute.getType(); - qualifierAttributes[i] = attribute.isPrimaryKey(); - allLOBAttributes[i] = type == Types.BLOB || type == Types.CLOB; - } - } - - /** - * Indexes attributes - */ - void indexLOBAttributes(BatchQueryRow row) { - int len = updatedLOBAttributes.length; - for (int i = 0; i < len; i++) { - updatedLOBAttributes[i] = null; - - if (allLOBAttributes[i]) { - // skip null and empty LOBs - Object value = row.getValue(i); - - if (value == null) { - continue; - } - - if (dbAttributes.get(i).getType() == Types.BLOB) { - updatedLOBAttributes[i] = convertToBlobValue(value); - } else { - updatedLOBAttributes[i] = convertToClobValue(value); - } - } - } - } - - /** - * Converts value to byte[] if possible. - */ - protected byte[] convertToBlobValue(Object value) { - if (value instanceof byte[]) { - byte[] bytes = (byte[]) value; - return bytes.length == 0 ? null : bytes; - } else if (value instanceof Serializable) { - ByteArrayOutputStream bytes = new ByteArrayOutputStream() { - - @Override - public synchronized byte[] toByteArray() { - return buf; - } - }; - - try { - ObjectOutputStream out = new ObjectOutputStream(bytes); - out.writeObject(value); - out.close(); - } catch (IOException e) { - throw new CayenneRuntimeException("Error serializing object", e); - } - - return bytes.toByteArray(); - } - - return null; - } - - /** - * Converts to char[] or String. Both are acceptable when writing CLOBs. - */ - protected Object convertToClobValue(Object value) { - - if (value instanceof char[]) { - char[] chars = (char[]) value; - return chars.length == 0 ? null : chars; - } else { - String strValue = value.toString(); - return strValue.length() == 0 ? null : strValue; - } - } - - /** - * Returns a list of DbAttributes used in the qualifier of the query that - * selects a LOB row for LOB update. - */ - List<DbAttribute> getDbAttributesForLOBSelectQualifier() { - - int len = qualifierAttributes.length; - List<DbAttribute> attributes = new ArrayList<DbAttribute>(len); - - for (int i = 0; i < len; i++) { - if (this.qualifierAttributes[i]) { - attributes.add(this.dbAttributes.get(i)); - } - } - return attributes; - } - - /** - * Returns a list of DbAttributes that correspond to the LOB columns updated - * in the current row in the batch query. The list will not include LOB - * attributes that are null or empty. - */ - List<DbAttribute> getDbAttributesForUpdatedLOBColumns() { - - int len = updatedLOBAttributes.length; - List<DbAttribute> attributes = new ArrayList<DbAttribute>(len); - - for (int i = 0; i < len; i++) { - if (this.updatedLOBAttributes[i] != null) { - attributes.add(this.dbAttributes.get(i)); - } - } - return attributes; - } - - List<Object> getValuesForLOBSelectQualifier(BatchQueryRow row) { - - int len = this.qualifierAttributes.length; - List<Object> values = new ArrayList<Object>(len); - for (int i = 0; i < len; i++) { - if (this.qualifierAttributes[i]) { - values.add(row.getValue(i)); - } - } - - return values; - } - - List<Object> getValuesForUpdatedLOBColumns() { - - int len = this.updatedLOBAttributes.length; - List<Object> values = new ArrayList<Object>(len); - for (int i = 0; i < len; i++) { - if (this.updatedLOBAttributes[i] != null) { - values.add(this.updatedLOBAttributes[i]); - } - } - - return values; - } + protected BatchQuery query; + + protected List<DbAttribute> dbAttributes; + + // attribute list decoders + protected boolean[] qualifierAttributes; + protected boolean[] allLOBAttributes; + protected Object[] updatedLOBAttributes; + + Oracle8LOBBatchQueryWrapper(BatchQuery query) { + this.query = query; + this.dbAttributes = query.getDbAttributes(); + + int len = dbAttributes.size(); + this.qualifierAttributes = new boolean[len]; + this.allLOBAttributes = new boolean[len]; + this.updatedLOBAttributes = new Object[len]; + + indexQualifierAttributes(); + } + + /** + * Indexes attributes + */ + protected void indexQualifierAttributes() { + int len = this.dbAttributes.size(); + for (int i = 0; i < len; i++) { + DbAttribute attribute = this.dbAttributes.get(i); + int type = attribute.getType(); + qualifierAttributes[i] = attribute.isPrimaryKey(); + allLOBAttributes[i] = type == Types.BLOB || type == Types.CLOB; + } + } + + /** + * Indexes attributes + */ + void indexLOBAttributes(BatchQueryRow row) { + int len = updatedLOBAttributes.length; + for (int i = 0; i < len; i++) { + updatedLOBAttributes[i] = null; + + if (allLOBAttributes[i]) { + // skip null and empty LOBs + Object value = row.getValue(i); + + if (value == null) { + continue; + } + + if (dbAttributes.get(i).getType() == Types.BLOB) { + updatedLOBAttributes[i] = convertToBlobValue(value); + } else { + updatedLOBAttributes[i] = convertToClobValue(value); + } + } + } + } + + /** + * Converts value to byte[] if possible. + */ + protected byte[] convertToBlobValue(Object value) { + if (value instanceof byte[]) { + byte[] bytes = (byte[]) value; + return bytes.length == 0 ? null : bytes; + } else if (value instanceof Serializable) { + ByteArrayOutputStream bytes = new ByteArrayOutputStream() { + + @Override + public synchronized byte[] toByteArray() { + return buf; + } + }; + + try (ObjectOutputStream out = new ObjectOutputStream(bytes);) { + out.writeObject(value); + } catch (IOException e) { + throw new CayenneRuntimeException("Error serializing object", e); + } + + return bytes.toByteArray(); + } + + return null; + } + + /** + * Converts to char[] or String. Both are acceptable when writing CLOBs. + */ + protected Object convertToClobValue(Object value) { + + if (value instanceof char[]) { + char[] chars = (char[]) value; + return chars.length == 0 ? null : chars; + } else { + String strValue = value.toString(); + return strValue.length() == 0 ? null : strValue; + } + } + + /** + * Returns a list of DbAttributes used in the qualifier of the query that + * selects a LOB row for LOB update. + */ + List<DbAttribute> getDbAttributesForLOBSelectQualifier() { + + int len = qualifierAttributes.length; + List<DbAttribute> attributes = new ArrayList<DbAttribute>(len); + + for (int i = 0; i < len; i++) { + if (this.qualifierAttributes[i]) { + attributes.add(this.dbAttributes.get(i)); + } + } + return attributes; + } + + /** + * Returns a list of DbAttributes that correspond to the LOB columns updated + * in the current row in the batch query. The list will not include LOB + * attributes that are null or empty. + */ + List<DbAttribute> getDbAttributesForUpdatedLOBColumns() { + + int len = updatedLOBAttributes.length; + List<DbAttribute> attributes = new ArrayList<DbAttribute>(len); + + for (int i = 0; i < len; i++) { + if (this.updatedLOBAttributes[i] != null) { + attributes.add(this.dbAttributes.get(i)); + } + } + return attributes; + } + + List<Object> getValuesForLOBSelectQualifier(BatchQueryRow row) { + + int len = this.qualifierAttributes.length; + List<Object> values = new ArrayList<Object>(len); + for (int i = 0; i < len; i++) { + if (this.qualifierAttributes[i]) { + values.add(row.getValue(i)); + } + } + + return values; + } + + List<Object> getValuesForUpdatedLOBColumns() { + + int len = this.updatedLOBAttributes.length; + List<Object> values = new ArrayList<Object>(len); + for (int i = 0; i < len; i++) { + if (this.updatedLOBAttributes[i] != null) { + values.add(this.updatedLOBAttributes[i]); + } + } + + return values; + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OraclePkGenerator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OraclePkGenerator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OraclePkGenerator.java index 6f775db..4c0f630 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OraclePkGenerator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OraclePkGenerator.java @@ -54,201 +54,186 @@ import org.apache.cayenne.map.DbKeyGenerator; */ public class OraclePkGenerator extends JdbcPkGenerator { - protected OraclePkGenerator(JdbcAdapter adapter) { - super(adapter); - } - - private static final String _SEQUENCE_PREFIX = "pk_"; - - @Override - public void createAutoPk(DataNode node, List dbEntities) throws Exception { - List sequences = getExistingSequences(node); - - // create needed sequences - Iterator it = dbEntities.iterator(); - while (it.hasNext()) { - DbEntity ent = (DbEntity) it.next(); - if (!sequences.contains(sequenceName(ent))) { - runUpdate(node, createSequenceString(ent)); - } - } - } - - @Override - public List createAutoPkStatements(List dbEntities) { - List<String> list = new ArrayList<String>(); - Iterator it = dbEntities.iterator(); - while (it.hasNext()) { - DbEntity ent = (DbEntity) it.next(); - list.add(createSequenceString(ent)); - } - - return list; - } - - @Override - public void dropAutoPk(DataNode node, List dbEntities) throws Exception { - List sequences = getExistingSequences(node); - - // drop obsolete sequences - Iterator it = dbEntities.iterator(); - while (it.hasNext()) { - DbEntity ent = (DbEntity) it.next(); - String name; - if (ent.getDataMap().isQuotingSQLIdentifiers()) { - DbEntity tempEnt = new DbEntity(); - DataMap dm = new DataMap(); - dm.setQuotingSQLIdentifiers(false); - tempEnt.setDataMap(dm); - tempEnt.setName(ent.getName()); - name = stripSchemaName(sequenceName(tempEnt)); - } else { - name = stripSchemaName(sequenceName(ent)); - } - if (sequences.contains(name)) { - runUpdate(node, dropSequenceString(ent)); - } - } - } - - @Override - public List dropAutoPkStatements(List dbEntities) { - List<String> list = new ArrayList<String>(); - Iterator it = dbEntities.iterator(); - while (it.hasNext()) { - DbEntity ent = (DbEntity) it.next(); - list.add(dropSequenceString(ent)); - } - - return list; - } - - protected String createSequenceString(DbEntity ent) { - return "CREATE SEQUENCE " + sequenceName(ent) + " START WITH " + pkStartValue - + " INCREMENT BY " + pkCacheSize(ent); - } - - /** - * Returns a SQL string needed to drop any database objects associated with - * automatic primary key generation process for a specific DbEntity. - */ - protected String dropSequenceString(DbEntity ent) { - - return "DROP SEQUENCE " + sequenceName(ent); - } - - /** - * Generates primary key by calling Oracle sequence corresponding to the - * <code>dbEntity</code>. Executed SQL looks like this: - * - * <pre> - * SELECT pk_table_name.nextval FROM DUAL - * </pre> - * - * @since 3.0 - */ - @Override - protected long longPkFromDatabase(DataNode node, DbEntity entity) throws Exception { - - DbKeyGenerator pkGenerator = entity.getPrimaryKeyGenerator(); - String pkGeneratingSequenceName; - if (pkGenerator != null && DbKeyGenerator.ORACLE_TYPE.equals(pkGenerator.getGeneratorType()) - && pkGenerator.getGeneratorName() != null) { - pkGeneratingSequenceName = pkGenerator.getGeneratorName(); - } else { - pkGeneratingSequenceName = sequenceName(entity); - } - - Connection con = node.getDataSource().getConnection(); - try { - Statement st = con.createStatement(); - try { - String sql = "SELECT " + pkGeneratingSequenceName + ".nextval FROM DUAL"; - adapter.getJdbcEventLogger().logQuery(sql, Collections.EMPTY_LIST); - ResultSet rs = st.executeQuery(sql); - try { - // Object pk = null; - if (!rs.next()) { - throw new CayenneRuntimeException("Error generating pk for DbEntity " + entity.getName()); - } - return rs.getLong(1); - } finally { - rs.close(); - } - } finally { - st.close(); - } - } finally { - con.close(); - } - - } - - protected int pkCacheSize(DbEntity entity) { - // use custom generator if possible - DbKeyGenerator keyGenerator = entity.getPrimaryKeyGenerator(); - if (keyGenerator != null && DbKeyGenerator.ORACLE_TYPE.equals(keyGenerator.getGeneratorType()) - && keyGenerator.getGeneratorName() != null) { - - Integer size = keyGenerator.getKeyCacheSize(); - return (size != null && size.intValue() >= 1) ? size.intValue() : super.getPkCacheSize(); - } else { - return super.getPkCacheSize(); - } - } - - /** Returns expected primary key sequence name for a DbEntity. */ - protected String sequenceName(DbEntity entity) { - - // use custom generator if possible - DbKeyGenerator keyGenerator = entity.getPrimaryKeyGenerator(); - if (keyGenerator != null && DbKeyGenerator.ORACLE_TYPE.equals(keyGenerator.getGeneratorType()) - && keyGenerator.getGeneratorName() != null) { - - return keyGenerator.getGeneratorName().toLowerCase(); - } else { - String entName = entity.getName(); - String seqName = _SEQUENCE_PREFIX + entName.toLowerCase(); - - return adapter.getQuotingStrategy().quotedIdentifier(entity, entity.getCatalog(), entity.getSchema(), - seqName); - } - } - - protected String stripSchemaName(String sequenceName) { - int ind = sequenceName.indexOf('.'); - return ind >= 0 ? sequenceName.substring(ind + 1) : sequenceName; - } - - /** - * Fetches a list of existing sequences that might match Cayenne generated - * ones. - */ - protected List getExistingSequences(DataNode node) throws SQLException { - - // check existing sequences - Connection con = node.getDataSource().getConnection(); - - try { - Statement sel = con.createStatement(); - try { - String sql = "SELECT LOWER(SEQUENCE_NAME) FROM ALL_SEQUENCES"; - adapter.getJdbcEventLogger().logQuery(sql, Collections.EMPTY_LIST); - ResultSet rs = sel.executeQuery(sql); - try { - List<String> sequenceList = new ArrayList<String>(); - while (rs.next()) { - sequenceList.add(rs.getString(1)); - } - return sequenceList; - } finally { - rs.close(); - } - } finally { - sel.close(); - } - } finally { - con.close(); - } - } + protected OraclePkGenerator(JdbcAdapter adapter) { + super(adapter); + } + + private static final String _SEQUENCE_PREFIX = "pk_"; + + @Override + public void createAutoPk(DataNode node, List dbEntities) throws Exception { + List sequences = getExistingSequences(node); + + // create needed sequences + Iterator it = dbEntities.iterator(); + while (it.hasNext()) { + DbEntity ent = (DbEntity) it.next(); + if (!sequences.contains(sequenceName(ent))) { + runUpdate(node, createSequenceString(ent)); + } + } + } + + @Override + public List createAutoPkStatements(List dbEntities) { + List<String> list = new ArrayList<String>(); + Iterator it = dbEntities.iterator(); + while (it.hasNext()) { + DbEntity ent = (DbEntity) it.next(); + list.add(createSequenceString(ent)); + } + + return list; + } + + @Override + public void dropAutoPk(DataNode node, List dbEntities) throws Exception { + List sequences = getExistingSequences(node); + + // drop obsolete sequences + Iterator it = dbEntities.iterator(); + while (it.hasNext()) { + DbEntity ent = (DbEntity) it.next(); + String name; + if (ent.getDataMap().isQuotingSQLIdentifiers()) { + DbEntity tempEnt = new DbEntity(); + DataMap dm = new DataMap(); + dm.setQuotingSQLIdentifiers(false); + tempEnt.setDataMap(dm); + tempEnt.setName(ent.getName()); + name = stripSchemaName(sequenceName(tempEnt)); + } else { + name = stripSchemaName(sequenceName(ent)); + } + if (sequences.contains(name)) { + runUpdate(node, dropSequenceString(ent)); + } + } + } + + @Override + public List dropAutoPkStatements(List dbEntities) { + List<String> list = new ArrayList<String>(); + Iterator it = dbEntities.iterator(); + while (it.hasNext()) { + DbEntity ent = (DbEntity) it.next(); + list.add(dropSequenceString(ent)); + } + + return list; + } + + protected String createSequenceString(DbEntity ent) { + return "CREATE SEQUENCE " + sequenceName(ent) + " START WITH " + pkStartValue + " INCREMENT BY " + + pkCacheSize(ent); + } + + /** + * Returns a SQL string needed to drop any database objects associated with + * automatic primary key generation process for a specific DbEntity. + */ + protected String dropSequenceString(DbEntity ent) { + + return "DROP SEQUENCE " + sequenceName(ent); + } + + /** + * Generates primary key by calling Oracle sequence corresponding to the + * <code>dbEntity</code>. Executed SQL looks like this: + * + * <pre> + * SELECT pk_table_name.nextval FROM DUAL + * </pre> + * + * @since 3.0 + */ + @Override + protected long longPkFromDatabase(DataNode node, DbEntity entity) throws Exception { + + DbKeyGenerator pkGenerator = entity.getPrimaryKeyGenerator(); + String pkGeneratingSequenceName; + if (pkGenerator != null && DbKeyGenerator.ORACLE_TYPE.equals(pkGenerator.getGeneratorType()) + && pkGenerator.getGeneratorName() != null) { + pkGeneratingSequenceName = pkGenerator.getGeneratorName(); + } else { + pkGeneratingSequenceName = sequenceName(entity); + } + + try (Connection con = node.getDataSource().getConnection();) { + + try (Statement st = con.createStatement();) { + String sql = "SELECT " + pkGeneratingSequenceName + ".nextval FROM DUAL"; + adapter.getJdbcEventLogger().logQuery(sql, Collections.EMPTY_LIST); + + try (ResultSet rs = st.executeQuery(sql);) { + // Object pk = null; + if (!rs.next()) { + throw new CayenneRuntimeException("Error generating pk for DbEntity " + entity.getName()); + } + return rs.getLong(1); + } + } + } + } + + protected int pkCacheSize(DbEntity entity) { + // use custom generator if possible + DbKeyGenerator keyGenerator = entity.getPrimaryKeyGenerator(); + if (keyGenerator != null && DbKeyGenerator.ORACLE_TYPE.equals(keyGenerator.getGeneratorType()) + && keyGenerator.getGeneratorName() != null) { + + Integer size = keyGenerator.getKeyCacheSize(); + return (size != null && size.intValue() >= 1) ? size.intValue() : super.getPkCacheSize(); + } else { + return super.getPkCacheSize(); + } + } + + /** Returns expected primary key sequence name for a DbEntity. */ + protected String sequenceName(DbEntity entity) { + + // use custom generator if possible + DbKeyGenerator keyGenerator = entity.getPrimaryKeyGenerator(); + if (keyGenerator != null && DbKeyGenerator.ORACLE_TYPE.equals(keyGenerator.getGeneratorType()) + && keyGenerator.getGeneratorName() != null) { + + return keyGenerator.getGeneratorName().toLowerCase(); + } else { + String entName = entity.getName(); + String seqName = _SEQUENCE_PREFIX + entName.toLowerCase(); + + return adapter.getQuotingStrategy().quotedIdentifier(entity, entity.getCatalog(), entity.getSchema(), + seqName); + } + } + + protected String stripSchemaName(String sequenceName) { + int ind = sequenceName.indexOf('.'); + return ind >= 0 ? sequenceName.substring(ind + 1) : sequenceName; + } + + /** + * Fetches a list of existing sequences that might match Cayenne generated + * ones. + */ + protected List getExistingSequences(DataNode node) throws SQLException { + + // check existing sequences + + try (Connection con = node.getDataSource().getConnection();) { + + try (Statement sel = con.createStatement();) { + String sql = "SELECT LOWER(SEQUENCE_NAME) FROM ALL_SEQUENCES"; + adapter.getJdbcEventLogger().logQuery(sql, Collections.EMPTY_LIST); + + try (ResultSet rs = sel.executeQuery(sql);) { + List<String> sequenceList = new ArrayList<String>(); + while (rs.next()) { + sequenceList.add(rs.getString(1)); + } + return sequenceList; + } + } + } + } }
