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:10 UTC

[06/19] cayenne git commit: Refactoring the PK definition

Refactoring the PK definition


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

Branch: refs/heads/master
Commit: 284151ce85e8a91824c1528a245b64b9a2075ba7
Parents: 3512f80
Author: Aleksey Pleshkanev <pr...@hotmail.com>
Authored: Sun Mar 25 18:31:55 2018 +0300
Committer: Aleksey Pleshkanev <pr...@hotmail.com>
Committed: Sun Mar 25 18:31:55 2018 +0300

----------------------------------------------------------------------
 .../reverse/configuration/ToolsModule.java      |  24 +-
 .../server/DefaultPkGeneratorFactory.java       | 118 ----
 .../server/PkGeneratorFactory.java              |  25 -
 .../server/PkGeneratorFactoryProvider.java      |  17 +
 .../configuration/server/ServerModule.java      |  45 +-
 .../java/org/apache/cayenne/dba/DbVersion.java  | 108 ----
 .../org/apache/cayenne/dba/JdbcPkGenerator.java | 641 ++++++++++---------
 .../org/apache/cayenne/dba/PkGenerator.java     |  25 +-
 .../cayenne/dba/sqlserver/SQLServerSniffer.java |  23 +-
 .../cayenne/dba/sybase/SybasePkGenerator.java   |   4 +
 .../server/DataDomainProviderTest.java          |  13 +-
 .../dba/sqlserver/SQLServerSnifferIT.java       |  16 +-
 .../unit/di/server/ServerCaseModule.java        |  20 +-
 13 files changed, 435 insertions(+), 644 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/284151ce/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/configuration/ToolsModule.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/configuration/ToolsModule.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/configuration/ToolsModule.java
index 3d3a1bd..2dd7f2e 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/configuration/ToolsModule.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/configuration/ToolsModule.java
@@ -19,8 +19,6 @@
 
 package org.apache.cayenne.dbsync.reverse.configuration;
 
-import java.util.Objects;
-
 import org.apache.cayenne.access.translator.batch.BatchTranslatorFactory;
 import org.apache.cayenne.access.translator.batch.DefaultBatchTranslatorFactory;
 import org.apache.cayenne.access.types.DefaultValueObjectTypeRegistry;
@@ -30,13 +28,19 @@ import org.apache.cayenne.configuration.DataChannelDescriptorLoader;
 import org.apache.cayenne.configuration.DataMapLoader;
 import org.apache.cayenne.configuration.DefaultRuntimeProperties;
 import org.apache.cayenne.configuration.RuntimeProperties;
-import org.apache.cayenne.configuration.server.*;
+import org.apache.cayenne.configuration.server.DataSourceFactory;
+import org.apache.cayenne.configuration.server.DbAdapterFactory;
+import org.apache.cayenne.configuration.server.DefaultDbAdapterFactory;
+import org.apache.cayenne.configuration.server.PkGeneratorFactoryProvider;
+import org.apache.cayenne.configuration.server.ServerModule;
 import org.apache.cayenne.configuration.xml.DataChannelMetaData;
 import org.apache.cayenne.configuration.xml.DefaultDataChannelMetaData;
 import org.apache.cayenne.configuration.xml.HandlerFactory;
 import org.apache.cayenne.configuration.xml.XMLDataChannelDescriptorLoader;
 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.DB2Sniffer;
 import org.apache.cayenne.dba.derby.DerbySniffer;
 import org.apache.cayenne.dba.firebird.FirebirdSniffer;
@@ -49,7 +53,7 @@ import org.apache.cayenne.dba.openbase.OpenBaseSniffer;
 import org.apache.cayenne.dba.oracle.OracleSniffer;
 import org.apache.cayenne.dba.postgres.PostgresSniffer;
 import org.apache.cayenne.dba.sqlite.SQLiteSniffer;
-import org.apache.cayenne.dba.sqlserver.SQLServerPkGenerator;
+import org.apache.cayenne.dba.sqlserver.SQLServerAdapter;
 import org.apache.cayenne.dba.sqlserver.SQLServerSniffer;
 import org.apache.cayenne.dba.sybase.SybasePkGenerator;
 import org.apache.cayenne.dba.sybase.SybaseSniffer;
@@ -60,8 +64,8 @@ import org.apache.cayenne.di.Key;
 import org.apache.cayenne.di.Module;
 import org.apache.cayenne.di.spi.DefaultAdhocObjectFactory;
 import org.apache.cayenne.di.spi.DefaultClassLoaderManager;
-import org.apache.cayenne.log.Slf4jJdbcEventLogger;
 import org.apache.cayenne.log.JdbcEventLogger;
+import org.apache.cayenne.log.Slf4jJdbcEventLogger;
 import org.apache.cayenne.project.ProjectModule;
 import org.apache.cayenne.project.extension.ExtensionAwareHandlerFactory;
 import org.apache.cayenne.resource.ClassLoaderResourceLocator;
@@ -69,8 +73,7 @@ import org.apache.cayenne.resource.ResourceLocator;
 import org.slf4j.Logger;
 import org.xml.sax.XMLReader;
 
-import static org.apache.cayenne.dba.DbVersion.MS_SQL_2008;
-import static org.apache.cayenne.dba.DbVersion.MS_SQL_2012;
+import java.util.Objects;
 
 /**
  * A DI module to bootstrap DI container for Cayenne Ant tasks and Maven
@@ -114,12 +117,11 @@ public class ToolsModule implements Module {
                 .add(SQLServerSniffer.class).add(OracleSniffer.class).add(PostgresSniffer.class)
                 .add(MySQLSniffer.class);
 
-        ServerModule.contributePkGenerators(binder)
-                .put(String.valueOf(MS_SQL_2008), SybasePkGenerator.class)
-                .put(String.valueOf(MS_SQL_2012), SQLServerPkGenerator.class);
+        binder.bind(PkGeneratorFactoryProvider.class).to(PkGeneratorFactoryProvider.class);
+        binder.bind(PkGenerator.class).to(JdbcPkGenerator.class);
+        ServerModule.contributePkGenerators(binder).put(SQLServerAdapter.class.getName(), SybasePkGenerator.class);
 
         binder.bind(DbAdapterFactory.class).to(DefaultDbAdapterFactory.class);
-        binder.bind(PkGeneratorFactory.class).to(DefaultPkGeneratorFactory.class);
         binder.bind(DataSourceFactory.class).to(DriverDataSourceFactory.class);
 
         binder.bind(DataMapLoader.class).to(XMLDataMapLoader.class);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/284151ce/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/DefaultPkGeneratorFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/DefaultPkGeneratorFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/DefaultPkGeneratorFactory.java
deleted file mode 100644
index 9a64f99..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/DefaultPkGeneratorFactory.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package org.apache.cayenne.configuration.server;
-
-import org.apache.cayenne.ConfigurationException;
-import org.apache.cayenne.configuration.Constants;
-import org.apache.cayenne.dba.DbVersion;
-import org.apache.cayenne.dba.JdbcAdapter;
-import org.apache.cayenne.dba.JdbcPkGenerator;
-import org.apache.cayenne.dba.PkGenerator;
-import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.log.JdbcEventLogger;
-
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.reflect.Constructor;
-import java.sql.DatabaseMetaData;
-import java.sql.SQLException;
-import java.util.Map;
-import java.util.Objects;
-
-import static org.apache.cayenne.dba.DbVersion.MS_SQL_2008;
-import static org.apache.cayenne.dba.DbVersion.MS_SQL_2012;
-
-/**
- * A factory of PkGenerators that also loads user-provided pkGenerator or guesses
- * the pkGenerator from the database metadata.
- */
-public class DefaultPkGeneratorFactory implements PkGeneratorFactory {
-
-    @Inject
-    protected JdbcEventLogger jdbcEventLogger;
-
-    protected Map<String, Class> pkGenerators;
-
-    public DefaultPkGeneratorFactory(@Inject(Constants.SERVER_PK_GENERATORS_MAP) Map<String, Class> pkGenerators) {
-        this.pkGenerators = Objects.requireNonNull(pkGenerators, () -> "Null pkGenerators list");
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    public PkGenerator detectPkGenerator(DbVersion.DbType dbType, JdbcAdapter adapter, DatabaseMetaData md) throws SQLException {
-
-        final int majorVersion = md.getDatabaseMajorVersion();
-        final int minorVersion = md.getDatabaseMinorVersion();
-
-        Class pkGeneratorClazz = null;
-        DbVersion currentDbVersion = new DbVersion(dbType, majorVersion, minorVersion);
-        switch (dbType) {
-            case MS_SQL:
-                pkGeneratorClazz = detectPkGenerator4MSSQL(currentDbVersion);
-                break;
-            default:
-                jdbcEventLogger.log("Failed to detect PkGenerator, using generic generator");
-                pkGeneratorClazz = defaultPkGenerator();
-        }
-
-        jdbcEventLogger.log("DB - '" + currentDbVersion + "'; PkGenerator - " + pkGeneratorClazz.getName());
-
-        try {
-            return newInstancePk(pkGeneratorClazz, adapter);
-        } catch (Throwable throwable) {
-            throw new ConfigurationException("Could not instantiate " + currentDbVersion, throwable);
-        }
-    }
-
-    /**
-     * @param pkGeneratorClazz {@link Class} for instantiation PkGenerator
-     * @param adapter          adapter for installation in PkGenerator
-     */
-    protected PkGenerator newInstancePk(Class pkGeneratorClazz, JdbcAdapter adapter) throws Throwable {
-        Constructor pkGeneratorConstructor = pkGeneratorClazz.getDeclaredConstructor(JdbcAdapter.class);
-        pkGeneratorConstructor.setAccessible(true);
-
-        MethodHandle createPkGenerator = MethodHandles.lookup()
-                .unreflectConstructor(pkGeneratorConstructor);
-
-        return (PkGenerator) createPkGenerator.invoke(adapter);
-    }
-
-    protected Class defaultPkGenerator() {
-        return JdbcPkGenerator.class;
-    }
-
-    /**
-     * Choosing a specific generator, depending on the version of the database
-     *
-     * @param currentDbVersion version of the database for which you need to determine the PkGenerator
-     */
-    protected Class detectPkGenerator4MSSQL(DbVersion currentDbVersion) {
-
-        if (!isCheckTypeGenerator(currentDbVersion, MS_SQL_2008, MS_SQL_2012)) {
-            return defaultPkGenerator();
-        }
-
-        if (currentDbVersion.compareTo(MS_SQL_2012) >= 0) {
-            String version = String.valueOf(MS_SQL_2012);
-            if (pkGenerators.containsKey(version)) {
-                return pkGenerators.get(version);
-            }
-        } else {
-            String version = String.valueOf(MS_SQL_2008);
-            if (pkGenerators.containsKey(version)) {
-                return pkGenerators.get(version);
-            }
-        }
-
-        jdbcEventLogger.log("Failed to detect PkGenerator, using generic generator");
-        return defaultPkGenerator();
-    }
-
-    private boolean isCheckTypeGenerator(DbVersion currentDbVersion, DbVersion... dbVersions) {
-        for (DbVersion item : dbVersions) {
-            if (!item.isTypeCheck(currentDbVersion)) {
-                return false;
-            }
-        }
-        return true;
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/284151ce/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/PkGeneratorFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/PkGeneratorFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/PkGeneratorFactory.java
deleted file mode 100644
index 2e3882e..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/PkGeneratorFactory.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package org.apache.cayenne.configuration.server;
-
-import org.apache.cayenne.dba.DbVersion;
-import org.apache.cayenne.dba.JdbcAdapter;
-import org.apache.cayenne.dba.PkGenerator;
-
-import java.sql.DatabaseMetaData;
-import java.sql.SQLException;
-
-/**
- * Interface for generator definition
- */
-public interface PkGeneratorFactory {
-
-    /**
-     * Discovering the primary key based on the database metadata
-     *
-     * @param dbType  database type
-     * @param adapter adapter for generator instantiation
-     * @param md      connection metadata
-     * @return an instantiated instance of a specific generator for the current version of the database
-     * @throws SQLException
-     */
-    PkGenerator detectPkGenerator(DbVersion.DbType dbType, JdbcAdapter adapter, DatabaseMetaData md) throws SQLException;
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/284151ce/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/PkGeneratorFactoryProvider.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/PkGeneratorFactoryProvider.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/PkGeneratorFactoryProvider.java
new file mode 100644
index 0000000..d003782
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/PkGeneratorFactoryProvider.java
@@ -0,0 +1,17 @@
+package org.apache.cayenne.configuration.server;
+
+import org.apache.cayenne.configuration.Constants;
+import org.apache.cayenne.dba.PerAdapterProvider;
+import org.apache.cayenne.dba.PkGenerator;
+import org.apache.cayenne.di.Inject;
+
+import java.util.Map;
+
+public class PkGeneratorFactoryProvider extends PerAdapterProvider<PkGenerator> {
+
+    public PkGeneratorFactoryProvider(
+            @Inject(Constants.SERVER_PK_GENERATORS_MAP) Map<String, PkGenerator> perAdapterValues,
+            @Inject PkGenerator defaultValue) {
+        super(perAdapterValues, defaultValue);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/284151ce/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 a864245..ac7753f 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
@@ -40,8 +40,8 @@ import org.apache.cayenne.access.types.BooleanType;
 import org.apache.cayenne.access.types.ByteArrayType;
 import org.apache.cayenne.access.types.ByteType;
 import org.apache.cayenne.access.types.CalendarType;
-import org.apache.cayenne.access.types.CharacterValueType;
 import org.apache.cayenne.access.types.CharType;
+import org.apache.cayenne.access.types.CharacterValueType;
 import org.apache.cayenne.access.types.DateType;
 import org.apache.cayenne.access.types.DefaultValueObjectTypeRegistry;
 import org.apache.cayenne.access.types.DoubleType;
@@ -58,6 +58,7 @@ import org.apache.cayenne.access.types.TimeType;
 import org.apache.cayenne.access.types.TimestampType;
 import org.apache.cayenne.access.types.UUIDValueType;
 import org.apache.cayenne.access.types.UtilDateType;
+import org.apache.cayenne.access.types.ValueObjectType;
 import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.access.types.VoidType;
 import org.apache.cayenne.ashwood.AshwoodEntitySorter;
@@ -82,6 +83,7 @@ import org.apache.cayenne.configuration.xml.NoopDataChannelMetaData;
 import org.apache.cayenne.configuration.xml.XMLDataChannelDescriptorLoader;
 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.DB2Sniffer;
 import org.apache.cayenne.dba.derby.DerbySniffer;
@@ -95,7 +97,7 @@ import org.apache.cayenne.dba.openbase.OpenBaseSniffer;
 import org.apache.cayenne.dba.oracle.OracleSniffer;
 import org.apache.cayenne.dba.postgres.PostgresSniffer;
 import org.apache.cayenne.dba.sqlite.SQLiteSniffer;
-import org.apache.cayenne.dba.sqlserver.SQLServerPkGenerator;
+import org.apache.cayenne.dba.sqlserver.SQLServerAdapter;
 import org.apache.cayenne.dba.sqlserver.SQLServerSniffer;
 import org.apache.cayenne.dba.sybase.SybasePkGenerator;
 import org.apache.cayenne.dba.sybase.SybaseSniffer;
@@ -109,13 +111,12 @@ import org.apache.cayenne.di.Module;
 import org.apache.cayenne.di.spi.DefaultAdhocObjectFactory;
 import org.apache.cayenne.di.spi.DefaultClassLoaderManager;
 import org.apache.cayenne.event.DefaultEventManager;
-import org.apache.cayenne.event.NoopEventBridgeProvider;
 import org.apache.cayenne.event.EventBridge;
 import org.apache.cayenne.event.EventManager;
-import org.apache.cayenne.log.Slf4jJdbcEventLogger;
+import org.apache.cayenne.event.NoopEventBridgeProvider;
 import org.apache.cayenne.log.JdbcEventLogger;
+import org.apache.cayenne.log.Slf4jJdbcEventLogger;
 import org.apache.cayenne.map.EntitySorter;
-import org.apache.cayenne.access.types.ValueObjectType;
 import org.apache.cayenne.resource.ClassLoaderResourceLocator;
 import org.apache.cayenne.resource.ResourceLocator;
 import org.apache.cayenne.template.CayenneSQLTemplateProcessor;
@@ -131,9 +132,6 @@ import org.xml.sax.XMLReader;
 import java.util.Calendar;
 import java.util.GregorianCalendar;
 
-import static org.apache.cayenne.dba.DbVersion.MS_SQL_2008;
-import static org.apache.cayenne.dba.DbVersion.MS_SQL_2012;
-
 /**
  * A DI module containing all Cayenne server runtime configuration.
  *
@@ -219,8 +217,8 @@ public class ServerModule implements Module {
      * @param binder DI binder passed to the module during injector startup.
      * @return MapBuilder for properties.
      */
-    public static MapBuilder<Class> contributePkGenerators(Binder binder) {
-        return binder.bindMap(Class.class, Constants.SERVER_PK_GENERATORS_MAP);
+    public static MapBuilder<PkGenerator> contributePkGenerators(Binder binder) {
+        return binder.bindMap(PkGenerator.class, Constants.SERVER_PK_GENERATORS_MAP);
     }
 
     /**
@@ -309,10 +307,25 @@ public class ServerModule implements Module {
                 .add(SQLServerSniffer.class).add(OracleSniffer.class).add(PostgresSniffer.class)
                 .add(MySQLSniffer.class);
 
-        contributePkGenerators(binder)
-                .put(String.valueOf(MS_SQL_2008), SybasePkGenerator.class) //adding a generator for MS SQL version 2012 and higher
-                .put(String.valueOf(MS_SQL_2012), SQLServerPkGenerator.class); //adding a generator since MS SQL version 2012
-
+        //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)
+                .put(DB2Adapter.class.getName(), DB2PkGenerator.class)
+                .put(DerbyAdapter.class.getName(), DerbyPkGenerator.class)
+                .put(FrontBaseAdapter.class.getName(), FrontBaseAdapter.class)
+                .put(H2Adapter.class.getName(), H2PkGenerator.class)
+                .put(IngresAdapter.class.getName(), IngresPkGenerator.class)
+                .put(MySQLAdapter.class.getName(), MySQLPkGenerator.class)
+                .put(OpenBaseAdapter.class.getName(), OpenBasePkGenerator.class)
+                .put(OracleAdapter.class.getName(), OraclePkGenerator.class)
+                .put(Oracle8Adapter.class.getName(), OraclePkGenerator.class)
+                .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);
 
@@ -393,10 +406,6 @@ public class ServerModule implements Module {
         // DbAdapters
         binder.bind(DbAdapterFactory.class).to(DefaultDbAdapterFactory.class);
 
-        //a default PkGeneratorFactory used to load custom and automatic
-        //PkGenerators
-        binder.bind(PkGeneratorFactory.class).to(DefaultPkGeneratorFactory.class);
-
         // binding AshwoodEntitySorter without scope, as this is a stateful
         // object and is
         // configured by the owning domain

http://git-wip-us.apache.org/repos/asf/cayenne/blob/284151ce/cayenne-server/src/main/java/org/apache/cayenne/dba/DbVersion.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/DbVersion.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/DbVersion.java
deleted file mode 100644
index 3caca4a..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/DbVersion.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package org.apache.cayenne.dba;
-
-import java.util.Objects;
-
-/**
- * Description of the database version and its type
- */
-public class DbVersion implements Comparable<DbVersion> {
-
-    /**
-     * DbVersion: majorVersion = 11, minorVersion = 0
-     */
-    public static final DbVersion MS_SQL_2012;
-
-    /**
-     * DbVersion: majorVersion = 10, minorVersion = 0
-     */
-    public static final DbVersion MS_SQL_2008;
-
-    static {
-        MS_SQL_2012 = new DbVersion(DbType.MS_SQL, 11, 0);
-        MS_SQL_2008 = new DbVersion(DbType.MS_SQL, 10, 0);
-    }
-
-    private final DbType dbType;
-    private final int majorVersion;
-    private final int minorVersion;
-
-    public DbVersion(DbType dbType, int majorVersion, int minorVersion) {
-        this.dbType = dbType;
-        this.majorVersion = majorVersion;
-        this.minorVersion = minorVersion;
-    }
-
-    public int getMajorVersion() {
-        return majorVersion;
-    }
-
-    public int getMinorVersion() {
-        return minorVersion;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == this) {
-            return true;
-        }
-        if (!(obj instanceof DbVersion)) {
-            return false;
-        }
-
-        DbVersion dbVersion = (DbVersion) obj;
-
-        return dbVersion.majorVersion == this.majorVersion &&
-                dbVersion.minorVersion == this.minorVersion;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(this.majorVersion, this.minorVersion);
-    }
-
-    @Override
-    public String toString() {
-        return "DbVersion: [dbType: '" + this.dbType.getType() +
-                "', majorVersion:'" + this.majorVersion +
-                "', minorVersion:'" + this.minorVersion + "']";
-    }
-
-    /**
-     * @return {@code true} if the types of equivalents,
-     * {@code false} otherwise
-     */
-    public boolean isTypeCheck(DbVersion o) {
-        return this.dbType == o.dbType;
-    }
-
-    /**
-     * Compare by version, type is not taken into account.
-     * Before calling this method, do a check:
-     * {@link DbVersion#isTypeCheck(org.apache.cayenne.dba.DbVersion)}
-     */
-    @Override
-    public int compareTo(DbVersion o) {
-        if (o.equals(this)) {
-            return 0;
-        }
-        if (this.majorVersion == o.minorVersion) {
-            return this.minorVersion - o.minorVersion;
-        }
-        return this.majorVersion - o.majorVersion;
-    }
-
-    public enum DbType {
-
-        MS_SQL("MICROSOFT SQL SERVER");
-
-        private final String type;
-
-        DbType(String type) {
-            this.type = type;
-        }
-
-        public String getType() {
-            return type;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/284151ce/cayenne-server/src/main/java/org/apache/cayenne/dba/JdbcPkGenerator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/JdbcPkGenerator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/JdbcPkGenerator.java
index 0d9e1fc..d427699 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/JdbcPkGenerator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/JdbcPkGenerator.java
@@ -51,321 +51,328 @@ import java.util.concurrent.ConcurrentMap;
  */
 public class JdbcPkGenerator implements PkGenerator {
 
-	public static final int DEFAULT_PK_CACHE_SIZE = 20;
-	static final long DEFAULT_PK_START_VALUE = 200;
-
-	protected JdbcAdapter adapter;
-	protected ConcurrentMap<String, Queue<Long>> pkCache = new ConcurrentHashMap<>();
-	protected int pkCacheSize = DEFAULT_PK_CACHE_SIZE;
-	protected long pkStartValue = DEFAULT_PK_START_VALUE;
-
-	public JdbcPkGenerator(JdbcAdapter adapter) {
-		this.adapter = adapter;
-	}
-
-	public JdbcAdapter getAdapter() {
-		return adapter;
-	}
-
-	public void createAutoPk(DataNode node, List<DbEntity> dbEntities) throws Exception {
-		// check if a table exists
-
-		// create AUTO_PK_SUPPORT table
-		if (!autoPkTableExists(node)) {
-			runUpdate(node, pkTableCreateString());
-		}
-
-		// delete any existing pk entries
-		if (!dbEntities.isEmpty()) {
-			runUpdate(node, pkDeleteString(dbEntities));
-		}
-
-		// insert all needed entries
-		for (DbEntity ent : dbEntities) {
-			runUpdate(node, pkCreateString(ent.getName()));
-		}
-	}
-
-	public List<String> createAutoPkStatements(List<DbEntity> dbEntities) {
-		List<String> list = new ArrayList<>(dbEntities.size() + 2);
-
-		list.add(pkTableCreateString());
-		list.add(pkDeleteString(dbEntities));
-
-		for (DbEntity ent : dbEntities) {
-			list.add(pkCreateString(ent.getName()));
-		}
-
-		return list;
-	}
-
-	/**
-	 * Drops table named "AUTO_PK_SUPPORT" if it exists in the database.
-	 */
-	public void dropAutoPk(DataNode node, List<DbEntity> dbEntities) throws Exception {
-		if (autoPkTableExists(node)) {
-			runUpdate(node, dropAutoPkString());
-		}
-	}
-
-	public List<String> dropAutoPkStatements(List<DbEntity> dbEntities) {
-		List<String> list = new ArrayList<>(1);
-		list.add(dropAutoPkString());
-		return list;
-	}
-
-	protected String pkTableCreateString() {
-		return "CREATE TABLE AUTO_PK_SUPPORT " +
-				"(TABLE_NAME CHAR(100) NOT NULL, NEXT_ID BIGINT NOT NULL, PRIMARY KEY(TABLE_NAME))";
-	}
-
-	protected String pkDeleteString(List<DbEntity> dbEntities) {
-		StringBuilder buf = new StringBuilder();
-		buf.append("DELETE FROM AUTO_PK_SUPPORT WHERE TABLE_NAME IN (");
-		int len = dbEntities.size();
-		for (int i = 0; i < len; i++) {
-			if (i > 0) {
-				buf.append(", ");
-			}
-			DbEntity ent = dbEntities.get(i);
-			buf.append('\'').append(ent.getName()).append('\'');
-		}
-		buf.append(')');
-		return buf.toString();
-	}
-
-	protected String pkCreateString(String entName) {
-		return "INSERT INTO AUTO_PK_SUPPORT (TABLE_NAME, NEXT_ID) VALUES ('" + entName + "', " + pkStartValue + ")";
-	}
-
-	protected String pkSelectString(String entName) {
-		return "SELECT NEXT_ID FROM AUTO_PK_SUPPORT WHERE TABLE_NAME = '" + entName + '\'';
-	}
-
-	protected String pkUpdateString(String entName) {
-		return "UPDATE AUTO_PK_SUPPORT SET NEXT_ID = NEXT_ID + " + pkCacheSize + " WHERE TABLE_NAME = '" + entName + '\'';
-	}
-
-	protected String dropAutoPkString() {
-		return "DROP TABLE AUTO_PK_SUPPORT";
-	}
-
-	/**
-	 * Checks if AUTO_PK_TABLE already exists in the database.
-	 */
-	protected boolean autoPkTableExists(DataNode node) throws SQLException {
-
-		try (Connection con = node.getDataSource().getConnection()) {
-			DatabaseMetaData md = con.getMetaData();
-			try (ResultSet tables = md.getTables(null, null, "AUTO_PK_SUPPORT", null)) {
-				return tables.next();
-			}
-		}
-	}
-
-	/**
-	 * Runs JDBC update over a Connection obtained from DataNode. Returns a
-	 * number of objects returned from update.
-	 * 
-	 * @throws SQLException
-	 *             in case of query failure.
-	 */
-	public int runUpdate(DataNode node, String sql) throws SQLException {
-		adapter.getJdbcEventLogger().log(sql);
-
-		try (Connection con = node.getDataSource().getConnection()) {
-			try (Statement upd = con.createStatement()) {
-				return upd.executeUpdate(sql);
-			}
-		}
-	}
-
-	/**
-	 * Generates a unique and non-repeating primary key for specified dbEntity.
-	 * <p>
-	 * This implementation is naive since it does not lock the database rows
-	 * when executing select and subsequent update. Adapter-specific
-	 * implementations are more robust.
-	 * </p>
-	 * 
-	 * @since 3.0
-	 */
-	public Object generatePk(DataNode node, DbAttribute pk) throws Exception {
-
-		DbEntity entity = pk.getEntity();
-
-		switch (pk.getType()) {
-		case Types.BINARY:
-		case Types.VARBINARY:
-			return IDUtil.pseudoUniqueSecureByteSequence(pk.getMaxLength());
-		}
-
-		DbKeyGenerator pkGenerator = entity.getPrimaryKeyGenerator();
-		long cacheSize;
-		if (pkGenerator != null && pkGenerator.getKeyCacheSize() != null) {
-			cacheSize = pkGenerator.getKeyCacheSize();
-		} else {
-			cacheSize = getPkCacheSize();
-		}
-
-		Long value;
-
-		// if no caching, always generate fresh
-		if (cacheSize <= 1) {
-			value = longPkFromDatabase(node, entity);
-		} else {
-			Queue<Long> pks = pkCache.get(entity.getName());
-
-			if (pks == null) {
-				// created exhausted LongPkRange
-				pks = new ConcurrentLinkedQueue<>();
-				Queue<Long> previousPks = pkCache.putIfAbsent(entity.getName(), pks);
-				if (previousPks != null) {
-					pks = previousPks;
-				}
-			}
-
-			value = pks.poll();
-			if (value == null) {
-				value = longPkFromDatabase(node, entity);
-				for (long i = value + 1; i < value + cacheSize; i++) {
-					pks.add(i);
-				}
-			}
-		}
-
-		if (pk.getType() == Types.BIGINT) {
-			return value;
-		} else {
-			// leaving it up to the user to ensure that PK does not exceed max int...
-			return value.intValue();
-		}
-	}
-
-	/**
-	 * Performs primary key generation ignoring cache. Generates a range of
-	 * primary keys as specified by "pkCacheSize" bean property.
-	 * <p>
-	 * This method is called internally from "generatePkForDbEntity" and then
-	 * generated range of key values is saved in cache for performance.
-	 * Subclasses that implement different primary key generation solutions
-	 * should override this method, not "generatePkForDbEntity".
-	 * </p>
-	 * 
-	 * @since 3.0
-	 */
-	protected long longPkFromDatabase(DataNode node, DbEntity entity) throws Exception {
-		String select = "SELECT #result('NEXT_ID' 'long' 'NEXT_ID') FROM AUTO_PK_SUPPORT "
-				+ "WHERE TABLE_NAME = '" + entity.getName() + '\'';
-
-		// run queries via DataNode to utilize its transactional behavior
-		List<Query> queries = new ArrayList<>(2);
-		queries.add(new SQLTemplate(entity, select));
-		queries.add(new SQLTemplate(entity, pkUpdateString(entity.getName())));
-
-		PkRetrieveProcessor observer = new PkRetrieveProcessor(entity.getName());
-		node.performQueries(queries, observer);
-		return observer.getId();
-	}
-
-	/**
-	 * Returns a size of the entity primary key cache. Default value is 20. If
-	 * cache size is set to a value less or equals than "one", no primary key
-	 * caching is done.
-	 */
-	public int getPkCacheSize() {
-		return pkCacheSize;
-	}
-
-	/**
-	 * Sets the size of the entity primary key cache. If
-	 * <code>pkCacheSize</code> parameter is less than 1, cache size is set to
-	 * "one".
-	 * <p>
-	 * <i>Note that our tests show that setting primary key cache value to
-	 * anything much bigger than 20 does not give any significant performance
-	 * increase. Therefore it does not make sense to use bigger values, since
-	 * this may potentially create big gaps in the database primary key
-	 * sequences in cases like application crashes or restarts. </i>
-	 * </p>
-	 */
-	public void setPkCacheSize(int pkCacheSize) {
-		this.pkCacheSize = (pkCacheSize < 1) ? 1 : pkCacheSize;
-	}
-
-	long getPkStartValue() {
-		return pkStartValue;
-	}
-
-	void setPkStartValue(long startValue) {
-		this.pkStartValue = startValue;
-	}
-
-	public void reset() {
-		pkCache.clear();
-	}
-
-	/**
-	 * OperationObserver for primary key retrieval.
-	 */
-	final class PkRetrieveProcessor implements OperationObserver {
-
-		Number id;
-		final String entityName;
-
-		PkRetrieveProcessor(String entityName) {
-			this.entityName = entityName;
-		}
-
-		public boolean isIteratedResult() {
-			return false;
-		}
-
-		public long getId() {
-			if (id == null) {
-				throw new CayenneRuntimeException("No key was retrieved for entity %s", entityName);
-			}
-
-			return id.longValue();
-		}
-
-		public void nextRows(Query query, List<?> dataRows) {
-			// process selected object, issue an update query
-			if (dataRows == null || dataRows.size() == 0) {
-				throw new CayenneRuntimeException("Error generating PK : entity not supported: %s", entityName);
-			}
-
-			if (dataRows.size() > 1) {
-				throw new CayenneRuntimeException("Error generating PK : too many rows for entity: %s", entityName);
-			}
-
-			DataRow lastPk = (DataRow) dataRows.get(0);
-			id = (Number) lastPk.get("NEXT_ID");
-		}
-
-		public void nextCount(Query query, int resultCount) {
-			if (resultCount != 1) {
-				throw new CayenneRuntimeException("Error generating PK for entity '%s': update count is wrong - %d"
-						, entityName, resultCount);
-			}
-		}
-
-		public void nextBatchCount(Query query, int[] resultCount) {
-		}
-
-		@Override
-		public void nextGeneratedRows(Query query, ResultIterator keys, ObjectId idToUpdate) {
-		}
-
-		public void nextRows(Query q, ResultIterator it) {
-		}
-
-		public void nextQueryException(Query query, Exception ex) {
-			throw new CayenneRuntimeException("Error generating PK for entity '" + entityName + "'.", ex);
-		}
-
-		public void nextGlobalException(Exception ex) {
-			throw new CayenneRuntimeException("Error generating PK for entity: " + entityName, ex);
-		}
-	}
+    public static final int DEFAULT_PK_CACHE_SIZE = 20;
+    static final long DEFAULT_PK_START_VALUE = 200;
+
+    protected JdbcAdapter adapter;
+    protected ConcurrentMap<String, Queue<Long>> pkCache = new ConcurrentHashMap<>();
+    protected int pkCacheSize = DEFAULT_PK_CACHE_SIZE;
+    protected long pkStartValue = DEFAULT_PK_START_VALUE;
+
+    public JdbcPkGenerator() {
+    }
+
+    public JdbcPkGenerator(JdbcAdapter adapter) {
+        this.adapter = adapter;
+    }
+
+    public JdbcAdapter getAdapter() {
+        return this.adapter;
+    }
+
+    public void createAutoPk(DataNode node, List<DbEntity> dbEntities) throws Exception {
+        // check if a table exists
+
+        // create AUTO_PK_SUPPORT table
+        if (!autoPkTableExists(node)) {
+            runUpdate(node, pkTableCreateString());
+        }
+
+        // delete any existing pk entries
+        if (!dbEntities.isEmpty()) {
+            runUpdate(node, pkDeleteString(dbEntities));
+        }
+
+        // insert all needed entries
+        for (DbEntity ent : dbEntities) {
+            runUpdate(node, pkCreateString(ent.getName()));
+        }
+    }
+
+    public List<String> createAutoPkStatements(List<DbEntity> dbEntities) {
+        List<String> list = new ArrayList<>(dbEntities.size() + 2);
+
+        list.add(pkTableCreateString());
+        list.add(pkDeleteString(dbEntities));
+
+        for (DbEntity ent : dbEntities) {
+            list.add(pkCreateString(ent.getName()));
+        }
+
+        return list;
+    }
+
+    /**
+     * Drops table named "AUTO_PK_SUPPORT" if it exists in the database.
+     */
+    public void dropAutoPk(DataNode node, List<DbEntity> dbEntities) throws Exception {
+        if (autoPkTableExists(node)) {
+            runUpdate(node, dropAutoPkString());
+        }
+    }
+
+    public List<String> dropAutoPkStatements(List<DbEntity> dbEntities) {
+        List<String> list = new ArrayList<>(1);
+        list.add(dropAutoPkString());
+        return list;
+    }
+
+    protected String pkTableCreateString() {
+        return "CREATE TABLE AUTO_PK_SUPPORT " +
+                "(TABLE_NAME CHAR(100) NOT NULL, NEXT_ID BIGINT NOT NULL, PRIMARY KEY(TABLE_NAME))";
+    }
+
+    protected String pkDeleteString(List<DbEntity> dbEntities) {
+        StringBuilder buf = new StringBuilder();
+        buf.append("DELETE FROM AUTO_PK_SUPPORT WHERE TABLE_NAME IN (");
+        int len = dbEntities.size();
+        for (int i = 0; i < len; i++) {
+            if (i > 0) {
+                buf.append(", ");
+            }
+            DbEntity ent = dbEntities.get(i);
+            buf.append('\'').append(ent.getName()).append('\'');
+        }
+        buf.append(')');
+        return buf.toString();
+    }
+
+    protected String pkCreateString(String entName) {
+        return "INSERT INTO AUTO_PK_SUPPORT (TABLE_NAME, NEXT_ID) VALUES ('" + entName + "', " + pkStartValue + ")";
+    }
+
+    protected String pkSelectString(String entName) {
+        return "SELECT NEXT_ID FROM AUTO_PK_SUPPORT WHERE TABLE_NAME = '" + entName + '\'';
+    }
+
+    protected String pkUpdateString(String entName) {
+        return "UPDATE AUTO_PK_SUPPORT SET NEXT_ID = NEXT_ID + " + pkCacheSize + " WHERE TABLE_NAME = '" + entName + '\'';
+    }
+
+    protected String dropAutoPkString() {
+        return "DROP TABLE AUTO_PK_SUPPORT";
+    }
+
+    /**
+     * Checks if AUTO_PK_TABLE already exists in the database.
+     */
+    protected boolean autoPkTableExists(DataNode node) throws SQLException {
+
+        try (Connection con = node.getDataSource().getConnection()) {
+            DatabaseMetaData md = con.getMetaData();
+            try (ResultSet tables = md.getTables(null, null, "AUTO_PK_SUPPORT", null)) {
+                return tables.next();
+            }
+        }
+    }
+
+    /**
+     * Runs JDBC update over a Connection obtained from DataNode. Returns a
+     * number of objects returned from update.
+     *
+     * @throws SQLException in case of query failure.
+     */
+    public int runUpdate(DataNode node, String sql) throws SQLException {
+        adapter.getJdbcEventLogger().log(sql);
+
+        try (Connection con = node.getDataSource().getConnection()) {
+            try (Statement upd = con.createStatement()) {
+                return upd.executeUpdate(sql);
+            }
+        }
+    }
+
+    /**
+     * Generates a unique and non-repeating primary key for specified dbEntity.
+     * <p>
+     * This implementation is naive since it does not lock the database rows
+     * when executing select and subsequent update. Adapter-specific
+     * implementations are more robust.
+     * </p>
+     *
+     * @since 3.0
+     */
+    public Object generatePk(DataNode node, DbAttribute pk) throws Exception {
+
+        DbEntity entity = pk.getEntity();
+
+        switch (pk.getType()) {
+            case Types.BINARY:
+            case Types.VARBINARY:
+                return IDUtil.pseudoUniqueSecureByteSequence(pk.getMaxLength());
+        }
+
+        DbKeyGenerator pkGenerator = entity.getPrimaryKeyGenerator();
+        long cacheSize;
+        if (pkGenerator != null && pkGenerator.getKeyCacheSize() != null) {
+            cacheSize = pkGenerator.getKeyCacheSize();
+        } else {
+            cacheSize = getPkCacheSize();
+        }
+
+        Long value;
+
+        // if no caching, always generate fresh
+        if (cacheSize <= 1) {
+            value = longPkFromDatabase(node, entity);
+        } else {
+            Queue<Long> pks = pkCache.get(entity.getName());
+
+            if (pks == null) {
+                // created exhausted LongPkRange
+                pks = new ConcurrentLinkedQueue<>();
+                Queue<Long> previousPks = pkCache.putIfAbsent(entity.getName(), pks);
+                if (previousPks != null) {
+                    pks = previousPks;
+                }
+            }
+
+            value = pks.poll();
+            if (value == null) {
+                value = longPkFromDatabase(node, entity);
+                for (long i = value + 1; i < value + cacheSize; i++) {
+                    pks.add(i);
+                }
+            }
+        }
+
+        if (pk.getType() == Types.BIGINT) {
+            return value;
+        } else {
+            // leaving it up to the user to ensure that PK does not exceed max int...
+            return value.intValue();
+        }
+    }
+
+    @Override
+    public void setAdapter(DbAdapter adapter) {
+        this.adapter = (JdbcAdapter) adapter;
+    }
+
+    /**
+     * Performs primary key generation ignoring cache. Generates a range of
+     * primary keys as specified by "pkCacheSize" bean property.
+     * <p>
+     * This method is called internally from "generatePkForDbEntity" and then
+     * generated range of key values is saved in cache for performance.
+     * Subclasses that implement different primary key generation solutions
+     * should override this method, not "generatePkForDbEntity".
+     * </p>
+     *
+     * @since 3.0
+     */
+    protected long longPkFromDatabase(DataNode node, DbEntity entity) throws Exception {
+        String select = "SELECT #result('NEXT_ID' 'long' 'NEXT_ID') FROM AUTO_PK_SUPPORT "
+                + "WHERE TABLE_NAME = '" + entity.getName() + '\'';
+
+        // run queries via DataNode to utilize its transactional behavior
+        List<Query> queries = new ArrayList<>(2);
+        queries.add(new SQLTemplate(entity, select));
+        queries.add(new SQLTemplate(entity, pkUpdateString(entity.getName())));
+
+        PkRetrieveProcessor observer = new PkRetrieveProcessor(entity.getName());
+        node.performQueries(queries, observer);
+        return observer.getId();
+    }
+
+    /**
+     * Returns a size of the entity primary key cache. Default value is 20. If
+     * cache size is set to a value less or equals than "one", no primary key
+     * caching is done.
+     */
+    public int getPkCacheSize() {
+        return pkCacheSize;
+    }
+
+    /**
+     * Sets the size of the entity primary key cache. If
+     * <code>pkCacheSize</code> parameter is less than 1, cache size is set to
+     * "one".
+     * <p>
+     * <i>Note that our tests show that setting primary key cache value to
+     * anything much bigger than 20 does not give any significant performance
+     * increase. Therefore it does not make sense to use bigger values, since
+     * this may potentially create big gaps in the database primary key
+     * sequences in cases like application crashes or restarts. </i>
+     * </p>
+     */
+    public void setPkCacheSize(int pkCacheSize) {
+        this.pkCacheSize = (pkCacheSize < 1) ? 1 : pkCacheSize;
+    }
+
+    long getPkStartValue() {
+        return pkStartValue;
+    }
+
+    void setPkStartValue(long startValue) {
+        this.pkStartValue = startValue;
+    }
+
+    public void reset() {
+        pkCache.clear();
+    }
+
+    /**
+     * OperationObserver for primary key retrieval.
+     */
+    final class PkRetrieveProcessor implements OperationObserver {
+
+        Number id;
+        final String entityName;
+
+        PkRetrieveProcessor(String entityName) {
+            this.entityName = entityName;
+        }
+
+        public boolean isIteratedResult() {
+            return false;
+        }
+
+        public long getId() {
+            if (id == null) {
+                throw new CayenneRuntimeException("No key was retrieved for entity %s", entityName);
+            }
+
+            return id.longValue();
+        }
+
+        public void nextRows(Query query, List<?> dataRows) {
+            // process selected object, issue an update query
+            if (dataRows == null || dataRows.size() == 0) {
+                throw new CayenneRuntimeException("Error generating PK : entity not supported: %s", entityName);
+            }
+
+            if (dataRows.size() > 1) {
+                throw new CayenneRuntimeException("Error generating PK : too many rows for entity: %s", entityName);
+            }
+
+            DataRow lastPk = (DataRow) dataRows.get(0);
+            id = (Number) lastPk.get("NEXT_ID");
+        }
+
+        public void nextCount(Query query, int resultCount) {
+            if (resultCount != 1) {
+                throw new CayenneRuntimeException("Error generating PK for entity '%s': update count is wrong - %d"
+                        , entityName, resultCount);
+            }
+        }
+
+        public void nextBatchCount(Query query, int[] resultCount) {
+        }
+
+        @Override
+        public void nextGeneratedRows(Query query, ResultIterator keys, ObjectId idToUpdate) {
+        }
+
+        public void nextRows(Query q, ResultIterator it) {
+        }
+
+        public void nextQueryException(Query query, Exception ex) {
+            throw new CayenneRuntimeException("Error generating PK for entity '" + entityName + "'.", ex);
+        }
+
+        public void nextGlobalException(Exception ex) {
+            throw new CayenneRuntimeException("Error generating PK for entity: " + entityName, ex);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/284151ce/cayenne-server/src/main/java/org/apache/cayenne/dba/PkGenerator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/PkGenerator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/PkGenerator.java
index d223e72..97d8f01 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/PkGenerator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/PkGenerator.java
@@ -30,12 +30,13 @@ import org.apache.cayenne.map.DbEntity;
  */
 public interface PkGenerator {
 
+
     /**
      * Generates necessary database objects to provide automatic primary key support.
-     * 
-     * @param node node that provides access to a DataSource.
+     *
+     * @param node       node that provides access to a DataSource.
      * @param dbEntities a list of entities that require primary key auto-generation
-     *            support
+     *                   support
      */
     void createAutoPk(DataNode node, List<DbEntity> dbEntities) throws Exception;
 
@@ -49,10 +50,10 @@ public interface PkGenerator {
     /**
      * Drops any common database objects associated with automatic primary key generation
      * process. This may be lookup tables, special stored procedures or sequences.
-     * 
-     * @param node node that provides access to a DataSource.
+     *
+     * @param node       node that provides access to a DataSource.
      * @param dbEntities a list of entities whose primary key auto-generation support
-     *            should be dropped.
+     *                   should be dropped.
      */
     void dropAutoPk(DataNode node, List<DbEntity> dbEntities) throws Exception;
 
@@ -64,12 +65,22 @@ public interface PkGenerator {
 
     /**
      * Generates a unique and non-repeating primary key for specified PK attribute.
-     * 
+     *
      * @since 3.0
      */
     Object generatePk(DataNode dataNode, DbAttribute pk) throws Exception;
 
     /**
+     * Install the adapter associated with current PkGenerator
+     */
+    void setAdapter(DbAdapter q);
+
+    /**
+     * Get an adapter associated with current PkGenerator
+     */
+    DbAdapter getAdapter();
+
+    /**
      * Resets any cached primary keys forcing generator to go to the database next time id
      * generation is requested. May not be applicable for all generator implementations.
      */

http://git-wip-us.apache.org/repos/asf/cayenne/blob/284151ce/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerSniffer.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerSniffer.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerSniffer.java
index aef4e9c..301ef98 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerSniffer.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerSniffer.java
@@ -20,17 +20,15 @@
 package org.apache.cayenne.dba.sqlserver;
 
 import org.apache.cayenne.configuration.server.DbAdapterDetector;
-import org.apache.cayenne.configuration.server.PkGeneratorFactory;
+import org.apache.cayenne.configuration.server.PkGeneratorFactoryProvider;
 import org.apache.cayenne.dba.DbAdapter;
-import org.apache.cayenne.dba.DbVersion;
 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 static org.apache.cayenne.dba.DbVersion.DbType.MS_SQL;
+import java.util.Objects;
 
 /**
  * Detects SQLServer database from JDBC metadata.
@@ -41,22 +39,16 @@ public class SQLServerSniffer implements DbAdapterDetector {
 
     protected AdhocObjectFactory objectFactory;
 
-    protected PkGeneratorFactory pkGeneratorFactory;
-
-    private final DbVersion.DbType dbType;
+    protected PkGeneratorFactoryProvider pkGeneratorProvider;
 
-    public SQLServerSniffer(@Inject AdhocObjectFactory objectFactory, @Inject PkGeneratorFactory pkGeneratorFactory) {
+    public SQLServerSniffer(@Inject AdhocObjectFactory objectFactory,
+                            @Inject PkGeneratorFactoryProvider pkGeneratorProvider) {
         this.objectFactory = objectFactory;
-        this.pkGeneratorFactory = pkGeneratorFactory;
-        this.dbType = MS_SQL;
+        this.pkGeneratorProvider = Objects.requireNonNull(pkGeneratorProvider, () -> "Null pkGeneratorProvider");
     }
 
     @Override
     public DbAdapter createAdapter(DatabaseMetaData md) throws SQLException {
-        String dbName = md.getDatabaseProductName();
-        if (dbName == null || !dbName.toUpperCase().contains(dbType.getType())) {
-            return null;
-        }
 
         SQLServerAdapter adapter = objectFactory.newInstance(
                 SQLServerAdapter.class,
@@ -70,7 +62,8 @@ public class SQLServerSniffer implements DbAdapterDetector {
         try {
             generatedKeys = md.supportsGetGeneratedKeys();
             if (generatedKeys) {
-                pkGenerator = pkGeneratorFactory.detectPkGenerator(dbType, adapter, md);
+                pkGenerator = pkGeneratorProvider.get(adapter);
+
             }
         } catch (Throwable th) {
             // catch exceptions resulting from incomplete JDBC3 implementation

http://git-wip-us.apache.org/repos/asf/cayenne/blob/284151ce/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybasePkGenerator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybasePkGenerator.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybasePkGenerator.java
index c815473..a309f23 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybasePkGenerator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybasePkGenerator.java
@@ -40,6 +40,10 @@ import java.util.List;
  */
 public class SybasePkGenerator extends JdbcPkGenerator {
 
+	public SybasePkGenerator(){
+		super();
+	}
+
 	protected SybasePkGenerator(JdbcAdapter adapter) {
 		super(adapter);
 	}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/284151ce/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/DataDomainProviderTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/DataDomainProviderTest.java b/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/DataDomainProviderTest.java
index beba838..44ab64a 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/DataDomainProviderTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/DataDomainProviderTest.java
@@ -53,6 +53,8 @@ import org.apache.cayenne.configuration.DefaultDataChannelDescriptorMerger;
 import org.apache.cayenne.configuration.DefaultRuntimeProperties;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.configuration.mock.MockDataSourceFactory;
+import org.apache.cayenne.dba.JdbcPkGenerator;
+import org.apache.cayenne.dba.PkGenerator;
 import org.apache.cayenne.dba.db2.DB2Sniffer;
 import org.apache.cayenne.dba.derby.DerbySniffer;
 import org.apache.cayenne.dba.firebird.FirebirdSniffer;
@@ -66,7 +68,7 @@ import org.apache.cayenne.dba.oracle.OracleAdapter;
 import org.apache.cayenne.dba.oracle.OracleSniffer;
 import org.apache.cayenne.dba.postgres.PostgresSniffer;
 import org.apache.cayenne.dba.sqlite.SQLiteSniffer;
-import org.apache.cayenne.dba.sqlserver.SQLServerPkGenerator;
+import org.apache.cayenne.dba.sqlserver.SQLServerAdapter;
 import org.apache.cayenne.dba.sqlserver.SQLServerSniffer;
 import org.apache.cayenne.dba.sybase.SybasePkGenerator;
 import org.apache.cayenne.dba.sybase.SybaseSniffer;
@@ -97,8 +99,6 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import static org.apache.cayenne.dba.DbVersion.MS_SQL_2008;
-import static org.apache.cayenne.dba.DbVersion.MS_SQL_2012;
 import static org.junit.Assert.*;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -161,9 +161,9 @@ public class DataDomainProviderTest {
             ServerModule.contributeDomainListeners(binder).add(mockListener);
             ServerModule.contributeProjectLocations(binder).add(testConfigName);
 
-            ServerModule.contributePkGenerators(binder)
-                    .put(String.valueOf(MS_SQL_2008), SybasePkGenerator.class)
-                    .put(String.valueOf(MS_SQL_2012), SQLServerPkGenerator.class);
+            binder.bind(PkGenerator.class).to(JdbcPkGenerator.class);
+            binder.bind(PkGeneratorFactoryProvider.class).to(PkGeneratorFactoryProvider.class);
+            ServerModule.contributePkGenerators(binder).put(SQLServerAdapter.class.getName(), SybasePkGenerator.class);
 
             // configure extended types
             ServerModule.contributeDefaultTypes(binder);
@@ -194,7 +194,6 @@ public class DataDomainProviderTest {
             binder.bind(DataChannelDescriptorMerger.class).to(DefaultDataChannelDescriptorMerger.class);
             binder.bind(DataChannelDescriptorLoader.class).toInstance(testLoader);
             binder.bind(DbAdapterFactory.class).to(DefaultDbAdapterFactory.class);
-            binder.bind(PkGeneratorFactory.class).to(DefaultPkGeneratorFactory.class);
             binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
             binder.bind(BatchTranslatorFactory.class).to(DefaultBatchTranslatorFactory.class);
             binder.bind(SelectTranslatorFactory.class).to(DefaultSelectTranslatorFactory.class);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/284151ce/cayenne-server/src/test/java/org/apache/cayenne/dba/sqlserver/SQLServerSnifferIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/dba/sqlserver/SQLServerSnifferIT.java b/cayenne-server/src/test/java/org/apache/cayenne/dba/sqlserver/SQLServerSnifferIT.java
index ab19411..23946ab 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/dba/sqlserver/SQLServerSnifferIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/dba/sqlserver/SQLServerSnifferIT.java
@@ -19,12 +19,7 @@
 
 package org.apache.cayenne.dba.sqlserver;
 
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import java.sql.Connection;
-
-import org.apache.cayenne.configuration.server.PkGeneratorFactory;
+import org.apache.cayenne.configuration.server.PkGeneratorFactoryProvider;
 import org.apache.cayenne.dba.DbAdapter;
 import org.apache.cayenne.di.AdhocObjectFactory;
 import org.apache.cayenne.di.Inject;
@@ -36,6 +31,11 @@ import org.apache.cayenne.unit.di.server.ServerCaseDataSourceFactory;
 import org.apache.cayenne.unit.di.server.UseServerRuntime;
 import org.junit.Test;
 
+import java.sql.Connection;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
 @UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
 public class SQLServerSnifferIT extends ServerCase {
 
@@ -49,12 +49,12 @@ public class SQLServerSnifferIT extends ServerCase {
 	private AdhocObjectFactory objectFactory;
 
 	@Inject
-	private PkGeneratorFactory pkGeneratorFactory;
+	private PkGeneratorFactoryProvider pkGeneratorProvider;
 
 	@Test
 	public void testCreateAdapter() throws Exception {
 
-		SQLServerSniffer sniffer = new SQLServerSniffer(objectFactory, pkGeneratorFactory);
+		SQLServerSniffer sniffer = new SQLServerSniffer(objectFactory, pkGeneratorProvider);
 
 		DbAdapter adapter = null;
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/284151ce/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseModule.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseModule.java b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseModule.java
index 75f4905..4b7701c 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseModule.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseModule.java
@@ -56,7 +56,10 @@ import org.apache.cayenne.configuration.DefaultObjectStoreFactory;
 import org.apache.cayenne.configuration.DefaultRuntimeProperties;
 import org.apache.cayenne.configuration.ObjectStoreFactory;
 import org.apache.cayenne.configuration.RuntimeProperties;
-import org.apache.cayenne.configuration.server.*;
+import org.apache.cayenne.configuration.server.DataSourceFactory;
+import org.apache.cayenne.configuration.server.PkGeneratorFactoryProvider;
+import org.apache.cayenne.configuration.server.ServerModule;
+import org.apache.cayenne.configuration.server.ServerRuntime;
 import org.apache.cayenne.configuration.xml.DataChannelMetaData;
 import org.apache.cayenne.configuration.xml.DefaultHandlerFactory;
 import org.apache.cayenne.configuration.xml.HandlerFactory;
@@ -66,6 +69,8 @@ import org.apache.cayenne.configuration.xml.XMLReaderProvider;
 import org.apache.cayenne.conn.DataSourceInfo;
 import org.apache.cayenne.dba.DbAdapter;
 import org.apache.cayenne.dba.JdbcAdapter;
+import org.apache.cayenne.dba.JdbcPkGenerator;
+import org.apache.cayenne.dba.PkGenerator;
 import org.apache.cayenne.dba.db2.DB2Adapter;
 import org.apache.cayenne.dba.derby.DerbyAdapter;
 import org.apache.cayenne.dba.firebird.FirebirdAdapter;
@@ -80,7 +85,6 @@ import org.apache.cayenne.dba.oracle.OracleAdapter;
 import org.apache.cayenne.dba.postgres.PostgresAdapter;
 import org.apache.cayenne.dba.sqlite.SQLiteAdapter;
 import org.apache.cayenne.dba.sqlserver.SQLServerAdapter;
-import org.apache.cayenne.dba.sqlserver.SQLServerPkGenerator;
 import org.apache.cayenne.dba.sybase.SybaseAdapter;
 import org.apache.cayenne.dba.sybase.SybasePkGenerator;
 import org.apache.cayenne.di.AdhocObjectFactory;
@@ -91,8 +95,8 @@ import org.apache.cayenne.di.Module;
 import org.apache.cayenne.di.spi.DefaultAdhocObjectFactory;
 import org.apache.cayenne.di.spi.DefaultClassLoaderManager;
 import org.apache.cayenne.di.spi.DefaultScope;
-import org.apache.cayenne.log.Slf4jJdbcEventLogger;
 import org.apache.cayenne.log.JdbcEventLogger;
+import org.apache.cayenne.log.Slf4jJdbcEventLogger;
 import org.apache.cayenne.map.EntityResolver;
 import org.apache.cayenne.resource.ClassLoaderResourceLocator;
 import org.apache.cayenne.resource.ResourceLocator;
@@ -120,9 +124,6 @@ import org.xml.sax.XMLReader;
 import java.util.Calendar;
 import java.util.GregorianCalendar;
 
-import static org.apache.cayenne.dba.DbVersion.MS_SQL_2008;
-import static org.apache.cayenne.dba.DbVersion.MS_SQL_2012;
-
 public class ServerCaseModule implements Module {
 
     protected DefaultScope testScope;
@@ -160,9 +161,9 @@ public class ServerCaseModule implements Module {
                 // Should remove problems with random-failing tests (those that are GC-sensitive).
                 .put(Constants.SERVER_OBJECT_RETAIN_STRATEGY_PROPERTY, "soft");
 
-        ServerModule.contributePkGenerators(binder)
-                .put(String.valueOf(MS_SQL_2008), SybasePkGenerator.class)
-                .put(String.valueOf(MS_SQL_2012), SQLServerPkGenerator.class);
+        binder.bind(PkGeneratorFactoryProvider.class).to(PkGeneratorFactoryProvider.class);
+        binder.bind(PkGenerator.class).to(JdbcPkGenerator.class);
+        ServerModule.contributePkGenerators(binder).put(SQLServerAdapter.class.getName(), SybasePkGenerator.class);
 
         // configure extended types
         ServerModule.contributeDefaultTypes(binder)
@@ -207,7 +208,6 @@ public class ServerCaseModule implements Module {
         binder.bind(DbAdapter.class).toProvider(ServerCaseDbAdapterProvider.class);
         binder.bind(JdbcAdapter.class).toProvider(ServerCaseDbAdapterProvider.class);
         binder.bind(UnitDbAdapter.class).toProvider(UnitDbAdapterProvider.class);
-        binder.bind(PkGeneratorFactory.class).to(DefaultPkGeneratorFactory.class);
 
         // this factory is a hack that allows to inject to DbAdapters loaded outside of
         // server runtime... BatchQueryBuilderFactory is hardcoded and whatever is placed