#1268 - SQL Server fix for "for update" plus Sequences support plus refactor of sequences

This commit is contained in:
Rob Bygrave
2018-02-22 22:45:44 +13:00
parent e3fac7c622
commit f8ea5619d0
32 changed files with 439 additions and 182 deletions
@@ -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(";");
@@ -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(";");
@@ -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);
}
/**
@@ -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) {
@@ -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();
}
@@ -107,7 +107,7 @@ public class DeployBeanDescriptor<T> {
private int sequenceInitialValue;
private int sequenceAllocationSize;
private int sequenceAllocationSize = 50;
/**
* Used with Identity columns but no getGeneratedKeys support.
@@ -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);
}
}
}
@@ -26,6 +26,8 @@ class DefaultDbSqlContext implements DbSqlContext {
private final ArrayStack<String> 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
@@ -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);
}
/**
@@ -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;
@@ -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.
*/