Refactoring PKs

Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/929b6cb4
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/929b6cb4
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/929b6cb4

Branch: refs/heads/master
Commit: 929b6cb45285758c042ebac5ac818b858f8e2448
Parents: 2733a9f
Author: Aleksey Pleshkanev <priest...@hotmail.com>
Authored: Sun Apr 1 00:19:04 2018 +0300
Committer: Aleksey Pleshkanev <priest...@hotmail.com>
Committed: Sun Apr 1 00:19:04 2018 +0300

----------------------------------------------------------------------
 .../configuration/server/ServerModule.java      |   58 +-
 .../org/apache/cayenne/dba/JdbcAdapter.java     | 1121 +++++++++---------
 .../apache/cayenne/dba/db2/DB2PkGenerator.java  |   62 +-
 .../org/apache/cayenne/dba/db2/DB2Sniffer.java  |   36 +-
 .../cayenne/dba/derby/DerbyPkGenerator.java     |   64 +-
 .../apache/cayenne/dba/derby/DerbySniffer.java  |   35 +-
 .../dba/frontbase/FrontBasePkGenerator.java     |  196 +--
 .../cayenne/dba/frontbase/FrontBaseSniffer.java |   33 +-
 .../apache/cayenne/dba/h2/H2PkGenerator.java    |   44 +-
 .../org/apache/cayenne/dba/h2/H2Sniffer.java    |   32 +-
 .../cayenne/dba/ingres/IngresPkGenerator.java   |   28 +-
 .../cayenne/dba/ingres/IngresSniffer.java       |   33 +-
 .../cayenne/dba/mysql/MySQLPkGenerator.java     |  276 ++---
 .../apache/cayenne/dba/mysql/MySQLSniffer.java  |   19 +-
 .../dba/openbase/OpenBasePkGenerator.java       |  464 ++++----
 .../cayenne/dba/openbase/OpenBaseSniffer.java   |   33 +-
 .../cayenne/dba/oracle/OraclePkGenerator.java   |  386 +++---
 .../cayenne/dba/oracle/OracleSniffer.java       |   30 +-
 .../dba/postgres/PostgresPkGenerator.java       |   46 +-
 .../cayenne/dba/postgres/PostgresSniffer.java   |   33 +-
 .../cayenne/dba/sqlite/SQLiteSniffer.java       |   28 +-
 .../dba/sqlserver/SQLServerPkGenerator.java     |    2 +-
 .../cayenne/dba/sqlserver/SQLServerSniffer.java |    2 +-
 .../cayenne/dba/sybase/SybaseSniffer.java       |   33 +-
 .../server/DataDomainProviderTest.java          |   33 +-
 .../dba/sqlserver/SQLServerSnifferIT.java       |    2 +-
 .../unit/di/server/ServerCaseModule.java        |   23 +-
 27 files changed, 1740 insertions(+), 1412 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/929b6cb4/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
index ac7753f..aa6450c 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
@@ -85,20 +85,40 @@ import 
org.apache.cayenne.configuration.xml.XMLDataMapLoader;
 import org.apache.cayenne.configuration.xml.XMLReaderProvider;
 import org.apache.cayenne.dba.JdbcPkGenerator;
 import org.apache.cayenne.dba.PkGenerator;
+import org.apache.cayenne.dba.db2.DB2Adapter;
+import org.apache.cayenne.dba.db2.DB2PkGenerator;
 import org.apache.cayenne.dba.db2.DB2Sniffer;
+import org.apache.cayenne.dba.derby.DerbyAdapter;
+import org.apache.cayenne.dba.derby.DerbyPkGenerator;
 import org.apache.cayenne.dba.derby.DerbySniffer;
 import org.apache.cayenne.dba.firebird.FirebirdSniffer;
+import org.apache.cayenne.dba.frontbase.FrontBaseAdapter;
+import org.apache.cayenne.dba.frontbase.FrontBasePkGenerator;
 import org.apache.cayenne.dba.frontbase.FrontBaseSniffer;
+import org.apache.cayenne.dba.h2.H2Adapter;
+import org.apache.cayenne.dba.h2.H2PkGenerator;
 import org.apache.cayenne.dba.h2.H2Sniffer;
 import org.apache.cayenne.dba.hsqldb.HSQLDBSniffer;
+import org.apache.cayenne.dba.ingres.IngresAdapter;
+import org.apache.cayenne.dba.ingres.IngresPkGenerator;
 import org.apache.cayenne.dba.ingres.IngresSniffer;
+import org.apache.cayenne.dba.mysql.MySQLAdapter;
+import org.apache.cayenne.dba.mysql.MySQLPkGenerator;
 import org.apache.cayenne.dba.mysql.MySQLSniffer;
+import org.apache.cayenne.dba.openbase.OpenBaseAdapter;
+import org.apache.cayenne.dba.openbase.OpenBasePkGenerator;
 import org.apache.cayenne.dba.openbase.OpenBaseSniffer;
+import org.apache.cayenne.dba.oracle.Oracle8Adapter;
+import org.apache.cayenne.dba.oracle.OracleAdapter;
+import org.apache.cayenne.dba.oracle.OraclePkGenerator;
 import org.apache.cayenne.dba.oracle.OracleSniffer;
+import org.apache.cayenne.dba.postgres.PostgresAdapter;
+import org.apache.cayenne.dba.postgres.PostgresPkGenerator;
 import org.apache.cayenne.dba.postgres.PostgresSniffer;
 import org.apache.cayenne.dba.sqlite.SQLiteSniffer;
 import org.apache.cayenne.dba.sqlserver.SQLServerAdapter;
 import org.apache.cayenne.dba.sqlserver.SQLServerSniffer;
+import org.apache.cayenne.dba.sybase.SybaseAdapter;
 import org.apache.cayenne.dba.sybase.SybasePkGenerator;
 import org.apache.cayenne.dba.sybase.SybaseSniffer;
 import org.apache.cayenne.di.AdhocObjectFactory;
@@ -301,21 +321,30 @@ public class ServerModule implements Module {
         // configure known DbAdapter detectors in reverse order of popularity.
         // Users can add their own to install custom adapters automatically
 
-        
contributeAdapterDetectors(binder).add(FirebirdSniffer.class).add(OpenBaseSniffer.class)
-                
.add(FrontBaseSniffer.class).add(IngresSniffer.class).add(SQLiteSniffer.class).add(DB2Sniffer.class)
-                
.add(H2Sniffer.class).add(HSQLDBSniffer.class).add(SybaseSniffer.class).add(DerbySniffer.class)
-                
.add(SQLServerSniffer.class).add(OracleSniffer.class).add(PostgresSniffer.class)
+        contributeAdapterDetectors(binder)
+                .add(FirebirdSniffer.class)
+                .add(OpenBaseSniffer.class)
+                .add(FrontBaseSniffer.class)
+                .add(IngresSniffer.class)
+                .add(SQLiteSniffer.class)
+                .add(DB2Sniffer.class)
+                .add(H2Sniffer.class)
+                .add(HSQLDBSniffer.class)
+                .add(SybaseSniffer.class)
+                .add(DerbySniffer.class)
+                .add(SQLServerSniffer.class)
+                .add(OracleSniffer.class)
+                .add(PostgresSniffer.class)
                 .add(MySQLSniffer.class);
 
         //installing Pk for adapters
         
binder.bind(PkGeneratorFactoryProvider.class).to(PkGeneratorFactoryProvider.class);
         binder.bind(PkGenerator.class).to(JdbcPkGenerator.class);
-        contributePkGenerators(binder).put(SQLServerAdapter.class.getName(), 
SybasePkGenerator.class);
 
-        /*contributePkGenerators(binder)
+        contributePkGenerators(binder)
                 .put(DB2Adapter.class.getName(), DB2PkGenerator.class)
                 .put(DerbyAdapter.class.getName(), DerbyPkGenerator.class)
-                .put(FrontBaseAdapter.class.getName(), FrontBaseAdapter.class)
+                .put(FrontBaseAdapter.class.getName(), 
FrontBasePkGenerator.class)
                 .put(H2Adapter.class.getName(), H2PkGenerator.class)
                 .put(IngresAdapter.class.getName(), IngresPkGenerator.class)
                 .put(MySQLAdapter.class.getName(), MySQLPkGenerator.class)
@@ -325,7 +354,7 @@ public class ServerModule implements Module {
                 .put(PostgresAdapter.class.getName(), 
PostgresPkGenerator.class)
                 .put(SQLServerAdapter.class.getName(), SybasePkGenerator.class)
                 .put(SybaseAdapter.class.getName(), SybasePkGenerator.class);
-*/
+
         // configure a filter chain with only one TransactionFilter as default
         contributeDomainFilters(binder).add(TransactionFilter.class);
 
@@ -336,10 +365,17 @@ public class ServerModule implements Module {
         contributeDefaultTypes(binder)
                 .add(new VoidType())
                 .add(new BigDecimalType())
-                .add(new BooleanType()).add(new ByteType(false)).add(new 
CharType(false, true))
-                .add(new DoubleType()).add(new FloatType()).add(new 
IntegerType()).add(new LongType()).add(new ShortType(false))
+                .add(new BooleanType())
+                .add(new ByteType(false))
+                .add(new CharType(false, true))
+                .add(new DoubleType())
+                .add(new FloatType())
+                .add(new IntegerType())
+                .add(new LongType())
+                .add(new ShortType(false))
                 .add(new ByteArrayType(false, true))
-                .add(new DateType()).add(new TimeType()).add(new 
TimestampType())
+                .add(new DateType()).add(new TimeType())
+                .add(new TimestampType())
                 // should be converted from ExtendedType to ValueType
                 .add(new UtilDateType()).add(new 
CalendarType<>(GregorianCalendar.class)).add(new 
CalendarType<>(Calendar.class));
         contributeUserTypes(binder);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/929b6cb4/cayenne-server/src/main/java/org/apache/cayenne/dba/JdbcAdapter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/JdbcAdapter.java 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/JdbcAdapter.java
index f4b0a1c..72b84a9 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/JdbcAdapter.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/JdbcAdapter.java
@@ -65,565 +65,566 @@ import java.util.List;
  */
 public class JdbcAdapter implements DbAdapter {
 
-       private PkGenerator pkGenerator;
-       protected QuotingStrategy quotingStrategy;
-
-       protected TypesHandler typesHandler;
-       protected ExtendedTypeMap extendedTypes;
-       protected boolean supportsBatchUpdates;
-       protected boolean supportsUniqueConstraints;
-       protected boolean supportsGeneratedKeys;
-       protected EJBQLTranslatorFactory ejbqlTranslatorFactory;
-
-       protected ResourceLocator resourceLocator;
-       protected boolean caseInsensitiveCollations;
-
-       /**
-        * @since 3.1
-        * @deprecated since 4.0 BatchQueryBuilderfactory is attached to the 
DataNode.
-        */
-       @Inject
-       protected BatchTranslatorFactory batchQueryBuilderFactory;
-
-       @Inject
-       protected JdbcEventLogger logger;
-
-       /**
-        * Creates new JdbcAdapter with a set of default parameters.
-        */
-       public JdbcAdapter(@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(Constants.SERVER_RESOURCE_LOCATOR) 
ResourceLocator resourceLocator,
-                                          @Inject ValueObjectTypeRegistry 
valueObjectTypeRegistry) {
-
-               // init defaults
-               this.setSupportsBatchUpdates(false);
-               this.setSupportsUniqueConstraints(true);
-               this.caseInsensitiveCollations = 
runtimeProperties.getBoolean(Constants.CI_PROPERTY, false);
-               this.resourceLocator = resourceLocator;
-
-               this.pkGenerator = createPkGenerator();
-               this.quotingStrategy = createQuotingStrategy();
-
-               this.ejbqlTranslatorFactory = createEJBQLTranslatorFactory();
-               this.typesHandler = 
TypesHandler.getHandler(findResource("/types.xml"));
-               this.extendedTypes = new ExtendedTypeMap();
-               initExtendedTypes(defaultExtendedTypes, userExtendedTypes, 
extendedTypeFactories, valueObjectTypeRegistry);
-       }
-
-       /**
-        * Returns default separator - a semicolon.
-        *
-        * @since 1.0.4
-        */
-       @Override
-       public String getBatchTerminator() {
-               return ";";
-       }
-
-       /**
-        * @since 3.1
-        */
-       public JdbcEventLogger getJdbcEventLogger() {
-               return this.logger;
-       }
-
-       /**
-        * Locates and returns a named adapter resource. A resource can be an 
XML
-        * file, etc.
-        * <p>
-        * This implementation is based on the premise that each adapter is 
located
-        * in its own Java package and all resources are in the same package as
-        * well. Resource lookup is recursive, so that if DbAdapter is a 
subclass of
-        * another adapter, parent adapter package is searched as a failover.
-        * </p>
-        *
-        * @since 3.0
-        */
-       protected URL findResource(String name) {
-               Class<?> adapterClass = getClass();
-
-               while (adapterClass != null && 
JdbcAdapter.class.isAssignableFrom(adapterClass)) {
-
-                       String path = 
Util.getPackagePath(adapterClass.getName()) + name;
-                       Collection<Resource> resources = 
resourceLocator.findResources(path);
-
-                       if (!resources.isEmpty()) {
-                               return resources.iterator().next().getURL();
-                       }
-
-                       adapterClass = adapterClass.getSuperclass();
-               }
-
-               return null;
-       }
-
-       /**
-        * Called from {@link #initExtendedTypes(List, List, List, 
ValueObjectTypeRegistry)} to load
-        * adapter-specific types into the ExtendedTypeMap right after the 
default
-        * types are loaded, but before the DI overrides are. This method has
-        * specific implementations in JdbcAdapter subclasses.
-        */
-       protected void configureExtendedTypes(ExtendedTypeMap map) {
-               // noop... subclasses may override to install custom types
-       }
-
-       /**
-        * @since 3.1
-        */
-       protected void initExtendedTypes(List<ExtendedType> 
defaultExtendedTypes, List<ExtendedType> userExtendedTypes,
-                                        List<ExtendedTypeFactory> 
extendedTypeFactories,
-                                                                        
ValueObjectTypeRegistry valueObjectTypeRegistry) {
-               for (ExtendedType type : defaultExtendedTypes) {
-                       extendedTypes.registerType(type);
-               }
-
-               // loading adapter specific extended types
-               configureExtendedTypes(extendedTypes);
-
-               for (ExtendedType type : userExtendedTypes) {
-                       extendedTypes.registerType(type);
-               }
-               for (ExtendedTypeFactory typeFactory : extendedTypeFactories) {
-                       extendedTypes.addFactory(typeFactory);
-               }
-               extendedTypes.addFactory(new 
ValueObjectTypeFactory(extendedTypes, valueObjectTypeRegistry));
-       }
-
-       /**
-        * Creates and returns a primary key generator. This factory method 
should
-        * be overriden by JdbcAdapter subclasses to provide custom 
implementations
-        * of PKGenerator.
-        */
-       protected PkGenerator createPkGenerator() {
-               return new JdbcPkGenerator(this);
-       }
-
-       /**
-        * Creates and returns an {@link EJBQLTranslatorFactory} used to 
generate
-        * visitors for EJBQL to SQL translations. This method should be 
overriden
-        * by subclasses that need to customize EJBQL generation.
-        *
-        * @since 3.0
-        */
-       protected EJBQLTranslatorFactory createEJBQLTranslatorFactory() {
-               JdbcEJBQLTranslatorFactory translatorFactory = new 
JdbcEJBQLTranslatorFactory();
-               translatorFactory.setCaseInsensitive(caseInsensitiveCollations);
-               return translatorFactory;
-       }
-
-       /**
-        * Returns primary key generator associated with this DbAdapter.
-        */
-       @Override
-       public PkGenerator getPkGenerator() {
-               return pkGenerator;
-       }
-
-       /**
-        * Sets new primary key generator.
-        *
-        * @since 1.1
-        */
-       public void setPkGenerator(PkGenerator pkGenerator) {
-               this.pkGenerator = pkGenerator;
-       }
-
-       /**
-        * Returns true.
-        *
-        * @since 1.1
-        */
-       @Override
-       public boolean supportsUniqueConstraints() {
-               return supportsUniqueConstraints;
-       }
-
-       /**
-        * Returns true.
-        *
-        * @since 4.0
-        */
-       @Override
-       public boolean supportsCatalogsOnReverseEngineering() {
-               return true;
-       }
-
-       /**
-        * @since 1.1
-        */
-       public void setSupportsUniqueConstraints(boolean flag) {
-               this.supportsUniqueConstraints = flag;
-       }
-
-       /**
-        * Returns true if supplied type can have a length attribute as a part 
of
-        * column definition
-        *
-        * @since 4.0
-        */
-       public boolean typeSupportsLength(int type) {
-               return JdbcAdapter.supportsLength(type);
-       }
-
-       /**
-        * Returns true if supplied type can have a length attribute as a part 
of
-        * column definition
-        * <p/>
-        * TODO: this is a static method only to support the deprecated method
-        * {@link TypesMapping#supportsLength(int)} When the deprecated method 
is
-        * removed this body should be moved in to {@link 
#typeSupportsLength(int)}
-        *
-        * @deprecated
-        */
-       static boolean supportsLength(int type) {
-               return type == Types.BINARY || type == Types.CHAR || type == 
Types.NCHAR || type == Types.NVARCHAR
-                               || type == Types.LONGNVARCHAR || type == 
Types.DECIMAL || type == Types.DOUBLE || type == Types.FLOAT
-                               || type == Types.NUMERIC || type == Types.REAL 
|| type == Types.VARBINARY || type == Types.VARCHAR;
-       }
-
-       /**
-        * @since 3.0
-        */
-       @Override
-       public Collection<String> dropTableStatements(DbEntity table) {
-               return Collections.singleton("DROP TABLE " + 
quotingStrategy.quotedFullyQualifiedName(table));
-       }
-
-       /**
-        * Returns a SQL string that can be used to create database table
-        * corresponding to <code>ent</code> parameter.
-        */
-       @Override
-       public String createTable(DbEntity entity) {
-
-               StringBuffer sqlBuffer = new StringBuffer();
-               sqlBuffer.append("CREATE TABLE ");
-               
sqlBuffer.append(quotingStrategy.quotedFullyQualifiedName(entity));
-
-               sqlBuffer.append(" (");
-               // columns
-               Iterator<DbAttribute> it = entity.getAttributes().iterator();
-               if (it.hasNext()) {
-                       boolean first = true;
-                       while (it.hasNext()) {
-                               if (first) {
-                                       first = false;
-                               } else {
-                                       sqlBuffer.append(", ");
-                               }
-
-                               DbAttribute column = it.next();
-
-                               // attribute may not be fully valid, do a 
simple check
-                               if (column.getType() == 
TypesMapping.NOT_DEFINED) {
-                                       throw new 
CayenneRuntimeException("Undefined type for attribute '%s.%s'."
-                                                       , 
entity.getFullyQualifiedName(), column.getName());
-                               }
-
-                               createTableAppendColumn(sqlBuffer, column);
-                       }
-
-                       createTableAppendPKClause(sqlBuffer, entity);
-               }
-
-               sqlBuffer.append(')');
-               return sqlBuffer.toString();
-       }
-
-       /**
-        * @since 1.2
-        */
-       protected void createTableAppendPKClause(StringBuffer sqlBuffer, 
DbEntity entity) {
-
-               Iterator<DbAttribute> pkit = entity.getPrimaryKeys().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(')');
-               }
-       }
-
-       /**
-        * Appends SQL for column creation to CREATE TABLE buffer.
-        *
-        * @since 1.2
-        */
-       @Override
-       public void createTableAppendColumn(StringBuffer sqlBuffer, DbAttribute 
column) {
-               sqlBuffer.append(quotingStrategy.quotedName(column));
-               sqlBuffer.append(' ').append(getType(this, column));
-
-               sqlBuffer.append(sizeAndPrecision(this, column));
-               sqlBuffer.append(column.isMandatory() ? " NOT NULL" : " NULL");
-       }
-
-       public static String sizeAndPrecision(DbAdapter adapter, DbAttribute 
column) {
-               if (!adapter.typeSupportsLength(column.getType())) {
-                       return "";
-               }
-
-               int len = column.getMaxLength();
-               int scale = TypesMapping.isDecimal(column.getType()) && 
column.getType() != Types.FLOAT ? column.getScale()
-                               : -1;
-
-               // sanity check
-               if (scale > len) {
-                       scale = -1;
-               }
-
-               if (len > 0) {
-                       return "(" + len + (scale >= 0 ? ", " + scale : "") + 
")";
-               }
-
-               return "";
-       }
-
-       public static String getType(DbAdapter adapter, DbAttribute column) {
-               String[] types = 
adapter.externalTypesForJdbcType(column.getType());
-               if (types == null || types.length == 0) {
-                       String entityName = column.getEntity() != null ? 
column.getEntity().getFullyQualifiedName() : "<null>";
-                       throw new CayenneRuntimeException("Undefined type for 
attribute '%s.%s': %s."
-                                       , entityName, column.getName(), 
column.getType());
-               }
-               return types[0];
-       }
-
-       /**
-        * 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.");
-               }
-
-               StringBuilder buf = new StringBuilder();
-
-               buf.append("ALTER TABLE ");
-               buf.append(quotingStrategy.quotedFullyQualifiedName(source));
-               buf.append(" ADD 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();
-       }
-
-       /**
-        * Returns a SQL string that can be used to create a foreign key 
constraint
-        * for the relationship.
-        */
-       @Override
-       public String createFkConstraint(DbRelationship rel) {
-
-               DbEntity source = rel.getSourceEntity();
-               StringBuilder buf = new StringBuilder();
-               StringBuilder refBuf = new StringBuilder();
-
-               buf.append("ALTER TABLE ");
-
-               buf.append(quotingStrategy.quotedFullyQualifiedName(source));
-               buf.append(" ADD FOREIGN KEY (");
-
-               boolean first = true;
-
-               for (DbJoin join : rel.getJoins()) {
-                       if (first) {
-                               first = false;
-                       } else {
-                               buf.append(", ");
-                               refBuf.append(", ");
-                       }
-
-                       buf.append(quotingStrategy.quotedSourceName(join));
-                       refBuf.append(quotingStrategy.quotedTargetName(join));
-               }
-
-               buf.append(") REFERENCES ");
-
-               
buf.append(quotingStrategy.quotedFullyQualifiedName(rel.getTargetEntity()));
-
-               buf.append(" (").append(refBuf.toString()).append(')');
-               return buf.toString();
-       }
-
-       @Override
-       public String[] externalTypesForJdbcType(int type) {
-               return typesHandler.externalTypesForJdbcType(type);
-       }
-
-       @Override
-       public ExtendedTypeMap getExtendedTypes() {
-               return extendedTypes;
-       }
-
-       @Override
-       public DbAttribute buildAttribute(String name, String typeName, int 
type, int size, int scale, boolean allowNulls) {
-
-               DbAttribute attr = new DbAttribute();
-               attr.setName(name);
-               attr.setType(type);
-               attr.setMandatory(!allowNulls);
-
-               if (size >= 0) {
-                       attr.setMaxLength(size);
-               }
-
-               if (scale >= 0) {
-                       attr.setScale(scale);
-               }
-
-               return attr;
-       }
-
-       @Override
-       public String tableTypeForTable() {
-               return "TABLE";
-       }
-
-       @Override
-       public String tableTypeForView() {
-               return "VIEW";
-       }
-
-       /**
-        * Creates and returns a default implementation of a qualifier 
translator.
-        */
-       @Override
-       public QualifierTranslator getQualifierTranslator(QueryAssembler 
queryAssembler) {
-               QualifierTranslator translator = new 
QualifierTranslator(queryAssembler);
-               translator.setCaseInsensitive(caseInsensitiveCollations);
-               return translator;
-       }
-
-       /**
-        * Uses JdbcActionBuilder to create the right action.
-        *
-        * @since 1.2
-        */
-       @Override
-       public SQLAction getAction(Query query, DataNode node) {
-               return query.createSQLAction(new JdbcActionBuilder(node));
-       }
-
-       @Override
-       public SelectTranslator getSelectTranslator(SelectQuery<?> query, 
EntityResolver entityResolver) {
-               return new DefaultSelectTranslator(query, this, entityResolver);
-       }
-
-       @Override
-       public void bindParameter(PreparedStatement statement, ParameterBinding 
binding)
-                       throws SQLException, Exception {
-
-               if (binding.getValue() == null) {
-                       statement.setNull(binding.getStatementPosition(), 
binding.getJdbcType());
-               } else {
-                       binding.getExtendedType().setJdbcObject(statement,
-                                       binding.getValue(),
-                                       binding.getStatementPosition(),
-                                       binding.getJdbcType(),
-                                       binding.getScale());
-               }
-       }
-
-       @Override
-       public boolean supportsBatchUpdates() {
-               return this.supportsBatchUpdates;
-       }
-
-       public void setSupportsBatchUpdates(boolean flag) {
-               this.supportsBatchUpdates = flag;
-       }
-
-       /**
-        * @since 1.2
-        */
-       @Override
-       public boolean supportsGeneratedKeys() {
-               return supportsGeneratedKeys;
-       }
-
-       /**
-        * @since 1.2
-        */
-       public void setSupportsGeneratedKeys(boolean flag) {
-               this.supportsGeneratedKeys = flag;
-       }
-
-       /**
-        * Returns a translator factory for EJBQL to SQL translation. The 
factory is
-        * normally initialized in constructor by calling
-        * {@link #createEJBQLTranslatorFactory()}, and can be changed later by
-        * calling {@link #setEjbqlTranslatorFactory(EJBQLTranslatorFactory)}.
-        *
-        * @since 3.0
-        */
-       public EJBQLTranslatorFactory getEjbqlTranslatorFactory() {
-               return ejbqlTranslatorFactory;
-       }
-
-       /**
-        * Sets a translator factory for EJBQL to SQL translation. This 
property is
-        * normally initialized in constructor by calling
-        * {@link #createEJBQLTranslatorFactory()}, so users would only 
override it
-        * if they need to customize EJBQL translation.
-        *
-        * @since 3.0
-        */
-       public void setEjbqlTranslatorFactory(EJBQLTranslatorFactory 
ejbqlTranslatorFactory) {
-               this.ejbqlTranslatorFactory = ejbqlTranslatorFactory;
-       }
-
-       /**
-        * @return
-        * @since 4.0
-        */
-       protected QuotingStrategy createQuotingStrategy() {
-               return new DefaultQuotingStrategy("\"", "\"");
-       }
-
-       /**
-        * @since 4.0
-        */
-       public QuotingStrategy getQuotingStrategy() {
-               return quotingStrategy;
-       }
-
-       /**
-        * Simply returns this, as JdbcAdapter is not a wrapper.
-        *
-        * @since 4.0
-        */
-       @Override
-       public DbAdapter unwrap() {
-               return this;
-       }
+    private PkGenerator pkGenerator;
+    protected QuotingStrategy quotingStrategy;
+
+    protected TypesHandler typesHandler;
+    protected ExtendedTypeMap extendedTypes;
+    protected boolean supportsBatchUpdates;
+    protected boolean supportsUniqueConstraints;
+    protected boolean supportsGeneratedKeys;
+    protected EJBQLTranslatorFactory ejbqlTranslatorFactory;
+
+    protected ResourceLocator resourceLocator;
+    protected boolean caseInsensitiveCollations;
+
+    /**
+     * @since 3.1
+     * @deprecated since 4.0 BatchQueryBuilderfactory is attached to the 
DataNode.
+     */
+    @Inject
+    protected BatchTranslatorFactory batchQueryBuilderFactory;
+
+    @Inject
+    protected JdbcEventLogger logger;
+
+    /**
+     * Creates new JdbcAdapter with a set of default parameters.
+     */
+    public JdbcAdapter(@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(Constants.SERVER_RESOURCE_LOCATOR) 
ResourceLocator resourceLocator,
+                       @Inject ValueObjectTypeRegistry 
valueObjectTypeRegistry) {
+
+        // init defaults
+        this.setSupportsBatchUpdates(false);
+        this.setSupportsUniqueConstraints(true);
+        this.caseInsensitiveCollations = 
runtimeProperties.getBoolean(Constants.CI_PROPERTY, false);
+        this.resourceLocator = resourceLocator;
+
+        this.pkGenerator = createPkGenerator();
+        this.quotingStrategy = createQuotingStrategy();
+
+        this.ejbqlTranslatorFactory = createEJBQLTranslatorFactory();
+        this.typesHandler = 
TypesHandler.getHandler(findResource("/types.xml"));
+        this.extendedTypes = new ExtendedTypeMap();
+        initExtendedTypes(defaultExtendedTypes, userExtendedTypes, 
extendedTypeFactories, valueObjectTypeRegistry);
+    }
+
+    /**
+     * Returns default separator - a semicolon.
+     *
+     * @since 1.0.4
+     */
+    @Override
+    public String getBatchTerminator() {
+        return ";";
+    }
+
+    /**
+     * @since 3.1
+     */
+    public JdbcEventLogger getJdbcEventLogger() {
+        return this.logger;
+    }
+
+    /**
+     * Locates and returns a named adapter resource. A resource can be an XML
+     * file, etc.
+     * <p>
+     * This implementation is based on the premise that each adapter is located
+     * in its own Java package and all resources are in the same package as
+     * well. Resource lookup is recursive, so that if DbAdapter is a subclass 
of
+     * another adapter, parent adapter package is searched as a failover.
+     * </p>
+     *
+     * @since 3.0
+     */
+    protected URL findResource(String name) {
+        Class<?> adapterClass = getClass();
+
+        while (adapterClass != null && 
JdbcAdapter.class.isAssignableFrom(adapterClass)) {
+
+            String path = Util.getPackagePath(adapterClass.getName()) + name;
+            Collection<Resource> resources = 
resourceLocator.findResources(path);
+
+            if (!resources.isEmpty()) {
+                return resources.iterator().next().getURL();
+            }
+
+            adapterClass = adapterClass.getSuperclass();
+        }
+
+        return null;
+    }
+
+    /**
+     * Called from {@link #initExtendedTypes(List, List, List, 
ValueObjectTypeRegistry)} to load
+     * adapter-specific types into the ExtendedTypeMap right after the default
+     * types are loaded, but before the DI overrides are. This method has
+     * specific implementations in JdbcAdapter subclasses.
+     */
+    protected void configureExtendedTypes(ExtendedTypeMap map) {
+        // noop... subclasses may override to install custom types
+    }
+
+    /**
+     * @since 3.1
+     */
+    protected void initExtendedTypes(List<ExtendedType> defaultExtendedTypes, 
List<ExtendedType> userExtendedTypes,
+                                     List<ExtendedTypeFactory> 
extendedTypeFactories,
+                                     ValueObjectTypeRegistry 
valueObjectTypeRegistry) {
+        for (ExtendedType type : defaultExtendedTypes) {
+            extendedTypes.registerType(type);
+        }
+
+        // loading adapter specific extended types
+        configureExtendedTypes(extendedTypes);
+
+        for (ExtendedType type : userExtendedTypes) {
+            extendedTypes.registerType(type);
+        }
+        for (ExtendedTypeFactory typeFactory : extendedTypeFactories) {
+            extendedTypes.addFactory(typeFactory);
+        }
+        extendedTypes.addFactory(new ValueObjectTypeFactory(extendedTypes, 
valueObjectTypeRegistry));
+    }
+
+    /**
+     * Creates and returns a primary key generator. This factory method should
+     * be overriden by JdbcAdapter subclasses to provide custom implementations
+     * of PKGenerator.
+     */
+    @Deprecated
+    protected PkGenerator createPkGenerator() {
+        return new JdbcPkGenerator(this);
+    }
+
+    /**
+     * Creates and returns an {@link EJBQLTranslatorFactory} used to generate
+     * visitors for EJBQL to SQL translations. This method should be overriden
+     * by subclasses that need to customize EJBQL generation.
+     *
+     * @since 3.0
+     */
+    protected EJBQLTranslatorFactory createEJBQLTranslatorFactory() {
+        JdbcEJBQLTranslatorFactory translatorFactory = new 
JdbcEJBQLTranslatorFactory();
+        translatorFactory.setCaseInsensitive(caseInsensitiveCollations);
+        return translatorFactory;
+    }
+
+    /**
+     * Returns primary key generator associated with this DbAdapter.
+     */
+    @Override
+    public PkGenerator getPkGenerator() {
+        return pkGenerator;
+    }
+
+    /**
+     * Sets new primary key generator.
+     *
+     * @since 1.1
+     */
+    public void setPkGenerator(PkGenerator pkGenerator) {
+        this.pkGenerator = pkGenerator;
+    }
+
+    /**
+     * Returns true.
+     *
+     * @since 1.1
+     */
+    @Override
+    public boolean supportsUniqueConstraints() {
+        return supportsUniqueConstraints;
+    }
+
+    /**
+     * Returns true.
+     *
+     * @since 4.0
+     */
+    @Override
+    public boolean supportsCatalogsOnReverseEngineering() {
+        return true;
+    }
+
+    /**
+     * @since 1.1
+     */
+    public void setSupportsUniqueConstraints(boolean flag) {
+        this.supportsUniqueConstraints = flag;
+    }
+
+    /**
+     * Returns true if supplied type can have a length attribute as a part of
+     * column definition
+     *
+     * @since 4.0
+     */
+    public boolean typeSupportsLength(int type) {
+        return JdbcAdapter.supportsLength(type);
+    }
+
+    /**
+     * Returns true if supplied type can have a length attribute as a part of
+     * column definition
+     * <p/>
+     * TODO: this is a static method only to support the deprecated method
+     * {@link TypesMapping#supportsLength(int)} When the deprecated method is
+     * removed this body should be moved in to {@link #typeSupportsLength(int)}
+     *
+     * @deprecated
+     */
+    static boolean supportsLength(int type) {
+        return type == Types.BINARY || type == Types.CHAR || type == 
Types.NCHAR || type == Types.NVARCHAR
+                || type == Types.LONGNVARCHAR || type == Types.DECIMAL || type 
== Types.DOUBLE || type == Types.FLOAT
+                || type == Types.NUMERIC || type == Types.REAL || type == 
Types.VARBINARY || type == Types.VARCHAR;
+    }
+
+    /**
+     * @since 3.0
+     */
+    @Override
+    public Collection<String> dropTableStatements(DbEntity table) {
+        return Collections.singleton("DROP TABLE " + 
quotingStrategy.quotedFullyQualifiedName(table));
+    }
+
+    /**
+     * Returns a SQL string that can be used to create database table
+     * corresponding to <code>ent</code> parameter.
+     */
+    @Override
+    public String createTable(DbEntity entity) {
+
+        StringBuffer sqlBuffer = new StringBuffer();
+        sqlBuffer.append("CREATE TABLE ");
+        sqlBuffer.append(quotingStrategy.quotedFullyQualifiedName(entity));
+
+        sqlBuffer.append(" (");
+        // columns
+        Iterator<DbAttribute> it = entity.getAttributes().iterator();
+        if (it.hasNext()) {
+            boolean first = true;
+            while (it.hasNext()) {
+                if (first) {
+                    first = false;
+                } else {
+                    sqlBuffer.append(", ");
+                }
+
+                DbAttribute column = it.next();
+
+                // attribute may not be fully valid, do a simple check
+                if (column.getType() == TypesMapping.NOT_DEFINED) {
+                    throw new CayenneRuntimeException("Undefined type for 
attribute '%s.%s'."
+                            , entity.getFullyQualifiedName(), 
column.getName());
+                }
+
+                createTableAppendColumn(sqlBuffer, column);
+            }
+
+            createTableAppendPKClause(sqlBuffer, entity);
+        }
+
+        sqlBuffer.append(')');
+        return sqlBuffer.toString();
+    }
+
+    /**
+     * @since 1.2
+     */
+    protected void createTableAppendPKClause(StringBuffer sqlBuffer, DbEntity 
entity) {
+
+        Iterator<DbAttribute> pkit = entity.getPrimaryKeys().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(')');
+        }
+    }
+
+    /**
+     * Appends SQL for column creation to CREATE TABLE buffer.
+     *
+     * @since 1.2
+     */
+    @Override
+    public void createTableAppendColumn(StringBuffer sqlBuffer, DbAttribute 
column) {
+        sqlBuffer.append(quotingStrategy.quotedName(column));
+        sqlBuffer.append(' ').append(getType(this, column));
+
+        sqlBuffer.append(sizeAndPrecision(this, column));
+        sqlBuffer.append(column.isMandatory() ? " NOT NULL" : " NULL");
+    }
+
+    public static String sizeAndPrecision(DbAdapter adapter, DbAttribute 
column) {
+        if (!adapter.typeSupportsLength(column.getType())) {
+            return "";
+        }
+
+        int len = column.getMaxLength();
+        int scale = TypesMapping.isDecimal(column.getType()) && 
column.getType() != Types.FLOAT ? column.getScale()
+                : -1;
+
+        // sanity check
+        if (scale > len) {
+            scale = -1;
+        }
+
+        if (len > 0) {
+            return "(" + len + (scale >= 0 ? ", " + scale : "") + ")";
+        }
+
+        return "";
+    }
+
+    public static String getType(DbAdapter adapter, DbAttribute column) {
+        String[] types = adapter.externalTypesForJdbcType(column.getType());
+        if (types == null || types.length == 0) {
+            String entityName = column.getEntity() != null ? 
column.getEntity().getFullyQualifiedName() : "<null>";
+            throw new CayenneRuntimeException("Undefined type for attribute 
'%s.%s': %s."
+                    , entityName, column.getName(), column.getType());
+        }
+        return types[0];
+    }
+
+    /**
+     * 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.");
+        }
+
+        StringBuilder buf = new StringBuilder();
+
+        buf.append("ALTER TABLE ");
+        buf.append(quotingStrategy.quotedFullyQualifiedName(source));
+        buf.append(" ADD 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();
+    }
+
+    /**
+     * Returns a SQL string that can be used to create a foreign key constraint
+     * for the relationship.
+     */
+    @Override
+    public String createFkConstraint(DbRelationship rel) {
+
+        DbEntity source = rel.getSourceEntity();
+        StringBuilder buf = new StringBuilder();
+        StringBuilder refBuf = new StringBuilder();
+
+        buf.append("ALTER TABLE ");
+
+        buf.append(quotingStrategy.quotedFullyQualifiedName(source));
+        buf.append(" ADD FOREIGN KEY (");
+
+        boolean first = true;
+
+        for (DbJoin join : rel.getJoins()) {
+            if (first) {
+                first = false;
+            } else {
+                buf.append(", ");
+                refBuf.append(", ");
+            }
+
+            buf.append(quotingStrategy.quotedSourceName(join));
+            refBuf.append(quotingStrategy.quotedTargetName(join));
+        }
+
+        buf.append(") REFERENCES ");
+
+        
buf.append(quotingStrategy.quotedFullyQualifiedName(rel.getTargetEntity()));
+
+        buf.append(" (").append(refBuf.toString()).append(')');
+        return buf.toString();
+    }
+
+    @Override
+    public String[] externalTypesForJdbcType(int type) {
+        return typesHandler.externalTypesForJdbcType(type);
+    }
+
+    @Override
+    public ExtendedTypeMap getExtendedTypes() {
+        return extendedTypes;
+    }
+
+    @Override
+    public DbAttribute buildAttribute(String name, String typeName, int type, 
int size, int scale, boolean allowNulls) {
+
+        DbAttribute attr = new DbAttribute();
+        attr.setName(name);
+        attr.setType(type);
+        attr.setMandatory(!allowNulls);
+
+        if (size >= 0) {
+            attr.setMaxLength(size);
+        }
+
+        if (scale >= 0) {
+            attr.setScale(scale);
+        }
+
+        return attr;
+    }
+
+    @Override
+    public String tableTypeForTable() {
+        return "TABLE";
+    }
+
+    @Override
+    public String tableTypeForView() {
+        return "VIEW";
+    }
+
+    /**
+     * Creates and returns a default implementation of a qualifier translator.
+     */
+    @Override
+    public QualifierTranslator getQualifierTranslator(QueryAssembler 
queryAssembler) {
+        QualifierTranslator translator = new 
QualifierTranslator(queryAssembler);
+        translator.setCaseInsensitive(caseInsensitiveCollations);
+        return translator;
+    }
+
+    /**
+     * Uses JdbcActionBuilder to create the right action.
+     *
+     * @since 1.2
+     */
+    @Override
+    public SQLAction getAction(Query query, DataNode node) {
+        return query.createSQLAction(new JdbcActionBuilder(node));
+    }
+
+    @Override
+    public SelectTranslator getSelectTranslator(SelectQuery<?> query, 
EntityResolver entityResolver) {
+        return new DefaultSelectTranslator(query, this, entityResolver);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void bindParameter(PreparedStatement statement, ParameterBinding 
binding)
+            throws SQLException, Exception {
+
+        if (binding.getValue() == null) {
+            statement.setNull(binding.getStatementPosition(), 
binding.getJdbcType());
+        } else {
+            binding.getExtendedType().setJdbcObject(statement,
+                    binding.getValue(),
+                    binding.getStatementPosition(),
+                    binding.getJdbcType(),
+                    binding.getScale());
+        }
+    }
+
+    @Override
+    public boolean supportsBatchUpdates() {
+        return this.supportsBatchUpdates;
+    }
+
+    public void setSupportsBatchUpdates(boolean flag) {
+        this.supportsBatchUpdates = flag;
+    }
+
+    /**
+     * @since 1.2
+     */
+    @Override
+    public boolean supportsGeneratedKeys() {
+        return supportsGeneratedKeys;
+    }
+
+    /**
+     * @since 1.2
+     */
+    public void setSupportsGeneratedKeys(boolean flag) {
+        this.supportsGeneratedKeys = flag;
+    }
+
+    /**
+     * Returns a translator factory for EJBQL to SQL translation. The factory 
is
+     * normally initialized in constructor by calling
+     * {@link #createEJBQLTranslatorFactory()}, and can be changed later by
+     * calling {@link #setEjbqlTranslatorFactory(EJBQLTranslatorFactory)}.
+     *
+     * @since 3.0
+     */
+    public EJBQLTranslatorFactory getEjbqlTranslatorFactory() {
+        return ejbqlTranslatorFactory;
+    }
+
+    /**
+     * Sets a translator factory for EJBQL to SQL translation. This property is
+     * normally initialized in constructor by calling
+     * {@link #createEJBQLTranslatorFactory()}, so users would only override it
+     * if they need to customize EJBQL translation.
+     *
+     * @since 3.0
+     */
+    public void setEjbqlTranslatorFactory(EJBQLTranslatorFactory 
ejbqlTranslatorFactory) {
+        this.ejbqlTranslatorFactory = ejbqlTranslatorFactory;
+    }
+
+    /**
+     * @since 4.0
+     */
+    protected QuotingStrategy createQuotingStrategy() {
+        return new DefaultQuotingStrategy("\"", "\"");
+    }
+
+    /**
+     * @since 4.0
+     */
+    public QuotingStrategy getQuotingStrategy() {
+        return quotingStrategy;
+    }
+
+    /**
+     * Simply returns this, as JdbcAdapter is not a wrapper.
+     *
+     * @since 4.0
+     */
+    @Override
+    public DbAdapter unwrap() {
+        return this;
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/929b6cb4/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2PkGenerator.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2PkGenerator.java 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2PkGenerator.java
index 9f5e3da..beed26b 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2PkGenerator.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2PkGenerator.java
@@ -27,40 +27,44 @@ import org.apache.cayenne.map.DbEntity;
  */
 public class DB2PkGenerator extends OraclePkGenerator {
 
-       DB2PkGenerator(JdbcAdapter adapter) {
-               super(adapter);
-       }
+    public DB2PkGenerator() {
+        super();
+    }
 
-       private static final String _SEQUENCE_PREFIX = "S_";
+    DB2PkGenerator(JdbcAdapter adapter) {
+        super(adapter);
+    }
 
-       @Override
-       protected String sequenceName(DbEntity entity) {
-               return super.sequenceName(entity).toUpperCase();
-       }
+    private static final String _SEQUENCE_PREFIX = "S_";
 
-       @Override
-       protected String getSequencePrefix() {
-               return _SEQUENCE_PREFIX;
-       }
+    @Override
+    protected String sequenceName(DbEntity entity) {
+        return super.sequenceName(entity).toUpperCase();
+    }
 
-       @Override
-       protected String selectNextValQuery(String pkGeneratingSequenceName) {
-               return "SELECT NEXTVAL FOR " + pkGeneratingSequenceName + " 
FROM SYSIBM.SYSDUMMY1";
-       }
+    @Override
+    protected String getSequencePrefix() {
+        return _SEQUENCE_PREFIX;
+    }
 
-       @Override
-       protected String selectAllSequencesQuery() {
-               return "SELECT SEQNAME FROM SYSCAT.SEQUENCES WHERE SEQNAME LIKE 
'" + _SEQUENCE_PREFIX + "%'";
-       }
+    @Override
+    protected String selectNextValQuery(String pkGeneratingSequenceName) {
+        return "SELECT NEXTVAL FOR " + pkGeneratingSequenceName + " FROM 
SYSIBM.SYSDUMMY1";
+    }
 
-       @Override
-       protected String dropSequenceString(DbEntity entity) {
-               return "DROP SEQUENCE " + sequenceName(entity) + " RESTRICT ";
-       }
+    @Override
+    protected String selectAllSequencesQuery() {
+        return "SELECT SEQNAME FROM SYSCAT.SEQUENCES WHERE SEQNAME LIKE '" + 
_SEQUENCE_PREFIX + "%'";
+    }
 
-       @Override
-       protected String createSequenceString(DbEntity entity) {
-               return "CREATE SEQUENCE " + sequenceName(entity) + " AS BIGINT 
START WITH " + pkStartValue +
-                               " INCREMENT BY " + getPkCacheSize() + " NO 
MAXVALUE NO CYCLE CACHE " + getPkCacheSize();
-       }
+    @Override
+    protected String dropSequenceString(DbEntity entity) {
+        return "DROP SEQUENCE " + sequenceName(entity) + " RESTRICT ";
+    }
+
+    @Override
+    protected String createSequenceString(DbEntity entity) {
+        return "CREATE SEQUENCE " + sequenceName(entity) + " AS BIGINT START 
WITH " + pkStartValue +
+                " INCREMENT BY " + getPkCacheSize() + " NO MAXVALUE NO CYCLE 
CACHE " + getPkCacheSize();
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/929b6cb4/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2Sniffer.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2Sniffer.java 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2Sniffer.java
index a4f1c1a..fc946f8 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2Sniffer.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2Sniffer.java
@@ -19,14 +19,18 @@
 
 package org.apache.cayenne.dba.db2;
 
-import java.sql.DatabaseMetaData;
-import java.sql.SQLException;
-
 import org.apache.cayenne.configuration.server.DbAdapterDetector;
+import org.apache.cayenne.configuration.server.PkGeneratorFactoryProvider;
 import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dba.JdbcAdapter;
+import org.apache.cayenne.dba.PkGenerator;
 import org.apache.cayenne.di.AdhocObjectFactory;
 import org.apache.cayenne.di.Inject;
 
+import java.sql.DatabaseMetaData;
+import java.sql.SQLException;
+import java.util.Objects;
+
 /**
  * @since 1.2
  */
@@ -34,17 +38,31 @@ public class DB2Sniffer implements DbAdapterDetector {
 
     protected AdhocObjectFactory objectFactory;
 
-    public DB2Sniffer(@Inject AdhocObjectFactory objectFactory) {
+    protected PkGeneratorFactoryProvider pkGeneratorProvider;
+
+    public DB2Sniffer(@Inject AdhocObjectFactory objectFactory,
+                      @Inject PkGeneratorFactoryProvider pkGeneratorProvider) {
         this.objectFactory = objectFactory;
+        this.pkGeneratorProvider = Objects.requireNonNull(pkGeneratorProvider, 
"Null pkGeneratorProvider");
     }
 
     @Override
     public DbAdapter createAdapter(DatabaseMetaData md) throws SQLException {
         String dbName = md.getDatabaseProductName();
-        return dbName != null && dbName.toUpperCase().contains("DB2")
-                ? (DbAdapter) objectFactory.newInstance(
-                        DbAdapter.class,
-                        DB2Adapter.class.getName())
-                : null;
+        if (dbName == null || !dbName.toUpperCase().contains("DB2")) {
+            return null;
+        }
+
+        JdbcAdapter adapter = objectFactory.newInstance(
+                DbAdapter.class,
+                DB2Adapter.class.getName());
+
+        PkGenerator pkGenerator = pkGeneratorProvider.get(adapter);
+
+        if (pkGenerator != null) {
+            adapter.setPkGenerator(pkGenerator);
+        }
+
+        return adapter;
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/929b6cb4/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbyPkGenerator.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbyPkGenerator.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbyPkGenerator.java
index 00a7fbf..6b9a7c4 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbyPkGenerator.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbyPkGenerator.java
@@ -25,38 +25,42 @@ import org.apache.cayenne.map.DbEntity;
 
 /**
  * PK generator for Derby that uses sequences.
- * 
+ *
  * @since 4.0 (old one used AUTO_PK_SUPPORT table)
  */
 public class DerbyPkGenerator extends OraclePkGenerator {
 
-       DerbyPkGenerator(JdbcAdapter adapter) {
-               super(adapter);
-       }
-
-       @Override
-       protected String sequenceName(DbEntity entity) {
-               return super.sequenceName(entity).toUpperCase();
-       }
-
-       @Override
-       protected String selectNextValQuery(String pkGeneratingSequenceName) {
-               return "VALUES (NEXT VALUE FOR " + pkGeneratingSequenceName + 
")";
-       }
-
-       @Override
-       protected String selectAllSequencesQuery() {
-               return "SELECT SEQUENCENAME FROM SYS.SYSSEQUENCES";
-       }
-
-       @Override
-       protected String dropSequenceString(DbEntity entity) {
-               return "DROP SEQUENCE " + sequenceName(entity) + " RESTRICT";
-       }
-
-       @Override
-       protected String createSequenceString(DbEntity entity) {
-               return "CREATE SEQUENCE " + sequenceName(entity) + " AS BIGINT 
START WITH " + pkStartValue +
-                               " INCREMENT BY " + getPkCacheSize() + " NO 
MAXVALUE NO CYCLE";
-       }
+    public DerbyPkGenerator() {
+        super();
+    }
+
+    DerbyPkGenerator(JdbcAdapter adapter) {
+        super(adapter);
+    }
+
+    @Override
+    protected String sequenceName(DbEntity entity) {
+        return super.sequenceName(entity).toUpperCase();
+    }
+
+    @Override
+    protected String selectNextValQuery(String pkGeneratingSequenceName) {
+        return "VALUES (NEXT VALUE FOR " + pkGeneratingSequenceName + ")";
+    }
+
+    @Override
+    protected String selectAllSequencesQuery() {
+        return "SELECT SEQUENCENAME FROM SYS.SYSSEQUENCES";
+    }
+
+    @Override
+    protected String dropSequenceString(DbEntity entity) {
+        return "DROP SEQUENCE " + sequenceName(entity) + " RESTRICT";
+    }
+
+    @Override
+    protected String createSequenceString(DbEntity entity) {
+        return "CREATE SEQUENCE " + sequenceName(entity) + " AS BIGINT START 
WITH " + pkStartValue +
+                " INCREMENT BY " + getPkCacheSize() + " NO MAXVALUE NO CYCLE";
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/929b6cb4/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbySniffer.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbySniffer.java 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbySniffer.java
index 1933fda..7370714 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbySniffer.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbySniffer.java
@@ -19,31 +19,52 @@
 
 package org.apache.cayenne.dba.derby;
 
-import java.sql.DatabaseMetaData;
-import java.sql.SQLException;
-
 import org.apache.cayenne.configuration.server.DbAdapterDetector;
+import org.apache.cayenne.configuration.server.PkGeneratorFactoryProvider;
 import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dba.JdbcAdapter;
+import org.apache.cayenne.dba.PkGenerator;
 import org.apache.cayenne.di.AdhocObjectFactory;
 import org.apache.cayenne.di.Inject;
 
+import java.sql.DatabaseMetaData;
+import java.sql.SQLException;
+import java.util.Objects;
+
 /**
  * Creates a DerbyAdapter if Apache Derby database is detected.
- * 
+ *
  * @since 1.2
  */
 public class DerbySniffer implements DbAdapterDetector {
 
     protected AdhocObjectFactory objectFactory;
 
-    public DerbySniffer(@Inject AdhocObjectFactory objectFactory) {
+    protected PkGeneratorFactoryProvider pkGeneratorProvider;
+
+    public DerbySniffer(@Inject AdhocObjectFactory objectFactory,
+                        @Inject PkGeneratorFactoryProvider 
pkGeneratorProvider) {
         this.objectFactory = objectFactory;
+        this.pkGeneratorProvider = Objects.requireNonNull(pkGeneratorProvider, 
"Null pkGeneratorProvider");
     }
 
     @Override
     public DbAdapter createAdapter(DatabaseMetaData md) throws SQLException {
         String dbName = md.getDatabaseProductName();
-        return dbName != null && dbName.toUpperCase().contains("APACHE DERBY")
-                ? (DbAdapter) objectFactory.newInstance(DbAdapter.class, 
DerbyAdapter.class.getName()) : null;
+        if (dbName == null || !dbName.toUpperCase().contains("APACHE DERBY")) {
+            return null;
+        }
+
+        JdbcAdapter adapter = objectFactory.newInstance(
+                DbAdapter.class,
+                DerbyAdapter.class.getName());
+
+        PkGenerator pkGenerator = pkGeneratorProvider.get(adapter);
+
+        if (pkGenerator != null) {
+            adapter.setPkGenerator(pkGenerator);
+        }
+
+        return adapter;
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/929b6cb4/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBasePkGenerator.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBasePkGenerator.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBasePkGenerator.java
index 3b8ac43..ecedfdc 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBasePkGenerator.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBasePkGenerator.java
@@ -39,100 +39,104 @@ import java.util.List;
  */
 public class FrontBasePkGenerator extends JdbcPkGenerator {
 
-       public FrontBasePkGenerator(JdbcAdapter adapter) {
-               super(adapter);
-               pkStartValue = 1000000;
-       }
-
-       /**
-        * Returns zero as PK caching is not supported by FrontBaseAdapter.
-        */
-       @Override
-       public int getPkCacheSize() {
-               return 0;
-       }
-
-       @Override
-       public void createAutoPk(DataNode node, List<DbEntity> dbEntities) 
throws Exception {
-               // For each entity (re)set the unique counter
-               for (DbEntity entity : dbEntities) {
-                       runUpdate(node, pkCreateString(entity.getName()));
-               }
-       }
-
-       @Override
-       public List<String> createAutoPkStatements(List<DbEntity> dbEntities) {
-               List<String> list = new ArrayList<>();
-               for (DbEntity entity : dbEntities) {
-                       list.add(pkCreateString(entity.getName()));
-               }
-               return list;
-       }
-
-       @Override
-       public void dropAutoPk(DataNode node, List<DbEntity> dbEntities) throws 
Exception {
-       }
-
-       @Override
-       protected String pkTableCreateString() {
-               return "";
-       }
-
-       @Override
-       protected String pkDeleteString(List<DbEntity> dbEntities) {
-               return "-- The 'Drop Primary Key Support' option is 
unavailable";
-       }
-
-       @Override
-       protected String pkCreateString(String entName) {
-               StringBuilder buf = new StringBuilder();
-               buf.append("SET UNIQUE = ").append(pkStartValue).append(" FOR 
\"").append(entName).append("\"");
-               return buf.toString();
-       }
-
-       @Override
-       protected String pkSelectString(String entName) {
-               StringBuilder buf = new StringBuilder();
-               buf.append("SELECT UNIQUE FROM 
\"").append(entName).append("\"");
-               return buf.toString();
-       }
-
-       @Override
-       protected String pkUpdateString(String entName) {
-               return "";
-       }
-
-       @Override
-       protected String dropAutoPkString() {
-               return "";
-       }
-
-       /**
-        * @since 3.0
-        */
-       @Override
-       protected long longPkFromDatabase(DataNode node, DbEntity entity) 
throws Exception {
-
-               String template = "SELECT #result('UNIQUE' 'long') FROM " + 
entity.getName();
-
-               final long[] pkHolder = new long[1];
-
-               SQLTemplate query = new SQLTemplate(entity, template);
-               OperationObserver observer = new DoNothingOperationObserver() {
-
-                       @Override
-                       public void nextRows(Query query, List<?> dataRows) {
-                               if (dataRows.size() != 1) {
-                                       throw new 
CayenneRuntimeException("Error fetching PK. Expected one row, got %d", 
dataRows.size());
-                               }
-
-                               DataRow row = (DataRow) dataRows.get(0);
-                               Number pk = (Number) row.get("UNIQUE");
-                               pkHolder[0] = pk.longValue();
-                       }
-               };
-
-               node.performQueries(Collections.singleton((Query) query), 
observer);
-               return pkHolder[0];
-       }
+    public FrontBasePkGenerator() {
+        super();
+    }
+
+    public FrontBasePkGenerator(JdbcAdapter adapter) {
+        super(adapter);
+        pkStartValue = 1000000;
+    }
+
+    /**
+     * Returns zero as PK caching is not supported by FrontBaseAdapter.
+     */
+    @Override
+    public int getPkCacheSize() {
+        return 0;
+    }
+
+    @Override
+    public void createAutoPk(DataNode node, List<DbEntity> dbEntities) throws 
Exception {
+        // For each entity (re)set the unique counter
+        for (DbEntity entity : dbEntities) {
+            runUpdate(node, pkCreateString(entity.getName()));
+        }
+    }
+
+    @Override
+    public List<String> createAutoPkStatements(List<DbEntity> dbEntities) {
+        List<String> list = new ArrayList<>();
+        for (DbEntity entity : dbEntities) {
+            list.add(pkCreateString(entity.getName()));
+        }
+        return list;
+    }
+
+    @Override
+    public void dropAutoPk(DataNode node, List<DbEntity> dbEntities) throws 
Exception {
+    }
+
+    @Override
+    protected String pkTableCreateString() {
+        return "";
+    }
+
+    @Override
+    protected String pkDeleteString(List<DbEntity> dbEntities) {
+        return "-- The 'Drop Primary Key Support' option is unavailable";
+    }
+
+    @Override
+    protected String pkCreateString(String entName) {
+        StringBuilder buf = new StringBuilder();
+        buf.append("SET UNIQUE = ").append(pkStartValue).append(" FOR 
\"").append(entName).append("\"");
+        return buf.toString();
+    }
+
+    @Override
+    protected String pkSelectString(String entName) {
+        StringBuilder buf = new StringBuilder();
+        buf.append("SELECT UNIQUE FROM \"").append(entName).append("\"");
+        return buf.toString();
+    }
+
+    @Override
+    protected String pkUpdateString(String entName) {
+        return "";
+    }
+
+    @Override
+    protected String dropAutoPkString() {
+        return "";
+    }
+
+    /**
+     * @since 3.0
+     */
+    @Override
+    protected long longPkFromDatabase(DataNode node, DbEntity entity) throws 
Exception {
+
+        String template = "SELECT #result('UNIQUE' 'long') FROM " + 
entity.getName();
+
+        final long[] pkHolder = new long[1];
+
+        SQLTemplate query = new SQLTemplate(entity, template);
+        OperationObserver observer = new DoNothingOperationObserver() {
+
+            @Override
+            public void nextRows(Query query, List<?> dataRows) {
+                if (dataRows.size() != 1) {
+                    throw new CayenneRuntimeException("Error fetching PK. 
Expected one row, got %d", dataRows.size());
+                }
+
+                DataRow row = (DataRow) dataRows.get(0);
+                Number pk = (Number) row.get("UNIQUE");
+                pkHolder[0] = pk.longValue();
+            }
+        };
+
+        node.performQueries(Collections.singleton((Query) query), observer);
+        return pkHolder[0];
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/929b6cb4/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseSniffer.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseSniffer.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseSniffer.java
index cfd5217..57a10a6 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseSniffer.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseSniffer.java
@@ -19,14 +19,18 @@
 
 package org.apache.cayenne.dba.frontbase;
 
-import java.sql.DatabaseMetaData;
-import java.sql.SQLException;
-
 import org.apache.cayenne.configuration.server.DbAdapterDetector;
+import org.apache.cayenne.configuration.server.PkGeneratorFactoryProvider;
 import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dba.JdbcAdapter;
+import org.apache.cayenne.dba.PkGenerator;
 import org.apache.cayenne.di.AdhocObjectFactory;
 import org.apache.cayenne.di.Inject;
 
+import java.sql.DatabaseMetaData;
+import java.sql.SQLException;
+import java.util.Objects;
+
 /**
  * @since 1.2
  */
@@ -34,14 +38,31 @@ public class FrontBaseSniffer implements DbAdapterDetector {
 
     protected AdhocObjectFactory objectFactory;
 
-    public FrontBaseSniffer(@Inject AdhocObjectFactory objectFactory) {
+    protected PkGeneratorFactoryProvider pkGeneratorProvider;
+
+    public FrontBaseSniffer(@Inject AdhocObjectFactory objectFactory,
+                            @Inject PkGeneratorFactoryProvider 
pkGeneratorProvider) {
         this.objectFactory = objectFactory;
+        this.pkGeneratorProvider = Objects.requireNonNull(pkGeneratorProvider, 
"Null pkGeneratorProvider");
     }
 
     @Override
     public DbAdapter createAdapter(DatabaseMetaData md) throws SQLException {
         String dbName = md.getDatabaseProductName();
-        return dbName != null && dbName.toUpperCase().contains("FRONTBASE")
-                ? (DbAdapter) objectFactory.newInstance(DbAdapter.class, 
FrontBaseAdapter.class.getName()) : null;
+        if (dbName == null || !dbName.toUpperCase().contains("FRONTBASE")) {
+            return null;
+        }
+
+        JdbcAdapter adapter = objectFactory.newInstance(
+                DbAdapter.class,
+                FrontBaseAdapter.class.getName());
+
+        PkGenerator pkGenerator = pkGeneratorProvider.get(adapter);
+
+        if (pkGenerator != null) {
+            adapter.setPkGenerator(pkGenerator);
+        }
+
+        return adapter;
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/929b6cb4/cayenne-server/src/main/java/org/apache/cayenne/dba/h2/H2PkGenerator.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/h2/H2PkGenerator.java 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/h2/H2PkGenerator.java
index 9e2e4d3..3c49d33 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/h2/H2PkGenerator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/h2/H2PkGenerator.java
@@ -25,28 +25,32 @@ import org.apache.cayenne.map.DbEntity;
 
 /**
  * Default PK generator for H2 that uses sequences for PK generation.
- * 
+ *
  * @since 4.0
  */
 public class H2PkGenerator extends OraclePkGenerator {
 
-       protected H2PkGenerator(JdbcAdapter adapter) {
-               super(adapter);
-       }
-
-       @Override
-       protected String createSequenceString(DbEntity ent) {
-               return "CREATE SEQUENCE " + sequenceName(ent) + " START WITH " 
+ pkStartValue + " INCREMENT BY "
-                               + pkCacheSize(ent) + " CACHE 1";
-       }
-
-       @Override
-       protected String selectNextValQuery(String sequenceName) {
-               return "SELECT NEXT VALUE FOR " + sequenceName;
-       }
-
-       @Override
-       protected String selectAllSequencesQuery() {
-               return "SELECT LOWER(sequence_name) FROM 
Information_Schema.Sequences";
-       }
+    public H2PkGenerator() {
+        super();
+    }
+
+    protected H2PkGenerator(JdbcAdapter adapter) {
+        super(adapter);
+    }
+
+    @Override
+    protected String createSequenceString(DbEntity ent) {
+        return "CREATE SEQUENCE " + sequenceName(ent) + " START WITH " + 
pkStartValue + " INCREMENT BY "
+                + pkCacheSize(ent) + " CACHE 1";
+    }
+
+    @Override
+    protected String selectNextValQuery(String sequenceName) {
+        return "SELECT NEXT VALUE FOR " + sequenceName;
+    }
+
+    @Override
+    protected String selectAllSequencesQuery() {
+        return "SELECT LOWER(sequence_name) FROM Information_Schema.Sequences";
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/929b6cb4/cayenne-server/src/main/java/org/apache/cayenne/dba/h2/H2Sniffer.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/h2/H2Sniffer.java 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/h2/H2Sniffer.java
index c8d6fee..bb898d3 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/h2/H2Sniffer.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/h2/H2Sniffer.java
@@ -19,14 +19,18 @@
 
 package org.apache.cayenne.dba.h2;
 
-import java.sql.DatabaseMetaData;
-import java.sql.SQLException;
-
 import org.apache.cayenne.configuration.server.DbAdapterDetector;
+import org.apache.cayenne.configuration.server.PkGeneratorFactoryProvider;
 import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dba.JdbcAdapter;
+import org.apache.cayenne.dba.PkGenerator;
 import org.apache.cayenne.di.AdhocObjectFactory;
 import org.apache.cayenne.di.Inject;
 
+import java.sql.DatabaseMetaData;
+import java.sql.SQLException;
+import java.util.Objects;
+
 /**
  * @since 3.0
  */
@@ -34,15 +38,31 @@ public class H2Sniffer implements DbAdapterDetector {
 
     protected AdhocObjectFactory objectFactory;
 
-    public H2Sniffer(@Inject AdhocObjectFactory objectFactory) {
+    protected PkGeneratorFactoryProvider pkGeneratorProvider;
+
+    public H2Sniffer(@Inject AdhocObjectFactory objectFactory,
+                     @Inject PkGeneratorFactoryProvider pkGeneratorProvider) {
         this.objectFactory = objectFactory;
+        this.pkGeneratorProvider = Objects.requireNonNull(pkGeneratorProvider, 
"Null pkGeneratorProvider");
     }
 
     @Override
     public DbAdapter createAdapter(DatabaseMetaData md) throws SQLException {
         String dbName = md.getDatabaseProductName();
-        return dbName != null && dbName.toUpperCase().contains("H2")
-                ? (DbAdapter) objectFactory.newInstance(DbAdapter.class, 
H2Adapter.class.getName()) : null;
+        if (dbName == null || !dbName.toUpperCase().contains("H2")) {
+            return null;
+        }
+
+        JdbcAdapter adapter = objectFactory.newInstance(
+                DbAdapter.class,
+                H2Adapter.class.getName());
+
+        PkGenerator pkGenerator = pkGeneratorProvider.get(adapter);
+
+        if (pkGenerator != null) {
+            adapter.setPkGenerator(pkGenerator);
+        }
+        return adapter;
     }
 
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/929b6cb4/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresPkGenerator.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresPkGenerator.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresPkGenerator.java
index 59a1183..c5f73f5 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresPkGenerator.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresPkGenerator.java
@@ -24,22 +24,26 @@ import org.apache.cayenne.dba.oracle.OraclePkGenerator;
 
 /**
  * Ingres-specific sequence based PK generator.
- * 
+ *
  * @since 1.2
  */
 public class IngresPkGenerator extends OraclePkGenerator {
 
-       protected IngresPkGenerator(JdbcAdapter adapter) {
-               super(adapter);
-       }
+    public IngresPkGenerator() {
+        super();
+    }
+
+    protected IngresPkGenerator(JdbcAdapter adapter) {
+        super(adapter);
+    }
 
-       @Override
-       protected String selectNextValQuery(String sequenceName) {
-               return "SELECT " + sequenceName + ".nextval";
-       }
+    @Override
+    protected String selectNextValQuery(String sequenceName) {
+        return "SELECT " + sequenceName + ".nextval";
+    }
 
-       @Override
-       protected String selectAllSequencesQuery() {
-               return "SELECT seq_name FROM iisequences WHERE seq_owner != 
'DBA'";
-       }
+    @Override
+    protected String selectAllSequencesQuery() {
+        return "SELECT seq_name FROM iisequences WHERE seq_owner != 'DBA'";
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/929b6cb4/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresSniffer.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresSniffer.java 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresSniffer.java
index 471d65c..267791d 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresSniffer.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresSniffer.java
@@ -19,14 +19,18 @@
 
 package org.apache.cayenne.dba.ingres;
 
-import java.sql.DatabaseMetaData;
-import java.sql.SQLException;
-
 import org.apache.cayenne.configuration.server.DbAdapterDetector;
+import org.apache.cayenne.configuration.server.PkGeneratorFactoryProvider;
 import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dba.JdbcAdapter;
+import org.apache.cayenne.dba.PkGenerator;
 import org.apache.cayenne.di.AdhocObjectFactory;
 import org.apache.cayenne.di.Inject;
 
+import java.sql.DatabaseMetaData;
+import java.sql.SQLException;
+import java.util.Objects;
+
 /**
  * Detects Ingres database from JDBC metadata.
  * 
@@ -36,14 +40,31 @@ public class IngresSniffer implements DbAdapterDetector {
 
     protected AdhocObjectFactory objectFactory;
 
-    public IngresSniffer(@Inject AdhocObjectFactory objectFactory) {
+    protected PkGeneratorFactoryProvider pkGeneratorProvider;
+
+    public IngresSniffer(@Inject AdhocObjectFactory objectFactory,
+                         @Inject PkGeneratorFactoryProvider 
pkGeneratorProvider) {
         this.objectFactory = objectFactory;
+        this.pkGeneratorProvider = Objects.requireNonNull(pkGeneratorProvider, 
"Null pkGeneratorProvider");
     }
 
     @Override
     public DbAdapter createAdapter(DatabaseMetaData md) throws SQLException {
         String dbName = md.getDatabaseProductName();
-        return dbName != null && dbName.toUpperCase().contains("INGRES")
-                ? (DbAdapter) objectFactory.newInstance(DbAdapter.class, 
IngresAdapter.class.getName()) : null;
+        if (dbName == null || !dbName.toUpperCase().contains("INGRES")) {
+            return null;
+        }
+
+        JdbcAdapter adapter = objectFactory.newInstance(
+                DbAdapter.class,
+                IngresAdapter.class.getName());
+
+        PkGenerator pkGenerator = pkGeneratorProvider.get(adapter);
+
+        if (pkGenerator != null) {
+            adapter.setPkGenerator(pkGenerator);
+        }
+
+        return adapter;
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/929b6cb4/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLPkGenerator.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLPkGenerator.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLPkGenerator.java
index 83be87c..3c1414d 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLPkGenerator.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLPkGenerator.java
@@ -38,140 +38,144 @@ import org.slf4j.LoggerFactory;
  */
 public class MySQLPkGenerator extends JdbcPkGenerator {
 
-       private static final Logger logger = 
LoggerFactory.getLogger(MySQLPkGenerator.class);
-
-       MySQLPkGenerator(JdbcAdapter adapter) {
-               super(adapter);
-       }
-
-       /**
-        * Overrides superclass's implementation to perform locking of the 
primary
-        * key lookup table.
-        * 
-        * @since 3.0
-        */
-       @Override
-       protected long longPkFromDatabase(DataNode node, DbEntity entity) 
throws Exception {
-
-               // must work directly with JDBC connection, since we
-               // must unlock the AUTO_PK_SUPPORT table in case of
-               // failures.... ah..JDBC is fun...
-
-               // chained SQL exception
-               SQLException exception = null;
-               long pk = -1L;
-
-               // Start new transaction if needed, can any way lead to 
problems when
-               // using external transaction manager. We can only warn about 
it.
-               // See https://issues.apache.org/jira/browse/CAY-2186 for 
details.
-               Transaction transaction = 
BaseTransaction.getThreadTransaction();
-               if(transaction != null && transaction.isExternal()) {
-                       logger.warn("Using MysqlPkGenerator with external 
transaction manager may lead to inconsistent state.");
-               }
-               BaseTransaction.bindThreadTransaction(null);
-
-               try (Connection con = node.getDataSource().getConnection()) {
-
-                       if (con.getAutoCommit()) {
-                               con.setAutoCommit(false);
-                       }
-
-                       try(Statement st = con.createStatement()) {
-                               try {
-                                       pk = getLongPrimaryKey(st, 
entity.getName());
-                                       con.commit();
-                               } catch (SQLException pkEx) {
-                                       try {
-                                               con.rollback();
-                                       } catch (SQLException ignored) {
-                                       }
-
-                                       exception = processSQLException(pkEx, 
null);
-                               } finally {
-                                       // UNLOCK!
-                                       // THIS MUST BE EXECUTED NO MATTER 
WHAT, OR WE WILL LOCK THE PRIMARY KEY TABLE!!
-                                       try {
-                                               String unlockString = "UNLOCK 
TABLES";
-                                               
adapter.getJdbcEventLogger().log(unlockString);
-                                               st.execute(unlockString);
-                                       } catch (SQLException unlockEx) {
-                                               exception = 
processSQLException(unlockEx, exception);
-                                       }
-                               }
-                       }
-               } catch (SQLException otherEx) {
-                       exception = processSQLException(otherEx, null);
-               } finally {
-                       BaseTransaction.bindThreadTransaction(transaction);
-               }
-
-               // check errors
-               if (exception != null) {
-                       throw exception;
-               }
-
-               return pk;
-
-       }
-
-       /**
-        * Appends a new SQLException to the chain. If parent is null, uses the
-        * exception as the chain root.
-        */
-       protected SQLException processSQLException(SQLException exception, 
SQLException parent) {
-               if (parent == null) {
-                       return exception;
-               }
-
-               parent.setNextException(exception);
-               return parent;
-       }
-
-       @Override
-       protected String dropAutoPkString() {
-               return "DROP TABLE IF EXISTS AUTO_PK_SUPPORT";
-       }
-
-       @Override
-       protected String pkTableCreateString() {
-               return "CREATE TABLE IF NOT EXISTS AUTO_PK_SUPPORT " +
-                               "(TABLE_NAME CHAR(100) NOT NULL, NEXT_ID BIGINT 
NOT NULL, UNIQUE (TABLE_NAME)) " +
-                               "ENGINE=" + MySQLAdapter.DEFAULT_STORAGE_ENGINE;
-       }
-
-       /**
-        * @since 3.0
-        */
-       protected long getLongPrimaryKey(Statement statement, String 
entityName) throws SQLException {
-               // lock
-               String lockString = "LOCK TABLES AUTO_PK_SUPPORT WRITE";
-               adapter.getJdbcEventLogger().log(lockString);
-               statement.execute(lockString);
-
-               // select
-               String selectString = super.pkSelectString(entityName);
-               adapter.getJdbcEventLogger().log(selectString);
-               long pk;
-               try(ResultSet rs = statement.executeQuery(selectString)) {
-                       if (!rs.next()) {
-                               throw new SQLException("No rows for '" + 
entityName + "'");
-                       }
-
-                       pk = rs.getLong(1);
-                       if (rs.next()) {
-                               throw new SQLException("More than one row for 
'" + entityName + "'");
-                       }
-               }
-
-               // update
-               String updateString = super.pkUpdateString(entityName) + " AND 
NEXT_ID = " + pk;
-               adapter.getJdbcEventLogger().log(updateString);
-               int updated = statement.executeUpdate(updateString);
-               // optimistic lock failure...
-               if (updated != 1) {
-                       throw new SQLException("Error updating PK count '" + 
entityName + "': " + updated);
-               }
-
-               return pk;
-       }
+    private static final Logger logger = 
LoggerFactory.getLogger(MySQLPkGenerator.class);
+
+    public MySQLPkGenerator() {
+        super();
+    }
+
+    MySQLPkGenerator(JdbcAdapter adapter) {
+        super(adapter);
+    }
+
+    /**
+     * Overrides superclass's implementation to perform locking of the primary
+     * key lookup table.
+     *
+     * @since 3.0
+     */
+    @Override
+    protected long longPkFromDatabase(DataNode node, DbEntity entity) throws 
Exception {
+
+        // must work directly with JDBC connection, since we
+        // must unlock the AUTO_PK_SUPPORT table in case of
+        // failures.... ah..JDBC is fun...
+
+        // chained SQL exception
+        SQLException exception = null;
+        long pk = -1L;
+
+        // Start new transaction if needed, can any way lead to problems when
+        // using external transaction manager. We can only warn about it.
+        // See https://issues.apache.org/jira/browse/CAY-2186 for details.
+        Transaction transaction = BaseTransaction.getThreadTransaction();
+        if (transaction != null && transaction.isExternal()) {
+            logger.warn("Using MysqlPkGenerator with external transaction 
manager may lead to inconsistent state.");
+        }
+        BaseTransaction.bindThreadTransaction(null);
+
+        try (Connection con = node.getDataSource().getConnection()) {
+
+            if (con.getAutoCommit()) {
+                con.setAutoCommit(false);
+            }
+
+            try (Statement st = con.createStatement()) {
+                try {
+                    pk = getLongPrimaryKey(st, entity.getName());
+                    con.commit();
+                } catch (SQLException pkEx) {
+                    try {
+                        con.rollback();
+                    } catch (SQLException ignored) {
+                    }
+
+                    exception = processSQLException(pkEx, null);
+                } finally {
+                    // UNLOCK!
+                    // THIS MUST BE EXECUTED NO MATTER WHAT, OR WE WILL LOCK 
THE PRIMARY KEY TABLE!!
+                    try {
+                        String unlockString = "UNLOCK TABLES";
+                        adapter.getJdbcEventLogger().log(unlockString);
+                        st.execute(unlockString);
+                    } catch (SQLException unlockEx) {
+                        exception = processSQLException(unlockEx, exception);
+                    }
+                }
+            }
+        } catch (SQLException otherEx) {
+            exception = processSQLException(otherEx, null);
+        } finally {
+            BaseTransaction.bindThreadTransaction(transaction);
+        }
+
+        // check errors
+        if (exception != null) {
+            throw exception;
+        }
+
+        return pk;
+
+    }
+
+    /**
+     * Appends a new SQLException to the chain. If parent is null, uses the
+     * exception as the chain root.
+     */
+    protected SQLException processSQLException(SQLException exception, 
SQLException parent) {
+        if (parent == null) {
+            return exception;
+        }
+
+        parent.setNextException(exception);
+        return parent;
+    }
+
+    @Override
+    protected String dropAutoPkString() {
+        return "DROP TABLE IF EXISTS AUTO_PK_SUPPORT";
+    }
+
+    @Override
+    protected String pkTableCreateString() {
+        return "CREATE TABLE IF NOT EXISTS AUTO_PK_SUPPORT " +
+                "(TABLE_NAME CHAR(100) NOT NULL, NEXT_ID BIGINT NOT NULL, 
UNIQUE (TABLE_NAME)) " +
+                "ENGINE=" + MySQLAdapter.DEFAULT_STORAGE_ENGINE;
+    }
+
+    /**
+     * @since 3.0
+     */
+    protected long getLongPrimaryKey(Statement statement, String entityName) 
throws SQLException {
+        // lock
+        String lockString = "LOCK TABLES AUTO_PK_SUPPORT WRITE";
+        adapter.getJdbcEventLogger().log(lockString);
+        statement.execute(lockString);
+
+        // select
+        String selectString = super.pkSelectString(entityName);
+        adapter.getJdbcEventLogger().log(selectString);
+        long pk;
+        try (ResultSet rs = statement.executeQuery(selectString)) {
+            if (!rs.next()) {
+                throw new SQLException("No rows for '" + entityName + "'");
+            }
+
+            pk = rs.getLong(1);
+            if (rs.next()) {
+                throw new SQLException("More than one row for '" + entityName 
+ "'");
+            }
+        }
+
+        // update
+        String updateString = super.pkUpdateString(entityName) + " AND NEXT_ID 
= " + pk;
+        adapter.getJdbcEventLogger().log(updateString);
+        int updated = statement.executeUpdate(updateString);
+        // optimistic lock failure...
+        if (updated != 1) {
+            throw new SQLException("Error updating PK count '" + entityName + 
"': " + updated);
+        }
+
+        return pk;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/929b6cb4/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSniffer.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSniffer.java 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSniffer.java
index be5334d..72e84b8 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSniffer.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLSniffer.java
@@ -20,7 +20,9 @@
 package org.apache.cayenne.dba.mysql;
 
 import org.apache.cayenne.configuration.server.DbAdapterDetector;
+import org.apache.cayenne.configuration.server.PkGeneratorFactoryProvider;
 import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dba.PkGenerator;
 import org.apache.cayenne.di.AdhocObjectFactory;
 import org.apache.cayenne.di.Inject;
 
@@ -28,6 +30,7 @@ import java.sql.DatabaseMetaData;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
+import java.util.Objects;
 
 /**
  * Detects MySQL database from JDBC metadata.
@@ -38,8 +41,12 @@ public class MySQLSniffer implements DbAdapterDetector {
 
        protected AdhocObjectFactory objectFactory;
 
-       public MySQLSniffer(@Inject AdhocObjectFactory objectFactory) {
+       protected PkGeneratorFactoryProvider pkGeneratorProvider;
+
+       public MySQLSniffer(@Inject AdhocObjectFactory objectFactory,
+                                               @Inject 
PkGeneratorFactoryProvider pkGeneratorProvider) {
                this.objectFactory = objectFactory;
+               this.pkGeneratorProvider = 
Objects.requireNonNull(pkGeneratorProvider, "Null pkGeneratorProvider");
        }
 
        @Override
@@ -53,7 +60,7 @@ public class MySQLSniffer implements DbAdapterDetector {
 
                String adapterStorageEngine = 
MySQLAdapter.DEFAULT_STORAGE_ENGINE;
 
-               try (Statement statement = 
md.getConnection().createStatement();) {
+               try (Statement statement = 
md.getConnection().createStatement()) {
                        // 
http://dev.mysql.com/doc/refman/5.0/en/storage-engines.html
                        // per link above "table type" concept is deprecated in 
favor of
                        // "storage
@@ -61,7 +68,7 @@ public class MySQLSniffer implements DbAdapterDetector {
                        // and in what
                        // version of MySQL it got introduced...
 
-                       try (ResultSet rs = statement.executeQuery("SHOW 
VARIABLES LIKE 'table_type'");) {
+                       try (ResultSet rs = statement.executeQuery("SHOW 
VARIABLES LIKE 'table_type'")) {
                                if (rs.next()) {
                                        String storageEngine = rs.getString(2);
                                        if (storageEngine != null) {
@@ -73,6 +80,12 @@ public class MySQLSniffer implements DbAdapterDetector {
 
                MySQLAdapter adapter = 
objectFactory.newInstance(MySQLAdapter.class, MySQLAdapter.class.getName());
                adapter.setStorageEngine(adapterStorageEngine);
+
+               PkGenerator pkGenerator = pkGeneratorProvider.get(adapter);
+
+               if (pkGenerator != null) {
+                       adapter.setPkGenerator(pkGenerator);
+               }
                return adapter;
        }
 }

Reply via email to