From f8ea5619d079cfccfd7069b6ecbf79bd8d2a9338 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Thu, 22 Feb 2018 22:45:44 +1300 Subject: [PATCH] #1268 - SQL Server fix for "for update" plus Sequences support plus refactor of sequences --- .../java/io/ebean/config/ServerConfig.java | 21 +++ .../config/dbplatform/DatabasePlatform.java | 56 ++++-- .../dbplatform/SequenceBatchIdGenerator.java | 56 ++++++ .../dbplatform/SequenceIdGenerator.java | 177 +++++++----------- .../dbplatform/SequenceStepIdGenerator.java | 38 ++++ .../config/dbplatform/db2/DB2Platform.java | 5 +- .../db2/DB2SequenceIdGenerator.java | 4 +- .../config/dbplatform/h2/H2Platform.java | 6 +- .../dbplatform/h2/H2SequenceIdGenerator.java | 4 +- .../dbplatform/hsqldb/HsqldbPlatform.java | 5 +- .../dbplatform/mysql/MySqlPlatform.java | 13 -- .../dbplatform/oracle/OraclePlatform.java | 5 +- .../oracle/OracleSequenceIdGenerator.java | 4 +- .../dbplatform/postgres/PostgresPlatform.java | 4 +- .../postgres/PostgresSequenceIdGenerator.java | 4 +- .../sqlserver/SqlServerBasicSqlLimiter.java | 2 +- .../sqlserver/SqlServerPlatform.java | 50 ++++- .../sqlserver/SqlServerStepSequence.java | 24 +++ .../ddlgeneration/platform/PlatformDdl.java | 4 +- .../ddlgeneration/platform/SqlServerDdl.java | 4 +- .../server/core/DefaultContainer.java | 2 +- .../server/deploy/BeanDescriptorManager.java | 15 +- .../server/deploy/DbSqlContext.java | 5 + .../deploy/meta/DeployBeanDescriptor.java | 2 +- .../server/query/CQueryBuilder.java | 13 ++ .../server/query/DefaultDbSqlContext.java | 13 +- .../server/query/SqlTreeBuilder.java | 3 +- .../server/query/SqlTreeNodeRoot.java | 1 + .../java/io/ebeaninternal/util/JdbcClose.java | 14 ++ .../config/MatchingNamingConventionTest.java | 9 +- .../io/ebean/config/ServerConfigTest.java | 3 + .../sqlserver/SqlServerStepSequenceTest.java | 55 ++++++ 32 files changed, 439 insertions(+), 182 deletions(-) create mode 100644 src/main/java/io/ebean/config/dbplatform/SequenceBatchIdGenerator.java create mode 100644 src/main/java/io/ebean/config/dbplatform/SequenceStepIdGenerator.java create mode 100644 src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerStepSequence.java create mode 100644 src/test/java/io/ebean/config/dbplatform/sqlserver/SqlServerStepSequenceTest.java diff --git a/src/main/java/io/ebean/config/ServerConfig.java b/src/main/java/io/ebean/config/ServerConfig.java index a3f9348c9..4b17b276f 100644 --- a/src/main/java/io/ebean/config/ServerConfig.java +++ b/src/main/java/io/ebean/config/ServerConfig.java @@ -12,6 +12,7 @@ import io.ebean.cache.ServerCachePlugin; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.config.dbplatform.DbEncrypt; import io.ebean.config.dbplatform.DbType; +import io.ebean.config.dbplatform.IdType; import io.ebean.event.BeanFindController; import io.ebean.event.BeanPersistController; import io.ebean.event.BeanPersistListener; @@ -181,6 +182,11 @@ public class ServerConfig { */ private DatabasePlatform databasePlatform; + /** + * The preferred IdType (to override the default Platform type). + */ + private IdType idType; + /** * For DB's using sequences this is the number of sequence values prefetched. */ @@ -1721,6 +1727,20 @@ public class ServerConfig { this.databasePlatform = databasePlatform; } + /** + * Return the preferred DB platform IdType. + */ + public IdType getIdType() { + return idType; + } + + /** + * Set the preferred DB platform IdType. + */ + public void setIdType(IdType idType) { + this.idType = idType; + } + /** * Return the EncryptKeyManager. */ @@ -2722,6 +2742,7 @@ public class ServerConfig { throw new IllegalArgumentException("Property 'batch.mode' or 'persistBatching' is being set but no longer used. Please change to use 'persistBatchMode'"); } + idType = p.getEnum(IdType.class, "idType", idType); persistBatch = p.getEnum(PersistBatch.class, "persistBatch", persistBatch); persistBatchOnCascade = p.getEnum(PersistBatch.class, "persistBatchOnCascade", persistBatchOnCascade); diff --git a/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java b/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java index 3b6073c60..190e69d97 100644 --- a/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java +++ b/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java @@ -6,6 +6,8 @@ import io.ebean.annotation.PersistBatch; import io.ebean.annotation.Platform; import io.ebean.config.CustomDbTypeMapping; import io.ebean.config.DbTypeConfig; +import io.ebean.config.ServerConfig; +import io.ebeaninternal.util.JdbcClose; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -98,6 +100,10 @@ public class DatabasePlatform { */ protected DbIdentity dbIdentity = new DbIdentity(); + protected boolean sequenceBatchMode = true; + + protected int sequenceBatchSize = 20; + /** * The history support for this database platform. */ @@ -206,10 +212,19 @@ public class DatabasePlatform { return exceptionTranslator.translate(message, e); } + /** + * Configure the platform given the server configuration. + */ + public void configure(ServerConfig config) { + this.sequenceBatchSize = config.getDatabaseSequenceBatchSize(); + configureIdType(config.getIdType()); + configure(config.getDbTypeConfig(), config.isAllQuotedIdentifiers()); + } + /** * Configure UUID Storage etc based on ServerConfig settings. */ - public void configure(DbTypeConfig config, boolean allQuotedIdentifiers) { + protected void configure(DbTypeConfig config, boolean allQuotedIdentifiers) { this.allQuotedIdentifiers = allQuotedIdentifiers; addGeoTypes(config.getGeometrySRID()); configureIdType(config.getIdType()); @@ -263,6 +278,20 @@ public class DatabasePlatform { return platform.name().toLowerCase(); } + /** + * Return true if we are using Sequence batch mode rather than STEP. + */ + public boolean isSequenceBatchMode() { + return sequenceBatchMode; + } + + /** + * Set to false to not use sequence batch mode but instead STEP mode. + */ + public void setSequenceBatchMode(boolean sequenceBatchMode) { + this.sequenceBatchMode = sequenceBatchMode; + } + /** * Return true if this database platform supports native ILIKE expression. */ @@ -304,10 +333,10 @@ public class DatabasePlatform { * @param be the BackgroundExecutor that can be used to load the sequence if * desired * @param ds the DataSource + * @param stepSize the sequence allocation size as defined by mapping (defaults to 50) * @param seqName the name of the sequence - * @param batchSize the number of sequences that should be loaded */ - public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) { + public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) { return null; } @@ -586,6 +615,14 @@ public class DatabasePlatform { return sql; } + /** + * For update hint on the FROM clause (SQL server only). + */ + public String fromForUpdate(Query.ForUpdate forUpdateMode) { + // return null except for sql server + return null; + } + protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) { // silently assume the database does not support the "for update" clause. logger.info("it seems your database does not support the 'for update' clause"); @@ -618,18 +655,7 @@ public class DatabasePlatform { try { return tables.next(); } finally { - close(tables); - } - } - - /** - * Close the resultSet. - */ - protected void close(ResultSet resultSet) { - try { - resultSet.close(); - } catch (SQLException e) { - logger.error("Error closing resultSet", e); + JdbcClose.close(tables); } } diff --git a/src/main/java/io/ebean/config/dbplatform/SequenceBatchIdGenerator.java b/src/main/java/io/ebean/config/dbplatform/SequenceBatchIdGenerator.java new file mode 100644 index 000000000..6444e0d55 --- /dev/null +++ b/src/main/java/io/ebean/config/dbplatform/SequenceBatchIdGenerator.java @@ -0,0 +1,56 @@ +package io.ebean.config.dbplatform; + +import io.ebean.BackgroundExecutor; + +import javax.sql.DataSource; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * Database sequence based IdGenerator using Sequence Step 1 but batch fetch many sequence values. + */ +public abstract class SequenceBatchIdGenerator extends SequenceIdGenerator { + + /** + * Construct where batchSize is the sequence step size. + * + */ + public SequenceBatchIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) { + super(be, ds, seqName, batchSize); + } + + /** + * If allocateSize is large load some sequences in a background thread. + *

+ * For example, when inserting a bean with a cascade on a OneToMany with many + * beans Ebean can call this to ensure . + *

+ */ + @Override + public void preAllocateIds(int requestSize) { + if (allocationSize > 1 && requestSize > allocationSize) { + // only bother if allocateSize is bigger than + // the normal loading batchSize + if (requestSize > 100) { + // max out at 100 for now + requestSize = 100; + } + loadInBackground(requestSize); + } + } + + /** + * Add the next set of Ids as the next value plus all the following numbers up to the step size. + */ + protected List readIds(ResultSet resultSet, int loadSize) throws SQLException { + + List newIds = new ArrayList<>(loadSize); + while (resultSet.next()) { + newIds.add(resultSet.getLong(1)); + } + return newIds; + } + +} diff --git a/src/main/java/io/ebean/config/dbplatform/SequenceIdGenerator.java b/src/main/java/io/ebean/config/dbplatform/SequenceIdGenerator.java index 3d6da7357..985a545c6 100644 --- a/src/main/java/io/ebean/config/dbplatform/SequenceIdGenerator.java +++ b/src/main/java/io/ebean/config/dbplatform/SequenceIdGenerator.java @@ -2,6 +2,7 @@ package io.ebean.config.dbplatform; import io.ebean.BackgroundExecutor; import io.ebean.Transaction; +import io.ebeaninternal.util.JdbcClose; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -12,13 +13,15 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** * Database sequence based IdGenerator. */ public abstract class SequenceIdGenerator implements PlatformIdGenerator { - private static final Logger logger = LoggerFactory.getLogger(SequenceIdGenerator.class); + protected static final Logger logger = LoggerFactory.getLogger("io.ebean.SEQ"); /** * Used to synchronise the idList access. @@ -41,18 +44,18 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator { protected final ArrayList idList = new ArrayList<>(50); - protected final int batchSize; + protected final int allocationSize; - protected int currentlyBackgroundLoading; + protected boolean currentlyBackgroundLoading; /** * Construct given a dataSource and sql to return the next sequence value. */ - public SequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) { + protected SequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int allocationSize) { this.backgroundExecutor = be; this.dataSource = ds; this.seqName = seqName; - this.batchSize = batchSize; + this.allocationSize = allocationSize; } public abstract String getSql(int batchSize); @@ -81,26 +84,8 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator { *

*/ @Override - public void preAllocateIds(int allocateSize) { - if (batchSize > 1 && allocateSize > batchSize) { - // only bother if allocateSize is bigger than - // the normal loading batchSize - if (allocateSize > 100) { - // max out at 100 for now - allocateSize = 100; - } - loadLargeAllocation(allocateSize); - } - } - - /** - * Called by preAllocateIds when we know that a large number of Id's is going - * to be needed shortly. - */ - protected void loadLargeAllocation(final int allocateSize) { - // preAllocateIds was called with a relatively large batchSize - // so we will just go ahead and load those anyway in background - backgroundExecutor.execute(() -> loadMoreIds(allocateSize, null)); + public void preAllocateIds(int requestSize) { + // do nothing by default } /** @@ -113,80 +98,80 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator { public Object nextId(Transaction t) { synchronized (monitor) { - if (idList.isEmpty()) { - loadMoreIds(batchSize, t); - } - Long nextId = idList.remove(0); - - if (batchSize > 1) { - if (idList.size() <= batchSize / 2) { - loadBatchInBackground(); - } + int size = idList.size(); + if (size > 0) { + maybeLoadMoreInBackground(size); + } else { + loadMore(allocationSize); } - return nextId; + return idList.remove(0); } } - /** - * Load another batch of Id's using a background thread. - */ - protected void loadBatchInBackground() { - - // single threaded processing... - synchronized (backgroundLoadMonitor) { - - if (currentlyBackgroundLoading > 0) { - // skip as already background loading - logger.debug("... skip background sequence load (another load in progress)"); - return; + private void maybeLoadMoreInBackground(int currentSize) { + if (allocationSize > 1) { + if (currentSize <= allocationSize / 2) { + loadInBackground(allocationSize); } - - currentlyBackgroundLoading = batchSize; - - backgroundExecutor.execute(() -> { - loadMoreIds(batchSize, null); - synchronized (backgroundLoadMonitor) { - currentlyBackgroundLoading = 0; - } - }); } } - protected void loadMoreIds(final int numberToLoad, Transaction t) { - - ArrayList newIds = getMoreIds(numberToLoad, t); - - if (logger.isDebugEnabled()) { - logger.debug("... seq:" + seqName + " loaded:" + numberToLoad + " ids:" + newIds); - } - + private void loadMore(int requestSize) { + List newIds = getMoreIds(requestSize); synchronized (monitor) { idList.addAll(newIds); } } + /** + * Load another batch of Id's using a background thread. + */ + protected void loadInBackground(final int requestSize) { + + // single threaded processing... + synchronized (backgroundLoadMonitor) { + if (currentlyBackgroundLoading) { + // skip as already background loading + logger.debug("... skip background sequence load (another load in progress)"); + return; + } + + currentlyBackgroundLoading = true; + + backgroundExecutor.execute(() -> { + loadMore(requestSize); + synchronized (backgroundLoadMonitor) { + currentlyBackgroundLoading = false; + } + }); + } + } + + /** + * Read the resultSet returning the list of Id values. + */ + protected abstract List readIds(ResultSet resultSet, int loadSize) throws SQLException; + /** * Get more Id's by executing a query and reading the Id's returned. */ - protected ArrayList getMoreIds(int loadSize, Transaction t) { + protected List getMoreIds(int requestSize) { - String sql = getSql(loadSize); + String sql = getSql(requestSize); - ArrayList newIds = new ArrayList<>(loadSize); - - boolean useTxnConnection = t != null; - - Connection c = null; - PreparedStatement pstmt = null; - ResultSet rset = null; + Connection connection = null; + PreparedStatement statement = null; + ResultSet resultSet = null; try { - c = useTxnConnection ? t.getConnection() : dataSource.getConnection(); + connection = dataSource.getConnection(); - pstmt = c.prepareStatement(sql); - rset = pstmt.executeQuery(); - while (rset.next()) { - newIds.add(rset.getLong(1)); + statement = connection.prepareStatement(sql); + resultSet = statement.executeQuery(); + + List newIds = readIds(resultSet, requestSize); + if (logger.isTraceEnabled()) { + logger.trace("seq:{} loaded:{} sql:{}", seqName, newIds.size(), sql); } if (newIds.isEmpty()) { throw new PersistenceException("Always expecting more than 1 row from " + sql); @@ -197,46 +182,24 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator { } catch (SQLException e) { if (e.getMessage().contains("Database is already closed")) { String msg = "Error getting SEQ when DB shutting down " + e.getMessage(); - logger.info(msg); + logger.error(msg); System.out.println(msg); - return newIds; + return Collections.emptyList(); } else { throw new PersistenceException("Error getting sequence nextval", e); } } finally { - if (useTxnConnection) { - closeResources(null, pstmt, rset); - } else { - closeResources(c, pstmt, rset); - } + closeResources(connection, statement, resultSet); } } /** * Close the JDBC resources. */ - protected void closeResources(Connection c, PreparedStatement pstmt, ResultSet rset) { - try { - if (rset != null) { - rset.close(); - } - } catch (SQLException e) { - logger.error("Error closing ResultSet", e); - } - try { - if (pstmt != null) { - pstmt.close(); - } - } catch (SQLException e) { - logger.error("Error closing PreparedStatement", e); - } - try { - if (c != null) { - c.close(); - } - } catch (SQLException e) { - logger.error("Error closing Connection", e); - } + private void closeResources(Connection connection, PreparedStatement statement, ResultSet resultSet) { + JdbcClose.close(resultSet); + JdbcClose.close(statement); + JdbcClose.close(connection); } } diff --git a/src/main/java/io/ebean/config/dbplatform/SequenceStepIdGenerator.java b/src/main/java/io/ebean/config/dbplatform/SequenceStepIdGenerator.java new file mode 100644 index 000000000..3e632e5fa --- /dev/null +++ b/src/main/java/io/ebean/config/dbplatform/SequenceStepIdGenerator.java @@ -0,0 +1,38 @@ +package io.ebean.config.dbplatform; + +import io.ebean.BackgroundExecutor; + +import javax.sql.DataSource; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * Database sequence based IdGenerator using Sequence Step (e.g. step 50). + */ +public abstract class SequenceStepIdGenerator extends SequenceIdGenerator { + + /** + * Construct with stepSize (typically 50). + */ + protected SequenceStepIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) { + super(be, ds, seqName, stepSize); + } + + /** + * Add the next set of Ids as the next value plus all the following numbers up to the step size. + */ + protected List readIds(ResultSet resultSet, int ignoreRequestSize) throws SQLException { + + List newIds = new ArrayList<>(allocationSize); + if (resultSet.next()) { + long start = resultSet.getLong(1); + for (int i = 0; i < allocationSize; i++) { + newIds.add(start + i); + } + } + return newIds; + } + +} diff --git a/src/main/java/io/ebean/config/dbplatform/db2/DB2Platform.java b/src/main/java/io/ebean/config/dbplatform/db2/DB2Platform.java index c8bcbd7f6..793afee2d 100644 --- a/src/main/java/io/ebean/config/dbplatform/db2/DB2Platform.java +++ b/src/main/java/io/ebean/config/dbplatform/db2/DB2Platform.java @@ -55,10 +55,9 @@ public class DB2Platform extends DatabasePlatform { * sequence values. */ @Override - public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, - DataSource ds, String seqName, int batchSize) { + public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) { - return new DB2SequenceIdGenerator(be, ds, seqName, batchSize); + return new DB2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize); } } diff --git a/src/main/java/io/ebean/config/dbplatform/db2/DB2SequenceIdGenerator.java b/src/main/java/io/ebean/config/dbplatform/db2/DB2SequenceIdGenerator.java index 788e4e396..a5170a759 100644 --- a/src/main/java/io/ebean/config/dbplatform/db2/DB2SequenceIdGenerator.java +++ b/src/main/java/io/ebean/config/dbplatform/db2/DB2SequenceIdGenerator.java @@ -1,14 +1,14 @@ package io.ebean.config.dbplatform.db2; import io.ebean.BackgroundExecutor; -import io.ebean.config.dbplatform.SequenceIdGenerator; +import io.ebean.config.dbplatform.SequenceBatchIdGenerator; import javax.sql.DataSource; /** * DB2 specific sequence Id Generator. */ -public class DB2SequenceIdGenerator extends SequenceIdGenerator { +public class DB2SequenceIdGenerator extends SequenceBatchIdGenerator { private final String baseSql; private final String unionBaseSql; diff --git a/src/main/java/io/ebean/config/dbplatform/h2/H2Platform.java b/src/main/java/io/ebean/config/dbplatform/h2/H2Platform.java index 7c3c78a36..1b093d880 100644 --- a/src/main/java/io/ebean/config/dbplatform/h2/H2Platform.java +++ b/src/main/java/io/ebean/config/dbplatform/h2/H2Platform.java @@ -46,10 +46,8 @@ public class H2Platform extends DatabasePlatform { * sequence values. */ @Override - public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, - String seqName, int batchSize) { - - return new H2SequenceIdGenerator(be, ds, seqName, batchSize); + public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) { + return new H2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize); } @Override diff --git a/src/main/java/io/ebean/config/dbplatform/h2/H2SequenceIdGenerator.java b/src/main/java/io/ebean/config/dbplatform/h2/H2SequenceIdGenerator.java index 4dafed231..b4e875511 100644 --- a/src/main/java/io/ebean/config/dbplatform/h2/H2SequenceIdGenerator.java +++ b/src/main/java/io/ebean/config/dbplatform/h2/H2SequenceIdGenerator.java @@ -1,14 +1,14 @@ package io.ebean.config.dbplatform.h2; import io.ebean.BackgroundExecutor; -import io.ebean.config.dbplatform.SequenceIdGenerator; +import io.ebean.config.dbplatform.SequenceBatchIdGenerator; import javax.sql.DataSource; /** * H2 specific sequence Id Generator. */ -public class H2SequenceIdGenerator extends SequenceIdGenerator { +public class H2SequenceIdGenerator extends SequenceBatchIdGenerator { private final String baseSql; private final String unionBaseSql; diff --git a/src/main/java/io/ebean/config/dbplatform/hsqldb/HsqldbPlatform.java b/src/main/java/io/ebean/config/dbplatform/hsqldb/HsqldbPlatform.java index 80a3a032e..1450b01f7 100644 --- a/src/main/java/io/ebean/config/dbplatform/hsqldb/HsqldbPlatform.java +++ b/src/main/java/io/ebean/config/dbplatform/hsqldb/HsqldbPlatform.java @@ -31,9 +31,8 @@ public class HsqldbPlatform extends DatabasePlatform { } @Override - public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) { - - return new H2SequenceIdGenerator(be, ds, seqName, batchSize); + public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) { + return new H2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize); } } diff --git a/src/main/java/io/ebean/config/dbplatform/mysql/MySqlPlatform.java b/src/main/java/io/ebean/config/dbplatform/mysql/MySqlPlatform.java index f004be7d5..f9973c243 100644 --- a/src/main/java/io/ebean/config/dbplatform/mysql/MySqlPlatform.java +++ b/src/main/java/io/ebean/config/dbplatform/mysql/MySqlPlatform.java @@ -1,16 +1,13 @@ package io.ebean.config.dbplatform.mysql; -import io.ebean.BackgroundExecutor; import io.ebean.Query; import io.ebean.annotation.Platform; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.config.dbplatform.DbPlatformType; import io.ebean.config.dbplatform.DbType; import io.ebean.config.dbplatform.IdType; -import io.ebean.config.dbplatform.PlatformIdGenerator; import io.ebean.config.dbplatform.SqlErrorCodes; -import javax.sql.DataSource; import java.sql.Types; /** @@ -64,16 +61,6 @@ public class MySqlPlatform extends DatabasePlatform { dbTypeMap.put(DbType.VARBINARY, new DbPlatformType("varbinary", 255)); } - /** - * Return null in case there is a sequence annotation. - */ - @Override - public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, - DataSource ds, String seqName, int batchSize) { - - return null; - } - @Override protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) { // NOWAIT and SKIP LOCKED currently not supported with MySQL diff --git a/src/main/java/io/ebean/config/dbplatform/oracle/OraclePlatform.java b/src/main/java/io/ebean/config/dbplatform/oracle/OraclePlatform.java index 5f0b1ca3e..636390dfc 100644 --- a/src/main/java/io/ebean/config/dbplatform/oracle/OraclePlatform.java +++ b/src/main/java/io/ebean/config/dbplatform/oracle/OraclePlatform.java @@ -70,9 +70,8 @@ public class OraclePlatform extends DatabasePlatform { } @Override - public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) { - - return new OracleSequenceIdGenerator(be, ds, seqName, batchSize); + public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) { + return new OracleSequenceIdGenerator(be, ds, seqName, sequenceBatchSize); } @Override diff --git a/src/main/java/io/ebean/config/dbplatform/oracle/OracleSequenceIdGenerator.java b/src/main/java/io/ebean/config/dbplatform/oracle/OracleSequenceIdGenerator.java index b0a7a2f70..32dd7a28e 100644 --- a/src/main/java/io/ebean/config/dbplatform/oracle/OracleSequenceIdGenerator.java +++ b/src/main/java/io/ebean/config/dbplatform/oracle/OracleSequenceIdGenerator.java @@ -1,14 +1,14 @@ package io.ebean.config.dbplatform.oracle; import io.ebean.BackgroundExecutor; -import io.ebean.config.dbplatform.SequenceIdGenerator; +import io.ebean.config.dbplatform.SequenceBatchIdGenerator; import javax.sql.DataSource; /** * Oracle specific sequence Id Generator. */ -public class OracleSequenceIdGenerator extends SequenceIdGenerator { +public class OracleSequenceIdGenerator extends SequenceBatchIdGenerator { private final String baseSql; diff --git a/src/main/java/io/ebean/config/dbplatform/postgres/PostgresPlatform.java b/src/main/java/io/ebean/config/dbplatform/postgres/PostgresPlatform.java index 04d1b0384..20d9107d1 100644 --- a/src/main/java/io/ebean/config/dbplatform/postgres/PostgresPlatform.java +++ b/src/main/java/io/ebean/config/dbplatform/postgres/PostgresPlatform.java @@ -90,9 +90,9 @@ public class PostgresPlatform extends DatabasePlatform { * Create a Postgres specific sequence IdGenerator. */ @Override - public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) { + public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) { - return new PostgresSequenceIdGenerator(be, ds, seqName, batchSize); + return new PostgresSequenceIdGenerator(be, ds, seqName, sequenceBatchSize); } @Override diff --git a/src/main/java/io/ebean/config/dbplatform/postgres/PostgresSequenceIdGenerator.java b/src/main/java/io/ebean/config/dbplatform/postgres/PostgresSequenceIdGenerator.java index d01a70db5..4550c926d 100644 --- a/src/main/java/io/ebean/config/dbplatform/postgres/PostgresSequenceIdGenerator.java +++ b/src/main/java/io/ebean/config/dbplatform/postgres/PostgresSequenceIdGenerator.java @@ -1,14 +1,14 @@ package io.ebean.config.dbplatform.postgres; import io.ebean.BackgroundExecutor; -import io.ebean.config.dbplatform.SequenceIdGenerator; +import io.ebean.config.dbplatform.SequenceBatchIdGenerator; import javax.sql.DataSource; /** * Postgres specific sequence Id Generator. */ -public class PostgresSequenceIdGenerator extends SequenceIdGenerator { +public class PostgresSequenceIdGenerator extends SequenceBatchIdGenerator { private final String baseSql; diff --git a/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerBasicSqlLimiter.java b/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerBasicSqlLimiter.java index 64494f34d..3ac56f727 100644 --- a/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerBasicSqlLimiter.java +++ b/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerBasicSqlLimiter.java @@ -13,7 +13,7 @@ public class SqlServerBasicSqlLimiter implements BasicSqlLimiter { StringBuilder sb = new StringBuilder(50 + dbSql.length()); sb.append(dbSql); if (!dbSql.toLowerCase().contains("order by")) { - sb.append(" order by 1 "); + sb.append(" order by 1"); } sb.append(" ").append("offset"); sb.append(" ").append(firstRow).append(" rows"); diff --git a/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerPlatform.java b/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerPlatform.java index f0ed6744a..102a7e0d5 100644 --- a/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerPlatform.java +++ b/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerPlatform.java @@ -1,13 +1,18 @@ package io.ebean.config.dbplatform.sqlserver; +import io.ebean.BackgroundExecutor; +import io.ebean.Query; import io.ebean.annotation.PersistBatch; import io.ebean.annotation.Platform; +import io.ebean.config.ServerConfig; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.config.dbplatform.DbPlatformType; import io.ebean.config.dbplatform.DbType; import io.ebean.config.dbplatform.IdType; +import io.ebean.config.dbplatform.PlatformIdGenerator; import io.ebean.config.dbplatform.SqlErrorCodes; +import javax.sql.DataSource; import java.sql.Types; /** @@ -18,8 +23,8 @@ public class SqlServerPlatform extends DatabasePlatform { public SqlServerPlatform() { super(); this.platform = Platform.SQLSERVER; - // effectively disable persistBatchOnCascade mode for SQL Server - // due to lack of support for getGeneratedKeys in batch mode + // disable persistBatchOnCascade mode for + // SQL Server unless we are using sequences this.persistBatchOnCascade = PersistBatch.NONE; this.idInExpandedForm = true; this.selectCountWithAlias = true; @@ -29,12 +34,14 @@ public class SqlServerPlatform extends DatabasePlatform { this.dbIdentity.setIdType(IdType.IDENTITY); this.dbIdentity.setSupportsGetGeneratedKeys(true); this.dbIdentity.setSupportsIdentity(true); + this.dbIdentity.setSupportsSequence(true); + this.sequenceBatchMode = false; this.exceptionTranslator = new SqlErrorCodes() .addAcquireLock("1222") .addDuplicateKey("2601", "2627") - .addDataIntegrity("544", "8114", "8115") + .addDataIntegrity("544", "547", "8114", "8115") .build(); this.openQuote = "["; @@ -67,8 +74,45 @@ public class SqlServerPlatform extends DatabasePlatform { } + @Override + public void configure(ServerConfig config) { + super.configure(config); + if (dbIdentity.getIdType() == IdType.SEQUENCE) { + this.persistBatchOnCascade = PersistBatch.ALL; + } + } + @Override protected void escapeLikeCharacter(char ch, StringBuilder sb) { sb.append('[').append(ch).append(']'); } + + /** + * Create a Postgres specific sequence IdGenerator. + */ + @Override + public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) { + return new SqlServerStepSequence(be, ds, seqName, stepSize); + } + + /** + * For update is part of the FROM clause on the base table for sql server. + */ + @Override + public String fromForUpdate(Query.ForUpdate forUpdateMode) { + switch (forUpdateMode) { + case SKIPLOCKED: + return "with (updlock,readpast)"; + case NOWAIT: + return "with (updlock,nowait)"; + default: + return "with (updlock)"; + } + } + + @Override + protected String withForUpdate(String sql, Query.ForUpdate forUpdateMode) { + // for update are hints on from clause of base table + return sql; + } } diff --git a/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerStepSequence.java b/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerStepSequence.java new file mode 100644 index 000000000..d9a53a9f7 --- /dev/null +++ b/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerStepSequence.java @@ -0,0 +1,24 @@ +package io.ebean.config.dbplatform.sqlserver; + +import io.ebean.BackgroundExecutor; +import io.ebean.config.dbplatform.SequenceStepIdGenerator; + +import javax.sql.DataSource; + +public class SqlServerStepSequence extends SequenceStepIdGenerator { + + private final String nextSql; + + /** + * Construct where batchSize is the sequence step size. + */ + public SqlServerStepSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) { + super(be, ds, seqName, stepSize); + this.nextSql = "select next value for "+seqName; + } + + @Override + public String getSql(int batchSize) { + return nextSql; + } +} diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java index 55057bddf..66e5e64e0 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java @@ -308,9 +308,7 @@ public class PlatformDdl { if (initialValue > 1) { sb.append(" start with ").append(initialValue); } - if (allocationSize > 0 && allocationSize != 50) { - // at this stage ignoring allocationSize 50 as this is the 'default' and - // not consistent with the way Ebean batch fetches sequence values + if (allocationSize > 1) { sb.append(" increment by ").append(allocationSize); } sb.append(";"); diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/SqlServerDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/SqlServerDdl.java index 16b541d88..843abad99 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/SqlServerDdl.java +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/SqlServerDdl.java @@ -121,9 +121,7 @@ public class SqlServerDdl extends PlatformDdl { } else { sb.append(" start with 1 "); } - if (allocationSize > 0 && allocationSize != 50) { - // at this stage ignoring allocationSize 50 as this is the 'default' and - // not consistent with the way Ebean batch fetches sequence values + if (allocationSize > 1) { sb.append(" increment by ").append(allocationSize); } sb.append(";"); diff --git a/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java b/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java index ff4fd4338..0f72ca48c 100644 --- a/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java +++ b/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java @@ -270,7 +270,7 @@ public class DefaultContainer implements SpiContainer { config.setDatabasePlatform(platform); } logger.info("DatabasePlatform name:{} platform:{}", config.getName(), platform.getName()); - platform.configure(config.getDbTypeConfig(), config.isAllQuotedIdentifiers()); + platform.configure(config); } /** diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java index ed2945ef6..618183220 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java @@ -173,8 +173,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap { private final BackgroundExecutor backgroundExecutor; - private final int dbSequenceBatchSize; - private final EncryptKeyManager encryptKeyManager; private final IdBinderFactory idBinderFactory; @@ -206,7 +204,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap { this.serverName = InternString.intern(serverConfig.getName()); this.cacheManager = config.getCacheManager(); this.docStoreFactory = config.getDocStoreFactory(); - this.dbSequenceBatchSize = serverConfig.getDatabaseSequenceBatchSize(); this.backgroundExecutor = config.getBackgroundExecutor(); this.dataSource = serverConfig.getDataSource(); this.encryptKeyManager = serverConfig.getEncryptKeyManager(); @@ -1391,12 +1388,16 @@ public class BeanDescriptorManager implements BeanDescriptorMap { seqName = namingConvention.getSequenceName(desc.getBaseTable(), primaryKeyColumn); } - // create the sequence based IdGenerator - desc.setIdGenerator(createSequenceIdGenerator(seqName)); + if (databasePlatform.isSequenceBatchMode()) { + // use sequence next step 1 as we are going to batch fetch them instead + desc.setSequenceAllocationSize(1); + } + int stepSize = desc.getSequenceAllocationSize(); + desc.setIdGenerator(createSequenceIdGenerator(seqName, stepSize)); } - private PlatformIdGenerator createSequenceIdGenerator(String seqName) { - return databasePlatform.createSequenceIdGenerator(backgroundExecutor, dataSource, seqName, dbSequenceBatchSize); + private PlatformIdGenerator createSequenceIdGenerator(String seqName, int stepSize) { + return databasePlatform.createSequenceIdGenerator(backgroundExecutor, dataSource, stepSize, seqName); } private void createByteCode(DeployBeanDescriptor deploy) { diff --git a/src/main/java/io/ebeaninternal/server/deploy/DbSqlContext.java b/src/main/java/io/ebeaninternal/server/deploy/DbSqlContext.java index fa1563866..ff5402dbc 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/DbSqlContext.java +++ b/src/main/java/io/ebeaninternal/server/deploy/DbSqlContext.java @@ -129,4 +129,9 @@ public interface DbSqlContext { * Start group by clause. */ void startGroupBy(); + + /** + * Append 'for update' lock hints on FROM clause (sql server only). + */ + void appendFromForUpdate(); } diff --git a/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java b/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java index 36e14a2e8..83416486a 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java +++ b/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java @@ -107,7 +107,7 @@ public class DeployBeanDescriptor { private int sequenceInitialValue; - private int sequenceAllocationSize; + private int sequenceAllocationSize = 50; /** * Used with Identity columns but no getGeneratedKeys support. diff --git a/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java b/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java index 6ae7a12a0..c178371ee 100644 --- a/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java +++ b/src/main/java/io/ebeaninternal/server/query/CQueryBuilder.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.query; +import io.ebean.Query; import io.ebean.RawSql; import io.ebean.RawSqlBuilder; import io.ebean.annotation.Platform; @@ -671,4 +672,16 @@ class CQueryBuilder { boolean isPlatformDistinctOn() { return dbPlatform.isPlatform(Platform.POSTGRES); } + + /** + * Return the 'for update' FROM hint (sql server). + */ + public String fromForUpdate(SpiQuery query) { + Query.ForUpdate mode = query.getForUpdateMode(); + if (mode == null) { + return null; + } else { + return dbPlatform.fromForUpdate(mode); + } + } } diff --git a/src/main/java/io/ebeaninternal/server/query/DefaultDbSqlContext.java b/src/main/java/io/ebeaninternal/server/query/DefaultDbSqlContext.java index cae5da9f0..a2138104b 100644 --- a/src/main/java/io/ebeaninternal/server/query/DefaultDbSqlContext.java +++ b/src/main/java/io/ebeaninternal/server/query/DefaultDbSqlContext.java @@ -26,6 +26,8 @@ class DefaultDbSqlContext implements DbSqlContext { private final ArrayStack prefixStack = new ArrayStack<>(); + private final String fromForUpdate; + private boolean useColumnAlias; private int columnIndex; @@ -55,7 +57,8 @@ class DefaultDbSqlContext implements DbSqlContext { * Construct for SELECT clause (with column alias settings). */ DefaultDbSqlContext(SqlTreeAlias alias, CQueryBuilder builder, - boolean alwaysUseColumnAlias, CQueryHistorySupport historySupport, CQueryDraftSupport draftSupport) { + boolean alwaysUseColumnAlias, CQueryHistorySupport historySupport, + CQueryDraftSupport draftSupport, String fromForUpdate) { this.alias = alias; this.tableAliasPlaceHolder = builder.tableAliasPlaceHolder; @@ -64,6 +67,14 @@ class DefaultDbSqlContext implements DbSqlContext { this.draftSupport = draftSupport; this.historySupport = historySupport; this.historyQuery = (historySupport != null); + this.fromForUpdate = fromForUpdate; + } + + @Override + public void appendFromForUpdate() { + if (fromForUpdate != null) { + append(" ").append(fromForUpdate); + } } @Override diff --git a/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java b/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java index d5e0f408c..d5ea84855 100644 --- a/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/src/main/java/io/ebeaninternal/server/query/SqlTreeBuilder.java @@ -119,9 +119,10 @@ public final class SqlTreeBuilder { this.alias = new SqlTreeAlias(request.getBaseTableAlias()); this.distinctOnPlatform = builder.isPlatformDistinctOn(); + String fromForUpdate = builder.fromForUpdate(query); CQueryHistorySupport historySupport = builder.getHistorySupport(query); CQueryDraftSupport draftSupport = builder.getDraftSupport(query); - this.ctx = new DefaultDbSqlContext(alias, builder, !subQuery, historySupport, draftSupport); + this.ctx = new DefaultDbSqlContext(alias, builder, !subQuery, historySupport, draftSupport, fromForUpdate); } /** diff --git a/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeRoot.java b/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeRoot.java index b956115b6..bb93b25e1 100644 --- a/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeRoot.java +++ b/src/main/java/io/ebeaninternal/server/query/SqlTreeNodeRoot.java @@ -68,6 +68,7 @@ final class SqlTreeNodeRoot extends SqlTreeNodeBean { ctx.append(desc.getBaseTable(temporalMode)); ctx.append(" ").append(baseTableAlias); + ctx.appendFromForUpdate(); if (includeJoin != null) { String a1 = baseTableAlias; diff --git a/src/main/java/io/ebeaninternal/util/JdbcClose.java b/src/main/java/io/ebeaninternal/util/JdbcClose.java index 647f34709..f74e3dd46 100644 --- a/src/main/java/io/ebeaninternal/util/JdbcClose.java +++ b/src/main/java/io/ebeaninternal/util/JdbcClose.java @@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Statement; /** * Utility for closing raw Jdbc resources. @@ -14,6 +15,19 @@ public class JdbcClose { private static final Logger logger = LoggerFactory.getLogger(JdbcClose.class); + /** + * Close the resultSet logging if an error occurs. + */ + public static void close(Statement statement) { + try { + if (statement != null) { + statement.close(); + } + } catch (SQLException e) { + logger.warn("Error closing statement", e); + } + } + /** * Close the resultSet logging if an error occurs. */ diff --git a/src/test/java/io/ebean/config/MatchingNamingConventionTest.java b/src/test/java/io/ebean/config/MatchingNamingConventionTest.java index 8f29e2357..2cca6c06d 100644 --- a/src/test/java/io/ebean/config/MatchingNamingConventionTest.java +++ b/src/test/java/io/ebean/config/MatchingNamingConventionTest.java @@ -16,10 +16,13 @@ public class MatchingNamingConventionTest { } @Test - public void getColumnFromProperty_when_allQuoted() throws Exception { + public void getColumnFromProperty_when_allQuoted() { SqlServerPlatform platform = new SqlServerPlatform(); - platform.configure(new DbTypeConfig(), true); + + ServerConfig config = new ServerConfig(); + config.setAllQuotedIdentifiers(true); + platform.configure(config); NamingConvention nc = new MatchingNamingConvention(); nc.setDatabasePlatform(platform); @@ -29,7 +32,7 @@ public class MatchingNamingConventionTest { } @Test - public void getColumnFromProperty() throws Exception { + public void getColumnFromProperty() { String fkCol = "bridgetab_userId"; String col = namingConvention.getColumnFromProperty(null, fkCol); diff --git a/src/test/java/io/ebean/config/ServerConfigTest.java b/src/test/java/io/ebean/config/ServerConfigTest.java index 860ad9582..0eea08122 100644 --- a/src/test/java/io/ebean/config/ServerConfigTest.java +++ b/src/test/java/io/ebean/config/ServerConfigTest.java @@ -1,6 +1,7 @@ package io.ebean.config; import io.ebean.annotation.PersistBatch; +import io.ebean.config.dbplatform.IdType; import org.avaje.datasource.DataSourceConfig; import org.junit.Test; @@ -46,6 +47,7 @@ public class ServerConfigTest { props.setProperty("autoReadOnlyDataSource", "true"); props.setProperty("disableL2Cache", "true"); props.setProperty("notifyL2CacheInForeground", "true"); + props.setProperty("idType", "SEQUENCE"); serverConfig.loadFromProperties(props); @@ -54,6 +56,7 @@ public class ServerConfigTest { assertTrue(serverConfig.isDbOffline()); assertTrue(serverConfig.isAutoReadOnlyDataSource()); + assertEquals(IdType.SEQUENCE, serverConfig.getIdType()); assertEquals(PersistBatch.INSERT, serverConfig.getPersistBatch()); assertEquals(PersistBatch.INSERT, serverConfig.getPersistBatchOnCascade()); assertEquals(ServerConfig.DbUuid.BINARY, serverConfig.getDbTypeConfig().getDbUuid()); diff --git a/src/test/java/io/ebean/config/dbplatform/sqlserver/SqlServerStepSequenceTest.java b/src/test/java/io/ebean/config/dbplatform/sqlserver/SqlServerStepSequenceTest.java new file mode 100644 index 000000000..3745ef4fa --- /dev/null +++ b/src/test/java/io/ebean/config/dbplatform/sqlserver/SqlServerStepSequenceTest.java @@ -0,0 +1,55 @@ +package io.ebean.config.dbplatform.sqlserver; + +import io.ebean.BackgroundExecutor; +import io.ebean.BaseTestCase; +import io.ebean.annotation.ForPlatform; +import io.ebean.annotation.Platform; +import org.junit.Ignore; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.sql.DataSource; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SqlServerStepSequenceTest extends BaseTestCase { + + private static final Logger log = LoggerFactory.getLogger(SqlServerStepSequenceTest.class); + + @Ignore + @ForPlatform(Platform.SQLSERVER) + @Test + public void seq() { + + + server().createSqlUpdate("drop sequence if exists sqls_testseq_9876").execute(); + server().createSqlUpdate("create sequence sqls_testseq_9876 start with 1 increment by 50").execute(); + + BackgroundExecutor be = server().getBackgroundExecutor(); + DataSource ds = server().getPluginApi().getDataSource(); + + SqlServerStepSequence s = new SqlServerStepSequence(be, ds, "sqls_testseq_9876", 50); + + Object id = s.nextId(null); + assertThat(id).isEqualTo(1L); + + for (int i = 0; i < 20; i++) { + Object val = s.nextId(null); + log.warn("val: "+val); + } + + log.warn("here"); + + for (int i = 0; i < 20; i++) { + Object val = s.nextId(null); + log.warn("val: "+val); + } + + for (int i = 0; i < 100; i++) { + Object val = s.nextId(null); + log.warn("val: "+val); + } + } + +}