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; * <p> * Foreign key constraints are supported by InnoDB engine and NOT supported by * MyISAM engine. This adapter by default assumes MyISAM, so - * <code>supportsFkConstraints</code> will - * be false. Users can manually change this by calling - * <em>setSupportsFkConstraints(true)</em> or better by using an + * <code>supportsFkConstraints</code> will be false. Users can manually change + * this by calling <em>setSupportsFkConstraints(true)</em> 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 <em>table_type</em> 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<ExtendedType> defaultExtendedTypes, - @Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> userExtendedTypes, - @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> 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<String> 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<DbAttribute> pkList = new ArrayList<DbAttribute>(entity.getPrimaryKeys()); - Collections.sort(pkList, new PKComparator()); - - Iterator<DbAttribute> 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<DbAttribute> 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() - : "<null>"; - 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<DbAttribute> { - - 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<ExtendedType> defaultExtendedTypes, + @Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> userExtendedTypes, + @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> 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<String> 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<DbAttribute> pkList = new ArrayList<DbAttribute>(entity.getPrimaryKeys()); + Collections.sort(pkList, new PKComparator()); + + Iterator<DbAttribute> 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<DbAttribute> 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() + : "<null>"; + 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<DbAttribute> { + + 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 <T> SQLAction objectSelectAction(SelectQuery<T> 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 <a href="http://www.openbase.com">OpenBase</a>. Sample - * connection settings to use with OpenBase are shown below: + * DbAdapter implementation for <a href="http://www.openbase.com">OpenBase</a>. + * Sample connection settings to use with OpenBase are shown below: * * <pre> * 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<ExtendedType> defaultExtendedTypes, - @Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> userExtendedTypes, - @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> 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 - * <code>ent</code> parameter. - */ - @Override - public String createTable(DbEntity ent) { - - StringBuilder buf = new StringBuilder(); - - buf.append("CREATE TABLE "); - buf.append(quotingStrategy.quotedFullyQualifiedName(ent)); - buf.append(" ("); - - // columns - Iterator<DbAttribute> 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<ExtendedType> defaultExtendedTypes, + @Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> userExtendedTypes, + @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> 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 <code>ent</code> parameter. + */ + @Override + public String createTable(DbEntity ent) { + + StringBuilder buf = new StringBuilder(); + + buf.append("CREATE TABLE "); + buf.append(quotingStrategy.quotedFullyQualifiedName(ent)); + buf.append(" ("); + + // columns + Iterator<DbAttribute> 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 <T> SQLAction objectSelectAction(SelectQuery<T> 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<ExtendedType> defaultExtendedTypes, - @Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> userExtendedTypes, - @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> 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<ExtendedType> defaultExtendedTypes, + @Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> userExtendedTypes, + @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> 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 { - - <T> Oracle8SelectAction(SelectQuery<T> 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 <a href="http://www.oracle.com">Oracle RDBMS </a>. Sample - * connection settings to use with Oracle are shown below: + * DbAdapter implementation for <a href="http://www.oracle.com">Oracle RDBMS + * </a>. Sample connection settings to use with Oracle are shown below: * * <pre> * 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 <code>true</code> 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<DbAttribute> 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<ExtendedType> defaultExtendedTypes, - @Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> userExtendedTypes, - @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> 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 <code>ent</code> DbEntity. - * Changes superclass behavior to drop all related foreign key constraints. - * - * @since 3.0 - */ - @Override - public Collection<String> 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 <code>true</code> 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<DbAttribute> 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<ExtendedType> defaultExtendedTypes, + @Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> userExtendedTypes, + @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> 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 <code>ent</code> + * DbEntity. Changes superclass behavior to drop all related foreign key + * constraints. + * + * @since 3.0 + */ + @Override + public Collection<String> 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; }