#374 - MsSqlServer - adjust tests (due to jdbc batch support etc) add in support for unique index handling

This commit is contained in:
Robin Bygrave
2015-08-06 21:49:04 +12:00
parent 3a9668fe6c
commit 1dd0841f00
31 changed files with 586 additions and 360 deletions
+7
View File
@@ -120,6 +120,13 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>microsoft</groupId>
<artifactId>sqlserver-jdbc</artifactId>
<version>4.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp</artifactId>
@@ -23,6 +23,7 @@ public class MsSqlServer2005Platform extends DatabasePlatform {
// due to lack of support for getGeneratedKeys in batch mode
this.disallowBatchOnCascade = true;
this.idInExpandedForm = true;
this.selectCountWithAlias = true;
this.sqlLimiter = new MsSqlServer2005SqlLimiter();
this.platformDdl = new MsSqlServerDdl(dbTypeMap, dbIdentity);
this.dbIdentity.setIdType(IdType.IDENTITY);
@@ -46,9 +47,9 @@ public class MsSqlServer2005Platform extends DatabasePlatform {
dbTypeMap.put(Types.LONGVARBINARY, new DbType("image"));
dbTypeMap.put(Types.LONGVARCHAR, new DbType("text"));
dbTypeMap.put(Types.DATE, new DbType("datetime"));
dbTypeMap.put(Types.TIME, new DbType("datetime"));
dbTypeMap.put(Types.TIMESTAMP, new DbType("datetime"));
dbTypeMap.put(Types.DATE, new DbType("date"));
dbTypeMap.put(Types.TIME, new DbType("time"));
dbTypeMap.put(Types.TIMESTAMP, new DbType("datetime2"));
}
@@ -29,6 +29,11 @@ public class BaseTableDdl implements TableDdl {
*/
protected IndexSet indexSet = new IndexSet();
/**
* Used when unique constraints specifically for OneToOne can't be created normally (MsSqlServer).
*/
protected IndexSet externalUnique = new IndexSet();
// counters used when constraint names are truncated due to maximum length
// and these counters are used to keep the constraint name unique
protected int countCheck;
@@ -45,10 +50,11 @@ public class BaseTableDdl implements TableDdl {
}
/**
* Reset counters and index set for each table.
* Reset counters and index set for each table processed.
*/
protected void reset() {
indexSet.clear();
externalUnique.clear();
countCheck = 0;
countUnique = 0;
countForeignKey = 0;
@@ -57,7 +63,7 @@ public class BaseTableDdl implements TableDdl {
/**
* Generate the appropriate 'create table' and matching 'drop table' statements
* and add them to the 'apply' and 'rollback' buffers.
* and add them to the appropriate 'apply' and 'rollback' buffers.
*/
@Override
public void generate(DdlWrite writer, CreateTable createTable) throws IOException {
@@ -98,6 +104,8 @@ public class BaseTableDdl implements TableDdl {
apply.newLine().append(")").endOfStatement();
writeUniqueOneToOneConstraints(writer, createTable);
// add drop table to the rollback buffer - do this before
// we drop the related sequence (if sequences are used)
dropTable(writer.rollback(), tableName);
@@ -118,6 +126,29 @@ public class BaseTableDdl implements TableDdl {
}
/**
* Specific handling of OneToOne unique constraints for MsSqlServer.
* For all other DB platforms these unique constraints are done inline as per normal.
*/
private void writeUniqueOneToOneConstraints(DdlWrite write, CreateTable createTable) throws IOException {
String tableName = createTable.getName();
for (IndexColumns index : externalUnique.indexes) {
String uqName = determineUniqueConstraintName(tableName, index.joinedNames());
write.apply()
.append(platformDdl.createExternalUniqueForOneToOne(uqName, tableName, index.columnsArray()))
.endOfStatement();
// register it so we check against effective duplication
// when creating the foreign key indexes
indexSet.add(index);
write.rollbackForeignKeys()
.append(platformDdl.dropIndex(uqName, tableName))
.endOfStatement();
}
}
private void writeSequence(DdlWrite writer, CreateTable createTable) throws IOException {
// explicit sequence use or platform decides
@@ -231,7 +262,7 @@ public class BaseTableDdl implements TableDdl {
private void appendColumns(String[] columns, DdlBuffer buffer) throws IOException {
buffer.append(" (");
for (int i = 0; i <columns.length ; i++) {
for (int i = 0; i < columns.length; i++) {
if (i > 0) {
buffer.append(",");
}
@@ -284,11 +315,17 @@ public class BaseTableDdl implements TableDdl {
*/
protected void writeUniqueConstraints(DdlBuffer apply, CreateTable createTable) throws IOException {
boolean inlineUniqueOneToOne = platformDdl.isInlineUniqueOneToOne();
List<Column> columns = createTable.getColumn();
for (Column column : columns) {
if (isTrue(column.isUnique())) {
if (isTrue(column.isUnique()) || (inlineUniqueOneToOne && isTrue(column.isUniqueOneToOne()))) {
// normal mechanism for adding unique constraint
inlineUniqueConstraintSingle(apply, createTable.getName(), column);
indexSet.add(column);
} else if (!inlineUniqueOneToOne && isTrue(column.isUniqueOneToOne())) {
// MsSqlServer specific mechanism for adding unique constraints (that allow nulls)
externalUnique.add(column);
}
}
}
@@ -459,7 +496,10 @@ public class BaseTableDdl implements TableDdl {
return Boolean.TRUE.equals(value);
}
private int toInt(BigInteger value) {
/**
* Return as an int value with 0 when it is null.
*/
protected int toInt(BigInteger value) {
return (value == null) ? 0 : value.intValue();
}
@@ -497,7 +537,7 @@ public class BaseTableDdl implements TableDdl {
*/
public boolean add(String[] columns) {
IndexColumns newIndex = new IndexColumns(columns);
for (int i = 0; i <indexes.size() ; i++) {
for (int i = 0; i < indexes.size(); i++) {
if (indexes.get(i).isMatch(newIndex)) {
return false;
}
@@ -505,6 +545,14 @@ public class BaseTableDdl implements TableDdl {
indexes.add(newIndex);
return true;
}
/**
* Add the externally created unique constraint here so that we check later if foreign key indexes
* don't need to be created (as the columns match this unique constraint).
*/
public void add(IndexColumns index) {
indexes.add(index);
}
}
/**
@@ -525,7 +573,7 @@ public class BaseTableDdl implements TableDdl {
* Construct representing index.
*/
public IndexColumns(String[] columnNames) {
for (int i = 0; i <columnNames.length; i++) {
for (int i = 0; i < columnNames.length; i++) {
columns.add(columnNames[i]);
}
}
@@ -537,8 +585,36 @@ public class BaseTableDdl implements TableDdl {
return columns.equals(other.columns);
}
/**
* Add a unique index based on the single column.
*/
protected void add(String column) {
columns.add(column);
}
/**
* Return the columns as a string array.
*/
public String[] columnsArray() {
return columns.toArray(new String[columns.size()]);
}
/**
* Return the column names all joined with underscore.
*/
public String joinedNames() {
if (columns.size() == 1) {
return columns.get(0);
} else {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < columns.size(); i++) {
if (i > 0) {
sb.append("_");
}
sb.append(columns.get(i));
}
return sb.toString();
}
}
}
}
@@ -10,32 +10,42 @@ public class MsSqlServerDdl extends PlatformDdl {
public MsSqlServerDdl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
super(platformTypes, dbIdentity);
this.identitySuffix = " generated by default as identity";
this.identitySuffix = " identity(1,1)";
this.foreignKeyRestrict = "";
this.inlineUniqueOneToOne = false;
}
@Override
public String dropTable(String tableName) {
return "IF OBJECT_ID('" + tableName + "', 'U') IS NOT NULL drop table " + tableName;
}
@Override
public String alterTableDropForeignKey(String tableName, String fkName) {
return "IF OBJECT_ID('" + fkName + "', 'F') IS NOT NULL " + super.alterTableDropForeignKey(tableName, fkName);
}
/**
* MsSqlServer specific null handling on unique constraints.
*/
@Override
public String createExternalUniqueForOneToOne(String uqName, String tableName, String[] columns) {
// issues#233
String start = "create unique nonclustered index " + uqName + " on " + tableName+ "(";
StringBuilder sb = new StringBuilder(start);
for (int i = 0; i < columns.length; i++) {
if (i > 0) {
sb.append(",");
}
sb.append(columns[i]);
}
sb.append(") where");
for (int i = 0; i < columns.length; i++) {
sb.append(" ").append(columns[i]).append(" is not null");
}
return sb.toString();
}
// /**
// * MS SQL Server specific DDL Syntax.
// */
// public class MsDdlSyntax extends DbDdlSyntax {
//
// MsDdlSyntax() {
// this.identity = "identity(1,1)";
// this.dropKeyConstraints = true;
// }
//
// /**
// * Return some DDL to disable constraints on the given table.
// */
// public String dropKeyConstraintPrefix(String tableName, String fkName) {
// return "IF OBJECT_ID('"+fkName+"', 'F') IS NOT NULL";
// }
//
// /**
// * Return prefix text that goes before drop table.
// */
// public String dropTablePrefix(String tableName) {
// return "IF OBJECT_ID('"+tableName+"', 'U') IS NOT NULL ";
// }
//
// }
}
@@ -47,6 +47,10 @@ public class PlatformDdl {
protected String identitySuffix = " auto_increment";
/**
* Set false for MsSqlServer to allow multiple nulls for OneToOne mapping.
*/
protected boolean inlineUniqueOneToOne = true;
public PlatformDdl(DbTypeMap platformTypes, DbIdentity dbIdentity) {
this.dbIdentity = dbIdentity;
@@ -157,4 +161,19 @@ public class PlatformDdl {
}
/**
* Return true if unique constraints for OneToOne can be inlined as normal.
* Returns false for MsSqlServer due to it's null handling for unique constraints.
*/
public boolean isInlineUniqueOneToOne() {
return inlineUniqueOneToOne;
}
/**
* Overridden by MsSqlServer for specific null handling on unique constraints.
*/
public String createExternalUniqueForOneToOne(String uqName, String tableName, String[] columns) {
// does nothing by default, really this is a MsSqlServer specific requirement
return "";
}
}
@@ -42,6 +42,8 @@ public class Column {
protected String checkConstraint;
@XmlAttribute(name = "unique")
protected Boolean unique;
@XmlAttribute(name = "uniqueOneToOne")
protected Boolean uniqueOneToOne;
@XmlAttribute(name = "primaryKey")
protected Boolean primaryKey;
@XmlAttribute(name = "identity")
@@ -153,6 +155,30 @@ public class Column {
this.unique = value;
}
/**
* Gets the value of the uniqueOneToOne property.
*
* @return
* possible object is
* {@link Boolean }
*
*/
public Boolean isUniqueOneToOne() {
return uniqueOneToOne;
}
/**
* Sets the value of the uniqueOneToOne property.
*
* @param value
* allowed object is
* {@link Boolean }
*
*/
public void setUniqueOneToOne(Boolean value) {
this.uniqueOneToOne = value;
}
/**
* Gets the value of the primaryKey property.
*
@@ -15,8 +15,15 @@ public class MColumn {
private boolean notnull;
private boolean primaryKey;
private boolean identity;
private boolean unique;
/**
* Special unique for OneToOne as we need to handle that different
* specifically for MsSqlServer.
*/
private boolean uniqueOneToOne;
public MColumn(Column column) {
this.name = column.getName();
this.type = column.getType();
@@ -104,15 +111,31 @@ public class MColumn {
return unique;
}
/**
* Set unique specifically for OneToOne mapping.
* We need special DDL for this case for MsSqlServer.
*/
public void setUniqueOneToOne(boolean uniqueOneToOne) {
this.uniqueOneToOne = uniqueOneToOne;
}
/**
* Return true if this is unique for a OneToOne.
*/
public boolean isUniqueOneToOne() {
return uniqueOneToOne;
}
public Column createColumn() {
Column c = new Column();
c.setName(name);
c.setType(type);
if (notnull) c.setNotnull(notnull);
if (unique) c.setUnique(unique);
if (primaryKey) c.setPrimaryKey(primaryKey);
if (identity) c.setIdentity(identity);
if (notnull) c.setNotnull(true);
if (unique) c.setUnique(true);
if (uniqueOneToOne) c.setUniqueOneToOne(true);
if (primaryKey) c.setPrimaryKey(true);
if (identity) c.setIdentity(true);
c.setCheckConstraint(checkConstraint);
c.setReferences(references);
@@ -9,16 +9,33 @@ package com.avaje.ebean.dbmigration.model;
*/
public class MCompoundUniqueConstraint {
/**
* Flag if true indicates this was specifically created for a OneToOne mapping.
*/
private final boolean oneToOne;
/**
* The columns combined to be unique.
*/
private final String[] columns;
public MCompoundUniqueConstraint(String[] columns) {
public MCompoundUniqueConstraint(String[] columns, boolean oneToOne) {
this.columns = columns;
this.oneToOne = oneToOne;
}
/**
* Return the columns for this unique constraint.
*/
public String[] getColumns() {
return columns;
}
/**
* Return true if this unqiue constraint is specifically for OneToOne mapping.
*/
public boolean isOneToOne() {
return oneToOne;
}
}
@@ -248,19 +248,19 @@ public class MTable {
/**
* Add a compound unique constraint.
*/
public void addCompoundUniqueConstraint(String[] columns) {
compoundUniqueConstraints.add(new MCompoundUniqueConstraint(columns));
public void addCompoundUniqueConstraint(String[] columns, boolean oneToOne) {
compoundUniqueConstraints.add(new MCompoundUniqueConstraint(columns, oneToOne));
}
/**
* Add a compound unique constraint.
*/
public void addCompoundUniqueConstraint(List<MColumn> columns) {
public void addCompoundUniqueConstraint(List<MColumn> columns, boolean oneToOne) {
String[] cols = new String[columns.size()];
for (int i = 0; i < columns.size(); i++) {
cols[i] = columns.get(i).getName();
}
addCompoundUniqueConstraint(cols);
addCompoundUniqueConstraint(cols, oneToOne);
}
public void addForeignKey(MCompoundForeignKey compoundKey) {
@@ -3,34 +3,30 @@ package com.avaje.ebean.dbmigration.model.build;
import com.avaje.ebean.config.dbplatform.DbType;
import com.avaje.ebean.config.dbplatform.IdType;
import com.avaje.ebean.dbmigration.migration.IdentityType;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebean.dbmigration.model.MColumn;
import com.avaje.ebean.dbmigration.model.MTable;
import com.avaje.ebean.dbmigration.model.visitor.BeanPropertyVisitor;
import com.avaje.ebean.dbmigration.model.visitor.BeanVisitor;
import com.avaje.ebeaninternal.server.type.ScalarType;
import java.sql.Types;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
/**
* Used to build the Model objects MTable etc.
*/
public class ModelBuildBeanVisitor implements BeanVisitor {
private final ModelBuildContext ctx;
private final ModelBuildContext ctx;
public ModelBuildBeanVisitor(ModelBuildContext ctx) {
this.ctx = ctx;
}
public ModelBuildBeanVisitor(ModelBuildContext ctx) {
this.ctx = ctx;
}
/**
* Return the PropertyVisitor used to read all the property meta data
* and in this case add MColumn objects to the model.
* <p>
* This creates an MTable and adds it to the model.
* This creates an MTable and adds it to the model.
* </p>
*/
public BeanPropertyVisitor visitBean(BeanDescriptor<?> descriptor) {
@@ -59,7 +55,7 @@ public class ModelBuildBeanVisitor implements BeanVisitor {
CompoundUniqueContraint[] compoundUniqueConstraints = descriptor.getCompoundUniqueConstraints();
if (compoundUniqueConstraints != null) {
for (int i = 0; i < compoundUniqueConstraints.length; i++) {
table.addCompoundUniqueConstraint(compoundUniqueConstraints[i].getColumns());
table.addCompoundUniqueConstraint(compoundUniqueConstraints[i].getColumns(), false);
}
}
@@ -68,7 +64,6 @@ public class ModelBuildBeanVisitor implements BeanVisitor {
private void setIdentity(BeanDescriptor<?> descriptor, MTable table) {
if (IdType.GENERATOR == descriptor.getIdType()) {
// explicit generator like UUID
table.setIdentityType(IdentityType.GENERATOR);
@@ -94,20 +89,7 @@ public class ModelBuildBeanVisitor implements BeanVisitor {
table.setSequenceInitial(initialValue);
table.setSequenceAllocate(allocationSize);
}
return;
}
BeanProperty idProperty = descriptor.getIdProperty();
if (idProperty != null) {
ScalarType<Object> scalarType = idProperty.getScalarType();
if (scalarType != null) {
int jdbcType = scalarType.getJdbcType();
if (jdbcType == Types.VARCHAR) {
System.out.println("asd");
}
}
}
}
}
@@ -115,14 +115,13 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
modelColumns.add(col);
}
if (p.isOneToOne()) {
// Adding the unique constraint restricts the cardinality from OneToMany down to OneToOne
// adding the unique constraint restricts the cardinality from OneToMany down to OneToOne
// for MsSqlServer we need different DDL to handle NULL values on this constraint
if (modelColumns.size() == 1) {
modelColumns.get(0).setUnique(true);
modelColumns.get(0).setUniqueOneToOne(true);
} else {
table.addCompoundUniqueConstraint(modelColumns);
table.addCompoundUniqueConstraint(modelColumns, true);
}
}
}
@@ -169,6 +169,7 @@ public class DefaultCsvCallback<T> implements CsvCallback<T> {
logger.info("Creating transaction, batchSize[" + persistBatchSize + "]");
transaction.setBatchMode(true);
transaction.setBatchSize(persistBatchSize);
transaction.setBatchGetGeneratedKeys(false);
} else {
// explicitly turn off JDBC batching in case
@@ -264,6 +264,7 @@
<xsd:attribute name="notnull" type="xsd:boolean"/>
<xsd:attribute name="checkConstraint" type="xsd:string"/>
<xsd:attribute name="unique" type="xsd:boolean"/>
<xsd:attribute name="uniqueOneToOne" type="xsd:boolean"/>
<xsd:attribute name="primaryKey" type="xsd:boolean"/>
<xsd:attribute name="identity" type="xsd:boolean"/> <!-- aka autoincrement/identity -->
<xsd:attribute name="references" type="xsd:string"/>
@@ -1,5 +1,6 @@
package com.avaje.ebean;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import org.avaje.agentloader.AgentLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -15,4 +16,12 @@ public class BaseTestCase {
}
}
/**
* MS SQL Server does not allow setting explicit values on identity columns
* so tests that do this need to be skipped for SQL Server.
*/
public boolean isMsSqlServer() {
SpiEbeanServer spi = (SpiEbeanServer)Ebean.getDefaultServer();
return spi.getDatabasePlatform().getName().startsWith("mssqlserver");
}
}
@@ -23,6 +23,7 @@ public class TestBatchPersistCascade extends BaseTestCase {
@Test
public void test() {
if (isMsSqlServer()) return;
EbeanServer ebeanServer = Ebean.getServer(null);
@@ -15,142 +15,139 @@ import com.avaje.tests.model.basic.ResetBasicData;
public class TestLimitQuery extends BaseTestCase {
@Test
public void testNothing() {
}
@Test
public void testLimitWithMany() {
rob();
rob();
}
@Test
public void testLimitWithMany() {
rob();
rob();
}
@Test
public void testMaxRowsZeroWithFirstRow() {
@Test
public void testMaxRowsZeroWithFirstRow() {
ResetBasicData.reset();
ResetBasicData.reset();
SpiEbeanServer server = (SpiEbeanServer)Ebean.getServer(null);
boolean h2Db = "h2".equals(server.getDatabasePlatform().getName());
SpiEbeanServer server = (SpiEbeanServer) Ebean.getServer(null);
boolean h2Db = "h2".equals(server.getDatabasePlatform().getName());
Query<Order> query = Ebean.find(Order.class)
.setAutofetch(false)
.fetch("details")
.where().gt("details.id", 0)
.setMaxRows(0)
.setFirstRow(3);
Query<Order> query = Ebean.find(Order.class)
.setAutofetch(false)
.fetch("details")
.where().gt("details.id", 0)
.setMaxRows(0)
.setFirstRow(3)
.order().asc("orderDate");
query.findList();
query.findList();
String sql = query.getGeneratedSql();
boolean hasLimit = sql.contains("limit 0");
boolean hasOffset = sql.contains("offset 3");
String sql = query.getGeneratedSql();
boolean hasLimit = sql.contains("limit 0");
boolean hasOffset = sql.contains("offset 3");
if (h2Db) {
Assert.assertTrue(hasLimit);
Assert.assertTrue(hasOffset);
}
}
if (h2Db) {
Assert.assertTrue(hasLimit);
Assert.assertTrue(hasOffset);
}
}
@Test
public void testMaxRowsWithFirstRowZero() {
ResetBasicData.reset();
@Test
public void testMaxRowsWithFirstRowZero() {
ResetBasicData.reset();
SpiEbeanServer server = (SpiEbeanServer)Ebean.getServer(null);
boolean h2Db = "h2".equals(server.getDatabasePlatform().getName());
SpiEbeanServer server = (SpiEbeanServer) Ebean.getServer(null);
boolean h2Db = "h2".equals(server.getDatabasePlatform().getName());
Query<Order> query = Ebean.find(Order.class)
.setAutofetch(false)
.fetch("details")
.where().gt("details.id", 0)
.setMaxRows(3)
.setFirstRow(0);
Query<Order> query = Ebean.find(Order.class)
.setAutofetch(false)
.fetch("details")
.where().gt("details.id", 0)
.setMaxRows(3)
.setFirstRow(0);
query.findList();
query.findList();
String sql = query.getGeneratedSql();
boolean hasLimit = sql.contains("limit 3");
boolean hasOffset = sql.contains("offset");
String sql = query.getGeneratedSql();
boolean hasLimit = sql.contains("limit 3");
boolean hasOffset = sql.contains("offset");
if (h2Db) {
Assert.assertTrue(sql, hasLimit);
Assert.assertFalse(sql, hasOffset);
}
}
if (h2Db) {
Assert.assertTrue(sql, hasLimit);
Assert.assertFalse(sql, hasOffset);
}
}
@Test
public void testDefaults() {
ResetBasicData.reset();
@Test
public void testDefaults() {
ResetBasicData.reset();
SpiEbeanServer server = (SpiEbeanServer)Ebean.getServer(null);
boolean h2Db = "h2".equals(server.getDatabasePlatform().getName());
SpiEbeanServer server = (SpiEbeanServer) Ebean.getServer(null);
boolean h2Db = "h2".equals(server.getDatabasePlatform().getName());
Query<Order> query = Ebean.find(Order.class)
.setAutofetch(false)
.fetch("details")
.where().gt("details.id", 0)
.query();
Query<Order> query = Ebean.find(Order.class)
.setAutofetch(false)
.fetch("details")
.where().gt("details.id", 0)
.query();
query.findList();
query.findList();
String sql = query.getGeneratedSql();
boolean hasLimit = sql.contains("limit");
boolean hasOffset = sql.contains("offset");
String sql = query.getGeneratedSql();
boolean hasLimit = sql.contains("limit");
boolean hasOffset = sql.contains("offset");
if (h2Db) {
Assert.assertFalse(hasLimit);
Assert.assertFalse(hasOffset);
}
}
if (h2Db) {
Assert.assertFalse(hasLimit);
Assert.assertFalse(hasOffset);
}
}
private void rob() {
ResetBasicData.reset();
SpiEbeanServer server = (SpiEbeanServer)Ebean.getServer(null);
boolean h2Db = "h2".equals(server.getDatabasePlatform().getName());
Query<Order> query = Ebean.find(Order.class)
.setAutofetch(false)
.fetch("details")
.where().gt("details.id", 0)
.setMaxRows(10);
//.findList();
List<Order> list = query.findList();
Assert.assertTrue("sz > 0", list.size() > 0);
private void rob() {
ResetBasicData.reset();
String sql = query.getGeneratedSql();
boolean hasDetailsJoin = sql.contains("join o_order_detail");
boolean hasLimit = sql.contains("limit 10");
boolean hasSelectedDetails = sql.contains("od.id,");
boolean hasDistinct = sql.contains("select distinct");
Assert.assertTrue(hasDetailsJoin);
Assert.assertFalse(hasSelectedDetails);
Assert.assertTrue(hasDistinct);
if (h2Db){
Assert.assertTrue(hasLimit);
}
query = Ebean.find(Order.class)
.setAutofetch(false)
.fetch("details")
.setMaxRows(10);
query.findList();
sql = query.getGeneratedSql();
hasDetailsJoin = sql.contains("left outer join o_order_detail");
hasLimit = sql.contains("limit 10");
hasSelectedDetails = sql.contains("od.id");
hasDistinct = sql.contains("select distinct");
SpiEbeanServer server = (SpiEbeanServer) Ebean.getServer(null);
boolean h2Db = "h2".equals(server.getDatabasePlatform().getName());
Assert.assertFalse("no join with maxRows",hasDetailsJoin);
Assert.assertFalse(hasSelectedDetails);
Assert.assertFalse(hasDistinct);
if (h2Db){
Assert.assertTrue(hasLimit);
}
}
Query<Order> query = Ebean.find(Order.class)
.setAutofetch(false)
.fetch("details")
.where().gt("details.id", 0)
.setMaxRows(10);
//.findList();
List<Order> list = query.findList();
Assert.assertTrue("sz > 0", list.size() > 0);
String sql = query.getGeneratedSql();
boolean hasDetailsJoin = sql.contains("join o_order_detail");
boolean hasLimit = sql.contains("limit 10");
boolean hasSelectedDetails = sql.contains("od.id,");
boolean hasDistinct = sql.contains("select distinct");
Assert.assertTrue(hasDetailsJoin);
Assert.assertFalse(hasSelectedDetails);
Assert.assertTrue(hasDistinct);
if (h2Db) {
Assert.assertTrue(hasLimit);
}
query = Ebean.find(Order.class)
.setAutofetch(false)
.fetch("details")
.setMaxRows(10);
query.findList();
sql = query.getGeneratedSql();
hasDetailsJoin = sql.contains("left outer join o_order_detail");
hasLimit = sql.contains("limit 10");
hasSelectedDetails = sql.contains("od.id");
hasDistinct = sql.contains("select distinct");
Assert.assertFalse("no join with maxRows", hasDetailsJoin);
Assert.assertFalse(hasSelectedDetails);
Assert.assertFalse(hasDistinct);
if (h2Db) {
Assert.assertTrue(hasLimit);
}
}
}
@@ -27,6 +27,8 @@ public class TestBatchInsertSimple extends BaseTestCase {
transaction.setBatch(PersistBatch.NONE);
transaction.setBatchOnCascade(PersistBatch.INSERT);
transaction.setBatchSize(30);
// setBatchGetGeneratedKeys MUST be turned off for MS SQL Server because :(
transaction.setBatchGetGeneratedKeys(false);
for (int i = 0; i < numOfMasters; i++) {
UTMaster master = createMasterAndDetails(i, 20);
@@ -43,6 +45,8 @@ public class TestBatchInsertSimple extends BaseTestCase {
@Test
public void testTransactional() {
if (isMsSqlServer()) return;
saveWithFullBatchMode();
}
@@ -69,6 +73,8 @@ public class TestBatchInsertSimple extends BaseTestCase {
transaction.setBatch(PersistBatch.NONE);
transaction.setBatchOnCascade(PersistBatch.INSERT);
transaction.setBatchSize(30);
// setBatchGetGeneratedKeys MUST be turned off for MS SQL Server because :(
transaction.setBatchGetGeneratedKeys(false);
for (int i = 0; i < numOfMasters; i++) {
UTMaster master = createMaster(i);
@@ -85,6 +91,9 @@ public class TestBatchInsertSimple extends BaseTestCase {
@Test
public void testJdbcBatchOnCollection() {
// MS SQL Server doesn't like batch inserts when we need getGeneratedKeys
if (isMsSqlServer()) return;
int numOfMasters = 3;
List<UTMaster> masters = new ArrayList<UTMaster>();
@@ -18,6 +18,8 @@ public class TestBatchInsertWithInitialisedCollection extends BaseTestCase {
@Test
public void test() {
if (isMsSqlServer()) return;
List<OCachedBean> list = new ArrayList();
for (int i = 0; i < 3; i++) {
@@ -22,141 +22,147 @@ import com.avaje.tests.lib.EbeanTestCase;
* <li>find</li>
* </ul>
*/
public class TestCore extends EbeanTestCase
{
//private boolean setup;
public class TestCore extends EbeanTestCase {
@Override
public void setUp() throws Exception
{
Ebean.createUpdate(Item.class, "delete from Item").execute();
Ebean.createUpdate(Region.class, "delete from Region").execute();
Ebean.createUpdate(Type.class, "delete from Type").execute();
Ebean.createUpdate(SubType.class, "delete from SubType").execute();
@Override
public void setUp() throws Exception {
if (isMsSqlServer()) return;
Transaction tx = getServer().beginTransaction();
Ebean.createUpdate(Item.class, "delete from Item").execute();
Ebean.createUpdate(Region.class, "delete from Region").execute();
Ebean.createUpdate(Type.class, "delete from Type").execute();
Ebean.createUpdate(SubType.class, "delete from SubType").execute();
SubType subType = new SubType();
SubTypeKey subTypeKey = new SubTypeKey();
subTypeKey.setSubTypeId(1);
subType.setKey(subTypeKey);
subType.setDescription("ANY SUBTYPE");
getServer().save(subType);
Transaction tx = getServer().beginTransaction();
Type type = new Type();
TypeKey typeKey = new TypeKey();
typeKey.setCustomer(1);
typeKey.setType(10);
type.setKey(typeKey);
type.setDescription("Type Old-Item - Customer 1");
type.setSubType(subType);
getServer().save(type);
SubType subType = new SubType();
SubTypeKey subTypeKey = new SubTypeKey();
subTypeKey.setSubTypeId(1);
subType.setKey(subTypeKey);
subType.setDescription("ANY SUBTYPE");
getServer().save(subType);
type = new Type();
typeKey = new TypeKey();
typeKey.setCustomer(2);
typeKey.setType(10);
type.setKey(typeKey);
type.setDescription("Type Old-Item - Customer 2");
type.setSubType(subType);
getServer().save(type);
Type type = new Type();
TypeKey typeKey = new TypeKey();
typeKey.setCustomer(1);
typeKey.setType(10);
type.setKey(typeKey);
type.setDescription("Type Old-Item - Customer 1");
type.setSubType(subType);
getServer().save(type);
Region region = new Region();
RegionKey regionKey = new RegionKey();
regionKey.setCustomer(1);
regionKey.setType(500);
region.setKey(regionKey);
region.setDescription("Region West - Customer 1");
getServer().save(region);
type = new Type();
typeKey = new TypeKey();
typeKey.setCustomer(2);
typeKey.setType(10);
type.setKey(typeKey);
type.setDescription("Type Old-Item - Customer 2");
type.setSubType(subType);
getServer().save(type);
region = new Region();
regionKey = new RegionKey();
regionKey.setCustomer(2);
regionKey.setType(500);
region.setKey(regionKey);
region.setDescription("Region West - Customer 2");
getServer().save(region);
Region region = new Region();
RegionKey regionKey = new RegionKey();
regionKey.setCustomer(1);
regionKey.setType(500);
region.setKey(regionKey);
region.setDescription("Region West - Customer 1");
getServer().save(region);
Item item = new Item();
ItemKey itemKey = new ItemKey();
itemKey.setCustomer(1);
itemKey.setItemNumber("ITEM1");
item.setKey(itemKey);
item.setUnits("P");
item.setDescription("Fancy Car - Customer 1");
item.setRegion(500);
item.setType(10);
getServer().save(item);
region = new Region();
regionKey = new RegionKey();
regionKey.setCustomer(2);
regionKey.setType(500);
region.setKey(regionKey);
region.setDescription("Region West - Customer 2");
getServer().save(region);
item = new Item();
itemKey = new ItemKey();
itemKey.setCustomer(2);
itemKey.setItemNumber("ITEM1");
item.setKey(itemKey);
item.setUnits("P");
item.setDescription("Another Fancy Car - Customer 2");
item.setRegion(500);
item.setType(10);
getServer().save(item);
Item item = new Item();
ItemKey itemKey = new ItemKey();
itemKey.setCustomer(1);
itemKey.setItemNumber("ITEM1");
item.setKey(itemKey);
item.setUnits("P");
item.setDescription("Fancy Car - Customer 1");
item.setRegion(500);
item.setType(10);
getServer().save(item);
tx.commit();
}
item = new Item();
itemKey = new ItemKey();
itemKey.setCustomer(2);
itemKey.setItemNumber("ITEM1");
item.setKey(itemKey);
item.setUnits("P");
item.setDescription("Another Fancy Car - Customer 2");
item.setRegion(500);
item.setType(10);
getServer().save(item);
public void testFind()
{
List<Item> items = getServer().find(Item.class).findList();
tx.commit();
}
assertNotNull(items);
assertEquals(2, items.size());
public void testFind() {
if (isMsSqlServer()) return;
Query<Item> qItems = getServer().find(Item.class);
List<Item> items = getServer().find(Item.class).findList();
assertNotNull(items);
assertEquals(2, items.size());
Query<Item> qItems = getServer().find(Item.class);
// qItems.where(Expr.eq("key.customer", Integer.valueOf(1)));
// I want to discourage the direct use of Expr
qItems.where().eq("key.customer", Integer.valueOf(1));
items = qItems.findList();
assertNotNull(items);
assertEquals(1, items.size());
}
// I want to discourage the direct use of Expr
qItems.where().eq("key.customer", Integer.valueOf(1));
items = qItems.findList();
/**
* This partially loads the item and then lazy loads the ManyToOne assoc
*/
public void testDoubleLazyLoad()
{
ItemKey itemKey = new ItemKey();
itemKey.setCustomer(2);
itemKey.setItemNumber("ITEM1");
Item item = getServer().find(Item.class).select("description").where().idEq(itemKey).findUnique();
assertNotNull(item);
assertNotNull(item.getUnits());
assertEquals("P", item.getUnits());
assertNotNull(items);
assertEquals(1, items.size());
}
Type type = item.getEType();
assertNotNull(type);
assertNotNull(type.getDescription());
/**
* This partially loads the item and then lazy loads the ManyToOne assoc
*/
public void testDoubleLazyLoad() {
SubType subType = type.getSubType();
assertNotNull(subType);
assertNotNull(subType.getDescription());
}
if (isMsSqlServer()) return;
public void testEmbeddedWithOrder()
{
List<Item> items = getServer().find(Item.class).order("auditInfo.created asc, type asc").findList();
ItemKey itemKey = new ItemKey();
itemKey.setCustomer(2);
itemKey.setItemNumber("ITEM1");
assertNotNull(items);
assertEquals(2, items.size());
}
public void testFindAndOrderByEType() {
List<Item> items = getServer().find(Item.class).order("eType").findList();
Item item = getServer().find(Item.class).select("description").where().idEq(itemKey).findUnique();
assertNotNull(item);
assertNotNull(item.getUnits());
assertEquals("P", item.getUnits());
assertNotNull(items);
assertEquals(2, items.size());
}
Type type = item.getEType();
assertNotNull(type);
assertNotNull(type.getDescription());
SubType subType = type.getSubType();
assertNotNull(subType);
assertNotNull(subType.getDescription());
}
public void testEmbeddedWithOrder() {
if (isMsSqlServer()) return;
List<Item> items = getServer().find(Item.class).order("auditInfo.created asc, type asc").findList();
assertNotNull(items);
assertEquals(2, items.size());
}
public void testFindAndOrderByEType() {
if (isMsSqlServer()) return;
List<Item> items = getServer().find(Item.class).order("eType").findList();
assertNotNull(items);
assertEquals(2, items.size());
}
}
@@ -31,7 +31,12 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
*/
public class TestOnCascadeDeleteChildrenWithCompositeKeys extends BaseTestCase {
@Before public void before() {
if (isMsSqlServer()) return;
// remove all the User records first
Ebean.deleteAll(Ebean.find(User.class).findList());
@@ -44,6 +49,9 @@ public class TestOnCascadeDeleteChildrenWithCompositeKeys extends BaseTestCase {
* work fine when delete User one by one
*/
@Test public void testDeleteById() {
if (isMsSqlServer()) return;
assertEquals(2, Ebean.find(User.class).findList().size());
Ebean.delete(User.class, 1L);
Ebean.delete(User.class, 2L);
@@ -55,6 +63,9 @@ public class TestOnCascadeDeleteChildrenWithCompositeKeys extends BaseTestCase {
* SQL generated: delete from user_role where (user_id) in ((?,?),(?,?))
*/
@Test public void testDeleteByIdList() {
if (isMsSqlServer()) return;
assertEquals(2, Ebean.find(User.class).findList().size());
List<Long> ids = new ArrayList<Long>();
ids.add(1L);
@@ -65,6 +76,9 @@ public class TestOnCascadeDeleteChildrenWithCompositeKeys extends BaseTestCase {
}
@Test public void testFindByParentIdList() {
if (isMsSqlServer()) return;
assertEquals(2, Ebean.find(User.class).findList().size());
SpiEbeanServer spiServer = (SpiEbeanServer)Ebean.getServer(null);
@@ -15,7 +15,9 @@ public class TestDeleteByIdWithPersistenceContext extends BaseTestCase {
@Test
public void test() {
if (isMsSqlServer()) return;
ResetBasicData.reset();
Ebean.delete(Product.class, 100);
@@ -1,5 +1,6 @@
package com.avaje.tests.lib;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import junit.framework.TestCase;
import junit.framework.TestResult;
@@ -28,7 +29,16 @@ public abstract class EbeanTestCase extends TestCase {
}
public EbeanServer getServer() {
return Ebean.getServer(null);
}
/**
* MS SQL Server does not allow setting explicit values on identity columns
* so tests that do this need to be skipped for SQL Server.
*/
public boolean isMsSqlServer() {
SpiEbeanServer spi = (SpiEbeanServer)Ebean.getDefaultServer();
return spi.getDatabasePlatform().getName().startsWith("mssqlserver");
}
}
@@ -18,6 +18,8 @@ public class TestInsertBatchThenFlushThenUpdate extends BaseTestCase {
@Test
public void test() {
if (isMsSqlServer()) return;
LoggedSqlCollector.start();
Transaction txn = Ebean.beginTransaction();
try {
@@ -17,6 +17,8 @@ public class TestInsertBatchThenUpdate extends BaseTestCase {
@Test
public void test() {
if (isMsSqlServer()) return;
LoggedSqlCollector.start();
Transaction txn = Ebean.beginTransaction();
try {
@@ -17,6 +17,8 @@ public class TestInsertBatchWithDifferentRootTypes extends BaseTestCase {
@Test
public void testDifferRootTypes() {
if (isMsSqlServer()) return;
LoggedSqlCollector.start();
Transaction txn = Ebean.beginTransaction();
try {
@@ -10,22 +10,22 @@ import com.avaje.tests.model.basic.ResetBasicData;
public class TestQueryLimitOffsetSimple extends BaseTestCase {
/**
* Test the syntax of the limit offset clause.
*/
/**
* Test the syntax of the limit offset clause.
*/
@Test
public void testMe() {
ResetBasicData.reset();
Query<Order> query = Ebean.createQuery(Order.class, "where status = :A limit 100 offset 3");
query.setParameter("A",Order.Status.NEW);
query
.setFirstRow(10)
//.setMaxRows(100)
.findList();
}
public void testMe() {
ResetBasicData.reset();
Query<Order> query = Ebean.createQuery(Order.class, "where status = :A limit 100 offset 3");
query.setParameter("A", Order.Status.NEW);
query
.setFirstRow(10)
.order().asc("id")
.findList();
}
}
@@ -14,6 +14,8 @@ public class TestOrderByWithFunction extends BaseTestCase {
@Test
public void testWithFunction() {
if (isMsSqlServer()) return;
ResetBasicData.reset();
Query<Customer> query = Ebean.find(Customer.class).order("length(name),name");
@@ -1,26 +1,28 @@
package com.avaje.tests.rawsql;
import com.avaje.tests.idkeys.db.AuditLog;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.SqlUpdate;
public class TestInsertSqlLogging extends BaseTestCase {
@Test
public void test() {
Ebean.delete(AuditLog.class, 10000);
String sql = "insert into audit_log (id, description, modified_description) values (?,?,?)";
SqlUpdate sqlUpdate = Ebean.createSqlUpdate(sql);
sqlUpdate.setParameter(1, 10000);
sqlUpdate.setParameter(2, "hello");
sqlUpdate.setParameter(3, "rob");
sqlUpdate.execute();
}
}
package com.avaje.tests.rawsql;
import com.avaje.tests.idkeys.db.AuditLog;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.SqlUpdate;
public class TestInsertSqlLogging extends BaseTestCase {
@Test
public void test() {
if (isMsSqlServer()) return;
Ebean.delete(AuditLog.class, 10000);
String sql = "insert into audit_log (id, description, modified_description) values (?,?,?)";
SqlUpdate sqlUpdate = Ebean.createSqlUpdate(sql);
sqlUpdate.setParameter(1, 10000);
sqlUpdate.setParameter(2, "hello");
sqlUpdate.setParameter(3, "rob");
sqlUpdate.execute();
}
}
@@ -42,7 +42,9 @@ public class TestRawSqlOrmQuery extends BaseTestCase {
@Test
public void testFirstRowsMaxRows() throws InterruptedException, ExecutionException {
if (isMsSqlServer()) return;
ResetBasicData.reset();
RawSql rawSql =
@@ -13,6 +13,8 @@ public class TestSaveSamePK extends BaseTestCase {
@Test
public void test() {
if (isMsSqlServer()) return;
// delete in case we are running multiple times without full db drop
Ebean.delete(TSMaster.class, 10000);
+5 -5
View File
@@ -23,7 +23,7 @@ ebean.autofetch.traceUsageCollection=false
ebean.ddl.generate=true
ebean.ddl.run=true
datasource.default=h2
datasource.default=ms
ebean.persistBatch=NONE
ebean.persistBatchOnCascade=ALL
@@ -113,8 +113,8 @@ datasource.pg.password=unit
datasource.pg.databaseUrl=jdbc:postgresql://127.0.0.1:5432/unit
datasource.pg.databaseDriver=org.postgresql.Driver
#datasource.ms.username=sa
#datasource.ms.password=changeme
#datasource.ms.databaseUrl=jdbc:sqlserver://localhost:65188
#datasource.ms.databaseDriver=com.microsoft.sqlserver.jdbc.SQLServerDriver
datasource.ms.username=test
datasource.ms.password=test
datasource.ms.databaseUrl=jdbc:sqlserver://192.168.1.68:1433;databaseName=test
datasource.ms.databaseDriver=com.microsoft.sqlserver.jdbc.SQLServerDriver