You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2018/04/18 09:59:13 UTC

[09/19] cayenne git commit: Refactoring PKs

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 <pr...@hotmail.com>
Authored: Sun Apr 1 00:19:04 2018 +0300
Committer: Aleksey Pleshkanev <pr...@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;
 	}
 }