Bug 522312: Add support to start sequence generation at nextval

Signed-off-by: Will Dazey <dazeydev.3@gmail.com>
Reviewed-by: Dalia Abo Sheasha <daliasheasha@gmail.com>
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/config/PersistenceUnitProperties.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/config/PersistenceUnitProperties.java
index 5051e58..a7415a0 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/config/PersistenceUnitProperties.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/config/PersistenceUnitProperties.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 1998, 2016 Oracle and/or its affiliates, IBM Corporation. All rights reserved.
+ * Copyright (c) 1998, 2017 Oracle and/or its affiliates, IBM Corporation. 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.
@@ -41,6 +41,8 @@
  *       - 478331 : Added support for defining local or server as the default locale for obtaining timestamps
  *     12/03/2015-2.6 Dalia Abo Sheasha
  *       - 483582: Add the javax.persistence.sharedCache.mode property
+ *     09/14/2017-2.6 Will Dazey
+ *       - 522312: Add the eclipselink.sequencing.start-sequence-at-nextval property
  ******************************************************************************/
 package org.eclipse.persistence.config;
 
@@ -2260,6 +2262,20 @@ public class PersistenceUnitProperties {
     public static final String SEQUENCING_SEQUENCE_DEFAULT = "eclipselink.sequencing.default-sequence-to-table";
 
     /**
+     * By default, EclipseLink generates sequence values at (NEXTVAL - allocationSize). For instance, if NEXTVAL returns a
+     * value of 100 and the allocationSize is 50 (default), EclipseLink will begin sequence values at 100 - allocationSize.
+     * When the "<code>eclipselink.sequencing.start-sequence-at-nextval</code>" property
+     * is set to true, the ID values generated from sequences starting at NEXTVAL and proceeding forward.
+     * <p>
+     * <b>Allowed Values</b> (String)<b>:</b>
+     * <ul>
+     * <li>"<code>false</code>" - (DEFAULT) uses default behavior of next value - allocationSize
+     * <li>"<code>true</code>"
+     * </ul>
+     */
+    public static final String SEQUENCING_START_AT_NEXTVAL = "eclipselink.sequencing.start-sequence-at-nextval";
+
+    /**
      * The "<code>eclipselink.session.customizer</code>" property configures a
      * {@link SessionCustomizer} used to alter the runtime configuration through
      * API.
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 607a526..b6fb00f 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, 2016 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2017 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.
@@ -11,6 +11,8 @@
  *     Oracle - initial API and implementation from Oracle TopLink
  *     09/29/2016-2.7 Tomas Kraus
  *       - 426852: @GeneratedValue(strategy=GenerationType.IDENTITY) support in Oracle 12c
+ *     09/14/2017-2.6 Will Dazey
+ *       - 522312: Add the eclipselink.sequencing.start-sequence-at-nextval property 
  ******************************************************************************/
 package org.eclipse.persistence.internal.databaseaccess;
 
@@ -87,6 +89,9 @@ public class DatasourcePlatform implements Platform {
     /** If the native sequence type is not supported, if table sequencing should be used. */
     protected boolean defaultNativeSequenceToTable;
 
+    /** If sequences should start at Next Value */
+    protected boolean defaultSeqenceAtNextValue;
+
     public DatasourcePlatform() {
         this.tableQualifier = "";
         this.startDelimiter = "";
@@ -107,6 +112,20 @@ public void setDefaultNativeSequenceToTable(boolean defaultNativeSequenceToTable
         this.defaultNativeSequenceToTable = defaultNativeSequenceToTable;
     }
 
+    /**
+     * Return if the sequence generation should start at next value.
+     */
+    public boolean getDefaultSeqenceAtNextValue() {
+        return defaultSeqenceAtNextValue;
+    }
+
+    /**
+     * Set if the sequence generation should start at next value.
+     */
+    public void setDefaultSeqenceAtNextValue(boolean defaultSeqenceAtNextValue) {
+        this.defaultSeqenceAtNextValue = defaultSeqenceAtNextValue;
+    }
+
     protected void addOperator(ExpressionOperator operator) {
         platformOperators.put(Integer.valueOf(operator.getSelector()), operator);
     }
@@ -220,7 +239,8 @@ public void copyInto(Platform platform) {
         }
         datasourcePlatform.setSequences(getSequences());
         datasourcePlatform.sequencesAfterCloneCleanup();
-        datasourcePlatform.defaultNativeSequenceToTable = this.defaultNativeSequenceToTable;
+        datasourcePlatform.setDefaultNativeSequenceToTable(getDefaultNativeSequenceToTable());
+        datasourcePlatform.setDefaultSeqenceAtNextValue(getDefaultSeqenceAtNextValue());
     }
 
     /**
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/sequencing/StandardSequence.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/sequencing/StandardSequence.java
index 00f9e98..cf144db 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/sequencing/StandardSequence.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/sequencing/StandardSequence.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2017 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/14/2017-2.6 Will Dazey
+ *       - 522312: Add the eclipselink.sequencing.start-sequence-at-nextval property
  ******************************************************************************/
 package org.eclipse.persistence.sequencing;
 
@@ -72,6 +74,9 @@ public Vector getGeneratedVector(Accessor accessor, AbstractSession writeSession
             if (value == null) {
                 throw DatabaseException.errorPreallocatingSequenceNumbers();
             }
+            if(writeSession.getPlatform().getDefaultSeqenceAtNextValue()) {
+                return createVectorAtNextVal(value, seqName, size);
+            }
             return createVector(value, seqName, size);
         } else {
             return null;
@@ -103,6 +108,30 @@ protected Vector createVector(Number sequence, String seqName, int size) {
         return sequencesForName;
     }
 
+    /**
+     * INTERNAL:
+     * given sequence = 10, size = 5 will create Vector (10,11,12,13,14)
+     * @param seqName String is sequencing number field name
+     * @param size int size of Vector to create.
+     */
+    protected Vector createVectorAtNextVal(Number sequence, String seqName, int size) {
+        long nextSequence = sequence.longValue();
+
+        Vector sequencesForName = new Vector(size);
+
+        // Check for incorrect values return to validate that the sequence is setup correctly.
+        // PRS 36451 intvalue would wrap
+        if (nextSequence < -1L) {
+            throw ValidationException.sequenceSetupIncorrectly(seqName);
+        }
+
+        for (int index = size; index > 0; index--) {
+            sequencesForName.add(nextSequence);
+            nextSequence = nextSequence + 1L;
+        }
+        return sequencesForName;
+    }
+
     public void setInitialValue(int initialValue) {
         // sequence value should be positive
         if (initialValue <= 0) {
diff --git a/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/sequence/TestSequenceStartAtNextValue.java b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/sequence/TestSequenceStartAtNextValue.java
new file mode 100644
index 0000000..232e0a1
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/sequence/TestSequenceStartAtNextValue.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2017 IBM Corporation. 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/14/2017-2.6.0 Will Dazey
+ *       - 522312: Add support for sequence generation to start forward from nextval
+ ******************************************************************************/
+package org.eclipse.persistence.jpa.test.sequence;
+
+import javax.persistence.EntityManagerFactory;
+
+import org.eclipse.persistence.internal.databaseaccess.DatasourcePlatform;
+import org.eclipse.persistence.internal.jpa.EntityManagerImpl;
+import org.eclipse.persistence.jpa.test.framework.DDLGen;
+import org.eclipse.persistence.jpa.test.framework.Emf;
+import org.eclipse.persistence.jpa.test.framework.EmfRunner;
+import org.eclipse.persistence.jpa.test.framework.Property;
+import org.eclipse.persistence.jpa.test.sequence.model.SequenceEntity;
+import org.eclipse.persistence.queries.ValueReadQuery;
+import org.eclipse.persistence.sequencing.QuerySequence;
+import org.eclipse.persistence.sequencing.Sequence;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * The purpose of this test is to test that when Sequencing defaultSeqenceAtNextValue is set,
+ * the ID values generated from the sequence will start at the Next Val value and not -size.
+ */
+@RunWith(EmfRunner.class)
+public class TestSequenceStartAtNextValue {
+
+    @Emf(createTables = DDLGen.DROP_CREATE, classes = { SequenceEntity.class, }, properties = { 
+            @Property(name = "eclipselink.sequencing.start-sequence-at-nextval", value = "true") })
+    private EntityManagerFactory emf;
+
+    @Test
+    public void testStartSequenceAtNextval() {
+        EntityManagerImpl em = emf.createEntityManager().unwrap(EntityManagerImpl.class);
+
+        Sequence seq = em.getServerSession().getDescriptor(SequenceEntity.class).getSequence();
+        if (seq instanceof QuerySequence) {
+            if(((DatasourcePlatform)seq.getDatasourcePlatform()).supportsSequenceObjects()) {
+                ValueReadQuery q = ((DatasourcePlatform)seq.getDatasourcePlatform()).buildSelectQueryForSequenceObject(seq.getQualified(seq.getName()), seq.getPreallocationSize());
+                if(q != null) {
+                    //Execute NextVal or whatever query moves the sequence forward size
+                    //This gives us a baseline to compare to
+                    int nextVal = ((Number)em.getServerSession().executeQuery(q)).intValue();
+                    //Calling this next should prompt another query for NextVal, returning the next ID value 
+                    int seqNum = em.getServerSession().getNextSequenceNumberValue(SequenceEntity.class).intValue();
+                    //Expect (nextVal + size), not (nextVal + size) - size
+                    Assert.assertEquals("Expected " + seqNum + " == " + nextVal + " + " + seq.getPreallocationSize() + "(Size)", nextVal + seq.getPreallocationSize(), seqNum);
+                } else {
+                   Assert.fail((DatasourcePlatform)seq.getDatasourcePlatform() + " does support SequenceObjects, but SelectQuery is null.");
+                }
+            } else {
+                System.out.println((DatasourcePlatform)seq.getDatasourcePlatform() + " does not support SequenceObjects. Test is not valid.");
+            }
+        } else {
+            Assert.fail("Expected sequence for " + SequenceEntity.class + " to be of type QuerySequence, but was " + seq);
+        }
+
+        em.close();
+    }
+}
diff --git a/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/sequence/model/SequenceEntity.java b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/sequence/model/SequenceEntity.java
new file mode 100644
index 0000000..3814971
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/sequence/model/SequenceEntity.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2017 IBM Corporation. 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/14/2017-2.6.0 Will Dazey
+ *       - 522312: Add support for sequence generation to start forward from nextval
+ ******************************************************************************/
+package org.eclipse.persistence.jpa.test.sequence.model;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Entity
+@Table(name="SEQ_ENTITY")
+public class SequenceEntity {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.SEQUENCE)
+    private Integer id;
+
+}
\ No newline at end of file
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 97a5121..f0fee31 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
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 1998, 2016 Oracle and/or its affiliates, IBM Corporation. All rights reserved.
+ * Copyright (c) 1998, 2017 Oracle and/or its affiliates, IBM Corporation. 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.
@@ -74,6 +74,8 @@
  *       - 483582: Add the javax.persistence.sharedCache.mode property
  *     09/29/2016-2.7 Tomas Kraus
  *       - 426852: @GeneratedValue(strategy=GenerationType.IDENTITY) support in Oracle 12c
+ *     09/14/2017-2.6 Will Dazey
+ *       - 522312: Add the eclipselink.sequencing.start-sequence-at-nextval property
  *****************************************************************************/
 package org.eclipse.persistence.internal.jpa;
 
@@ -2801,6 +2803,7 @@ protected void updateSession(Map m, ClassLoader loader) {
 
             updateNativeSQLSetting(m);
             updateSequencing(m);
+            updateSequencingStart(m);
             updateAllowNativeSQLQueriesSetting(m);
             updateSQLCastSetting(m);
             updateUppercaseSetting(m);
@@ -3291,6 +3294,17 @@ protected void updateSequencing(Map m){
         }
     }
 
+    protected void updateSequencingStart(Map m) {
+        String local = EntityManagerFactoryProvider.getConfigPropertyAsStringLogDebug(PersistenceUnitProperties.SEQUENCING_START_AT_NEXTVAL, m, session);
+        try {
+            if (local != null) {
+                this.session.getPlatform().setDefaultSeqenceAtNextValue(Boolean.parseBoolean(local));
+            }
+        } catch (NumberFormatException exception) {
+            this.session.handleException(ValidationException.invalidValueForProperty(local, PersistenceUnitProperties.USE_LOCAL_TIMESTAMP, exception));
+        }
+    }
+
     /**
      * Load the projectCacheAccessor for JPA project caching
      */