Return-Path: X-Original-To: apmail-cayenne-commits-archive@www.apache.org Delivered-To: apmail-cayenne-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 05F9D17C36 for ; Mon, 11 May 2015 04:42:37 +0000 (UTC) Received: (qmail 11378 invoked by uid 500); 11 May 2015 04:42:36 -0000 Delivered-To: apmail-cayenne-commits-archive@cayenne.apache.org Received: (qmail 11311 invoked by uid 500); 11 May 2015 04:42:36 -0000 Mailing-List: contact commits-help@cayenne.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cayenne.apache.org Delivered-To: mailing list commits@cayenne.apache.org Received: (qmail 11260 invoked by uid 99); 11 May 2015 04:42:36 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 11 May 2015 04:42:36 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id AE2B1DFB3B; Mon, 11 May 2015 04:42:36 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: aadamchik@apache.org To: commits@cayenne.apache.org Date: Mon, 11 May 2015 04:42:38 -0000 Message-Id: <2e6acbf9cf244850a7b4e5ff73380409@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [3/8] cayenne git commit: CAY-2007 Refactoring SelectTranslator for better extensibility http://git-wip-us.apache.org/repos/asf/cayenne/blob/741ad3be/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java index 1a37f36..57456f5 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java @@ -36,6 +36,7 @@ import org.apache.cayenne.access.translator.ejbql.EJBQLTranslatorFactory; import org.apache.cayenne.access.translator.ejbql.JdbcEJBQLTranslatorFactory; import org.apache.cayenne.access.translator.select.QualifierTranslator; import org.apache.cayenne.access.translator.select.QueryAssembler; +import org.apache.cayenne.access.translator.select.SelectTranslator; import org.apache.cayenne.access.types.ByteArrayType; import org.apache.cayenne.access.types.CharType; import org.apache.cayenne.access.types.ExtendedType; @@ -52,9 +53,11 @@ import org.apache.cayenne.di.Inject; import org.apache.cayenne.map.DbAttribute; import org.apache.cayenne.map.DbEntity; import org.apache.cayenne.map.DbRelationship; +import org.apache.cayenne.map.EntityResolver; import org.apache.cayenne.merge.MergerFactory; import org.apache.cayenne.query.Query; import org.apache.cayenne.query.SQLAction; +import org.apache.cayenne.query.SelectQuery; import org.apache.cayenne.resource.ResourceLocator; /** @@ -64,9 +67,8 @@ import org.apache.cayenne.resource.ResourceLocator; *

* Foreign key constraints are supported by InnoDB engine and NOT supported by * MyISAM engine. This adapter by default assumes MyISAM, so - * supportsFkConstraints will - * be false. Users can manually change this by calling - * setSupportsFkConstraints(true) or better by using an + * supportsFkConstraints will be false. Users can manually change + * this by calling setSupportsFkConstraints(true) or better by using an * {@link org.apache.cayenne.dba.AutoAdapter}, i.e. not entering the adapter * name at all for the DataNode, letting Cayenne guess it in runtime. In the * later case Cayenne will check the table_type MySQL variable to @@ -80,330 +82,341 @@ import org.apache.cayenne.resource.ResourceLocator; */ public class MySQLAdapter extends JdbcAdapter { - static final String DEFAULT_STORAGE_ENGINE = "InnoDB"; - static final String MYSQL_QUOTE_SQL_IDENTIFIERS_CHAR_START = "`"; - static final String MYSQL_QUOTE_SQL_IDENTIFIERS_CHAR_END = "`"; - - protected String storageEngine; - protected boolean supportsFkConstraints; - - public MySQLAdapter(@Inject RuntimeProperties runtimeProperties, - @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List defaultExtendedTypes, - @Inject(Constants.SERVER_USER_TYPES_LIST) List userExtendedTypes, - @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List extendedTypeFactories, - @Inject ResourceLocator resourceLocator) { - super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, extendedTypeFactories, resourceLocator); - - // init defaults - this.storageEngine = DEFAULT_STORAGE_ENGINE; - - setSupportsBatchUpdates(true); - setSupportsFkConstraints(true); - setSupportsUniqueConstraints(true); - setSupportsGeneratedKeys(true); - } - - void setSupportsFkConstraints(boolean flag) { - this.supportsFkConstraints = flag; - } - - @Override - protected QuotingStrategy createQuotingStrategy() { - return new DefaultQuotingStrategy("`", "`"); - } - - @Override - public QualifierTranslator getQualifierTranslator(QueryAssembler queryAssembler) { - QualifierTranslator translator = new MySQLQualifierTranslator(queryAssembler); - translator.setCaseInsensitive(caseInsensitiveCollations); - return translator; - } - - /** - * Uses special action builder to create the right action. - * - * @since 1.2 - */ - @Override - public SQLAction getAction(Query query, DataNode node) { - return query.createSQLAction(new MySQLActionBuilder(node)); - } - - /** - * @since 3.0 - */ - @Override - public Collection dropTableStatements(DbEntity table) { - // note that CASCADE is a noop as of MySQL 5.0, so we have to use FK - // checks - // statement - StringBuilder buf = new StringBuilder(); - QuotingStrategy context = getQuotingStrategy(); - buf.append(context.quotedFullyQualifiedName(table)); - - return Arrays.asList("SET FOREIGN_KEY_CHECKS=0", "DROP TABLE IF EXISTS " + buf.toString() + " CASCADE", - "SET FOREIGN_KEY_CHECKS=1"); - } - - /** - * Installs appropriate ExtendedTypes used as converters for passing values - * between JDBC and Java layers. - */ - @Override - protected void configureExtendedTypes(ExtendedTypeMap map) { - super.configureExtendedTypes(map); - - // must handle CLOBs as strings, otherwise there - // are problems with NULL clobs that are treated - // as empty strings... somehow this doesn't happen - // for BLOBs (ConnectorJ v. 3.0.9) - map.registerType(new CharType(false, false)); - map.registerType(new ByteArrayType(false, false)); - } - - @Override - public DbAttribute buildAttribute(String name, String typeName, int type, int size, int precision, - boolean allowNulls) { - - if (typeName != null) { - typeName = typeName.toLowerCase(); - } - - // all LOB types are returned by the driver as OTHER... must remap them - // manually - // (at least on MySQL 3.23) - if (type == Types.OTHER) { - if ("longblob".equals(typeName)) { - type = Types.BLOB; - } else if ("mediumblob".equals(typeName)) { - type = Types.BLOB; - } else if ("blob".equals(typeName)) { - type = Types.BLOB; - } else if ("tinyblob".equals(typeName)) { - type = Types.VARBINARY; - } else if ("longtext".equals(typeName)) { - type = Types.CLOB; - } else if ("mediumtext".equals(typeName)) { - type = Types.CLOB; - } else if ("text".equals(typeName)) { - type = Types.CLOB; - } else if ("tinytext".equals(typeName)) { - type = Types.VARCHAR; - } - } - // types like "int unsigned" map to Long - else if (typeName != null && typeName.endsWith(" unsigned")) { - // per - // http://dev.mysql.com/doc/refman/5.0/en/connector-j-reference-type-conversions.html - if (typeName.equals("int unsigned") || typeName.equals("integer unsigned") - || typeName.equals("mediumint unsigned")) { - type = Types.BIGINT; - } - // BIGINT UNSIGNED maps to BigInteger according to MySQL docs, but - // there is no - // JDBC mapping for BigInteger - } - - return super.buildAttribute(name, typeName, type, size, precision, allowNulls); - } - - @Override - public void bindParameter(PreparedStatement statement, Object object, int pos, int sqlType, int scale) throws SQLException, Exception { - super.bindParameter(statement, object, pos, mapNTypes(sqlType), scale); - } - - private int mapNTypes(int sqlType) { - switch (sqlType) { - case Types.NCHAR : return Types.CHAR; - case Types.NCLOB : return Types.CLOB; - case Types.NVARCHAR : return Types.VARCHAR; - case Types.LONGNVARCHAR : return Types.LONGVARCHAR; - - default: - return sqlType; - } - } - - /** - * Creates and returns a primary key generator. Overrides superclass - * implementation to return an instance of MySQLPkGenerator that does the - * correct table locking. - */ - @Override - protected PkGenerator createPkGenerator() { - return new MySQLPkGenerator(this); - } - - /** - * @since 3.0 - */ - @Override - protected EJBQLTranslatorFactory createEJBQLTranslatorFactory() { - JdbcEJBQLTranslatorFactory translatorFactory = new MySQLEJBQLTranslatorFactory(); - translatorFactory.setCaseInsensitive(caseInsensitiveCollations); - return translatorFactory; - } - - /** - * Overrides super implementation to explicitly set table engine to InnoDB - * if FK constraints are supported by this adapter. - */ - @Override - public String createTable(DbEntity entity) { - String ddlSQL = super.createTable(entity); - - if (storageEngine != null) { - ddlSQL += " ENGINE=" + storageEngine; - } - - return ddlSQL; - } - - /** - * Customizes PK clause semantics to ensure that generated columns are in - * the beginning of the PK definition, as this seems to be a requirement for - * InnoDB tables. - * - * @since 1.2 - */ - // See CAY-358 for details of the InnoDB problem - @Override - protected void createTableAppendPKClause(StringBuffer sqlBuffer, DbEntity entity) { - - // must move generated to the front... - List pkList = new ArrayList(entity.getPrimaryKeys()); - Collections.sort(pkList, new PKComparator()); - - Iterator pkit = pkList.iterator(); - if (pkit.hasNext()) { - - sqlBuffer.append(", PRIMARY KEY ("); - boolean firstPk = true; - while (pkit.hasNext()) { - if (firstPk) - firstPk = false; - else - sqlBuffer.append(", "); - - DbAttribute at = pkit.next(); - sqlBuffer.append(quotingStrategy.quotedName(at)); - } - sqlBuffer.append(')'); - } - - // if FK constraints are supported, we must add indices to all FKs - // Note that according to MySQL docs, FK indexes are created - // automatically when - // constraint is defined, starting at MySQL 4.1.2 - if (supportsFkConstraints) { - for (DbRelationship r : entity.getRelationships()) { - if (r.getJoins().size() > 0 && r.isToPK() && !r.isToDependentPK()) { - - sqlBuffer.append(", KEY ("); - - Iterator columns = r.getSourceAttributes().iterator(); - DbAttribute column = columns.next(); - sqlBuffer.append(quotingStrategy.quotedName(column)); - - while (columns.hasNext()) { - column = columns.next(); - sqlBuffer.append(", ").append(quotingStrategy.quotedName(column)); - } - - sqlBuffer.append(")"); - } - } - } - } - - /** - * Appends AUTO_INCREMENT clause to the column definition for generated - * columns. - */ - @Override - public void createTableAppendColumn(StringBuffer sqlBuffer, DbAttribute column) { - - String[] types = externalTypesForJdbcType(column.getType()); - if (types == null || types.length == 0) { - String entityName = column.getEntity() != null ? ((DbEntity) column.getEntity()).getFullyQualifiedName() - : ""; - throw new CayenneRuntimeException("Undefined type for attribute '" + entityName + "." + column.getName() - + "': " + column.getType()); - } - - String type = types[0]; - sqlBuffer.append(quotingStrategy.quotedName(column)); - sqlBuffer.append(' ').append(type); - - // append size and precision (if applicable)s - if (typeSupportsLength(column.getType())) { - int len = column.getMaxLength(); - - int scale = TypesMapping.isDecimal(column.getType()) ? column.getScale() : -1; - - // sanity check - if (scale > len) { - scale = -1; - } - - if (len > 0) { - sqlBuffer.append('(').append(len); - - if (scale >= 0) { - sqlBuffer.append(", ").append(scale); - } - - sqlBuffer.append(')'); - } - } - - sqlBuffer.append(column.isMandatory() ? " NOT NULL" : " NULL"); - - if (column.isGenerated()) { - sqlBuffer.append(" AUTO_INCREMENT"); - } - } - - @Override - public boolean typeSupportsLength(int type) { - // As of MySQL 5.6.4 the "TIMESTAMP" and "TIME" types support length, which is the number of decimal places for fractional seconds - // http://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html - switch (type) { - case Types.TIMESTAMP: - case Types.TIME: - return true; - default: - return super.typeSupportsLength(type); - } - } - - @Override - public MergerFactory mergerFactory() { - return new MySQLMergerFactory(); - } - - final class PKComparator implements Comparator { - - public int compare(DbAttribute a1, DbAttribute a2) { - if (a1.isGenerated() != a2.isGenerated()) { - return a1.isGenerated() ? -1 : 1; - } else { - return a1.getName().compareTo(a2.getName()); - } - } - } - - /** - * @since 3.0 - */ - public String getStorageEngine() { - return storageEngine; - } - - /** - * @since 3.0 - */ - public void setStorageEngine(String engine) { - this.storageEngine = engine; - } + static final String DEFAULT_STORAGE_ENGINE = "InnoDB"; + static final String MYSQL_QUOTE_SQL_IDENTIFIERS_CHAR_START = "`"; + static final String MYSQL_QUOTE_SQL_IDENTIFIERS_CHAR_END = "`"; + + protected String storageEngine; + protected boolean supportsFkConstraints; + + public MySQLAdapter(@Inject RuntimeProperties runtimeProperties, + @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List defaultExtendedTypes, + @Inject(Constants.SERVER_USER_TYPES_LIST) List userExtendedTypes, + @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List extendedTypeFactories, + @Inject ResourceLocator resourceLocator) { + super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, extendedTypeFactories, resourceLocator); + + // init defaults + this.storageEngine = DEFAULT_STORAGE_ENGINE; + + setSupportsBatchUpdates(true); + setSupportsFkConstraints(true); + setSupportsUniqueConstraints(true); + setSupportsGeneratedKeys(true); + } + + void setSupportsFkConstraints(boolean flag) { + this.supportsFkConstraints = flag; + } + + @Override + protected QuotingStrategy createQuotingStrategy() { + return new DefaultQuotingStrategy("`", "`"); + } + + @Override + public SelectTranslator getSelectTranslator(SelectQuery query, EntityResolver entityResolver) { + return new MySQLSelectTranslator(query, this, entityResolver); + } + + @Override + public QualifierTranslator getQualifierTranslator(QueryAssembler queryAssembler) { + QualifierTranslator translator = new MySQLQualifierTranslator(queryAssembler); + translator.setCaseInsensitive(caseInsensitiveCollations); + return translator; + } + + /** + * Uses special action builder to create the right action. + * + * @since 1.2 + */ + @Override + public SQLAction getAction(Query query, DataNode node) { + return query.createSQLAction(new MySQLActionBuilder(node)); + } + + /** + * @since 3.0 + */ + @Override + public Collection dropTableStatements(DbEntity table) { + // note that CASCADE is a noop as of MySQL 5.0, so we have to use FK + // checks + // statement + StringBuilder buf = new StringBuilder(); + QuotingStrategy context = getQuotingStrategy(); + buf.append(context.quotedFullyQualifiedName(table)); + + return Arrays.asList("SET FOREIGN_KEY_CHECKS=0", "DROP TABLE IF EXISTS " + buf.toString() + " CASCADE", + "SET FOREIGN_KEY_CHECKS=1"); + } + + /** + * Installs appropriate ExtendedTypes used as converters for passing values + * between JDBC and Java layers. + */ + @Override + protected void configureExtendedTypes(ExtendedTypeMap map) { + super.configureExtendedTypes(map); + + // must handle CLOBs as strings, otherwise there + // are problems with NULL clobs that are treated + // as empty strings... somehow this doesn't happen + // for BLOBs (ConnectorJ v. 3.0.9) + map.registerType(new CharType(false, false)); + map.registerType(new ByteArrayType(false, false)); + } + + @Override + public DbAttribute buildAttribute(String name, String typeName, int type, int size, int precision, + boolean allowNulls) { + + if (typeName != null) { + typeName = typeName.toLowerCase(); + } + + // all LOB types are returned by the driver as OTHER... must remap them + // manually + // (at least on MySQL 3.23) + if (type == Types.OTHER) { + if ("longblob".equals(typeName)) { + type = Types.BLOB; + } else if ("mediumblob".equals(typeName)) { + type = Types.BLOB; + } else if ("blob".equals(typeName)) { + type = Types.BLOB; + } else if ("tinyblob".equals(typeName)) { + type = Types.VARBINARY; + } else if ("longtext".equals(typeName)) { + type = Types.CLOB; + } else if ("mediumtext".equals(typeName)) { + type = Types.CLOB; + } else if ("text".equals(typeName)) { + type = Types.CLOB; + } else if ("tinytext".equals(typeName)) { + type = Types.VARCHAR; + } + } + // types like "int unsigned" map to Long + else if (typeName != null && typeName.endsWith(" unsigned")) { + // per + // http://dev.mysql.com/doc/refman/5.0/en/connector-j-reference-type-conversions.html + if (typeName.equals("int unsigned") || typeName.equals("integer unsigned") + || typeName.equals("mediumint unsigned")) { + type = Types.BIGINT; + } + // BIGINT UNSIGNED maps to BigInteger according to MySQL docs, but + // there is no + // JDBC mapping for BigInteger + } + + return super.buildAttribute(name, typeName, type, size, precision, allowNulls); + } + + @Override + public void bindParameter(PreparedStatement statement, Object object, int pos, int sqlType, int scale) + throws SQLException, Exception { + super.bindParameter(statement, object, pos, mapNTypes(sqlType), scale); + } + + private int mapNTypes(int sqlType) { + switch (sqlType) { + case Types.NCHAR: + return Types.CHAR; + case Types.NCLOB: + return Types.CLOB; + case Types.NVARCHAR: + return Types.VARCHAR; + case Types.LONGNVARCHAR: + return Types.LONGVARCHAR; + + default: + return sqlType; + } + } + + /** + * Creates and returns a primary key generator. Overrides superclass + * implementation to return an instance of MySQLPkGenerator that does the + * correct table locking. + */ + @Override + protected PkGenerator createPkGenerator() { + return new MySQLPkGenerator(this); + } + + /** + * @since 3.0 + */ + @Override + protected EJBQLTranslatorFactory createEJBQLTranslatorFactory() { + JdbcEJBQLTranslatorFactory translatorFactory = new MySQLEJBQLTranslatorFactory(); + translatorFactory.setCaseInsensitive(caseInsensitiveCollations); + return translatorFactory; + } + + /** + * Overrides super implementation to explicitly set table engine to InnoDB + * if FK constraints are supported by this adapter. + */ + @Override + public String createTable(DbEntity entity) { + String ddlSQL = super.createTable(entity); + + if (storageEngine != null) { + ddlSQL += " ENGINE=" + storageEngine; + } + + return ddlSQL; + } + + /** + * Customizes PK clause semantics to ensure that generated columns are in + * the beginning of the PK definition, as this seems to be a requirement for + * InnoDB tables. + * + * @since 1.2 + */ + // See CAY-358 for details of the InnoDB problem + @Override + protected void createTableAppendPKClause(StringBuffer sqlBuffer, DbEntity entity) { + + // must move generated to the front... + List pkList = new ArrayList(entity.getPrimaryKeys()); + Collections.sort(pkList, new PKComparator()); + + Iterator pkit = pkList.iterator(); + if (pkit.hasNext()) { + + sqlBuffer.append(", PRIMARY KEY ("); + boolean firstPk = true; + while (pkit.hasNext()) { + if (firstPk) + firstPk = false; + else + sqlBuffer.append(", "); + + DbAttribute at = pkit.next(); + sqlBuffer.append(quotingStrategy.quotedName(at)); + } + sqlBuffer.append(')'); + } + + // if FK constraints are supported, we must add indices to all FKs + // Note that according to MySQL docs, FK indexes are created + // automatically when + // constraint is defined, starting at MySQL 4.1.2 + if (supportsFkConstraints) { + for (DbRelationship r : entity.getRelationships()) { + if (r.getJoins().size() > 0 && r.isToPK() && !r.isToDependentPK()) { + + sqlBuffer.append(", KEY ("); + + Iterator columns = r.getSourceAttributes().iterator(); + DbAttribute column = columns.next(); + sqlBuffer.append(quotingStrategy.quotedName(column)); + + while (columns.hasNext()) { + column = columns.next(); + sqlBuffer.append(", ").append(quotingStrategy.quotedName(column)); + } + + sqlBuffer.append(")"); + } + } + } + } + + /** + * Appends AUTO_INCREMENT clause to the column definition for generated + * columns. + */ + @Override + public void createTableAppendColumn(StringBuffer sqlBuffer, DbAttribute column) { + + String[] types = externalTypesForJdbcType(column.getType()); + if (types == null || types.length == 0) { + String entityName = column.getEntity() != null ? ((DbEntity) column.getEntity()).getFullyQualifiedName() + : ""; + throw new CayenneRuntimeException("Undefined type for attribute '" + entityName + "." + column.getName() + + "': " + column.getType()); + } + + String type = types[0]; + sqlBuffer.append(quotingStrategy.quotedName(column)); + sqlBuffer.append(' ').append(type); + + // append size and precision (if applicable)s + if (typeSupportsLength(column.getType())) { + int len = column.getMaxLength(); + + int scale = TypesMapping.isDecimal(column.getType()) ? column.getScale() : -1; + + // sanity check + if (scale > len) { + scale = -1; + } + + if (len > 0) { + sqlBuffer.append('(').append(len); + + if (scale >= 0) { + sqlBuffer.append(", ").append(scale); + } + + sqlBuffer.append(')'); + } + } + + sqlBuffer.append(column.isMandatory() ? " NOT NULL" : " NULL"); + + if (column.isGenerated()) { + sqlBuffer.append(" AUTO_INCREMENT"); + } + } + + @Override + public boolean typeSupportsLength(int type) { + // As of MySQL 5.6.4 the "TIMESTAMP" and "TIME" types support length, + // which is the number of decimal places for fractional seconds + // http://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html + switch (type) { + case Types.TIMESTAMP: + case Types.TIME: + return true; + default: + return super.typeSupportsLength(type); + } + } + + @Override + public MergerFactory mergerFactory() { + return new MySQLMergerFactory(); + } + + final class PKComparator implements Comparator { + + public int compare(DbAttribute a1, DbAttribute a2) { + if (a1.isGenerated() != a2.isGenerated()) { + return a1.isGenerated() ? -1 : 1; + } else { + return a1.getName().compareTo(a2.getName()); + } + } + } + + /** + * @since 3.0 + */ + public String getStorageEngine() { + return storageEngine; + } + + /** + * @since 3.0 + */ + public void setStorageEngine(String engine) { + this.storageEngine = engine; + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/741ad3be/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSelectAction.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSelectAction.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSelectAction.java index a39bd16..0b522d3 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSelectAction.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSelectAction.java @@ -20,7 +20,6 @@ package org.apache.cayenne.dba.mysql; import org.apache.cayenne.access.DataNode; import org.apache.cayenne.access.jdbc.SelectAction; -import org.apache.cayenne.access.translator.select.SelectTranslator; import org.apache.cayenne.query.SelectQuery; /** @@ -36,9 +35,4 @@ class MySQLSelectAction extends SelectAction { protected int getInMemoryOffset(int queryOffset) { return 0; } - - @Override - protected SelectTranslator createTranslator() { - return new MySQLSelectTranslator(query, dataNode.getAdapter(), dataNode.getEntityResolver()); - } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/741ad3be/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseActionBuilder.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseActionBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseActionBuilder.java deleted file mode 100644 index fb03bde..0000000 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseActionBuilder.java +++ /dev/null @@ -1,48 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - ****************************************************************/ - -package org.apache.cayenne.dba.openbase; - -import org.apache.cayenne.access.DataNode; -import org.apache.cayenne.access.jdbc.SelectAction; -import org.apache.cayenne.access.translator.select.SelectTranslator; -import org.apache.cayenne.dba.JdbcActionBuilder; -import org.apache.cayenne.query.SQLAction; -import org.apache.cayenne.query.SelectQuery; - -/** - * @since 1.2 - */ -class OpenBaseActionBuilder extends JdbcActionBuilder { - - OpenBaseActionBuilder(DataNode dataNode) { - super(dataNode); - } - - @Override - public SQLAction objectSelectAction(SelectQuery query) { - return new SelectAction(query, dataNode) { - - @Override - protected SelectTranslator createTranslator() { - return new OpenBaseSelectTranslator(query, dataNode.getAdapter(), dataNode.getEntityResolver()); - } - }; - } -} http://git-wip-us.apache.org/repos/asf/cayenne/blob/741ad3be/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseAdapter.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseAdapter.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseAdapter.java index f7aeb82..a018aa4 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseAdapter.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseAdapter.java @@ -27,9 +27,9 @@ import java.util.Iterator; import java.util.List; import org.apache.cayenne.CayenneRuntimeException; -import org.apache.cayenne.access.DataNode; import org.apache.cayenne.access.translator.select.QualifierTranslator; import org.apache.cayenne.access.translator.select.QueryAssembler; +import org.apache.cayenne.access.translator.select.SelectTranslator; import org.apache.cayenne.access.types.ByteType; import org.apache.cayenne.access.types.CharType; import org.apache.cayenne.access.types.ExtendedType; @@ -45,14 +45,14 @@ import org.apache.cayenne.map.DbAttribute; import org.apache.cayenne.map.DbEntity; import org.apache.cayenne.map.DbJoin; import org.apache.cayenne.map.DbRelationship; +import org.apache.cayenne.map.EntityResolver; import org.apache.cayenne.merge.MergerFactory; -import org.apache.cayenne.query.Query; -import org.apache.cayenne.query.SQLAction; +import org.apache.cayenne.query.SelectQuery; import org.apache.cayenne.resource.ResourceLocator; /** - * DbAdapter implementation for OpenBase. Sample - * connection settings to use with OpenBase are shown below: + * DbAdapter implementation for OpenBase. + * Sample connection settings to use with OpenBase are shown below: * *

  * openbase.jdbc.username = test
@@ -65,284 +65,239 @@ import org.apache.cayenne.resource.ResourceLocator;
  */
 public class OpenBaseAdapter extends JdbcAdapter {
 
-    public OpenBaseAdapter(
-            @Inject RuntimeProperties runtimeProperties,
-            @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List defaultExtendedTypes,
-            @Inject(Constants.SERVER_USER_TYPES_LIST) List userExtendedTypes,
-            @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List extendedTypeFactories,
-            @Inject ResourceLocator resourceLocator) {
-        super(
-                runtimeProperties,
-                defaultExtendedTypes,
-                userExtendedTypes,
-                extendedTypeFactories,
-                resourceLocator);
-
-        // init defaults
-        this.setSupportsUniqueConstraints(false);
-    }
-
-    /**
-     * Uses special action builder to create the right action.
-     * 
-     * @since 1.2
-     */
-    @Override
-    public SQLAction getAction(Query query, DataNode node) {
-        return query.createSQLAction(new OpenBaseActionBuilder(node));
-    }
-
-    @Override
-    protected void configureExtendedTypes(ExtendedTypeMap map) {
-        super.configureExtendedTypes(map);
-
-        // Byte handling doesn't work on read...
-        // need special converter
-        map.registerType(new OpenBaseByteType());
-
-        map.registerType(new OpenBaseCharType());
-    }
-
-    @Override
-    public DbAttribute buildAttribute(
-            String name,
-            String typeName,
-            int type,
-            int size,
-            int scale,
-            boolean allowNulls) {
-
-        // OpenBase makes no distinction between CHAR and VARCHAR
-        // so lets use VARCHAR, since it seems more generic
-        if (type == Types.CHAR) {
-            type = Types.VARCHAR;
-        }
-
-        return super.buildAttribute(name, typeName, type, size, scale, allowNulls);
-    }
-
-    /**
-     * Returns word "go".
-     */
-    @Override
-    public String getBatchTerminator() {
-        return "go";
-    }
-
-    /**
-     * Returns null, since views are not yet supported in openbase.
-     */
-    @Override
-    public String tableTypeForView() {
-        // TODO: according to OpenBase docs views *ARE* supported.
-        return null;
-    }
-
-    /**
-     * Returns OpenBase-specific translator for queries.
-     */
-    @Override
-    public QualifierTranslator getQualifierTranslator(QueryAssembler queryAssembler) {
-        return new OpenBaseQualifierTranslator(queryAssembler);
-    }
-
-    /**
-     * Creates and returns a primary key generator. Overrides superclass implementation to
-     * return an instance of OpenBasePkGenerator that uses built-in multi-server primary
-     * key generation.
-     */
-    @Override
-    protected PkGenerator createPkGenerator() {
-        return new OpenBasePkGenerator(this);
-    }
-
-    /**
-     * Returns a SQL string that can be used to create database table corresponding to
-     * ent parameter.
-     */
-    @Override
-    public String createTable(DbEntity ent) {
-
-        StringBuilder buf = new StringBuilder();
-
-        buf.append("CREATE TABLE ");
-        buf.append(quotingStrategy.quotedFullyQualifiedName(ent));
-        buf.append(" (");
-
-        // columns
-        Iterator it = ent.getAttributes().iterator();
-        boolean first = true;
-        while (it.hasNext()) {
-            if (first) {
-                first = false;
-            }
-            else {
-                buf.append(", ");
-            }
-
-            DbAttribute at = it.next();
-
-            // attribute may not be fully valid, do a simple check
-            if (at.getType() == TypesMapping.NOT_DEFINED) {
-                throw new CayenneRuntimeException("Undefined type for attribute '"
-                        + ent.getFullyQualifiedName()
-                        + "."
-                        + at.getName()
-                        + "'.");
-            }
-
-            String[] types = externalTypesForJdbcType(at.getType());
-            if (types == null || types.length == 0) {
-                throw new CayenneRuntimeException("Undefined type for attribute '"
-                        + ent.getFullyQualifiedName()
-                        + "."
-                        + at.getName()
-                        + "': "
-                        + at.getType());
-            }
-
-            String type = types[0];
-            buf.append(quotingStrategy.quotedName(at)).append(' ').append(type);
-
-            // append size and precision (if applicable)
-            if (typeSupportsLength(at.getType())) {
-                int len = at.getMaxLength();
-                int scale = TypesMapping.isDecimal(at.getType()) ? at.getScale() : -1;
-
-                // sanity check
-                if (scale > len) {
-                    scale = -1;
-                }
-
-                if (len > 0) {
-                    buf.append('(').append(len);
-
-                    if (scale >= 0) {
-                        buf.append(", ").append(scale);
-                    }
-
-                    buf.append(')');
-                }
-            }
-
-            if (at.isMandatory()) {
-                buf.append(" NOT NULL");
-            }
-            else {
-                buf.append(" NULL");
-            }
-        }
-
-        buf.append(')');
-        return buf.toString();
-    }
-
-    /**
-     * Returns a SQL string that can be used to create a foreign key constraint for the
-     * relationship.
-     */
-    @Override
-    public String createFkConstraint(DbRelationship rel) {
-        StringBuilder buf = new StringBuilder();
-
-        // OpendBase Specifics is that we need to create a constraint going
-        // from destination to source for this to work...
-
-        DbEntity sourceEntity = (DbEntity) rel.getSourceEntity();
-        DbEntity targetEntity = (DbEntity) rel.getTargetEntity();
-        String toMany = (!rel.isToMany()) ? "'1'" : "'0'";
-
-        // TODO: doesn't seem like OpenBase supports compound joins...
-        // need to doublecheck that
-
-        int joinsLen = rel.getJoins().size();
-        if (joinsLen == 0) {
-            throw new CayenneRuntimeException("Relationship has no joins: "
-                    + rel.getName());
-        }
-        else if (joinsLen > 1) {
-            // ignore extra joins
-        }
-
-        DbJoin join = rel.getJoins().get(0);
-
-        buf
-                .append("INSERT INTO _SYS_RELATIONSHIP (")
-                .append("dest_table, dest_column, source_table, source_column, ")
-                .append(
-                        "block_delete, cascade_delete, one_to_many, operator, relationshipName")
-                .append(") VALUES ('")
-                .append(sourceEntity.getFullyQualifiedName())
-                .append("', '")
-                .append(join.getSourceName())
-                .append("', '")
-                .append(targetEntity.getFullyQualifiedName())
-                .append("', '")
-                .append(join.getTargetName())
-                .append("', 0, 0, ")
-                .append(toMany)
-                .append(", '=', '")
-                .append(rel.getName())
-                .append("')");
-
-        return buf.toString();
-    }
-
-    // OpenBase JDBC driver has trouble reading "integer" as byte
-    // this converter addresses such problem
-    static class OpenBaseByteType extends ByteType {
-
-        OpenBaseByteType() {
-            super(true);
-        }
-
-        @Override
-        public Object materializeObject(ResultSet rs, int index, int type)
-                throws Exception {
-
-            // read value as int, and then narrow it down
-            int val = rs.getInt(index);
-            return (rs.wasNull()) ? null : Byte.valueOf((byte) val);
-        }
-
-        @Override
-        public Object materializeObject(CallableStatement rs, int index, int type)
-                throws Exception {
-
-            // read value as int, and then narrow it down
-            int val = rs.getInt(index);
-            return (rs.wasNull()) ? null : Byte.valueOf((byte) val);
-        }
-    }
-
-    static class OpenBaseCharType extends CharType {
-
-        OpenBaseCharType() {
-            super(false, true);
-        }
-
-        @Override
-        public void setJdbcObject(
-                PreparedStatement st,
-                Object val,
-                int pos,
-                int type,
-                int precision) throws Exception {
-
-            // These to types map to "text"; and when setting "text" as object
-            // OB assumes that the object is the actual CLOB... weird
-            if (type == Types.CLOB || type == Types.LONGVARCHAR) {
-                st.setString(pos, (String) val);
-            }
-            else {
-                super.setJdbcObject(st, val, pos, type, precision);
-            }
-        }
-    }
-
-    @Override
-    public MergerFactory mergerFactory() {
-        return new OpenBaseMergerFactory();
-    }
+	public OpenBaseAdapter(@Inject RuntimeProperties runtimeProperties,
+			@Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List defaultExtendedTypes,
+			@Inject(Constants.SERVER_USER_TYPES_LIST) List userExtendedTypes,
+			@Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List extendedTypeFactories,
+			@Inject ResourceLocator resourceLocator) {
+		super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, extendedTypeFactories, resourceLocator);
+
+		// init defaults
+		this.setSupportsUniqueConstraints(false);
+	}
+
+	/**
+	 * @since 4.0
+	 */
+	@Override
+	public SelectTranslator getSelectTranslator(SelectQuery query, EntityResolver entityResolver) {
+		return new OpenBaseSelectTranslator(query, this, entityResolver);
+	}
+
+	@Override
+	protected void configureExtendedTypes(ExtendedTypeMap map) {
+		super.configureExtendedTypes(map);
+
+		// Byte handling doesn't work on read...
+		// need special converter
+		map.registerType(new OpenBaseByteType());
+
+		map.registerType(new OpenBaseCharType());
+	}
+
+	@Override
+	public DbAttribute buildAttribute(String name, String typeName, int type, int size, int scale, boolean allowNulls) {
+
+		// OpenBase makes no distinction between CHAR and VARCHAR
+		// so lets use VARCHAR, since it seems more generic
+		if (type == Types.CHAR) {
+			type = Types.VARCHAR;
+		}
+
+		return super.buildAttribute(name, typeName, type, size, scale, allowNulls);
+	}
+
+	/**
+	 * Returns word "go".
+	 */
+	@Override
+	public String getBatchTerminator() {
+		return "go";
+	}
+
+	/**
+	 * Returns null, since views are not yet supported in openbase.
+	 */
+	@Override
+	public String tableTypeForView() {
+		// TODO: according to OpenBase docs views *ARE* supported.
+		return null;
+	}
+
+	/**
+	 * Returns OpenBase-specific translator for queries.
+	 */
+	@Override
+	public QualifierTranslator getQualifierTranslator(QueryAssembler queryAssembler) {
+		return new OpenBaseQualifierTranslator(queryAssembler);
+	}
+
+	/**
+	 * Creates and returns a primary key generator. Overrides superclass
+	 * implementation to return an instance of OpenBasePkGenerator that uses
+	 * built-in multi-server primary key generation.
+	 */
+	@Override
+	protected PkGenerator createPkGenerator() {
+		return new OpenBasePkGenerator(this);
+	}
+
+	/**
+	 * Returns a SQL string that can be used to create database table
+	 * corresponding to ent parameter.
+	 */
+	@Override
+	public String createTable(DbEntity ent) {
+
+		StringBuilder buf = new StringBuilder();
+
+		buf.append("CREATE TABLE ");
+		buf.append(quotingStrategy.quotedFullyQualifiedName(ent));
+		buf.append(" (");
+
+		// columns
+		Iterator it = ent.getAttributes().iterator();
+		boolean first = true;
+		while (it.hasNext()) {
+			if (first) {
+				first = false;
+			} else {
+				buf.append(", ");
+			}
+
+			DbAttribute at = it.next();
+
+			// attribute may not be fully valid, do a simple check
+			if (at.getType() == TypesMapping.NOT_DEFINED) {
+				throw new CayenneRuntimeException("Undefined type for attribute '" + ent.getFullyQualifiedName() + "."
+						+ at.getName() + "'.");
+			}
+
+			String[] types = externalTypesForJdbcType(at.getType());
+			if (types == null || types.length == 0) {
+				throw new CayenneRuntimeException("Undefined type for attribute '" + ent.getFullyQualifiedName() + "."
+						+ at.getName() + "': " + at.getType());
+			}
+
+			String type = types[0];
+			buf.append(quotingStrategy.quotedName(at)).append(' ').append(type);
+
+			// append size and precision (if applicable)
+			if (typeSupportsLength(at.getType())) {
+				int len = at.getMaxLength();
+				int scale = TypesMapping.isDecimal(at.getType()) ? at.getScale() : -1;
+
+				// sanity check
+				if (scale > len) {
+					scale = -1;
+				}
+
+				if (len > 0) {
+					buf.append('(').append(len);
+
+					if (scale >= 0) {
+						buf.append(", ").append(scale);
+					}
+
+					buf.append(')');
+				}
+			}
+
+			if (at.isMandatory()) {
+				buf.append(" NOT NULL");
+			} else {
+				buf.append(" NULL");
+			}
+		}
+
+		buf.append(')');
+		return buf.toString();
+	}
+
+	/**
+	 * Returns a SQL string that can be used to create a foreign key constraint
+	 * for the relationship.
+	 */
+	@Override
+	public String createFkConstraint(DbRelationship rel) {
+		StringBuilder buf = new StringBuilder();
+
+		// OpendBase Specifics is that we need to create a constraint going
+		// from destination to source for this to work...
+
+		DbEntity sourceEntity = (DbEntity) rel.getSourceEntity();
+		DbEntity targetEntity = (DbEntity) rel.getTargetEntity();
+		String toMany = (!rel.isToMany()) ? "'1'" : "'0'";
+
+		// TODO: doesn't seem like OpenBase supports compound joins...
+		// need to doublecheck that
+
+		int joinsLen = rel.getJoins().size();
+		if (joinsLen == 0) {
+			throw new CayenneRuntimeException("Relationship has no joins: " + rel.getName());
+		} else if (joinsLen > 1) {
+			// ignore extra joins
+		}
+
+		DbJoin join = rel.getJoins().get(0);
+
+		buf.append("INSERT INTO _SYS_RELATIONSHIP (").append("dest_table, dest_column, source_table, source_column, ")
+				.append("block_delete, cascade_delete, one_to_many, operator, relationshipName").append(") VALUES ('")
+				.append(sourceEntity.getFullyQualifiedName()).append("', '").append(join.getSourceName())
+				.append("', '").append(targetEntity.getFullyQualifiedName()).append("', '")
+				.append(join.getTargetName()).append("', 0, 0, ").append(toMany).append(", '=', '")
+				.append(rel.getName()).append("')");
+
+		return buf.toString();
+	}
+
+	// OpenBase JDBC driver has trouble reading "integer" as byte
+	// this converter addresses such problem
+	static class OpenBaseByteType extends ByteType {
+
+		OpenBaseByteType() {
+			super(true);
+		}
+
+		@Override
+		public Object materializeObject(ResultSet rs, int index, int type) throws Exception {
+
+			// read value as int, and then narrow it down
+			int val = rs.getInt(index);
+			return (rs.wasNull()) ? null : Byte.valueOf((byte) val);
+		}
+
+		@Override
+		public Object materializeObject(CallableStatement rs, int index, int type) throws Exception {
+
+			// read value as int, and then narrow it down
+			int val = rs.getInt(index);
+			return (rs.wasNull()) ? null : Byte.valueOf((byte) val);
+		}
+	}
+
+	static class OpenBaseCharType extends CharType {
+
+		OpenBaseCharType() {
+			super(false, true);
+		}
+
+		@Override
+		public void setJdbcObject(PreparedStatement st, Object val, int pos, int type, int precision) throws Exception {
+
+			// These to types map to "text"; and when setting "text" as object
+			// OB assumes that the object is the actual CLOB... weird
+			if (type == Types.CLOB || type == Types.LONGVARCHAR) {
+				st.setString(pos, (String) val);
+			} else {
+				super.setJdbcObject(st, val, pos, type, precision);
+			}
+		}
+	}
+
+	@Override
+	public MergerFactory mergerFactory() {
+		return new OpenBaseMergerFactory();
+	}
 
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/741ad3be/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8ActionBuilder.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8ActionBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8ActionBuilder.java
index 3cd8f31..4dec81c 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8ActionBuilder.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8ActionBuilder.java
@@ -23,7 +23,6 @@ import org.apache.cayenne.access.DataNode;
 import org.apache.cayenne.query.BatchQuery;
 import org.apache.cayenne.query.SQLAction;
 import org.apache.cayenne.query.SQLTemplate;
-import org.apache.cayenne.query.SelectQuery;
 
 /**
  * An action builder for Oracle8Adapter.
@@ -32,33 +31,28 @@ import org.apache.cayenne.query.SelectQuery;
  */
 class Oracle8ActionBuilder extends OracleActionBuilder {
 
-    Oracle8ActionBuilder(DataNode dataNode) {
-        super(dataNode);
-    }
-
-    @Override
-    public SQLAction sqlAction(SQLTemplate query) {
-        return new Oracle8SQLTemplateAction(query, dataNode);
-    }
-
-    @Override
-    public  SQLAction objectSelectAction(SelectQuery query) {
-        return new Oracle8SelectAction(query, dataNode);
-    }
-
-    @Override
-    public SQLAction batchAction(BatchQuery query) {
-        // special handling for LOB updates
-        if (OracleAdapter.isSupportsOracleLOB() && OracleAdapter.updatesLOBColumns(query)) {
-            // Special action for Oracle8. See CAY-1307.
-            return new Oracle8LOBBatchAction(query, dataNode.getAdapter(), dataNode.getJdbcEventLogger());
-        } else {
-            // optimistic locking is not supported in batches due to JDBC driver
-            // limitations
-            boolean useOptimisticLock = query.isUsingOptimisticLocking();
-            boolean runningAsBatch = !useOptimisticLock && dataNode.getAdapter().supportsBatchUpdates();
-
-            return new OracleBatchAction(query, dataNode, runningAsBatch);
-        }
-    }
+	Oracle8ActionBuilder(DataNode dataNode) {
+		super(dataNode);
+	}
+
+	@Override
+	public SQLAction sqlAction(SQLTemplate query) {
+		return new Oracle8SQLTemplateAction(query, dataNode);
+	}
+
+	@Override
+	public SQLAction batchAction(BatchQuery query) {
+		// special handling for LOB updates
+		if (OracleAdapter.isSupportsOracleLOB() && OracleAdapter.updatesLOBColumns(query)) {
+			// Special action for Oracle8. See CAY-1307.
+			return new Oracle8LOBBatchAction(query, dataNode.getAdapter(), dataNode.getJdbcEventLogger());
+		} else {
+			// optimistic locking is not supported in batches due to JDBC driver
+			// limitations
+			boolean useOptimisticLock = query.isUsingOptimisticLocking();
+			boolean runningAsBatch = !useOptimisticLock && dataNode.getAdapter().supportsBatchUpdates();
+
+			return new OracleBatchAction(query, dataNode, runningAsBatch);
+		}
+	}
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/741ad3be/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8Adapter.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8Adapter.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8Adapter.java
index 2a85312..c0acdb9 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8Adapter.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8Adapter.java
@@ -26,82 +26,91 @@ import java.util.List;
 import org.apache.cayenne.access.DataNode;
 import org.apache.cayenne.access.translator.select.QualifierTranslator;
 import org.apache.cayenne.access.translator.select.QueryAssembler;
+import org.apache.cayenne.access.translator.select.SelectTranslator;
 import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.map.EntityResolver;
 import org.apache.cayenne.query.Query;
 import org.apache.cayenne.query.SQLAction;
+import org.apache.cayenne.query.SelectQuery;
 import org.apache.cayenne.resource.ResourceLocator;
 
 /**
- * A flavor of OracleAdapter that implements workarounds for some old driver limitations.
+ * A flavor of OracleAdapter that implements workarounds for some old driver
+ * limitations.
  * 
  * @since 1.2
  */
 public class Oracle8Adapter extends OracleAdapter {
 
-    private static Method outputStreamFromBlobMethod;
-    private static Method writerFromClobMethod;
-
-    static {
-        initOracle8DriverInformation();
-    }
-    
-    public Oracle8Adapter(@Inject RuntimeProperties runtimeProperties,
-            @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List defaultExtendedTypes,
-            @Inject(Constants.SERVER_USER_TYPES_LIST) List userExtendedTypes,
-            @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List extendedTypeFactories,
-            @Inject ResourceLocator resourceLocator) {
-        super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, extendedTypeFactories, resourceLocator);
-    }
-
-    private static void initOracle8DriverInformation() {
-        initDone = true;
-
-        // configure static information
-        try {
-            outputStreamFromBlobMethod = Class.forName("oracle.sql.BLOB").getMethod(
-                    "getBinaryOutputStream");
-            writerFromClobMethod = Class.forName("oracle.sql.CLOB").getMethod(
-                    "getCharacterOutputStream");
-        }
-        catch (Throwable th) {
-            // ignoring...
-        }
-    }
-
-    static Method getWriterFromClobMethod() {
-        return writerFromClobMethod;
-    }
-
-    static Method getOutputStreamFromBlobMethod() {
-        return outputStreamFromBlobMethod;
-    }
-
-    /**
-     * Uses OracleActionBuilder to create the right action.
-     */
-    @Override
-    public SQLAction getAction(Query query, DataNode node) {
-        return query.createSQLAction(new Oracle8ActionBuilder(node));
-    }
-
-    @Override
-    protected URL findResource(String name) {
-
-        if ("/types.xml".equals(name)) {
-            name = "/types-oracle8.xml";
-        }
-
-        return super.findResource(name);
-    }
-
-    @Override
-    public QualifierTranslator getQualifierTranslator(QueryAssembler queryAssembler) {
-        QualifierTranslator translator = new Oracle8QualifierTranslator(queryAssembler);
-        translator.setCaseInsensitive(caseInsensitiveCollations);
-        return translator;
-    }
+	private static Method outputStreamFromBlobMethod;
+	private static Method writerFromClobMethod;
+
+	static {
+		initOracle8DriverInformation();
+	}
+
+	public Oracle8Adapter(@Inject RuntimeProperties runtimeProperties,
+			@Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List defaultExtendedTypes,
+			@Inject(Constants.SERVER_USER_TYPES_LIST) List userExtendedTypes,
+			@Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List extendedTypeFactories,
+			@Inject ResourceLocator resourceLocator) {
+		super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, extendedTypeFactories, resourceLocator);
+	}
+
+	private static void initOracle8DriverInformation() {
+		initDone = true;
+
+		// configure static information
+		try {
+			outputStreamFromBlobMethod = Class.forName("oracle.sql.BLOB").getMethod("getBinaryOutputStream");
+			writerFromClobMethod = Class.forName("oracle.sql.CLOB").getMethod("getCharacterOutputStream");
+		} catch (Throwable th) {
+			// ignoring...
+		}
+	}
+
+	static Method getWriterFromClobMethod() {
+		return writerFromClobMethod;
+	}
+
+	static Method getOutputStreamFromBlobMethod() {
+		return outputStreamFromBlobMethod;
+	}
+
+	/**
+	 * @since 4.0
+	 */
+	@Override
+	public SelectTranslator getSelectTranslator(SelectQuery query, EntityResolver entityResolver) {
+		return new Oracle8SelectTranslator(query, this, entityResolver);
+	}
+
+	/**
+	 * Uses OracleActionBuilder to create the right action.
+	 */
+	@Override
+	public SQLAction getAction(Query query, DataNode node) {
+		return query.createSQLAction(new Oracle8ActionBuilder(node));
+	}
+
+	@Override
+	protected URL findResource(String name) {
+
+		if ("/types.xml".equals(name)) {
+			name = "/types-oracle8.xml";
+		}
+
+		return super.findResource(name);
+	}
+
+	@Override
+	public QualifierTranslator getQualifierTranslator(QueryAssembler queryAssembler) {
+		QualifierTranslator translator = new Oracle8QualifierTranslator(queryAssembler);
+		translator.setCaseInsensitive(caseInsensitiveCollations);
+		return translator;
+	}
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/741ad3be/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8SelectAction.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8SelectAction.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8SelectAction.java
deleted file mode 100644
index abef7a7..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8SelectAction.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-package org.apache.cayenne.dba.oracle;
-
-import org.apache.cayenne.access.DataNode;
-import org.apache.cayenne.access.translator.select.SelectTranslator;
-import org.apache.cayenne.query.SelectQuery;
-
-/**
- * @since 3.0
- */
-class Oracle8SelectAction extends OracleSelectAction {
-
-	 Oracle8SelectAction(SelectQuery query, DataNode dataNode) {
-		super(query, dataNode);
-	}
-
-	@Override
-	protected SelectTranslator createTranslator() {
-		return new Oracle8SelectTranslator(query, dataNode.getAdapter(), dataNode.getEntityResolver());
-	}
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/741ad3be/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleAdapter.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleAdapter.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleAdapter.java
index 50d621c..f16b69d 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleAdapter.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleAdapter.java
@@ -34,6 +34,7 @@ import org.apache.cayenne.access.DataNode;
 import org.apache.cayenne.access.translator.ejbql.EJBQLTranslatorFactory;
 import org.apache.cayenne.access.translator.select.QualifierTranslator;
 import org.apache.cayenne.access.translator.select.QueryAssembler;
+import org.apache.cayenne.access.translator.select.SelectTranslator;
 import org.apache.cayenne.access.types.ByteType;
 import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
@@ -43,21 +44,22 @@ import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.dba.JdbcAdapter;
 import org.apache.cayenne.dba.PkGenerator;
-import org.apache.cayenne.dba.QuotingStrategy;
 import org.apache.cayenne.di.Inject;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.EntityResolver;
 import org.apache.cayenne.merge.MergerFactory;
 import org.apache.cayenne.query.BatchQuery;
 import org.apache.cayenne.query.InsertBatchQuery;
 import org.apache.cayenne.query.Query;
 import org.apache.cayenne.query.SQLAction;
+import org.apache.cayenne.query.SelectQuery;
 import org.apache.cayenne.query.UpdateBatchQuery;
 import org.apache.cayenne.resource.ResourceLocator;
 
 /**
- * DbAdapter implementation for Oracle RDBMS . Sample
- * connection settings to use with Oracle are shown below:
+ * DbAdapter implementation for Oracle RDBMS
+ * . Sample connection settings to use with Oracle are shown below:
  * 
  * 
  *          test-oracle.jdbc.username = test
@@ -68,288 +70,280 @@ import org.apache.cayenne.resource.ResourceLocator;
  */
 public class OracleAdapter extends JdbcAdapter {
 
-    public static final String ORACLE_FLOAT = "FLOAT";
-    public static final String ORACLE_BLOB = "BLOB";
-    public static final String ORACLE_CLOB = "CLOB";
-    public static final String ORACLE_NCLOB = "NCLOB";
-
-    public static final String TRIM_FUNCTION = "RTRIM";
-    public static final String NEW_CLOB_FUNCTION = "EMPTY_CLOB()";
-    public static final String NEW_BLOB_FUNCTION = "EMPTY_BLOB()";
-
-    protected static boolean initDone;
-    protected static int oracleCursorType = Integer.MAX_VALUE;
-
-    protected static boolean supportsOracleLOB;
-
-    static {
-        // TODO: as CAY-234 shows, having such initialization done in a static fashion
-        // makes it untestable and any potential problems hard to reproduce. Make this
-        // an instance method (with all the affected vars) and write unit tests.
-        initDriverInformation();
-    }
-
-    protected static void initDriverInformation() {
-        initDone = true;
-
-        // configure static information
-        try {
-            Class oraTypes = Class.forName("oracle.jdbc.driver.OracleTypes");
-            Field cursorField = oraTypes.getField("CURSOR");
-            oracleCursorType = cursorField.getInt(null);
-
-            supportsOracleLOB = true;
-        }
-        catch (Throwable th) {
-            // ignoring...
-        }
-    }
-
-    // TODO: rename to something that looks like English ...
-    public static boolean isSupportsOracleLOB() {
-        return supportsOracleLOB;
-    }
-
-    /**
-     * Utility method that returns true if the query will update at least one
-     * BLOB or CLOB DbAttribute.
-     * 
-     * @since 1.2
-     */
-    static boolean updatesLOBColumns(BatchQuery query) {
-        boolean isInsert = query instanceof InsertBatchQuery;
-        boolean isUpdate = query instanceof UpdateBatchQuery;
-
-        if (!isInsert && !isUpdate) {
-            return false;
-        }
-
-        List updatedAttributes = (isInsert)
-                ? query.getDbAttributes()
-                : ((UpdateBatchQuery) query).getUpdatedAttributes();
-
-        for (DbAttribute attr : updatedAttributes) {
-            int type = attr.getType();
-            if (type == Types.CLOB || type == Types.BLOB) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Returns an Oracle JDBC extension type defined in
-     * oracle.jdbc.driver.OracleTypes.CURSOR. This value is determined from Oracle driver
-     * classes via reflection in runtime, so that Cayenne code has no compile dependency
-     * on the driver. This means that calling this method when the driver is not available
-     * will result in an exception.
-     */
-    public static int getOracleCursorType() {
-
-        if (oracleCursorType == Integer.MAX_VALUE) {
-            throw new CayenneRuntimeException(
-                    "No information exists about oracle types. "
-                            + "Check that Oracle JDBC driver is available to the application.");
-        }
-
-        return oracleCursorType;
-    }
-
-    public OracleAdapter(
-            @Inject RuntimeProperties runtimeProperties,
-            @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List defaultExtendedTypes,
-            @Inject(Constants.SERVER_USER_TYPES_LIST) List userExtendedTypes,
-            @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List extendedTypeFactories,
-            @Inject ResourceLocator resourceLocator) {
-        super(
-                runtimeProperties,
-                defaultExtendedTypes,
-                userExtendedTypes,
-                extendedTypeFactories,
-                resourceLocator);
-
-        // enable batch updates by default
-        setSupportsBatchUpdates(true);
-    }
-
-    /**
-     * @since 3.0
-     */
-    @Override
-    protected EJBQLTranslatorFactory createEJBQLTranslatorFactory() {
-        return new OracleEJBQLTranslatorFactory();
-    }
-
-    /**
-     * Installs appropriate ExtendedTypes as converters for passing values between JDBC
-     * and Java layers.
-     */
-    @Override
-    protected void configureExtendedTypes(ExtendedTypeMap map) {
-        super.configureExtendedTypes(map);
-
-        // create specially configured CharType handler
-        map.registerType(new OracleCharType());
-
-        // create specially configured ByteArrayType handler
-        map.registerType(new OracleByteArrayType());
-
-        // override date handler with Oracle handler
-        map.registerType(new OracleUtilDateType());
-
-        // At least on MacOS X, driver does not handle Short and Byte properly
-        map.registerType(new ShortType(true));
-        map.registerType(new ByteType(true));
-        map.registerType(new OracleBooleanType());
-    }
-
-    /**
-     * Creates and returns a primary key generator. Overrides superclass implementation to
-     * return an instance of OraclePkGenerator.
-     */
-    @Override
-    protected PkGenerator createPkGenerator() {
-        return new OraclePkGenerator(this);
-    }
-
-    /**
-     * Returns a query string to drop a table corresponding to ent DbEntity.
-     * Changes superclass behavior to drop all related foreign key constraints.
-     * 
-     * @since 3.0
-     */
-    @Override
-    public Collection dropTableStatements(DbEntity table) {
-        return Collections.singleton("DROP TABLE " + getQuotingStrategy().quotedFullyQualifiedName(table)
-                + " CASCADE CONSTRAINTS");
-    }
-
-    @Override
-    public void bindParameter(
-            PreparedStatement statement,
-            Object object,
-            int pos,
-            int sqlType,
-            int scale) throws SQLException, Exception {
-
-        // Oracle doesn't support BOOLEAN even when binding NULL, so have to intercept
-        // NULL Boolean here, as super doesn't pass it through ExtendedType...
-        if (object == null && sqlType == Types.BOOLEAN) {
-            ExtendedType typeProcessor = getExtendedTypes().getRegisteredType(
-                    Boolean.class);
-            typeProcessor.setJdbcObject(statement, object, pos, sqlType, scale);
-        }
-        else {
-            super.bindParameter(statement, object, pos, sqlType, scale);
-        }
-    }
-
-    /**
-     * Fixes some reverse engineering problems. Namely if a columns is created as DECIMAL
-     * and has non-positive precision it is converted to INTEGER.
-     */
-    @Override
-    public DbAttribute buildAttribute(String name, String typeName, int type, int size, int scale, boolean allowNulls) {
-        DbAttribute attr = super.buildAttribute(name, typeName, type, size, scale, allowNulls);
-
-        if (type == Types.DECIMAL && scale <= 0) {
-            attr.setType(Types.INTEGER);
-            attr.setScale(-1);
-        } else if (type == Types.OTHER) {
-            // in this case we need to guess the attribute type
-            // based on its string value
-            if (ORACLE_FLOAT.equals(typeName)) {
-                attr.setType(Types.FLOAT);
-            } else if (ORACLE_BLOB.equals(typeName)) {
-                attr.setType(Types.BLOB);
-            } else if (ORACLE_CLOB.equals(typeName)) {
-                attr.setType(Types.CLOB);
-            } else if (ORACLE_NCLOB.equals(typeName)) {
-                attr.setType(Types.NCLOB);
-            }
-        } else if (type == Types.DATE) {
-            // Oracle DATE can store JDBC TIMESTAMP
-            if ("DATE".equals(typeName)) {
-                attr.setType(Types.TIMESTAMP);
-            }
-        }
-
-        return attr;
-    }
-
-    /**
-     * Returns a trimming translator.
-     */
-    @Override
-    public QualifierTranslator getQualifierTranslator(QueryAssembler queryAssembler) {
-        QualifierTranslator translator = new Oracle8QualifierTranslator(queryAssembler);
-        translator.setCaseInsensitive(caseInsensitiveCollations);
-        return translator;
-    }
-
-    /**
-     * Uses OracleActionBuilder to create the right action.
-     * 
-     * @since 1.2
-     */
-    @Override
-    public SQLAction getAction(Query query, DataNode node) {
-        return query.createSQLAction(new OracleActionBuilder(node));
-    }
-
-    /**
-     * @since 3.0
-     */
-    final class OracleBooleanType implements ExtendedType {
-
-        @Override
-        public String getClassName() {
-            return Boolean.class.getName();
-        }
-
-        @Override
-        public void setJdbcObject(
-                PreparedStatement st,
-                Object val,
-                int pos,
-                int type,
-                int precision) throws Exception {
-
-            // Oracle does not support Types.BOOLEAN, so we have to override user mapping
-            // unconditionally
-            if (val == null) {
-                st.setNull(pos, Types.INTEGER);
-            }
-            else {
-                boolean flag = Boolean.TRUE.equals(val);
-                st.setInt(pos, flag ? 1 : 0);
-            }
-        }
-
-        @Override
-        public Object materializeObject(ResultSet rs, int index, int type)
-                throws Exception {
-
-            // Oracle does not support Types.BOOLEAN, so we have to override user mapping
-            // unconditionally
-            int i = rs.getInt(index);
-            return rs.wasNull() ? null : i == 0 ? Boolean.FALSE : Boolean.TRUE;
-        }
-
-        @Override
-        public Object materializeObject(CallableStatement st, int index, int type)
-                throws Exception {
-
-            // Oracle does not support Types.BOOLEAN, so we have to override user mapping
-            // unconditionally
-            int i = st.getInt(index);
-            return st.wasNull() ? null : i == 0 ? Boolean.FALSE : Boolean.TRUE;
-        }
-    }
-
-    @Override
-    public MergerFactory mergerFactory() {
-        return new OracleMergerFactory();
-    }
+	public static final String ORACLE_FLOAT = "FLOAT";
+	public static final String ORACLE_BLOB = "BLOB";
+	public static final String ORACLE_CLOB = "CLOB";
+	public static final String ORACLE_NCLOB = "NCLOB";
+
+	public static final String TRIM_FUNCTION = "RTRIM";
+	public static final String NEW_CLOB_FUNCTION = "EMPTY_CLOB()";
+	public static final String NEW_BLOB_FUNCTION = "EMPTY_BLOB()";
+
+	protected static boolean initDone;
+	protected static int oracleCursorType = Integer.MAX_VALUE;
+
+	protected static boolean supportsOracleLOB;
+
+	static {
+		// TODO: as CAY-234 shows, having such initialization done in a static
+		// fashion
+		// makes it untestable and any potential problems hard to reproduce.
+		// Make this
+		// an instance method (with all the affected vars) and write unit tests.
+		initDriverInformation();
+	}
+
+	protected static void initDriverInformation() {
+		initDone = true;
+
+		// configure static information
+		try {
+			Class oraTypes = Class.forName("oracle.jdbc.driver.OracleTypes");
+			Field cursorField = oraTypes.getField("CURSOR");
+			oracleCursorType = cursorField.getInt(null);
+
+			supportsOracleLOB = true;
+		} catch (Throwable th) {
+			// ignoring...
+		}
+	}
+
+	// TODO: rename to something that looks like English ...
+	public static boolean isSupportsOracleLOB() {
+		return supportsOracleLOB;
+	}
+
+	/**
+	 * Utility method that returns true if the query will update at
+	 * least one BLOB or CLOB DbAttribute.
+	 * 
+	 * @since 1.2
+	 */
+	static boolean updatesLOBColumns(BatchQuery query) {
+		boolean isInsert = query instanceof InsertBatchQuery;
+		boolean isUpdate = query instanceof UpdateBatchQuery;
+
+		if (!isInsert && !isUpdate) {
+			return false;
+		}
+
+		List updatedAttributes = (isInsert) ? query.getDbAttributes() : ((UpdateBatchQuery) query)
+				.getUpdatedAttributes();
+
+		for (DbAttribute attr : updatedAttributes) {
+			int type = attr.getType();
+			if (type == Types.CLOB || type == Types.BLOB) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	/**
+	 * Returns an Oracle JDBC extension type defined in
+	 * oracle.jdbc.driver.OracleTypes.CURSOR. This value is determined from
+	 * Oracle driver classes via reflection in runtime, so that Cayenne code has
+	 * no compile dependency on the driver. This means that calling this method
+	 * when the driver is not available will result in an exception.
+	 */
+	public static int getOracleCursorType() {
+
+		if (oracleCursorType == Integer.MAX_VALUE) {
+			throw new CayenneRuntimeException("No information exists about oracle types. "
+					+ "Check that Oracle JDBC driver is available to the application.");
+		}
+
+		return oracleCursorType;
+	}
+
+	public OracleAdapter(@Inject RuntimeProperties runtimeProperties,
+			@Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List defaultExtendedTypes,
+			@Inject(Constants.SERVER_USER_TYPES_LIST) List userExtendedTypes,
+			@Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List extendedTypeFactories,
+			@Inject ResourceLocator resourceLocator) {
+		super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, extendedTypeFactories, resourceLocator);
+
+		// enable batch updates by default
+		setSupportsBatchUpdates(true);
+	}
+
+	/**
+	 * @since 4.0
+	 */
+	@Override
+	public SelectTranslator getSelectTranslator(SelectQuery query, EntityResolver entityResolver) {
+		return new OracleSelectTranslator(query, this, entityResolver);
+	}
+
+	/**
+	 * @since 3.0
+	 */
+	@Override
+	protected EJBQLTranslatorFactory createEJBQLTranslatorFactory() {
+		return new OracleEJBQLTranslatorFactory();
+	}
+
+	/**
+	 * Installs appropriate ExtendedTypes as converters for passing values
+	 * between JDBC and Java layers.
+	 */
+	@Override
+	protected void configureExtendedTypes(ExtendedTypeMap map) {
+		super.configureExtendedTypes(map);
+
+		// create specially configured CharType handler
+		map.registerType(new OracleCharType());
+
+		// create specially configured ByteArrayType handler
+		map.registerType(new OracleByteArrayType());
+
+		// override date handler with Oracle handler
+		map.registerType(new OracleUtilDateType());
+
+		// At least on MacOS X, driver does not handle Short and Byte properly
+		map.registerType(new ShortType(true));
+		map.registerType(new ByteType(true));
+		map.registerType(new OracleBooleanType());
+	}
+
+	/**
+	 * Creates and returns a primary key generator. Overrides superclass
+	 * implementation to return an instance of OraclePkGenerator.
+	 */
+	@Override
+	protected PkGenerator createPkGenerator() {
+		return new OraclePkGenerator(this);
+	}
+
+	/**
+	 * Returns a query string to drop a table corresponding to ent
+	 * DbEntity. Changes superclass behavior to drop all related foreign key
+	 * constraints.
+	 * 
+	 * @since 3.0
+	 */
+	@Override
+	public Collection dropTableStatements(DbEntity table) {
+		return Collections.singleton("DROP TABLE " + getQuotingStrategy().quotedFullyQualifiedName(table)
+				+ " CASCADE CONSTRAINTS");
+	}
+
+	@Override
+	public void bindParameter(PreparedStatement statement, Object object, int pos, int sqlType, int scale)
+			throws SQLException, Exception {
+
+		// Oracle doesn't support BOOLEAN even when binding NULL, so have to
+		// intercept
+		// NULL Boolean here, as super doesn't pass it through ExtendedType...
+		if (object == null && sqlType == Types.BOOLEAN) {
+			ExtendedType typeProcessor = getExtendedTypes().getRegisteredType(Boolean.class);
+			typeProcessor.setJdbcObject(statement, object, pos, sqlType, scale);
+		} else {
+			super.bindParameter(statement, object, pos, sqlType, scale);
+		}
+	}
+
+	/**
+	 * Fixes some reverse engineering problems. Namely if a columns is created
+	 * as DECIMAL and has non-positive precision it is converted to INTEGER.
+	 */
+	@Override
+	public DbAttribute buildAttribute(String name, String typeName, int type, int size, int scale, boolean allowNulls) {
+		DbAttribute attr = super.buildAttribute(name, typeName, type, size, scale, allowNulls);
+
+		if (type == Types.DECIMAL && scale <= 0) {
+			attr.setType(Types.INTEGER);
+			attr.setScale(-1);
+		} else if (type == Types.OTHER) {
+			// in this case we need to guess the attribute type
+			// based on its string value
+			if (ORACLE_FLOAT.equals(typeName)) {
+				attr.setType(Types.FLOAT);
+			} else if (ORACLE_BLOB.equals(typeName)) {
+				attr.setType(Types.BLOB);
+			} else if (ORACLE_CLOB.equals(typeName)) {
+				attr.setType(Types.CLOB);
+			} else if (ORACLE_NCLOB.equals(typeName)) {
+				attr.setType(Types.NCLOB);
+			}
+		} else if (type == Types.DATE) {
+			// Oracle DATE can store JDBC TIMESTAMP
+			if ("DATE".equals(typeName)) {
+				attr.setType(Types.TIMESTAMP);
+			}
+		}
+
+		return attr;
+	}
+
+	/**
+	 * Returns a trimming translator.
+	 */
+	@Override
+	public QualifierTranslator getQualifierTranslator(QueryAssembler queryAssembler) {
+		QualifierTranslator translator = new Oracle8QualifierTranslator(queryAssembler);
+		translator.setCaseInsensitive(caseInsensitiveCollations);
+		return translator;
+	}
+
+	/**
+	 * Uses OracleActionBuilder to create the right action.
+	 * 
+	 * @since 1.2
+	 */
+	@Override
+	public SQLAction getAction(Query query, DataNode node) {
+		return query.createSQLAction(new OracleActionBuilder(node));
+	}
+
+	/**
+	 * @since 3.0
+	 */
+	final class OracleBooleanType implements ExtendedType {
+
+		@Override
+		public String getClassName() {
+			return Boolean.class.getName();
+		}
+
+		@Override
+		public void setJdbcObject(PreparedStatement st, Object val, int pos, int type, int precision) throws Exception {
+
+			// Oracle does not support Types.BOOLEAN, so we have to override
+			// user mapping
+			// unconditionally
+			if (val == null) {
+				st.setNull(pos, Types.INTEGER);
+			} else {
+				boolean flag = Boolean.TRUE.equals(val);
+				st.setInt(pos, flag ? 1 : 0);
+			}
+		}
+
+		@Override
+		public Object materializeObject(ResultSet rs, int index, int type) throws Exception {
+
+			// Oracle does not support Types.BOOLEAN, so we have to override
+			// user mapping
+			// unconditionally
+			int i = rs.getInt(index);
+			return rs.wasNull() ? null : i == 0 ? Boolean.FALSE : Boolean.TRUE;
+		}
+
+		@Override
+		public Object materializeObject(CallableStatement st, int index, int type) throws Exception {
+
+			// Oracle does not support Types.BOOLEAN, so we have to override
+			// user mapping
+			// unconditionally
+			int i = st.getInt(index);
+			return st.wasNull() ? null : i == 0 ? Boolean.FALSE : Boolean.TRUE;
+		}
+	}
+
+	@Override
+	public MergerFactory mergerFactory() {
+		return new OracleMergerFactory();
+	}
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/741ad3be/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleSelectAction.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleSelectAction.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleSelectAction.java
index f361aa4..60e8e5a 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleSelectAction.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleSelectAction.java
@@ -21,7 +21,6 @@ package org.apache.cayenne.dba.oracle;
 
 import org.apache.cayenne.access.DataNode;
 import org.apache.cayenne.access.jdbc.SelectAction;
-import org.apache.cayenne.access.translator.select.SelectTranslator;
 import org.apache.cayenne.query.SelectQuery;
 
 /**
@@ -34,11 +33,6 @@ class OracleSelectAction extends SelectAction {
 	}
 
 	@Override
-	protected SelectTranslator createTranslator() {
-		return new OracleSelectTranslator(query, dataNode.getAdapter(), dataNode.getEntityResolver());
-	}
-
-	@Override
 	protected int getInMemoryOffset(int queryOffset) {
 		return 0;
 	}