Bug 426852 - @GeneratedValue(strategy=GenerationType.IDENTITY) support in Oracle 12c
Signed-off-by: Tomas Kraus <tomas.kraus@oracle.com>
Reviewed-by: Petros Splinakis <petros.splinakis@oracle.com>
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/databaseaccess/DatasourcePlatform.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/databaseaccess/DatasourcePlatform.java
index 5914ec1..607a526 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/databaseaccess/DatasourcePlatform.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/databaseaccess/DatasourcePlatform.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2016 Oracle and/or its affiliates. All rights reserved.
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
  * which accompanies this distribution.
@@ -9,6 +9,8 @@
  *
  * Contributors:
  *     Oracle - initial API and implementation from Oracle TopLink
+ *     09/29/2016-2.7 Tomas Kraus
+ *       - 426852: @GeneratedValue(strategy=GenerationType.IDENTITY) support in Oracle 12c
  ******************************************************************************/
 package org.eclipse.persistence.internal.databaseaccess;
 
@@ -18,6 +20,7 @@
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Set;
 import java.util.Vector;
 
 import org.eclipse.persistence.descriptors.DescriptorQueryManager;
@@ -37,6 +40,7 @@
 import org.eclipse.persistence.sequencing.DefaultSequence;
 import org.eclipse.persistence.sequencing.QuerySequence;
 import org.eclipse.persistence.sequencing.Sequence;
+import org.eclipse.persistence.sessions.Session;
 
 /**
  * DatasourcePlatform is private to TopLink. It encapsulates behavior specific to a datasource platform
@@ -71,7 +75,7 @@ public class DatasourcePlatform implements Platform {
     protected Sequence defaultSequence;
 
     /** Store map of sequence names to sequences */
-    protected Map sequences;
+    protected Map<String, Sequence> sequences;
 
     /** Delimiter to use for fields and tables using spaces or other special values */
     protected String startDelimiter = null;
@@ -551,6 +555,11 @@ public boolean isOracle9() {
         return false;
     }
 
+    @Override
+    public boolean isOracle12() {
+        return false;
+    }
+
     public boolean isPervasive(){
         return false;
     }
@@ -817,7 +826,7 @@ public void removeAllSequences() {
      * Returns a map of sequence names to Sequences (may be null).
      */
     @Override
-    public Map getSequences() {
+    public Map<String, Sequence> getSequences() {
         return this.sequences;
     }
 
@@ -826,7 +835,7 @@ public Map getSequences() {
      * Used only for writing into XML or Java.
      */
     @Override
-    public Map getSequencesToWrite() {
+    public Map<String, Sequence> getSequencesToWrite() {
         if ((getSequences() == null) || getSequences().isEmpty()) {
             return null;
         }
@@ -1007,4 +1016,29 @@ public ValueReadQuery buildSelectQueryForIdentity(String seqName, Integer size)
     public DatasourceCall buildNativeCall(String queryString) {
         return new SQLCall(queryString);
     }
+
+    /**
+     * INTERNAL:
+     * Initialize platform specific identity sequences.
+     * @param session Active database session (in connected state).
+     * @param defaultIdentityGenerator Default identity generator sequence name.
+     * @since 2.7
+     */
+    @Override
+    public void initIdentitySequences(final Session session, final String defaultIdentityGenerator) {
+    }
+
+    /**
+     * INTERNAL:
+     * Remove platform specific identity sequences for specified tables. Default identity sequences are restored.
+     * @param dbSession Active database session (in connected state).
+     * @param defaultIdentityGenerator Default identity generator sequence name.
+     * @param tableNames Set of table names to check for identity sequence removal.
+     * @since 2.7
+     */
+    @Override
+    public void removeIdentitySequences(
+            final Session session, final String defaultIdentityGenerator, final Set<String> tableNames) {
+    }
+
 }
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/databaseaccess/Platform.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/databaseaccess/Platform.java
index d40335f..a47b9a0 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/databaseaccess/Platform.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/databaseaccess/Platform.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2016 Oracle and/or its affiliates. All rights reserved.
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
  * which accompanies this distribution.
@@ -12,14 +12,20 @@
  ******************************************************************************/
 package org.eclipse.persistence.internal.databaseaccess;
 
-import java.io.*;
+import java.io.Serializable;
+import java.io.Writer;
 import java.util.Map;
-import org.eclipse.persistence.exceptions.*;
-import org.eclipse.persistence.queries.*;
+import java.util.Set;
+
+import org.eclipse.persistence.exceptions.ConversionException;
 import org.eclipse.persistence.internal.core.databaseaccess.CorePlatform;
-import org.eclipse.persistence.internal.helper.*;
+import org.eclipse.persistence.internal.helper.ConversionManager;
+import org.eclipse.persistence.internal.helper.DatabaseField;
 import org.eclipse.persistence.internal.sessions.AbstractSession;
+import org.eclipse.persistence.queries.Call;
+import org.eclipse.persistence.queries.ValueReadQuery;
 import org.eclipse.persistence.sequencing.Sequence;
+import org.eclipse.persistence.sessions.Session;
 
 /**
  * Platform is private to TopLink. It encapsulates behavior specific to a datasource platform
@@ -108,6 +114,8 @@ public interface Platform extends CorePlatform<ConversionManager>, Serializable,
 
     public boolean isOracle9();
 
+    public boolean isOracle12();
+
     public boolean isPointBase();
 
     public boolean isSQLAnywhere();
@@ -259,4 +267,28 @@ public interface Platform extends CorePlatform<ConversionManager>, Serializable,
      * Indicates whether defaultSequence is the same as platform default sequence.
      */
     public boolean usesPlatformDefaultSequence();
+
+    /**
+     * INTERNAL:
+     * Initialize platform specific identity sequences.
+     * This method is called from {@code EntityManagerSetupImpl} after login and optional schema generation.
+     * Method is also called from {@code TableCreator} class during tables creation and update..
+     * @param session Active database session (in connected state).
+     * @param defaultIdentityGenerator Default identity generator sequence name.
+     * @since 2.7
+     */
+    public void initIdentitySequences(final Session session, final String defaultIdentityGenerator);
+
+    /**
+     * INTERNAL:
+     * Remove platform specific identity sequences for specified tables. Default identity sequences are restored.
+     * Method is also called from {@code TableCreator} class during tables removal.
+     * @param dbSession Active database session (in connected state).
+     * @param defaultIdentityGenerator Default identity generator sequence name.
+     * @param tableNames Set of table names to check for identity sequence removal.
+     * @since 2.7
+     */
+    public void removeIdentitySequences(
+            final Session session, final String defaultIdentityGenerator, final Set<String> tableNames);
+
 }
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/localization/i18n/LoggingLocalizationResource.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/localization/i18n/LoggingLocalizationResource.java
index cb51f71..d85edf8 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/localization/i18n/LoggingLocalizationResource.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/localization/i18n/LoggingLocalizationResource.java
@@ -740,6 +740,8 @@ public class LoggingLocalizationResource extends ListResourceBundle {
         { "dbPlatformHelper_detectedVendorPlatform", "Detected database platform: {0}"},
         { "dbPlatformHelper_regExprDbPlatform", "Database platform: {1}, regular expression: {0}"},
         { "dbPlatformHelper_patternSyntaxException", "Exception while using regular expression: {0}" },
+        { "platform_ora_init_id_seq", "Init Oracle identity sequence {0} -> {1} for table {2}"},
+        { "platform_ora_remove_id_seq", "Remove Oracle identity sequence {0} -> {1} for table {2}"},
         { "unknown_query_hint", "query {0}: unknown query hint {1} will be ignored"},
         { "query_hint", "query {0}: query hint {1}; value {2}"},
         { "property_value_specified", "property={0}; value={1}"},
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/tools/schemaframework/TableCreator.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/tools/schemaframework/TableCreator.java
index b459239..1ebc535 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/tools/schemaframework/TableCreator.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/tools/schemaframework/TableCreator.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2016 Oracle and/or its affiliates. All rights reserved.
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
  * which accompanies this distribution.
@@ -17,8 +17,10 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.Vector;
 
 import org.eclipse.persistence.exceptions.DatabaseException;
@@ -41,6 +43,10 @@
  * @author Peter Krogh
  */
 public class TableCreator {
+    /** Default identity generator sequence name.
+     *  Copy of value from JPA: {@code MetadataProject.DEFAULT_IDENTITY_GENERATOR}. */
+    public static final String DEFAULT_IDENTITY_GENERATOR = "SEQ_GEN_IDENTITY";
+
     /** Flag to disable table existence check before create. */
     public static boolean CHECK_EXISTENCE = true;
 
@@ -151,12 +157,19 @@ public void createTables(DatabaseSession session, SchemaManager schemaManager, b
     /**
      * This creates the tables on the database.
      * If the table already exists this will fail.
+     * @param session Active database session.
+     * @param schemaManager Database schema manipulation manager.
+     * @param build Whether to build constraints.
+     * @param check Whether to check for tables existence.
+     * @param createSequenceTables Whether to create sequence tables.
+     * @param createSequences Whether to create sequences.
      */
-    public void createTables(DatabaseSession session, SchemaManager schemaManager, boolean build, boolean check, boolean createSequenceTables, boolean createSequences) {
+    public void createTables(final DatabaseSession session, final SchemaManager schemaManager, final boolean build,
+            final boolean check, final boolean createSequenceTables, final boolean createSequences) {
         buildConstraints(schemaManager, build);
 
-        String sequenceTableName = getSequenceTableName(session);
-        List<TableDefinition> missingTables = new ArrayList<TableDefinition>();
+        final String sequenceTableName = getSequenceTableName(session);
+        final List<TableDefinition> missingTables = new ArrayList<TableDefinition>();
         for (TableDefinition table : getTableDefinitions()) {
             // Must not create sequence table as done in createSequences.
             if (!table.getName().equals(sequenceTableName)) {
@@ -183,6 +196,7 @@ public void createTables(DatabaseSession session, SchemaManager schemaManager, b
         createConstraints(missingTables, session, schemaManager, false);
 
         schemaManager.createOrReplaceSequences(createSequenceTables, createSequences);
+        session.getDatasourcePlatform().initIdentitySequences(session, DEFAULT_IDENTITY_GENERATOR);
     }
 
     /**
@@ -232,13 +246,16 @@ public void dropTables(DatabaseSession session, SchemaManager schemaManager) {
 
     /**
      * Drop the tables from the database.
+     * @param session Active database session.
+     * @param schemaManager Database schema manipulation manager.
+     * @param build Whether to build constraints.
      */
-    public void dropTables(DatabaseSession session, SchemaManager schemaManager, boolean build) {
+    public void dropTables(final DatabaseSession session, final SchemaManager schemaManager, final boolean build) {
         buildConstraints(schemaManager, build);
 
         // CR 3870467, do not log stack, or log at all if not fine
         boolean shouldLogExceptionStackTrace = session.getSessionLog().shouldLogExceptionStackTrace();
-        int level = session.getSessionLog().getLevel();
+        final int level = session.getSessionLog().getLevel();
         if (shouldLogExceptionStackTrace) {
             session.getSessionLog().setShouldLogExceptionStackTrace(false);
         }
@@ -248,7 +265,7 @@ public void dropTables(DatabaseSession session, SchemaManager schemaManager, boo
         try {
             dropConstraints(session, schemaManager, false);
 
-            String sequenceTableName = getSequenceTableName(session);
+            final String sequenceTableName = getSequenceTableName(session);
             List<TableDefinition> tables = getTableDefinitions();
             int trys = 1;
             if (SchemaManager.FORCE_DROP) {
@@ -256,12 +273,15 @@ public void dropTables(DatabaseSession session, SchemaManager schemaManager, boo
             }
             while ((trys > 0) && !tables.isEmpty()) {
                 trys--;
-                List<TableDefinition> failed = new ArrayList<TableDefinition>();
-                for (TableDefinition table : tables) {
+                final List<TableDefinition> failed = new ArrayList<TableDefinition>();
+                final Set<String> tableNames = new HashSet<>(tables.size());
+                for (final TableDefinition table : tables) {
+                    final String tableName = table.getName();
                     // Must not create sequence table as done in createSequences.
-                    if (!table.getName().equals(sequenceTableName)) {
+                    if (!tableName.equals(sequenceTableName)) {
                         try {
                             schemaManager.dropObject(table);
+                            tableNames.add(tableName);
                         } catch (DatabaseException exception) {
                             failed.add(table);
                             if (!shouldIgnoreDatabaseException()) {
@@ -270,6 +290,7 @@ public void dropTables(DatabaseSession session, SchemaManager schemaManager, boo
                         }
                     }
                 }
+                session.getDatasourcePlatform().removeIdentitySequences(session, DEFAULT_IDENTITY_GENERATOR, tableNames);
                 tables = failed;
             }
         } finally {
@@ -422,15 +443,18 @@ protected void extendTablesAndConstraints(SchemaManager schemaManager, DatabaseS
 
     /**
      * This creates/extends the tables on the database.
+     * @param session Active database session.
+     * @param schemaManager Database schema manipulation manager.
+     * @param build Whether to build constraints.
      */
-    public void extendTables(DatabaseSession session, SchemaManager schemaManager, boolean build) {
+    public void extendTables(final DatabaseSession session, final SchemaManager schemaManager, final boolean build) {
         buildConstraints(schemaManager, build);
 
-        String sequenceTableName = getSequenceTableName(session);
-        for (TableDefinition table : getTableDefinitions()) {
+        final String sequenceTableName = getSequenceTableName(session);
+        for (final TableDefinition table : getTableDefinitions()) {
             // Must not create sequence table as done in createSequences.
             if (!table.getName().equals(sequenceTableName)) {
-                AbstractSession abstractSession = (AbstractSession) session;
+                final AbstractSession abstractSession = (AbstractSession) session;
                 boolean alreadyExists = false;
                 // Check if the table already exists, to avoid logging create error.
                 if (CHECK_EXISTENCE && schemaManager.shouldWriteToDatabase()) {
@@ -442,7 +466,7 @@ public void extendTables(DatabaseSession session, SchemaManager schemaManager, b
                     try {
                         schemaManager.createObject(table);
                         session.getSessionLog().log(SessionLog.FINEST, SessionLog.DDL, "default_tables_created", table.getFullName());
-                    } catch (DatabaseException exception) {
+                    } catch (final DatabaseException exception) {
                         createTableException = exception;
                         alreadyExists = true;
                     }
@@ -453,7 +477,7 @@ public void extendTables(DatabaseSession session, SchemaManager schemaManager, b
                     //While SQL is case insensitive, getColumnInfo is and will not return the table info unless the name is passed in
                     //as it is stored internally.
                     String tableName = table.getTable()==null? table.getName(): table.getTable().getName();
-                    boolean usesDelimiting = (table.getTable()!=null && table.getTable().shouldUseDelimiters());
+                    final boolean usesDelimiting = (table.getTable()!=null && table.getTable().shouldUseDelimiters());
                     List<DatabaseRecord> columnInfo = null;
 
                     //I need the actual table catalog, schema and tableName for getTableInfo.
@@ -471,9 +495,9 @@ public void extendTables(DatabaseSession session, SchemaManager schemaManager, b
                         //Table exists, add individual fields as necessary
 
                         //hash the table's existing columns by name
-                        Map<DatabaseField, DatabaseRecord> columns = new HashMap(columnInfo.size());
-                        DatabaseField columnNameLookupField = new DatabaseField("COLUMN_NAME");
-                        DatabaseField schemaLookupField = new DatabaseField("TABLE_SCHEM");
+                        final Map<DatabaseField, DatabaseRecord> columns = new HashMap(columnInfo.size());
+                        final DatabaseField columnNameLookupField = new DatabaseField("COLUMN_NAME");
+                        final DatabaseField schemaLookupField = new DatabaseField("TABLE_SCHEM");
                         boolean schemaMatchFound = false;
                         // Determine the probably schema for the table, this is a heuristic, so should not cause issues if wrong.
                         String qualifier = table.getQualifier();
@@ -490,15 +514,15 @@ public void extendTables(DatabaseSession session, SchemaManager schemaManager, b
                                 }
                             }
                         }
-                        boolean checkSchema = (qualifier != null) && (qualifier.length() > 0);
-                        for (DatabaseRecord record : columnInfo) {
-                            String fieldName = (String)record.get(columnNameLookupField);
+                        final boolean checkSchema = (qualifier != null) && (qualifier.length() > 0);
+                        for (final DatabaseRecord record : columnInfo) {
+                            final String fieldName = (String)record.get(columnNameLookupField);
                             if (fieldName != null && fieldName.length() > 0) {
-                                DatabaseField column = new DatabaseField(fieldName);
+                                final DatabaseField column = new DatabaseField(fieldName);
                                 if (session.getPlatform().shouldForceFieldNamesToUpperCase()) {
                                     column.useUpperCaseForComparisons(true);
                                 }
-                                String schema = (String)record.get(schemaLookupField);
+                                final String schema = (String)record.get(schemaLookupField);
                                 // Check the schema as well.  Ignore columns for other schema if a schema match is found.
                                 if (schemaMatchFound) {
                                     if (qualifier.equalsIgnoreCase(schema)) {
@@ -519,7 +543,7 @@ public void extendTables(DatabaseSession session, SchemaManager schemaManager, b
                         }
 
                         //Go through each field we need to have in the table to see if it already exists
-                        for (FieldDefinition fieldDef : table.getFields()){
+                        for (final FieldDefinition fieldDef : table.getFields()){
                             DatabaseField dbField = fieldDef.getDatabaseField();
                             if ( dbField == null ) {
                                 dbField = new DatabaseField(fieldDef.getName());
@@ -528,7 +552,7 @@ public void extendTables(DatabaseSession session, SchemaManager schemaManager, b
                                 //field does not exist so add it to the table
                                 try {
                                     table.addFieldOnDatabase(abstractSession, fieldDef);
-                                } catch(DatabaseException addFieldEx) {
+                                } catch (final DatabaseException addFieldEx) {
                                     session.getSessionLog().log(SessionLog.FINEST,  SessionLog.DDL, "table_cannot_add_field", dbField.getName(), table.getFullName(), addFieldEx.getMessage());
                                     if (!shouldIgnoreDatabaseException()) {
                                         throw addFieldEx;
@@ -548,5 +572,7 @@ public void extendTables(DatabaseSession session, SchemaManager schemaManager, b
         createConstraints(session, schemaManager, false);
 
         schemaManager.createSequences();
+        session.getDatasourcePlatform().initIdentitySequences(session, DEFAULT_IDENTITY_GENERATOR);
+
     }
 }
diff --git a/foundation/org.eclipse.persistence.oracle/src/org/eclipse/persistence/platform/database/oracle/Oracle12Platform.java b/foundation/org.eclipse.persistence.oracle/src/org/eclipse/persistence/platform/database/oracle/Oracle12Platform.java
index 8c816e5..2540ee5 100644
--- a/foundation/org.eclipse.persistence.oracle/src/org/eclipse/persistence/platform/database/oracle/Oracle12Platform.java
+++ b/foundation/org.eclipse.persistence.oracle/src/org/eclipse/persistence/platform/database/oracle/Oracle12Platform.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014, 2015  Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2016 Oracle and/or its affiliates. All rights reserved.
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
  * which accompanies this distribution.
@@ -9,15 +9,144 @@
  *
  * Contributors:
  *     Oracle - initial API and implementation from Oracle TopLink
+ *     09/29/2016-2.7 Tomas Kraus
+ *       - 426852: @GeneratedValue(strategy=GenerationType.IDENTITY) support in Oracle 12c
  ******************************************************************************/
 package org.eclipse.persistence.platform.database.oracle;
 
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.eclipse.persistence.descriptors.ClassDescriptor;
+import org.eclipse.persistence.exceptions.ValidationException;
+import org.eclipse.persistence.internal.sessions.AbstractSession;
+import org.eclipse.persistence.internal.sessions.EmptyRecord;
+import org.eclipse.persistence.logging.SessionLog;
+import org.eclipse.persistence.queries.ValueReadQuery;
+import org.eclipse.persistence.sequencing.NativeSequence;
+import org.eclipse.persistence.sequencing.Sequence;
+import org.eclipse.persistence.sessions.Session;
+
 /**
  * <p><b>Purpose:</b>
- * Supports usage of certain Oracle JDBC specific APIs for the Oracle 12 database.
+ * Supports usage of certain Oracle JDBC specific APIs for the Oracle 12 database.<br>
+ * Identity column:<br>
+ * <dd>{@code GENERATED [ ALWAYS | BY DEFAULT [ ON NULL ] ] AS IDENTITY [ ( identity_options ) ]}
  */
 public class Oracle12Platform extends Oracle11Platform {
+
+    /** Table name to identity sequence name storage. */
+    private final Map<String, String> identitySequences;
+
     public Oracle12Platform() {
         super();
+        supportsIdentity = true;
+        identitySequences = new ConcurrentHashMap<>();
     }
+
+    /**
+     * INTERNAL:
+     * Check whether current platform is Oracle 12c or later.
+     * @return Always returns {@code true} for instances of Oracle 12c platform.
+     * @since 2.7
+     */
+    @Override
+    public boolean isOracle12() {
+        return true;
+    }
+
+    /**
+     * INTERNAL:
+     * Initialize platform specific identity sequences.
+     * @param dbSession Active database session (in connected state).
+     * @param defaultIdentityGenerator Default identity generator sequence name.
+     * @since 2.7
+     */
+    @Override
+    public void initIdentitySequences(final Session session, final String defaultIdentityGenerator) {
+        if (sequences != null && sequences.containsKey(defaultIdentityGenerator)) {
+            for (final ClassDescriptor descriptor : session.getDescriptors().values()) {
+                final Sequence sequence = descriptor.getSequence();
+                if (sequence != null && defaultIdentityGenerator.equals(sequence.getName())) {
+                    final String tableName = descriptor.getTableName();
+                    final String seqName = getIdentitySequence(tableName, session);
+                    if (seqName != null) {
+                        final NativeSequence newSequence = new NativeSequence(seqName, 1, true);
+                        newSequence.setShouldAcquireValueAfterInsert(true);
+                        newSequence.onConnect(this);
+                        descriptor.setSequence(newSequence);
+                        descriptor.setSequenceNumberName(seqName);
+                        identitySequences.put(tableName, seqName);
+                        addSequence(newSequence);
+                        if (session.getSessionLog().shouldLog(SessionLog.FINE)) {
+                            session.getSessionLog().log(SessionLog.FINE, "platform_ora_init_id_seq",
+                                    new Object[] {defaultIdentityGenerator, seqName, tableName});
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * INTERNAL:
+     * Remove platform specific identity sequence for specified table. Default identity sequence is restored.
+     * @param dbSession Active database session (in connected state).
+     * @param defaultIdentityGenerator Default identity generator sequence name.
+     * @param tableNames Set of table names to check for identity sequence removal.
+     * @since 2.7
+     */
+    @Override
+    public void removeIdentitySequences(final Session session, final String defaultIdentityGenerator, final Set<String> tableNames) {
+        if (sequences != null && sequences.containsKey(defaultIdentityGenerator)) {
+            final Sequence defaultSeq = getSequence(defaultIdentityGenerator);
+            for (final ClassDescriptor descriptor : session.getDescriptors().values()) {
+                final String tableName = descriptor.getTableName();
+                if (tableName != null && identitySequences.containsKey(tableName)) {
+                    final String seqName = identitySequences.remove(tableName);
+                    removeSequence(seqName);
+                    descriptor.setSequence(defaultSeq);
+                    descriptor.setSequenceNumberName(defaultIdentityGenerator);
+                    if (session.getSessionLog().shouldLog(SessionLog.FINE)) {
+                        session.getSessionLog().log(SessionLog.FINE, "platform_ora_remove_id_seq",
+                                new Object[] {seqName, defaultIdentityGenerator, tableName});
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Get sequence name corresponding to the table name.
+     * @param tableName Name of the table.
+     * @param session Active data source session.
+     * @return Sequence name corresponding to the table name or {@code null} if no such sequence exists.
+     * @since 2.7
+     */
+    private String getIdentitySequence(final String tableName, final Session session) {
+        // TABLE_NAME values are converted to upper case by default.
+        // Also TableDefinition.buildCreationWriter(AbstractSession,Writer) does not have support for quoting table names
+        // to make them case sensitive on Oracle DB.
+        final String sql = "SELECT SEQUENCE_NAME FROM USER_TAB_IDENTITY_COLS WHERE TABLE_NAME='" + tableName.toUpperCase() + "'";
+        return (String)(new ValueReadQuery(sql)).execute((AbstractSession)session, EmptyRecord.getEmptyRecord());
+    }
+
+    /**
+     * INTERNAL:
+     * Append the receiver's field 'identity' constraint clause to a writer.
+     * @param writer Target writer.
+     * @since 2.7
+     */
+    @Override
+    public void printFieldIdentityClause(final Writer writer) throws ValidationException {
+        try {
+            writer.write(" GENERATED AS IDENTITY");
+        } catch (IOException ioException) {
+            throw ValidationException.fileError(ioException);
+        }
+    }
+
 }
diff --git a/jpa/eclipselink.jpa.test/antbuild.properties b/jpa/eclipselink.jpa.test/antbuild.properties
index d3de5a8..acd0bd1 100644
--- a/jpa/eclipselink.jpa.test/antbuild.properties
+++ b/jpa/eclipselink.jpa.test/antbuild.properties
@@ -102,6 +102,7 @@
 eclipselink.validation.failed.model=eclipselink-validation-failed-model
 eclipselink.advanced.field.access.model=eclipselink-advanced-field-access-model
 eclipselink.advanced.properties=eclipselink-advanced-properties
+eclipselink.identity.model=eclipselink-identity-model
 eclipselink.xml.extended.model=eclipselink-xml-extended-model
 eclipselink.jpa21.model=eclipselink-jpa21-model
 eclipselink.jpa22.model=eclipselink-jpa22-model
diff --git a/jpa/eclipselink.jpa.test/antbuild.xml b/jpa/eclipselink.jpa.test/antbuild.xml
index 7214403..4375dd0 100644
--- a/jpa/eclipselink.jpa.test/antbuild.xml
+++ b/jpa/eclipselink.jpa.test/antbuild.xml
@@ -619,6 +619,7 @@
         <mkdir dir="${jpatest.basedir}/${build.dir}/${eclipselink.validation.failed.model}"/>
         <mkdir dir="${jpatest.basedir}/${build.dir}/${eclipselink.advanced.field.access.model}"/>
         <mkdir dir="${jpatest.basedir}/${build.dir}/${eclipselink.advanced.properties}"/>
+        <mkdir dir="${jpatest.basedir}/${build.dir}/${eclipselink.identity.model}"/>
         <mkdir dir="${jpatest.basedir}/${build.dir}/${eclipselink.pu.with.spaces}"/>
         <mkdir dir="${jpatest.basedir}/${build.dir}/eclipselink-partitioned-model"/>
         <mkdir dir="${jpatest.basedir}/${build.dir}/eclipselink-plsql-model"/>
@@ -663,7 +664,7 @@
             <!-- compilerarg value="-Xlint:unchecked" compiler="javac1.7"/ -->
             <compilerarg value="-Aeclipselink.persistencexml=${PERSISTENCE_XML_PATH}"/>
             <!-- compilerarg value="-Aeclipselink.canonicalmodel.prefix=_" compiler="javac1.7"/ -->
-           <classpath refid="${COMPILE_PATH_REF}"/>
+            <classpath refid="${COMPILE_PATH_REF}"/>
         </javac>
     </target>
 
@@ -734,6 +735,13 @@
             <param name="PERSISTENCE_XML_PATH" value="eclipselink-metamodel-model/persistence.xml"/>
             <param name="COMPILE_PATH_REF" value="compile.modelgen.path"/>
         </antcall>
+        <antcall target="model-compile" inheritRefs="true">
+            <param name="SRC_PATH" value="${jpatest.basedir}/${src.dir}"/>
+            <param name="DEST_PATH" value="${jpatest.basedir}/${classes.dir}"/>
+            <param name="MODEL_PATH" value="org/eclipse/persistence/testing/models/jpa/identity"/>
+            <param name="PERSISTENCE_XML_PATH" value="eclipselink-identity-model/persistence.xml"/>
+            <param name="COMPILE_PATH_REF" value="compile.modelgen.path"/>
+        </antcall>
         <!-- This persistence.xml MUST be deleted because tests will pick it up otherwise and fail -->
         <delete dir="${jpatest.build.location}/${classes.dir}/META-INF"/>
         <available file="${jpatest.build.location}/${classes.dir}/org/eclipse/persistence/testing/models/jpa/advanced/Address_.class" property="modelgen.created.classes.exist"/>
@@ -912,7 +920,8 @@
     package-composite-advanced-member_1, package-composite-advanced-member_2, package-composite-advanced-member_3, package-xml-composite-advanced, 
     package-xml-composite-advanced-member_1, package-xml-composite-advanced-member_2, package-xml-composite-advanced-member_3,
     package-xml-extended-composite-advanced, package-xml-extended-composite-advanced-member_1, package-xml-extended-composite-advanced-member_2, 
-    package-xml-extended-composite-advanced-member_3, package-extensibility, package-jpa-remote, package-xml-mapping-metadata-complete" description="build EclipseLink jar">
+    package-xml-extended-composite-advanced-member_3, package-extensibility, package-jpa-remote, package-identity-model,
+    package-xml-mapping-metadata-complete" description="build EclipseLink jar">
         <jar jarfile="${jpatest.basedir}/${jpatest.framework.jar}">
            <fileset dir="${jpatest.basedir}/${classes.dir}"  includes="org/eclipse/persistence/testing/framework/**/*.class"/>
         </jar>
@@ -1299,6 +1308,20 @@
         </jar>
     </target>
 
+    <target name="package-identity-model" depends="">
+        <copy todir="${jpatest.basedir}/${build.dir}/${eclipselink.identity.model}/META-INF">
+            <fileset dir="${jpatest.basedir}/resource/${eclipselink.identity.model}" includes="*.xml"/>
+        </copy>
+        <copy todir="${jpatest.basedir}/${build.dir}/${eclipselink.identity.model}">
+            <fileset dir="${jpatest.basedir}/${classes22.dir}"
+                     includes="org/eclipse/persistence/testing/models/jpa22/identity/**"/>
+        </copy>
+        <jar jarfile="${jpatest.basedir}/${eclipselink.identity.model}.jar">
+            <fileset dir="${jpatest.basedir}/${build.dir}/${eclipselink.identity.model}">
+            </fileset>
+        </jar>
+    </target>
+
     <target name="package-jpa-performance2" depends="">
         <copy todir="${jpatest.basedir}/${build.dir}/${jpa.performance2}/META-INF">
             <fileset dir="${jpatest.basedir}/resource/${jpa.performance2}/META-INF" includes="*.xml"/>
@@ -1711,6 +1734,14 @@
             <pathelement path="${jpatest.basedir}/${eclipselink.xml.extended.composite.advanced.model.member_3}.jar"/>
             <pathelement path="${jpatest.2.common.plugins.dir}/${json.jar}"/>
         </path>
+        <path id="run.identity.classpath">
+            <pathelement path="${jpatest.basedir}/${classes22.dir}"/>
+            <path refid="jpa22.compile.path"/>
+            <pathelement path="${jdbc.driver.jar}"/>
+            <pathelement path="${jpatest.basedir}/${jpatest.framework.jar}"/>
+            <pathelement path="${jpatest.basedir}/${jpa22.test.jar}"/>
+            <pathelement path="${jpatest.basedir}/${eclipselink.identity.model}.jar"/>
+        </path>
         <path id="run.weaver.classpath">
             <pathelement path="${jpatest.basedir}/${classes.dir}"/>
             <path refid="jpa21.compile.path"/>
@@ -1786,6 +1817,7 @@
     </target>
 
     <!-- Default test target, run the FullRegressionTestSuite -->
+    <!-- persistence.xml with PU "default" is in resource/eclipselink-annotation-model -->
     <target name="test" depends="clean-test, config-classpath">
         <antcall target="run-test" inheritRefs="true">
             <param name="TEST_CLASS" value="${test.class}"/>
@@ -1943,7 +1975,20 @@
         </antcall>
     </target>
 
-    <!-- Run dynamic entities tests standalone and generate tests report. -->
+    <!-- Run dynamic entities tests standalone. -->
+    <target name="test-identity" depends="config-classpath">
+        <antcall target="run-test" inheritRefs="true">
+            <param name="TEST_CLASS" value="org.eclipse.persistence.testing.tests.jpa22.identity.IdentityTest"/>
+            <param name="TEST_AGENT" value="${test.agent}"/>
+            <param name="TEST_WEAVING" value="${test.weaving}"/>
+            <param name="TEST_WEAVING_OPTION" value="${test.weaving.option}"/>
+            <param name="ORM_TESTING" value="-Dorm.testing=eclipselink"/>
+            <param name="RUN_PATH" value="run.identity.classpath"/>
+            <param name="DB_URL" value="${db.url}"/>
+        </antcall>
+    </target>
+
+	<!-- Run dynamic entities tests standalone and generate tests report. -->
     <target name="test-dynamic-report" depends="test-dynamic, generate-report"/>
 
     <!-- Runs JPA 2.0 BeanValidation tests. -->
diff --git a/jpa/eclipselink.jpa.test/resource/eclipselink-identity-model/persistence.xml b/jpa/eclipselink.jpa.test/resource/eclipselink-identity-model/persistence.xml
new file mode 100644
index 0000000..6fe4d91
--- /dev/null
+++ b/jpa/eclipselink.jpa.test/resource/eclipselink-identity-model/persistence.xml
@@ -0,0 +1,15 @@
+<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence persistence_1_0.xsd" version="1.0">
+    <persistence-unit name="identity-pu" transaction-type="RESOURCE_LOCAL">
+        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
+        <exclude-unlisted-classes>false</exclude-unlisted-classes>
+        <properties>
+            <property name="javax.persistence.jdbc.driver" value="@driverClass@"/>
+            <property name="javax.persistence.jdbc.url" value="@dbURL@"/>
+            <property name="javax.persistence.jdbc.user" value="@dbUser@"/>
+            <property name="javax.persistence.jdbc.password" value="@dbPassword@"/>
+            <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
+            <!--property name="eclipselink.logging.level.sql" value="FINE"/-->
+        </properties>
+        <class>org.eclipse.persistence.testing.models.jpa.identity.Person</class>
+    </persistence-unit>
+</persistence>
diff --git a/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa22/identity/Person.java b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa22/identity/Person.java
new file mode 100644
index 0000000..a0c131a
--- /dev/null
+++ b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa22/identity/Person.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
+ * which accompanies this distribution.
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *     09/29/2016-2.7 Tomas Kraus
+ *       - 426852: @GeneratedValue(strategy=GenerationType.IDENTITY) support in Oracle 12c
+ ******************************************************************************/
+package org.eclipse.persistence.testing.models.jpa22.identity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+/**
+ * Simple entity with ID as generated value using identity column
+ */
+@Entity
+@Table(name="MOD_IDENT_PERSON")
+public class Person {
+
+    @Id
+    @GeneratedValue(strategy=GenerationType.IDENTITY)
+    private int id;
+
+    @Column
+    private String firstName;
+
+    @Column String secondName;
+
+    public Person() {
+    }
+
+    public Person(final String firstName, final String secondName) {
+        this.firstName = firstName;
+        this.secondName = secondName;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public void setSecondName(final String firstName) {
+        this.firstName = firstName;
+    }
+
+    public String getSecondName() {
+        return secondName;
+    }
+
+    public void setFirstName(final String secondName) {
+        this.secondName = secondName;
+    }
+
+}
diff --git a/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa22/identity/IdentityTest.java b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa22/identity/IdentityTest.java
new file mode 100644
index 0000000..943bfbb
--- /dev/null
+++ b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa22/identity/IdentityTest.java
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
+ * which accompanies this distribution.
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *     09/29/2016-2.7 Tomas Kraus
+ *       - 426852: @GeneratedValue(strategy=GenerationType.IDENTITY) support in Oracle 12c
+ ******************************************************************************/
+package org.eclipse.persistence.testing.tests.jpa22.identity;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.EntityTransaction;
+import javax.persistence.PersistenceException;
+import javax.persistence.TypedQuery;
+
+import org.eclipse.persistence.internal.databaseaccess.DatasourcePlatform;
+import org.eclipse.persistence.platform.database.DatabasePlatform;
+import org.eclipse.persistence.sessions.server.ServerSession;
+import org.eclipse.persistence.testing.framework.junit.JUnitTestCase;
+import org.eclipse.persistence.testing.models.jpa22.identity.Person;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ * Test proper identity column value generation.
+ */
+public class IdentityTest {
+
+    /** Name of persistence unit used in test. */
+    private static final String PU_NAME = "identity-pu";
+
+    /** Entity manager factory. */
+    private static EntityManagerFactory EMF;
+
+    /** Database platform. */
+    private static DatabasePlatform DBP;
+
+    /** Entity manager. */
+    private EntityManager em;
+
+    /**
+     * Initialize test static content.
+     */
+    @BeforeClass
+    public static void setupClass() {
+        EMF = JUnitTestCase.getEntityManagerFactory(PU_NAME);
+        DBP = EMF.<ServerSession>unwrap(ServerSession.class).getPlatform();
+    }
+
+    /**
+     * Destroy test static content.
+     */
+    @AfterClass
+    public static void  cleanupClass() {
+        EMF = null;
+        DBP = null;
+    }
+
+    /**
+     * Initialize test environment.
+     */
+    @Before
+    public void setup() {
+        em = EMF.createEntityManager();
+    }
+
+    /**
+     * Destroy test environment.
+     */
+    @After
+    public void cleanup() {
+        if (em != null) {
+            em.close();
+        }
+    }
+
+    /**
+     * Test identity column value generation.
+     */
+    @Test
+    public void testIdentity() {
+        if (!DBP.supportsIdentity()) {
+            return;
+        }
+        EntityTransaction t = em.getTransaction();
+        final Person p1 = new Person("John", "Smith");
+        final Person p2 = new Person("Bob", "Brown");
+        t.begin();
+        try {
+            em.persist(p1);
+            em.persist(p2);
+            t.commit();
+        } catch (PersistenceException | IllegalArgumentException ex) {
+            if (t.isActive()) {
+                t.rollback();
+            }
+            ex.printStackTrace();
+            throw ex;
+        }
+        final Map<String, Person> pMap = new HashMap<>(2);
+        pMap.put(p1.getSecondName(), p1);
+        pMap.put(p2.getSecondName(), p2);
+        final TypedQuery<Person> pQuery = em.createQuery("SELECT p FROM Person p", Person.class);
+        final List<Person> pList = pQuery.getResultList();
+        for (final Person p : pList) {
+            final Person pV = pMap.get(p.getSecondName());
+            assertEquals(p.getId(), pV.getId());
+        }
+    }
+
+}
diff --git a/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/EntityManagerSetupImpl.java b/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/EntityManagerSetupImpl.java
index bfebad2..97a5121 100644
--- a/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/EntityManagerSetupImpl.java
+++ b/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/EntityManagerSetupImpl.java
@@ -72,6 +72,8 @@
  *       - 480787 : Wrap several privileged method calls with a doPrivileged block
  *     12/03/2015-2.6 Dalia Abo Sheasha
  *       - 483582: Add the javax.persistence.sharedCache.mode property
+ *     09/29/2016-2.7 Tomas Kraus
+ *       - 426852: @GeneratedValue(strategy=GenerationType.IDENTITY) support in Oracle 12c
  *****************************************************************************/
 package org.eclipse.persistence.internal.jpa;
 
@@ -802,6 +804,8 @@ public Object handleException(RuntimeException exception) {
                             writeDDL(deployProperties, getDatabaseSession(deployProperties), classLoaderToUse);
                         }
                     }
+                    // Initialize platform specific identity sequences.
+                    session.getDatasourcePlatform().initIdentitySequences(getDatabaseSession(), MetadataProject.DEFAULT_IDENTITY_GENERATOR);
                     updateTunerPostDeploy(deployProperties, classLoaderToUse);
                     this.deployLock.release();
                     isLockAcquired = false;