This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch past-M2 in repository https://gitbox.apache.org/repos/asf/cayenne.git
commit 2b3e265ac7784dc0bb6b04920ca84f4eabada2f9 Author: Andrus Adamchik <[email protected]> AuthorDate: Sat Jun 13 08:56:07 2026 -0400 minor cleanup --- .../java/org/apache/cayenne/dba/JdbcAdapter.java | 6 +- .../org/apache/cayenne/dba/db2/DB2Adapter.java | 20 +- .../org/apache/cayenne/dba/derby/DerbyAdapter.java | 19 +- .../cayenne/dba/firebird/FirebirdAdapter.java | 13 +- .../cayenne/dba/frontbase/FrontBaseAdapter.java | 322 ++++++----- .../java/org/apache/cayenne/dba/h2/H2Adapter.java | 15 +- .../apache/cayenne/dba/hsqldb/HSQLDBAdapter.java | 420 ++++++++------- .../apache/cayenne/dba/ingres/IngresAdapter.java | 163 +++--- .../org/apache/cayenne/dba/mysql/MySQLAdapter.java | 575 ++++++++++---------- .../apache/cayenne/dba/oracle/OracleAdapter.java | 588 ++++++++++----------- .../cayenne/dba/postgres/PostgresAdapter.java | 15 +- .../apache/cayenne/dba/sqlite/SQLiteAdapter.java | 14 +- .../cayenne/dba/sqlserver/SQLServerAdapter.java | 40 +- .../apache/cayenne/dba/sybase/SybaseAdapter.java | 2 +- 14 files changed, 1030 insertions(+), 1182 deletions(-) diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/JdbcAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/JdbcAdapter.java index 9ff9c3e06..691d6d94e 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/JdbcAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/JdbcAdapter.java @@ -498,15 +498,15 @@ public class JdbcAdapter implements DbAdapter { } @Override - public DbAttribute buildAttribute(String name, String typeName, int type, int size, int scale, boolean allowNulls) { + public DbAttribute buildAttribute(String name, String typeName, int type, int maxLength, int scale, boolean allowNulls) { DbAttribute attr = new DbAttribute(); attr.setName(name); attr.setType(type); attr.setMandatory(!allowNulls); - if (size >= 0) { - attr.setMaxLength(size); + if (maxLength >= 0) { + attr.setMaxLength(maxLength); } if (scale >= 0) { diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/db2/DB2Adapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/db2/DB2Adapter.java index e9db3d86b..76bc02a39 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/db2/DB2Adapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/db2/DB2Adapter.java @@ -19,11 +19,6 @@ package org.apache.cayenne.dba.db2; -import java.sql.PreparedStatement; -import java.sql.Types; -import java.util.List; - -import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.DataNode; import org.apache.cayenne.access.sqlbuilder.sqltree.SQLTreeProcessor; import org.apache.cayenne.access.translator.ParameterBinding; @@ -40,24 +35,19 @@ import org.apache.cayenne.access.types.ValueObjectTypeRegistry; 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.di.Inject; import org.apache.cayenne.map.DbAttribute; import org.apache.cayenne.query.Query; import org.apache.cayenne.query.SQLAction; + +import java.sql.PreparedStatement; +import java.sql.Types; import java.util.HashMap; +import java.util.List; import java.util.Map; /** - * DbAdapter implementation for the <a href="http://www.ibm.com/db2/"> DB2 RDBMS </a>. - * Sample connection settings to use with DB2 are shown below: - * - * <pre> - * test-db2.jdbc.username = test - * test-db2.jdbc.password = secret - * test-db2.jdbc.url = jdbc:db2://servername:50000/databasename - * test-db2.jdbc.driver = com.ibm.db2.jcc.DB2Driver - * </pre> + * DbAdapter implementation for the DB2 RDBMS. */ public class DB2Adapter extends JdbcAdapter { diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/derby/DerbyAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/derby/DerbyAdapter.java index 3fd91e1eb..86766fdb3 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/derby/DerbyAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/derby/DerbyAdapter.java @@ -19,7 +19,6 @@ package org.apache.cayenne.dba.derby; -import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.DataNode; import org.apache.cayenne.access.sqlbuilder.sqltree.SQLTreeProcessor; import org.apache.cayenne.access.translator.ParameterBinding; @@ -36,7 +35,6 @@ import org.apache.cayenne.access.types.ValueObjectTypeRegistry; 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.di.Inject; import org.apache.cayenne.map.DbAttribute; import org.apache.cayenne.query.Query; @@ -50,22 +48,7 @@ import java.util.List; import java.util.Map; /** - * DbAdapter implementation for the <a href="http://db.apache.org/derby/"> Derby RDBMS - * </a>. Sample connection settings to use with Derby are shown below. <h3>Embedded</h3> - * <p> - * <pre> - * test-derby.jdbc.url = jdbc:derby:testdb;create=true - * test-derby.jdbc.driver = org.apache.derby.jdbc.EmbeddedDriver - * </pre> - * <p> - * <h3>Network Server</h3> - * <p> - * <pre> - * derbynet.jdbc.url = jdbc:derby://localhost/cayenne - * derbynet.jdbc.driver = org.apache.derby.jdbc.ClientDriver - * derbynet.jdbc.username = someuser - * derbynet.jdbc.password = secret; - * </pre> + * DbAdapter implementation for the Derby RDBMS */ public class DerbyAdapter extends JdbcAdapter { diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/firebird/FirebirdAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/firebird/FirebirdAdapter.java index 0fab4f53c..61611922d 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/firebird/FirebirdAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/firebird/FirebirdAdapter.java @@ -19,7 +19,6 @@ package org.apache.cayenne.dba.firebird; -import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.DataNode; import org.apache.cayenne.access.sqlbuilder.sqltree.SQLTreeProcessor; import org.apache.cayenne.access.translator.ejbql.EJBQLTranslator; @@ -43,17 +42,7 @@ import java.util.List; import java.util.Map; /** - * DbAdapter implementation for <a href="http://www.firebirdsql.org">FirebirdSQL - * RDBMS </a>. Sample connection settings to use with Firebird are shown - * below: - * - * <pre> - * firebird.cayenne.adapter = org.apache.cayenne.dba.firebird.FirebirdAdapter - * firebird.jdbc.username = test - * firebird.jdbc.password = secret - * firebird.jdbc.url = jdbc:firebirdsql:localhost:/home/firebird/test.fdb - * firebird.jdbc.driver = org.firebirdsql.jdbc.FBDriver - * </pre> + * DbAdapter implementation for the FirebirdSQL RDBMS */ public class FirebirdAdapter extends JdbcAdapter { diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseAdapter.java index 83f480bf6..e60cc6ff6 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseAdapter.java @@ -19,12 +19,6 @@ package org.apache.cayenne.dba.frontbase; -import java.sql.Types; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.DataNode; import org.apache.cayenne.access.sqlbuilder.sqltree.SQLTreeProcessor; @@ -35,7 +29,6 @@ import org.apache.cayenne.access.types.ValueObjectTypeRegistry; 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.dba.TypesMapping; import org.apache.cayenne.di.Inject; @@ -43,40 +36,37 @@ import org.apache.cayenne.map.DbAttribute; import org.apache.cayenne.map.DbEntity; import org.apache.cayenne.query.Query; import org.apache.cayenne.query.SQLAction; + +import java.sql.Types; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; +import java.util.List; import java.util.Map; /** - * DbAdapter implementation for <a href="http://www.frontbase.com/">FrontBase - * RDBMS</a>. Sample connection settings to use with FrontBase are shown below: - * - * <pre> - * fb.jdbc.username = _system - * fb.jdbc.password = secret - * fb.jdbc.url = jdbc:FrontBase://localhost/cayenne/ - * fb.jdbc.driver = jdbc.FrontBase.FBJDriver - * </pre> - * + * DbAdapter implementation for FrontBase RDBMS + * * @since 1.2 */ -// TODO, Andrus 11/8/2005: + +// TODO: Andrus 11/8/2005: // Limitations (also see FrontBaseStackAdapter in unit tests): // -// 1. Case insensitive ordering (i.e. UPPER in the ORDER BY clause) is supported -// by +// Case insensitive ordering (i.e. UPPER in the ORDER BY clause) is supported by // FrontBase, however aliases don't work ( ORDER BY UPPER(t0.ARTIST_NAME)) ... -// not sure -// what to do about it. public class FrontBaseAdapter extends JdbcAdapter { - public FrontBaseAdapter(@Inject RuntimeProperties runtimeProperties, - @Inject(Constants.DEFAULT_TYPES_LIST) List<ExtendedType> defaultExtendedTypes, - @Inject(Constants.USER_TYPES_LIST) List<ExtendedType> userExtendedTypes, - @Inject(Constants.TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> extendedTypeFactories, - @Inject ValueObjectTypeRegistry valueObjectTypeRegistry) { - super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, extendedTypeFactories, valueObjectTypeRegistry); - setSupportsBatchUpdates(true); - } + public FrontBaseAdapter( + @Inject RuntimeProperties runtimeProperties, + @Inject(Constants.DEFAULT_TYPES_LIST) List<ExtendedType> defaultExtendedTypes, + @Inject(Constants.USER_TYPES_LIST) List<ExtendedType> userExtendedTypes, + @Inject(Constants.TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> extendedTypeFactories, + @Inject ValueObjectTypeRegistry valueObjectTypeRegistry) { + super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, extendedTypeFactories, valueObjectTypeRegistry); + setSupportsBatchUpdates(true); + } @Override protected Map<Integer, String[]> createExternalTypes() { @@ -114,146 +104,146 @@ public class FrontBaseAdapter extends JdbcAdapter { /** * @since 4.2 */ - @Override - public SQLTreeProcessor getSqlTreeProcessor() { - return new FrontBaseSQLTreeProcessor(); - } - - @Override - public String tableTypeForTable() { - return "BASE TABLE"; - } - - @Override - protected void configureExtendedTypes(ExtendedTypeMap map) { - super.configureExtendedTypes(map); - - map.registerType(new FrontBaseByteArrayType()); - map.registerType(new FrontBaseBooleanType()); - map.registerType(new FrontBaseCharType()); - } - - /** - * Customizes table creating procedure for FrontBase. - */ - @Override - public String createTable(DbEntity ent) { - QuotingStrategy context = getQuotingStrategy(); - StringBuilder buf = new StringBuilder(); - buf.append("CREATE TABLE "); - buf.append(context.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 '%s.%s'." - , ent.getFullyQualifiedName(), at.getName()); - } - - String[] types = externalTypesForJdbcType(at.getType()); - if (types == null || types.length == 0) { - throw new CayenneRuntimeException("Undefined type for attribute '%s.%s': %s" - , ent.getFullyQualifiedName(), at.getName(), at.getType()); - } - - String type = types[0]; - buf.append(context.quotedName(at)).append(' ').append(type); - - // Mapping LONGVARCHAR without length creates a column with length - // "1" which - // is definitely not what we want...so just use something very large - // (1Gb seems - // to be the limit for FB) - if (at.getType() == Types.LONGVARCHAR) { - - int len = at.getMaxLength() > 0 ? at.getMaxLength() : 1073741824; - buf.append("(").append(len).append(")"); - } else if (at.getType() == Types.VARBINARY || at.getType() == Types.BINARY) { - - // use a BIT column with size * 8 - int len = at.getMaxLength() > 0 ? at.getMaxLength() : 1073741824; - len *= 8; - buf.append("(").append(len).append(")"); - } else 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(')'); - } - } + @Override + public SQLTreeProcessor getSqlTreeProcessor() { + return new FrontBaseSQLTreeProcessor(); + } - if (at.isMandatory()) { - buf.append(" NOT NULL"); - } - // else: don't appen NULL for FrontBase: - } + @Override + public String tableTypeForTable() { + return "BASE TABLE"; + } - // primary key clause - Iterator<DbAttribute> pkit = ent.getPrimaryKeys().iterator(); - if (pkit.hasNext()) { - if (first) { - first = false; - } else { - buf.append(", "); - } + @Override + protected void configureExtendedTypes(ExtendedTypeMap map) { + super.configureExtendedTypes(map); - buf.append("PRIMARY KEY ("); - boolean firstPk = true; - while (pkit.hasNext()) { - if (firstPk) { - firstPk = false; - } else { - buf.append(", "); - } + map.registerType(new FrontBaseByteArrayType()); + map.registerType(new FrontBaseBooleanType()); + map.registerType(new FrontBaseCharType()); + } - DbAttribute at = pkit.next(); - buf.append(quotingStrategy.quotedName(at)); - } - buf.append(')'); - } - buf.append(')'); - return buf.toString(); - } + /** + * Customizes table creating procedure for FrontBase. + */ + @Override + public String createTable(DbEntity ent) { + QuotingStrategy context = getQuotingStrategy(); + StringBuilder buf = new StringBuilder(); + buf.append("CREATE TABLE "); + buf.append(context.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 '%s.%s'." + , ent.getFullyQualifiedName(), at.getName()); + } + + String[] types = externalTypesForJdbcType(at.getType()); + if (types == null || types.length == 0) { + throw new CayenneRuntimeException("Undefined type for attribute '%s.%s': %s" + , ent.getFullyQualifiedName(), at.getName(), at.getType()); + } + + String type = types[0]; + buf.append(context.quotedName(at)).append(' ').append(type); + + // Mapping LONGVARCHAR without length creates a column with length + // "1" which + // is definitely not what we want...so just use something very large + // (1Gb seems + // to be the limit for FB) + if (at.getType() == Types.LONGVARCHAR) { + + int len = at.getMaxLength() > 0 ? at.getMaxLength() : 1073741824; + buf.append("(").append(len).append(")"); + } else if (at.getType() == Types.VARBINARY || at.getType() == Types.BINARY) { + + // use a BIT column with size * 8 + int len = at.getMaxLength() > 0 ? at.getMaxLength() : 1073741824; + len *= 8; + buf.append("(").append(len).append(")"); + } else 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: don't appen NULL for FrontBase: + } + + // primary key clause + Iterator<DbAttribute> pkit = ent.getPrimaryKeys().iterator(); + if (pkit.hasNext()) { + if (first) { + first = false; + } else { + buf.append(", "); + } + + buf.append("PRIMARY KEY ("); + boolean firstPk = true; + while (pkit.hasNext()) { + if (firstPk) { + firstPk = false; + } else { + buf.append(", "); + } + + DbAttribute at = pkit.next(); + buf.append(quotingStrategy.quotedName(at)); + } + buf.append(')'); + } + buf.append(')'); + return buf.toString(); + } - /** - * Adds the CASCADE option to the DROP TABLE clause. - */ - @Override - public Collection<String> dropTableStatements(DbEntity table) { - return Collections.singleton("DROP TABLE " + getQuotingStrategy().quotedFullyQualifiedName(table) + " CASCADE"); - } + /** + * Adds the CASCADE option to the DROP TABLE clause. + */ + @Override + public Collection<String> dropTableStatements(DbEntity table) { + return Collections.singleton("DROP TABLE " + getQuotingStrategy().quotedFullyQualifiedName(table) + " CASCADE"); + } - /** - * Uses FrontBaseActionBuilder to create the right action. - * - * @since 4.2 - */ - @Override - public SQLAction getAction(Query query, DataNode node) { - return query.createSQLAction(new FrontBaseActionBuilder(node)); - } + /** + * Uses FrontBaseActionBuilder to create the right action. + * + * @since 4.2 + */ + @Override + public SQLAction getAction(Query query, DataNode node) { + return query.createSQLAction(new FrontBaseActionBuilder(node)); + } } diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/h2/H2Adapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/h2/H2Adapter.java index fa5cb1f2e..7e1d5decd 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/h2/H2Adapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/h2/H2Adapter.java @@ -42,16 +42,7 @@ import java.util.List; import java.util.Map; /** - * DbAdapter implementation for <a href="http://www.h2database.com/">H2 - * RDBMS </a>. Sample connection settings to use with H2 are shown - * below: - * - * <pre> - * postgres.jdbc.username = sa - * postgres.jdbc.password = - * postgres.jdbc.url = jdbc:h2:cayenne - * postgres.jdbc.driver = org.h2.Driver - * </pre> + * DbAdapter implementation for H2 RDBMS. * * @since 3.0 */ @@ -154,11 +145,11 @@ public class H2Adapter extends JdbcAdapter { } @Override - public DbAttribute buildAttribute(String name, String typeName, int type, int size, int scale, boolean allowNulls) { + public DbAttribute buildAttribute(String name, String typeName, int type, int maxLength, int scale, boolean allowNulls) { if ("json".equalsIgnoreCase(typeName)) { type = Types.OTHER; } - return super.buildAttribute(name, typeName, type, size, scale, allowNulls); + return super.buildAttribute(name, typeName, type, maxLength, scale, allowNulls); } diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLDBAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLDBAdapter.java index 5ed13c90c..cd1ab162b 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLDBAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLDBAdapter.java @@ -52,29 +52,21 @@ import java.util.List; import java.util.Map; /** - * DbAdapter implementation for the <a href="http://hsqldb.sourceforge.net/"> - * HSQLDB RDBMS </a>. Sample connection settings to use with HSQLDB are shown - * below: - * - * <pre> - * test-hsqldb.jdbc.username = test - * test-hsqldb.jdbc.password = secret - * test-hsqldb.jdbc.url = jdbc:hsqldb:hsql://serverhostname - * test-hsqldb.jdbc.driver = org.hsqldb.jdbcDriver - * </pre> + * DbAdapter implementation for the HSQLDB RDBMS. */ public class HSQLDBAdapter extends JdbcAdapter { - public static final String TRIM_FUNCTION = "RTRIM"; + public static final String TRIM_FUNCTION = "RTRIM"; - public HSQLDBAdapter(@Inject RuntimeProperties runtimeProperties, - @Inject(Constants.DEFAULT_TYPES_LIST) List<ExtendedType> defaultExtendedTypes, - @Inject(Constants.USER_TYPES_LIST) List<ExtendedType> userExtendedTypes, - @Inject(Constants.TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> extendedTypeFactories, - @Inject ValueObjectTypeRegistry valueObjectTypeRegistry) { - super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, extendedTypeFactories, valueObjectTypeRegistry); - setSupportsGeneratedKeys(true); - } + public HSQLDBAdapter( + @Inject RuntimeProperties runtimeProperties, + @Inject(Constants.DEFAULT_TYPES_LIST) List<ExtendedType> defaultExtendedTypes, + @Inject(Constants.USER_TYPES_LIST) List<ExtendedType> userExtendedTypes, + @Inject(Constants.TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> extendedTypeFactories, + @Inject ValueObjectTypeRegistry valueObjectTypeRegistry) { + super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, extendedTypeFactories, valueObjectTypeRegistry); + setSupportsGeneratedKeys(true); + } @Override protected Map<Integer, String[]> createExternalTypes() { @@ -114,199 +106,199 @@ public class HSQLDBAdapter extends JdbcAdapter { return types; } - /** - * @since 4.0 - */ - @Override - protected void configureExtendedTypes(ExtendedTypeMap map) { - super.configureExtendedTypes(map); - - // create specially configured CharType handler - CharType charType = new CharType(true, true); - map.registerType(charType); - - map.registerType(new JsonType(charType, true)); - } - - /** - * @since 4.2 - */ - @Override - public SQLTreeProcessor getSqlTreeProcessor() { - return new HSQLTreeProcessor(); - } - - /** - * @since 5.0 - */ - @Override - public ProcedureTranslator getProcedureTranslator(ProcedureQuery query, EntityResolver entityResolver) { - return new HSQLDBProcedureTranslator(); - } - - /** - * @since 4.0 - */ - @Override - protected EJBQLTranslator createEJBQLTranslator() { - JdbcEJBQLTranslator translatorFactory = new HSQLEJBQLTranslator(); - translatorFactory.setCaseInsensitive(caseInsensitiveCollations); - return translatorFactory; - } - - /** - * Generate fully-qualified name for 1.8 and on. Subclass generates - * unqualified name. - * - * @since 1.2 - */ - protected String getTableName(DbEntity entity) { - return quotingStrategy.quotedFullyQualifiedName(entity); - } - - /** - * Returns DbEntity schema name for 1.8 and on. Subclass generates - * unqualified name. - * - * @since 1.2 - */ - protected String getSchemaName(DbEntity entity) { - return entity.getSchema(); - } - - /** - * Uses special action builder to create the right action. - * - * @since 1.2 - */ - @Override - public SQLAction getAction(Query query, DataNode node) { - return query.createSQLAction(new HSQLActionBuilder(node)); - } - - /** - * Returns a DDL string to create a unique constraint over a set of columns. - * - * @since 1.1 - */ - @Override - public String createUniqueConstraint(DbEntity source, Collection<DbAttribute> columns) { - - if (columns == null || columns.isEmpty()) { - throw new CayenneRuntimeException("Can't create UNIQUE constraint - no columns specified."); - } - - String srcName = getTableName(source); - - StringBuilder buf = new StringBuilder(); - - buf.append("ALTER TABLE ").append(srcName); - buf.append(" ADD CONSTRAINT "); - - String name = "U_" + source.getName() + "_" + (long) (System.currentTimeMillis() / (Math.random() * 100000)); - buf.append(quotingStrategy.quotedIdentifier(source, source.getSchema(), name)); - buf.append(" UNIQUE ("); - - Iterator<DbAttribute> it = columns.iterator(); - DbAttribute first = it.next(); - buf.append(quotingStrategy.quotedName(first)); - - while (it.hasNext()) { - DbAttribute next = it.next(); - buf.append(", "); - buf.append(quotingStrategy.quotedName(next)); - } - - buf.append(")"); - - return buf.toString(); - } - - /** - * Adds an ADD CONSTRAINT clause to a relationship constraint. - * - * @see JdbcAdapter#createFkConstraint(DbRelationship) - */ - @Override - public String createFkConstraint(DbRelationship rel) { - - StringBuilder buf = new StringBuilder(); - StringBuilder refBuf = new StringBuilder(); - - String srcName = getTableName(rel.getSourceEntity()); - String dstName = getTableName(rel.getTargetEntity()); - - buf.append("ALTER TABLE "); - buf.append(srcName); - - // hsqldb requires the ADD CONSTRAINT statement - buf.append(" ADD CONSTRAINT "); - - String name = "U_" + rel.getSourceEntity().getName() + "_" - + (long) (System.currentTimeMillis() / (Math.random() * 100000)); - - DbEntity sourceEntity = rel.getSourceEntity(); - - buf.append(quotingStrategy.quotedIdentifier(sourceEntity, sourceEntity.getSchema(), name)); - buf.append(" FOREIGN KEY ("); - - boolean first = true; - for (DbJoin join : rel.getJoins()) { - if (!first) { - buf.append(", "); - refBuf.append(", "); - } else { - first = false; - } - - buf.append(quotingStrategy.quotedSourceName(join)); - refBuf.append(quotingStrategy.quotedTargetName(join)); - } - - buf.append(") REFERENCES "); - buf.append(dstName); - buf.append(" ("); - buf.append(refBuf.toString()); - buf.append(')'); - - // also make sure we delete dependent FKs - buf.append(" ON DELETE CASCADE"); - - return buf.toString(); - } - - /** - * Uses "CREATE CACHED TABLE" instead of "CREATE TABLE". - * - * @since 1.2 - */ - @Override - public String createTable(DbEntity ent) { - // SET SCHEMA <schemaname> - String sql = super.createTable(ent); - if (sql != null && sql.toUpperCase().startsWith("CREATE TABLE ")) { - sql = "CREATE CACHED TABLE " + sql.substring("CREATE TABLE ".length()); - } - - return sql; - } - - @Override - public void createTableAppendColumn(StringBuffer sqlBuffer, DbAttribute column) { - // CAY-1095: if the column is type double, temporarily set the max - // length to 0 to - // avoid adding precision information. - if (column.getType() == Types.DOUBLE && column.getMaxLength() > 0) { - int len = column.getMaxLength(); - column.setMaxLength(0); - super.createTableAppendColumn(sqlBuffer, column); - column.setMaxLength(len); - } else { - super.createTableAppendColumn(sqlBuffer, column); - } - - if(column.isGenerated()) { - sqlBuffer.append(" GENERATED BY DEFAULT AS IDENTITY (START WITH 1)"); - } - } + /** + * @since 4.0 + */ + @Override + protected void configureExtendedTypes(ExtendedTypeMap map) { + super.configureExtendedTypes(map); + + // create specially configured CharType handler + CharType charType = new CharType(true, true); + map.registerType(charType); + + map.registerType(new JsonType(charType, true)); + } + + /** + * @since 4.2 + */ + @Override + public SQLTreeProcessor getSqlTreeProcessor() { + return new HSQLTreeProcessor(); + } + + /** + * @since 5.0 + */ + @Override + public ProcedureTranslator getProcedureTranslator(ProcedureQuery query, EntityResolver entityResolver) { + return new HSQLDBProcedureTranslator(); + } + + /** + * @since 4.0 + */ + @Override + protected EJBQLTranslator createEJBQLTranslator() { + JdbcEJBQLTranslator translatorFactory = new HSQLEJBQLTranslator(); + translatorFactory.setCaseInsensitive(caseInsensitiveCollations); + return translatorFactory; + } + + /** + * Generate fully-qualified name for 1.8 and on. Subclass generates + * unqualified name. + * + * @since 1.2 + */ + protected String getTableName(DbEntity entity) { + return quotingStrategy.quotedFullyQualifiedName(entity); + } + + /** + * Returns DbEntity schema name for 1.8 and on. Subclass generates + * unqualified name. + * + * @since 1.2 + */ + protected String getSchemaName(DbEntity entity) { + return entity.getSchema(); + } + + /** + * Uses special action builder to create the right action. + * + * @since 1.2 + */ + @Override + public SQLAction getAction(Query query, DataNode node) { + return query.createSQLAction(new HSQLActionBuilder(node)); + } + + /** + * Returns a DDL string to create a unique constraint over a set of columns. + * + * @since 1.1 + */ + @Override + public String createUniqueConstraint(DbEntity source, Collection<DbAttribute> columns) { + + if (columns == null || columns.isEmpty()) { + throw new CayenneRuntimeException("Can't create UNIQUE constraint - no columns specified."); + } + + String srcName = getTableName(source); + + StringBuilder buf = new StringBuilder(); + + buf.append("ALTER TABLE ").append(srcName); + buf.append(" ADD CONSTRAINT "); + + String name = "U_" + source.getName() + "_" + (long) (System.currentTimeMillis() / (Math.random() * 100000)); + buf.append(quotingStrategy.quotedIdentifier(source, source.getSchema(), name)); + buf.append(" UNIQUE ("); + + Iterator<DbAttribute> it = columns.iterator(); + DbAttribute first = it.next(); + buf.append(quotingStrategy.quotedName(first)); + + while (it.hasNext()) { + DbAttribute next = it.next(); + buf.append(", "); + buf.append(quotingStrategy.quotedName(next)); + } + + buf.append(")"); + + return buf.toString(); + } + + /** + * Adds an ADD CONSTRAINT clause to a relationship constraint. + * + * @see JdbcAdapter#createFkConstraint(DbRelationship) + */ + @Override + public String createFkConstraint(DbRelationship rel) { + + StringBuilder buf = new StringBuilder(); + StringBuilder refBuf = new StringBuilder(); + + String srcName = getTableName(rel.getSourceEntity()); + String dstName = getTableName(rel.getTargetEntity()); + + buf.append("ALTER TABLE "); + buf.append(srcName); + + // hsqldb requires the ADD CONSTRAINT statement + buf.append(" ADD CONSTRAINT "); + + String name = "U_" + rel.getSourceEntity().getName() + "_" + + (long) (System.currentTimeMillis() / (Math.random() * 100000)); + + DbEntity sourceEntity = rel.getSourceEntity(); + + buf.append(quotingStrategy.quotedIdentifier(sourceEntity, sourceEntity.getSchema(), name)); + buf.append(" FOREIGN KEY ("); + + boolean first = true; + for (DbJoin join : rel.getJoins()) { + if (!first) { + buf.append(", "); + refBuf.append(", "); + } else { + first = false; + } + + buf.append(quotingStrategy.quotedSourceName(join)); + refBuf.append(quotingStrategy.quotedTargetName(join)); + } + + buf.append(") REFERENCES "); + buf.append(dstName); + buf.append(" ("); + buf.append(refBuf.toString()); + buf.append(')'); + + // also make sure we delete dependent FKs + buf.append(" ON DELETE CASCADE"); + + return buf.toString(); + } + + /** + * Uses "CREATE CACHED TABLE" instead of "CREATE TABLE". + * + * @since 1.2 + */ + @Override + public String createTable(DbEntity ent) { + // SET SCHEMA <schemaname> + String sql = super.createTable(ent); + if (sql != null && sql.toUpperCase().startsWith("CREATE TABLE ")) { + sql = "CREATE CACHED TABLE " + sql.substring("CREATE TABLE ".length()); + } + + return sql; + } + + @Override + public void createTableAppendColumn(StringBuffer sqlBuffer, DbAttribute column) { + // CAY-1095: if the column is type double, temporarily set the max + // length to 0 to + // avoid adding precision information. + if (column.getType() == Types.DOUBLE && column.getMaxLength() > 0) { + int len = column.getMaxLength(); + column.setMaxLength(0); + super.createTableAppendColumn(sqlBuffer, column); + column.setMaxLength(len); + } else { + super.createTableAppendColumn(sqlBuffer, column); + } + + if (column.isGenerated()) { + sqlBuffer.append(" GENERATED BY DEFAULT AS IDENTITY (START WITH 1)"); + } + } } diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/ingres/IngresAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/ingres/IngresAdapter.java index d56c80e4b..fd15c1bd3 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/ingres/IngresAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/ingres/IngresAdapter.java @@ -19,7 +19,6 @@ package org.apache.cayenne.dba.ingres; -import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.DataNode; import org.apache.cayenne.access.sqlbuilder.sqltree.SQLTreeProcessor; import org.apache.cayenne.access.translator.ParameterBinding; @@ -30,7 +29,6 @@ import org.apache.cayenne.access.types.ValueObjectTypeRegistry; 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.TypesMapping; import org.apache.cayenne.di.Inject; import org.apache.cayenne.map.DbAttribute; @@ -38,37 +36,25 @@ import org.apache.cayenne.query.Query; import org.apache.cayenne.query.SQLAction; import java.sql.PreparedStatement; -import java.sql.SQLException; import java.sql.Types; import java.util.HashMap; import java.util.List; import java.util.Map; /** - * DbAdapter implementation for <a - * href="http://opensource.ca.com/projects/ingres/">Ingres</a>. Sample - * connection settings to use with Ingres are shown below: - * - * <pre> - * ingres.jdbc.username = test - * ingres.jdbc.password = secret - * ingres.jdbc.url = jdbc:ingres://serverhostname:II7/cayenne - * ingres.jdbc.driver = ca.ingres.jdbc.IngresDriver - * </pre> + * DbAdapter implementation for Ingres RDBMS. */ public class IngresAdapter extends JdbcAdapter { - public static final String TRIM_FUNCTION = "TRIM"; - - public IngresAdapter(@Inject RuntimeProperties runtimeProperties, - @Inject(Constants.DEFAULT_TYPES_LIST) List<ExtendedType> defaultExtendedTypes, - @Inject(Constants.USER_TYPES_LIST) List<ExtendedType> userExtendedTypes, - @Inject(Constants.TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> extendedTypeFactories, - @Inject ValueObjectTypeRegistry valueObjectTypeRegistry) { - super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, extendedTypeFactories, valueObjectTypeRegistry); - setSupportsUniqueConstraints(true); - setSupportsGeneratedKeys(true); - } + public IngresAdapter(@Inject RuntimeProperties runtimeProperties, + @Inject(Constants.DEFAULT_TYPES_LIST) List<ExtendedType> defaultExtendedTypes, + @Inject(Constants.USER_TYPES_LIST) List<ExtendedType> userExtendedTypes, + @Inject(Constants.TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> extendedTypeFactories, + @Inject ValueObjectTypeRegistry valueObjectTypeRegistry) { + super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, extendedTypeFactories, valueObjectTypeRegistry); + setSupportsUniqueConstraints(true); + setSupportsGeneratedKeys(true); + } @Override protected Map<Integer, String[]> createExternalTypes() { @@ -110,69 +96,68 @@ public class IngresAdapter extends JdbcAdapter { /** * @since 4.2 */ - @Override - public SQLTreeProcessor getSqlTreeProcessor() { - return new IngressSQLTreeProcessor(); - } - - @Override - public SQLAction getAction(Query query, DataNode node) { - return query.createSQLAction(new IngresActionBuilder(node)); - } - - @Override - protected void configureExtendedTypes(ExtendedTypeMap map) { - super.configureExtendedTypes(map); - map.registerType(new IngresCharType()); - - // configure boolean type to work with numeric columns - map.registerType(new IngresBooleanType()); - } - - @Override - public void bindParameter(PreparedStatement statement, ParameterBinding binding) - throws SQLException, Exception { - - if (binding.getValue() == null && (binding.getJdbcType() == Types.BIT)) { - statement.setNull(binding.getStatementPosition(), Types.SMALLINT); - } else { - super.bindParameter(statement, binding); - } - } - - @Override - public void createTableAppendColumn(StringBuffer buf, DbAttribute at) { - String type = getType(this, at); - 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.isGenerated()) { - buf.append(" GENERATED BY DEFAULT AS IDENTITY "); - } - - // Ingres does not like "null" for non mandatory fields - if (at.isMandatory()) { - buf.append(" NOT NULL"); - } - } + @Override + public SQLTreeProcessor getSqlTreeProcessor() { + return new IngressSQLTreeProcessor(); + } + + @Override + public SQLAction getAction(Query query, DataNode node) { + return query.createSQLAction(new IngresActionBuilder(node)); + } + + @Override + protected void configureExtendedTypes(ExtendedTypeMap map) { + super.configureExtendedTypes(map); + map.registerType(new IngresCharType()); + + // configure boolean type to work with numeric columns + map.registerType(new IngresBooleanType()); + } + + @Override + public void bindParameter(PreparedStatement statement, ParameterBinding binding) throws Exception { + + if (binding.getValue() == null && (binding.getJdbcType() == Types.BIT)) { + statement.setNull(binding.getStatementPosition(), Types.SMALLINT); + } else { + super.bindParameter(statement, binding); + } + } + + @Override + public void createTableAppendColumn(StringBuffer buf, DbAttribute at) { + String type = getType(this, at); + 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.isGenerated()) { + buf.append(" GENERATED BY DEFAULT AS IDENTITY "); + } + + // Ingres does not like "null" for non mandatory fields + if (at.isMandatory()) { + buf.append(" NOT NULL"); + } + } } diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java index 12ec6a15f..6cfde192b 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java @@ -21,10 +21,9 @@ package org.apache.cayenne.dba.mysql; import org.apache.cayenne.access.DataNode; import org.apache.cayenne.access.sqlbuilder.sqltree.SQLTreeProcessor; -import org.apache.cayenne.access.translator.ParameterBinding; -import org.apache.cayenne.access.translator.procedure.ProcedureTranslator; import org.apache.cayenne.access.translator.ejbql.EJBQLTranslator; import org.apache.cayenne.access.translator.ejbql.JdbcEJBQLTranslator; +import org.apache.cayenne.access.translator.procedure.ProcedureTranslator; import org.apache.cayenne.access.types.ByteArrayType; import org.apache.cayenne.access.types.CharType; import org.apache.cayenne.access.types.DateType; @@ -50,7 +49,6 @@ import org.apache.cayenne.query.ProcedureQuery; import org.apache.cayenne.query.Query; import org.apache.cayenne.query.SQLAction; -import java.sql.PreparedStatement; import java.sql.Types; import java.util.ArrayList; import java.util.Collection; @@ -61,46 +59,29 @@ import java.util.List; import java.util.Map; /** - * DbAdapter implementation for <a href="http://www.mysql.com">MySQL RDBMS</a>. - * <h3> - * Foreign Key Constraint Handling</h3> - * <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 - * {@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 - * detect whether InnoDB is the default, and configure the adapter accordingly. - * <h3>Sample Connection Settings</h3> - * <ul> - * <li>Adapter name: org.apache.cayenne.dba.mysql.MySQLAdapter</li> - * <li>DB URL: jdbc:mysql://serverhostname/dbname</li> - * <li>Driver Class: com.mysql.jdbc.Driver</li> - * </ul> + * DbAdapter implementation for MySQL RDBMS. */ public class MySQLAdapter extends JdbcAdapter { - static final String DEFAULT_STORAGE_ENGINE = "InnoDB"; - static final List<String> SYSTEM_CATALOGS = List.of("sys", "information_schema", "mysql", "performance_schema"); + static final String DEFAULT_STORAGE_ENGINE = "InnoDB"; + static final List<String> SYSTEM_CATALOGS = List.of("sys", "information_schema", "mysql", "performance_schema"); - protected String storageEngine; + protected String storageEngine; - public MySQLAdapter(@Inject RuntimeProperties runtimeProperties, - @Inject(Constants.DEFAULT_TYPES_LIST) List<ExtendedType> defaultExtendedTypes, - @Inject(Constants.USER_TYPES_LIST) List<ExtendedType> userExtendedTypes, - @Inject(Constants.TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> extendedTypeFactories, - @Inject ValueObjectTypeRegistry valueObjectTypeRegistry) { - super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, extendedTypeFactories, valueObjectTypeRegistry); + public MySQLAdapter(@Inject RuntimeProperties runtimeProperties, + @Inject(Constants.DEFAULT_TYPES_LIST) List<ExtendedType> defaultExtendedTypes, + @Inject(Constants.USER_TYPES_LIST) List<ExtendedType> userExtendedTypes, + @Inject(Constants.TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> extendedTypeFactories, + @Inject ValueObjectTypeRegistry valueObjectTypeRegistry) { + super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, extendedTypeFactories, valueObjectTypeRegistry); - // init defaults - this.storageEngine = DEFAULT_STORAGE_ENGINE; + // init defaults + this.storageEngine = DEFAULT_STORAGE_ENGINE; - setSupportsBatchUpdates(true); - setSupportsUniqueConstraints(true); - setSupportsGeneratedKeys(true); - } + setSupportsBatchUpdates(true); + setSupportsUniqueConstraints(true); + setSupportsGeneratedKeys(true); + } @Override protected Map<Integer, String[]> createExternalTypes() { @@ -135,143 +116,149 @@ public class MySQLAdapter extends JdbcAdapter { return types; } - @Override - protected QuotingStrategy createQuotingStrategy() { - return new DefaultQuotingStrategy("`", "`"); - } + @Override + protected QuotingStrategy createQuotingStrategy() { + return new DefaultQuotingStrategy("`", "`"); + } /** * @since 4.2 */ - @Override - public SQLTreeProcessor getSqlTreeProcessor() { - return MySQLTreeProcessor.getInstance(caseInsensitiveCollations); - } - - /** - * 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 5.0 - */ - @Override - public ProcedureTranslator getProcedureTranslator(ProcedureQuery query, EntityResolver entityResolver) { - return new MySQLProcedureTranslator(); - } - - /** - * @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 List.of("SET FOREIGN_KEY_CHECKS=0", "DROP TABLE IF EXISTS " + buf + " 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); - - // TODO: this may need to be made universal across adapters - map.registerType(new MySQLLocalDateTimeType()); - - // 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) - CharType charType = new CharType(false, false); - map.registerType(charType); - map.registerType(new ByteArrayType(false, false)); - map.registerType(new JsonType(charType, true)); - - // register non-default types for the dates, see CAY-2691 - map.registerType(new DateType(true)); - map.registerType(new TimeType(true)); - map.registerType(new TimestampType(true)); - map.registerType(new UtilDateType(true)); - } - - @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 ("int unsigned".equals(typeName) || "integer unsigned".equals(typeName) - || "mediumint unsigned".equals(typeName)) { - type = Types.BIGINT; - } - // BIGINT UNSIGNED maps to BigInteger according to MySQL docs, but - // there is no - // JDBC mapping for BigInteger - } - - // This is a special case for the Json type and older MySQL drivers (5.x) - if(type == Types.CHAR && "json".equals(typeName)) { - type = Types.LONGVARCHAR; - } - - // driver reports column size that we should "translate" to the column precision - // see CAY-2694 for details - if(type == Types.TIME) { - precision = Math.max(0, size - 9); - size = -1; - } else if(type == Types.TIMESTAMP) { - precision = Math.max(0, size - 20); - size = -1; - } - - return super.buildAttribute(name, typeName, type, size, precision, allowNulls); - } - - @Override - public int preferredBindingType(int jdbcType) { + @Override + public SQLTreeProcessor getSqlTreeProcessor() { + return MySQLTreeProcessor.getInstance(caseInsensitiveCollations); + } + + /** + * 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 5.0 + */ + @Override + public ProcedureTranslator getProcedureTranslator(ProcedureQuery query, EntityResolver entityResolver) { + return new MySQLProcedureTranslator(); + } + + /** + * @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 List.of("SET FOREIGN_KEY_CHECKS=0", "DROP TABLE IF EXISTS " + buf + " 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); + + // TODO: this may need to be made universal across adapters + map.registerType(new MySQLLocalDateTimeType()); + + // 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) + CharType charType = new CharType(false, false); + map.registerType(charType); + map.registerType(new ByteArrayType(false, false)); + map.registerType(new JsonType(charType, true)); + + // register non-default types for the dates, see CAY-2691 + map.registerType(new DateType(true)); + map.registerType(new TimeType(true)); + map.registerType(new TimestampType(true)); + map.registerType(new UtilDateType(true)); + } + + @Override + public DbAttribute buildAttribute( + String name, + String typeName, + int type, + int maxLength, + int scale, + boolean allowNulls) { + + String normalTypeName = typeName != null ? typeName.toLowerCase() : null; + switch (type) { + // all LOB types are returned by the driver as OTHER... must remap them manually (at least on MySQL 3.23) + case Types.OTHER -> { + if (normalTypeName != null) { + type = switch (normalTypeName) { + case "longblob", "mediumblob", "blob" -> Types.BLOB; + case "tinyblob" -> Types.VARBINARY; + case "longtext", "mediumtext", "text" -> Types.CLOB; + case "tinytext" -> Types.VARCHAR; + default -> type; + }; + } + } + // A special case for the JSON type and older MySQL drivers (5.x) + case Types.CHAR -> { + if ("json".equals(normalTypeName)) { + type = Types.LONGVARCHAR; + } + } + // driver reports column size that we should "translate" to the column precision see CAY-2694 for details + case Types.TIME -> { + scale = Math.max(0, maxLength - 9); + maxLength = -1; + } + case Types.TIMESTAMP -> { + scale = Math.max(0, maxLength - 20); + maxLength = -1; + } + // types like "int unsigned" map to Long + default -> { + if (normalTypeName != null && normalTypeName.endsWith(" unsigned")) { + // per http://dev.mysql.com/doc/refman/5.0/en/connector-j-reference-type-conversions.html + if ("int unsigned".equals(normalTypeName) + || "integer unsigned".equals(normalTypeName) + || "mediumint unsigned".equals(normalTypeName)) { + type = Types.BIGINT; + } + // BIGINT UNSIGNED maps to BigInteger according to MySQL docs, but there is no JDBC mapping for BigInteger + } + } + } + + DbAttribute a = new DbAttribute(name); + a.setMandatory(!allowNulls); + a.setType(type); + + if (maxLength >= 0) { + a.setMaxLength(maxLength); + } + + if (scale >= 0) { + a.setScale(scale); + } + + return a; + } + + @Override + public int preferredBindingType(int jdbcType) { return switch (jdbcType) { case Types.NCHAR -> Types.CHAR; case Types.NCLOB -> Types.CLOB; @@ -279,158 +266,158 @@ public class MySQLAdapter extends JdbcAdapter { case Types.LONGNVARCHAR -> Types.LONGVARCHAR; default -> jdbcType; }; - } - - /** - * @since 3.0 - */ - @Override - protected EJBQLTranslator createEJBQLTranslator() { - JdbcEJBQLTranslator translatorFactory = new MySQLEJBQLTranslator(); - 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<>(entity.getPrimaryKeys()); - pkList.sort(PKComparator.INSTANCE); - - Iterator<DbAttribute> pkit = pkList.iterator(); - if (pkit.hasNext()) { + } + + /** + * @since 3.0 + */ + @Override + protected EJBQLTranslator createEJBQLTranslator() { + JdbcEJBQLTranslator translatorFactory = new MySQLEJBQLTranslator(); + 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<>(entity.getPrimaryKeys()); + pkList.sort(PKComparator.INSTANCE); + + 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(", "); - } + firstPk = false; + } else { + sqlBuffer.append(", "); + } DbAttribute at = pkit.next(); sqlBuffer.append(quotingStrategy.quotedName(at)); } sqlBuffer.append(')'); } - } + } - /** - * Appends AUTO_INCREMENT clause to the column definition for generated - * columns. - */ - @Override - public void createTableAppendColumn(StringBuffer sqlBuffer, DbAttribute column) { + /** + * Appends AUTO_INCREMENT clause to the column definition for generated + * columns. + */ + @Override + public void createTableAppendColumn(StringBuffer sqlBuffer, DbAttribute column) { - String type = getType(this, column); + String type = getType(this, column); - sqlBuffer.append(quotingStrategy.quotedName(column)); - sqlBuffer.append(' ').append(type); + sqlBuffer.append(quotingStrategy.quotedName(column)); + sqlBuffer.append(' ').append(type); - // append size and precision (if applicable)s - appendLengthAndScale(sqlBuffer, column); + // append size and precision (if applicable)s + appendLengthAndScale(sqlBuffer, column); - sqlBuffer.append(column.isMandatory() ? " NOT NULL" : " NULL"); + sqlBuffer.append(column.isMandatory() ? " NOT NULL" : " NULL"); - if (column.isGenerated()) { - sqlBuffer.append(" AUTO_INCREMENT"); - } - } + if (column.isGenerated()) { + sqlBuffer.append(" AUTO_INCREMENT"); + } + } - private void appendLengthAndScale(StringBuffer sqlBuffer, DbAttribute column) { - if(column.getType() == Types.TIME || column.getType() == Types.TIMESTAMP) { - int scale = column.getScale(); - if(scale >= 0) { - sqlBuffer.append('(').append(scale).append(')'); - } - } else if (typeSupportsLength(column.getType())) { - int len = column.getMaxLength(); + private void appendLengthAndScale(StringBuffer sqlBuffer, DbAttribute column) { + if (column.getType() == Types.TIME || column.getType() == Types.TIMESTAMP) { + int scale = column.getScale(); + if (scale >= 0) { + sqlBuffer.append('(').append(scale).append(')'); + } + } else if (typeSupportsLength(column.getType())) { + int len = column.getMaxLength(); - int scale = TypesMapping.isDecimal(column.getType()) ? column.getScale() : -1; + int scale = TypesMapping.isDecimal(column.getType()) ? column.getScale() : -1; - // sanity check - if (scale > len) { - scale = -1; - } + // sanity check + if (scale > len) { + scale = -1; + } - if (len > 0) { - sqlBuffer.append('(').append(len); + if (len > 0) { + sqlBuffer.append('(').append(len); - if (scale >= 0) { - sqlBuffer.append(", ").append(scale); - } + if (scale >= 0) { + sqlBuffer.append(", ").append(scale); + } - sqlBuffer.append(')'); - } - } - } + sqlBuffer.append(')'); + } + } + } - @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 + @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 return switch (type) { case Types.TIMESTAMP, Types.TIME -> true; default -> super.typeSupportsLength(type); }; - } - - @Override - public List<String> getSystemCatalogs() { - return SYSTEM_CATALOGS; - } - - /** - * @since 3.0 - */ - public String getStorageEngine() { - return storageEngine; - } - - /** - * @since 3.0 - */ - public void setStorageEngine(String engine) { - this.storageEngine = engine; - } - - static final class PKComparator implements Comparator<DbAttribute> { - - static final PKComparator INSTANCE = new PKComparator(); - - public int compare(DbAttribute a1, DbAttribute a2) { - if (a1.isGenerated() != a2.isGenerated()) { - return a1.isGenerated() ? -1 : 1; - } else { - return a1.getName().compareTo(a2.getName()); - } - } - } + } + + @Override + public List<String> getSystemCatalogs() { + return SYSTEM_CATALOGS; + } + + /** + * @since 3.0 + */ + public String getStorageEngine() { + return storageEngine; + } + + /** + * @since 3.0 + */ + public void setStorageEngine(String engine) { + this.storageEngine = engine; + } + + static final class PKComparator implements Comparator<DbAttribute> { + + static final PKComparator INSTANCE = new PKComparator(); + + public int compare(DbAttribute a1, DbAttribute a2) { + if (a1.isGenerated() != a2.isGenerated()) { + return a1.isGenerated() ? -1 : 1; + } else { + return a1.getName().compareTo(a2.getName()); + } + } + } } diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/oracle/OracleAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/oracle/OracleAdapter.java index d82dc2565..4bf4c6aeb 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/oracle/OracleAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/oracle/OracleAdapter.java @@ -55,123 +55,115 @@ import java.util.List; import java.util.Map; /** - * 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 - * test-oracle.jdbc.password = secret - * test-oracle.jdbc.url = jdbc:oracle:thin:@//192.168.0.20:1521/ora1 - * test-oracle.jdbc.driver = oracle.jdbc.driver.OracleDriver - * </pre> + * DbAdapter implementation for Oracle RDBMS */ 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; - - private List<String> SYSTEM_SCHEMAS = List.of( - "ANONYMOUS", "APPQOSSYS", "AUDSYS", "CTXSYS", "DBSFWUSER", - "DBSNMP", "DIP", "DVF", "GGSYS", "DVSYS", "GSMADMIN_INTERNAL", - "GSMCATUSER", "GSMUSER", "LBACSYS", "MDDATA", "MDSYS", "OJVMSYS", - "OLAPSYS", "ORACLE_OCM", "ORDDATA", "ORDPLUGINS", "ORDSYS", "OUTLN", - "REMOTE_SCHEDULER_AGENT", "SYSTEM", "WMSYS", "SI_INFORMTN_SCHEMA", - "SYS", "SYSBACKUP", "SYSDG", "SYSKM", "SYSRAC", "SYS$UMF", "XDB", "XS$NULL"); - - 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.DEFAULT_TYPES_LIST) List<ExtendedType> defaultExtendedTypes, - @Inject(Constants.USER_TYPES_LIST) List<ExtendedType> userExtendedTypes, - @Inject(Constants.TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> extendedTypeFactories, - @Inject ValueObjectTypeRegistry valueObjectTypeRegistry) { - super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, extendedTypeFactories, valueObjectTypeRegistry); - - // enable batch updates by default - setSupportsBatchUpdates(true); - } + 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; + + private List<String> SYSTEM_SCHEMAS = List.of( + "ANONYMOUS", "APPQOSSYS", "AUDSYS", "CTXSYS", "DBSFWUSER", + "DBSNMP", "DIP", "DVF", "GGSYS", "DVSYS", "GSMADMIN_INTERNAL", + "GSMCATUSER", "GSMUSER", "LBACSYS", "MDDATA", "MDSYS", "OJVMSYS", + "OLAPSYS", "ORACLE_OCM", "ORDDATA", "ORDPLUGINS", "ORDSYS", "OUTLN", + "REMOTE_SCHEDULER_AGENT", "SYSTEM", "WMSYS", "SI_INFORMTN_SCHEMA", + "SYS", "SYSBACKUP", "SYSDG", "SYSKM", "SYSRAC", "SYS$UMF", "XDB", "XS$NULL"); + + 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.DEFAULT_TYPES_LIST) List<ExtendedType> defaultExtendedTypes, + @Inject(Constants.USER_TYPES_LIST) List<ExtendedType> userExtendedTypes, + @Inject(Constants.TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> extendedTypeFactories, + @Inject ValueObjectTypeRegistry valueObjectTypeRegistry) { + super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, extendedTypeFactories, valueObjectTypeRegistry); + + // enable batch updates by default + setSupportsBatchUpdates(true); + } @Override protected Map<Integer, String[]> createExternalTypes() { @@ -207,188 +199,188 @@ public class OracleAdapter extends JdbcAdapter { return types; } - /** - * @since 4.2 - */ - @Override - public SQLTreeProcessor getSqlTreeProcessor() { - return new OracleSQLTreeProcessor(); - } - - /** - * @since 3.0 - */ - @Override - protected EJBQLTranslator createEJBQLTranslator() { - return new OracleEJBQLTranslator(); - } - - /** - * 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 - OracleCharType charType = new OracleCharType(); - map.registerType(charType); - - // 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()); - map.registerType(new JsonType(charType, true)); - } - - /** - * 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, ParameterBinding binding) throws 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 (binding.getValue() == null && binding.getJdbcType() == Types.BOOLEAN) { - ExtendedType typeProcessor = getExtendedTypes().getRegisteredType(Boolean.class); - typeProcessor.setJdbcObject(statement, binding.getValue(), binding.getStatementPosition(), binding - .getJdbcType(),binding.getScale()); - } else { - super.bindParameter(statement, binding); - } - } - - /** - * 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) { - if (size <= 9) { - attr.setType(Types.INTEGER); - } else if(size <= 19) { - attr.setType(Types.BIGINT); - } - 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; - } - - /** - * Uses OracleActionBuilder to create the right action. - * - * @since 1.2 - */ - @Override - public SQLAction getAction(Query query, DataNode node) { - return query.createSQLAction(new OracleActionBuilder(node)); - } - - @Override - public List<String> getSystemSchemas() { - return SYSTEM_SCHEMAS; - } - - /** - * @since 5.0 - */ - @Override - public boolean typeSupportsScale(int type) { - return type != Types.TIME && super.typeSupportsScale(type); - } - - /** - * @since 3.0 - */ - final class OracleBooleanType implements ExtendedType<Boolean> { - - @Override - public String getClassName() { - return Boolean.class.getName(); - } - - @Override - public void setJdbcObject(PreparedStatement st, Boolean 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 Boolean 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 Boolean 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 String toString(Boolean value) { - if (value == null) { - return "NULL"; - } - - return '\'' + value.toString() + '\''; - } - - } + /** + * @since 4.2 + */ + @Override + public SQLTreeProcessor getSqlTreeProcessor() { + return new OracleSQLTreeProcessor(); + } + + /** + * @since 3.0 + */ + @Override + protected EJBQLTranslator createEJBQLTranslator() { + return new OracleEJBQLTranslator(); + } + + /** + * 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 + OracleCharType charType = new OracleCharType(); + map.registerType(charType); + + // 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()); + map.registerType(new JsonType(charType, true)); + } + + /** + * 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, ParameterBinding binding) throws 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 (binding.getValue() == null && binding.getJdbcType() == Types.BOOLEAN) { + ExtendedType typeProcessor = getExtendedTypes().getRegisteredType(Boolean.class); + typeProcessor.setJdbcObject(statement, binding.getValue(), binding.getStatementPosition(), binding + .getJdbcType(), binding.getScale()); + } else { + super.bindParameter(statement, binding); + } + } + + /** + * 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 maxLength, int scale, boolean allowNulls) { + DbAttribute attr = super.buildAttribute(name, typeName, type, maxLength, scale, allowNulls); + + if (type == Types.DECIMAL && scale <= 0) { + if (maxLength <= 9) { + attr.setType(Types.INTEGER); + } else if (maxLength <= 19) { + attr.setType(Types.BIGINT); + } + 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; + } + + /** + * Uses OracleActionBuilder to create the right action. + * + * @since 1.2 + */ + @Override + public SQLAction getAction(Query query, DataNode node) { + return query.createSQLAction(new OracleActionBuilder(node)); + } + + @Override + public List<String> getSystemSchemas() { + return SYSTEM_SCHEMAS; + } + + /** + * @since 5.0 + */ + @Override + public boolean typeSupportsScale(int type) { + return type != Types.TIME && super.typeSupportsScale(type); + } + + /** + * @since 3.0 + */ + final class OracleBooleanType implements ExtendedType<Boolean> { + + @Override + public String getClassName() { + return Boolean.class.getName(); + } + + @Override + public void setJdbcObject(PreparedStatement st, Boolean 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 Boolean 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 Boolean 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 String toString(Boolean value) { + if (value == null) { + return "NULL"; + } + + return '\'' + value.toString() + '\''; + } + + } } diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/postgres/PostgresAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/postgres/PostgresAdapter.java index af48db7d5..e18db6d23 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/postgres/PostgresAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/postgres/PostgresAdapter.java @@ -51,16 +51,7 @@ import java.util.List; import java.util.Map; /** - * DbAdapter implementation for <a href="http://www.postgresql.org">PostgreSQL - * RDBMS </a>. Sample connection settings to use with PostgreSQL are shown - * below: - * - * <pre> - * postgres.jdbc.username = test - * postgres.jdbc.password = secret - * postgres.jdbc.url = jdbc:postgresql://serverhostname/cayenne - * postgres.jdbc.driver = org.postgresql.Driver - * </pre> + * DbAdapter implementation for PostgreSQL RDBMS */ public class PostgresAdapter extends JdbcAdapter { @@ -154,7 +145,7 @@ public class PostgresAdapter extends JdbcAdapter { } @Override - public DbAttribute buildAttribute(String name, String typeName, int type, int size, int scale, boolean allowNulls) { + public DbAttribute buildAttribute(String name, String typeName, int type, int maxLength, int scale, boolean allowNulls) { int amendedType = switch (typeName != null ? typeName.toLowerCase() : "") { case "json" -> Types.OTHER; @@ -166,7 +157,7 @@ public class PostgresAdapter extends JdbcAdapter { default -> type; }; - return super.buildAttribute(name, typeName, amendedType, size, scale, allowNulls); + return super.buildAttribute(name, typeName, amendedType, maxLength, scale, allowNulls); } @Override diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteAdapter.java index b22ed5024..ef3e3e37c 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteAdapter.java @@ -43,18 +43,10 @@ import java.util.List; import java.util.Map; /** - * A SQLite database adapter that works with Zentus JDBC driver. See - * http://www.zentus.com/sqlitejdbc/ for the driver information. Also look at - * http://www.xerial.org/trac/Xerial/wiki/SQLiteJDBC for another adaptor option. - * - * <pre> - * sqlite.jdbc.url = jdbc:sqlite:sqlitetest.db - * sqlite.jdbc.driver = org.sqlite.JDBC - * </pre> - * + * A SQLite database adapter. + * * @since 3.0 */ -// check http://cwiki.apache.org/CAY/sqliteadapter.html for current limitations. public class SQLiteAdapter extends JdbcAdapter { public SQLiteAdapter( @@ -168,7 +160,7 @@ public class SQLiteAdapter extends JdbcAdapter { // do not append " PRIMARY KEY () " for single column generated primary key if (entity.getPrimaryKeys().size() == 1) { - DbAttribute column = entity.getPrimaryKeys().iterator().next(); + DbAttribute column = entity.getPrimaryKeys().getFirst(); if (column.isGenerated()) { return; } diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerAdapter.java index 496042f2a..82acb6874 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerAdapter.java @@ -53,48 +53,14 @@ import java.util.Map; import java.util.stream.Collectors; /** - * <p> - * Cayenne DbAdapter implementation for <a - * href="http://www.microsoft.com/sql/">Microsoft SQL Server </a> engine. - * </p> - * <h3>Microsoft Driver Settings</h3> - * <p> - * Sample connection settings to use with MS SQL Server are shown below: - * - * <pre> - * sqlserver.jdbc.username = test - * sqlserver.jdbc.password = secret - * sqlserver.jdbc.url = jdbc:sqlserver://192.168.0.65;databaseName=cayenne;SelectMethod=cursor - * sqlserver.jdbc.driver = com.microsoft.sqlserver.jdbc.SQLServerDriver - * </pre> - * <p> - * <i>Note on case-sensitive LIKE: if your application requires case-sensitive - * LIKE support, ask your DBA to configure the database to use a case-senstitive - * collation (one with "CS" in symbolic collation name instead of "CI", e.g. - * "SQL_Latin1_general_CP1_CS_AS"). </i> - * </p> - * <h3>jTDS Driver Settings</h3> - * <p> - * jTDS is an open source driver that can be downloaded from <a href= - * "http://jtds.sourceforge.net">http://jtds.sourceforge.net </a>. It supports - * both SQLServer and Sybase. Sample SQLServer settings are the following: - * </p> - * - * <pre> - * sqlserver.jdbc.username = test - * sqlserver.jdbc.password = secret - * sqlserver.jdbc.url = jdbc:jtds:sqlserver://192.168.0.65/cayenne - * sqlserver.jdbc.driver = net.sourceforge.jtds.jdbc.Driver - * </pre> + * Cayenne DbAdapter implementation for Microsoft SQL Server RDBMS. * * @since 1.1 */ public class SQLServerAdapter extends JdbcAdapter { - /** - * Stores the major version of the database. - * Database versions 12 and higher supports the use of LIMIT,lower versions use TOP N. - */ + // Stores the major version of the database. + // Database versions 12 and higher supports the use of LIMIT,lower versions use TOP N. private Integer version; private final List<String> SYSTEM_SCHEMAS = List.of( diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/sybase/SybaseAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/sybase/SybaseAdapter.java index 5e7c14cab..2b86f054e 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/sybase/SybaseAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/sybase/SybaseAdapter.java @@ -45,7 +45,7 @@ import java.util.List; import java.util.Map; /** - * DbAdapter implementation for <a href="http://www.sybase.com">Sybase RDBMS</a>. + * DbAdapter implementation for Sybase RDBMS. */ public class SybaseAdapter extends JdbcAdapter {
