diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml
index ca3d094a5..9aac6017d 100644
--- a/ebean-api/pom.xml
+++ b/ebean-api/pom.xml
@@ -49,7 +49,7 @@
io.ebeanebean-annotation
- 7.4-FOC3
+ 7.6
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java
index d46f6dd37..3950411aa 100644
--- a/ebean-api/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java
@@ -55,12 +55,12 @@ public class DatabasePlatform {
protected boolean supportsSavepointId = true;
protected boolean useMigrationStoredProcedures = false;
-
+
/**
* Can we use native java time API objects in
* {@link ResultSet#getObject(int, Class)} and
* {@link PreparedStatement#setObject(int, Object)}.
- *
+ *
* Not all drivers (DB2 e.g.) will support this.
*/
protected boolean supportsNativeJavaTime = true;
@@ -214,6 +214,8 @@ public class DatabasePlatform {
*/
protected PersistBatch persistBatchOnCascade = PersistBatch.ALL;
+ protected int maxInBinding;
+
/**
* The maximum length of table names - used specifically when derived
* default table names for intersection tables.
@@ -370,6 +372,13 @@ public class DatabasePlatform {
return inlineSqlUpdateLimit;
}
+ /**
+ * Return the maximum number of bind values this database platform allows or zero for no limit.
+ */
+ public int getMaxInBinding() {
+ return maxInBinding;
+ }
+
/**
* Return the maximum table name length.
*
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2Platform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/BaseDB2Platform.java
similarity index 85%
rename from ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2Platform.java
rename to ebean-api/src/main/java/io/ebean/config/dbplatform/db2/BaseDB2Platform.java
index 2d2673b46..949e978c3 100644
--- a/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2Platform.java
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/BaseDB2Platform.java
@@ -15,16 +15,11 @@ import io.ebean.config.dbplatform.SqlErrorCodes;
/**
* DB2 specific platform.
*/
-public class DB2Platform extends DatabasePlatform {
+public abstract class BaseDB2Platform extends DatabasePlatform {
- public DB2Platform() {
+ public BaseDB2Platform() {
super();
this.platform = Platform.DB2;
- // Note: DB2 (at least LUW supports length up to 128)
- // TOOD: Check if we need to introduce a new platform (DB2_LUW_11 ?)
- // FIXME: This differs to original ebean branch, but is required run tests.
- this.maxTableNameLength = 128;
- this.maxConstraintNameLength = 128;
this.supportsNativeJavaTime = false;
this.truncateTable = "truncate table %s reuse storage ignore delete triggers immediate";
this.likeClauseRaw = "like ?";
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2ForIPlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2ForIPlatform.java
new file mode 100644
index 000000000..fa6028858
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2ForIPlatform.java
@@ -0,0 +1,39 @@
+package io.ebean.config.dbplatform.db2;
+
+import io.ebean.annotation.Platform;
+import io.ebean.config.dbplatform.DbPlatformType;
+import io.ebean.config.dbplatform.DbType;
+import io.ebean.config.dbplatform.SqlErrorCodes;
+
+import java.sql.Types;
+
+/**
+ * DB2 specific platform for i Series.
+ *
+ * @author Cédric Sougné
+ */
+public class DB2ForIPlatform extends BaseDB2Platform {
+
+ public DB2ForIPlatform() {
+ super();
+ this.platform = Platform.DB2FORI;
+ // Note: IBM i from 7.1 allow up to to 128
+ // TODO: Check if we need to introduce older platform (DB2ForI_6 ? but older
+ // documentation is not anymore published on ibm.com),
+ this.maxTableNameLength = 128;
+ this.maxConstraintNameLength = 128;
+
+ this.dbIdentity.setSupportsIdentity(true);
+
+ this.exceptionTranslator = new SqlErrorCodes().addAcquireLock("57033") // key -913
+ .addDuplicateKey("23505") // -803
+ // .addDataIntegrity("-407","-530","-531","-532","-543","-544","-545","-603","-667")
+ // we need SQLState, not code:
+ // https://www.ibm.com/support/knowledgecenter/en/SSEPEK_10.0.0/codes/src/tpc/db2z_n.html
+ .addDataIntegrity("23502", "23503", "23504", "23507", "23511", "23512", "23513", "42917", "23515")
+ .build();
+
+ booleanDbType = Types.SMALLINT;
+ dbTypeMap.put(DbType.BOOLEAN, new DbPlatformType("smallint default 0"));
+ }
+}
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2LegacyPlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2LegacyPlatform.java
new file mode 100644
index 000000000..51f03c2a4
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2LegacyPlatform.java
@@ -0,0 +1,20 @@
+package io.ebean.config.dbplatform.db2;
+
+import io.ebean.annotation.Platform;
+
+/**
+ * DB2 specific platform for older DB2 versions. This platform is here for
+ * compatibility reasons. It uses a length limit of 18 chars for table and
+ * constraint names. Newer DB2 versions will support up to 128. It is strongly
+ * recommended to migrate to db2luw/DB2ForI or db2zos platform.
+ */
+public class DB2LegacyPlatform extends BaseDB2Platform {
+ public DB2LegacyPlatform() {
+ super();
+ this.platform = Platform.DB2;
+ // Note: DB2 (at least LUW supports length up to 128)
+ // TOOD: Check if we need to introduce a new platform (DB2_LUW_11 ?)
+ this.maxTableNameLength = 18;
+ this.maxConstraintNameLength = 18;
+ }
+}
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2LuwPlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2LuwPlatform.java
new file mode 100644
index 000000000..967cc2428
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2LuwPlatform.java
@@ -0,0 +1,17 @@
+package io.ebean.config.dbplatform.db2;
+
+import io.ebean.annotation.Platform;
+
+/**
+ * DB2 platform for Linux/Unix/Windows.
+ * @author Roland Praml, FOCONIS AG
+ *
+ */
+public class DB2LuwPlatform extends BaseDB2Platform {
+ public DB2LuwPlatform() {
+ super();
+ this.platform = Platform.DB2LUW;
+ this.maxTableNameLength = 128;
+ this.maxConstraintNameLength = 128;
+ }
+}
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2ZosPlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2ZosPlatform.java
new file mode 100644
index 000000000..2cb4cb35b
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/db2/DB2ZosPlatform.java
@@ -0,0 +1,18 @@
+package io.ebean.config.dbplatform.db2;
+
+import io.ebean.annotation.Platform;
+
+/**
+ * DB2 platform for z/OS.
+ * Note: This platform is currently not tested!
+ * @author Roland Praml, FOCONIS AG
+ *
+ */
+public class DB2ZosPlatform extends BaseDB2Platform {
+ public DB2ZosPlatform() {
+ super();
+ this.platform = Platform.DB2ZOS;
+ this.maxTableNameLength = 128;
+ this.maxConstraintNameLength = 128;
+ }
+}
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OraclePlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OraclePlatform.java
index e9dd57207..74f68ced7 100644
--- a/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OraclePlatform.java
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/oracle/OraclePlatform.java
@@ -23,6 +23,7 @@ public class OraclePlatform extends DatabasePlatform {
super();
this.platform = Platform.ORACLE;
this.supportsDeleteTableAlias = true;
+ this.maxInBinding = 1000;
this.maxTableNameLength = 30;
this.maxConstraintNameLength = 30;
this.dbEncrypt = new OracleDbEncrypt();
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerBasePlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerBasePlatform.java
index eb3dad7be..4dd2077b2 100644
--- a/ebean-api/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerBasePlatform.java
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/sqlserver/SqlServerBasePlatform.java
@@ -27,6 +27,7 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
// SQL Server unless we are using sequences
this.dbEncrypt = new SqlServerDbEncrypt();
this.persistBatchOnCascade = PersistBatch.NONE;
+ this.maxInBinding = 2000;
this.idInExpandedForm = true;
this.selectCountWithAlias = true;
this.selectCountWithColumnAlias = true;
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/yugabyte/YugabytePlaform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/yugabyte/YugabytePlaform.java
new file mode 100644
index 000000000..622f8e5f8
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/yugabyte/YugabytePlaform.java
@@ -0,0 +1,12 @@
+package io.ebean.config.dbplatform.yugabyte;
+
+import io.ebean.annotation.Platform;
+import io.ebean.config.dbplatform.postgres.PostgresPlatform;
+
+public class YugabytePlaform extends PostgresPlatform {
+
+ public YugabytePlaform() {
+ super();
+ this.platform = Platform.YUGABYTE;
+ }
+}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java
index 96600609a..5b49d5292 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DatabasePlatformFactory.java
@@ -4,7 +4,10 @@ import io.ebean.config.DatabaseConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.clickhouse.ClickHousePlatform;
import io.ebean.config.dbplatform.cockroach.CockroachPlatform;
-import io.ebean.config.dbplatform.db2.DB2Platform;
+import io.ebean.config.dbplatform.db2.DB2ForIPlatform;
+import io.ebean.config.dbplatform.db2.DB2LegacyPlatform;
+import io.ebean.config.dbplatform.db2.DB2LuwPlatform;
+import io.ebean.config.dbplatform.db2.DB2ZosPlatform;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hana.HanaPlatform;
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
@@ -21,12 +24,14 @@ import io.ebean.config.dbplatform.sqlanywhere.SqlAnywherePlatform;
import io.ebean.config.dbplatform.sqlite.SQLitePlatform;
import io.ebean.config.dbplatform.sqlserver.SqlServer16Platform;
import io.ebean.config.dbplatform.sqlserver.SqlServer17Platform;
+import io.ebean.config.dbplatform.yugabyte.YugabytePlaform;
import io.ebeaninternal.api.CoreLog;
import io.ebeaninternal.api.DbOffline;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import java.sql.*;
+import java.util.Locale;
/**
* Create a DatabasePlatform from the configuration.
@@ -103,7 +108,19 @@ public class DatabasePlatformFactory {
return new SqlAnywherePlatform();
}
if (dbName.equals("db2")) {
- return new DB2Platform();
+ throw new IllegalArgumentException("Please choose the more specific db2luw/db2zos/db2fori platform. Refer to issue #2514 for details");
+ }
+ if (dbName.equals("db2legacy")) {
+ return new DB2LegacyPlatform();
+ }
+ if (dbName.equals("db2zos")) {
+ return new DB2ZosPlatform();
+ }
+ if (dbName.equals("db2fori")) {
+ return new DB2ForIPlatform();
+ }
+ if (dbName.equals("db2luw")) {
+ return new DB2LuwPlatform();
}
if (dbName.equals("clickhouse")) {
return new ClickHousePlatform();
@@ -149,7 +166,8 @@ public class DatabasePlatformFactory {
} else if (dbProductName.contains("hsql database engine")) {
return new HsqldbPlatform();
} else if (dbProductName.contains("postgres")) {
- return readPostgres(connection, majorVersion);
+ String productVersion = metaData.getDatabaseProductVersion().toLowerCase(Locale.ENGLISH);
+ return readPostgres(connection, majorVersion, productVersion);
} else if (dbProductName.contains("mariadb")) {
return new MariaDbPlatform();
} else if (dbProductName.contains("mysql")) {
@@ -159,7 +177,7 @@ public class DatabasePlatformFactory {
} else if (dbProductName.contains("sqlite")) {
return new SQLitePlatform();
} else if (dbProductName.contains("db2")) {
- return new DB2Platform();
+ throw new IllegalArgumentException("For DB2 please explicitly choose either db2legacy/db2luw/db2zos/db2fori platform. Refer to issue #2514 for details");
} else if (dbProductName.contains("sql anywhere")) {
return new SqlAnywherePlatform();
} else if (dbProductName.contains("hdb")) {
@@ -192,11 +210,14 @@ public class DatabasePlatformFactory {
/**
* Use a select version() query as it could be Postgres or CockroachDB.
*/
- private static DatabasePlatform readPostgres(Connection connection, int majorVersion) {
+ private static DatabasePlatform readPostgres(Connection connection, int majorVersion, String productVersion) {
+ if (productVersion.contains("-yb-")) {
+ return new YugabytePlaform();
+ }
try (PreparedStatement statement = connection.prepareStatement("select version() as \"version\"")) {
try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
- String productVersion = resultSet.getString("version").toLowerCase();
+ productVersion = resultSet.getString("version").toLowerCase();
if (productVersion.contains("cockroach")) {
return new CockroachPlatform();
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java b/ebean-core/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java
index bc80213c0..da3eee778 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/expression/platform/DbExpressionHandlerFactory.java
@@ -15,6 +15,8 @@ public final class DbExpressionHandlerFactory {
case H2:
return new H2DbExpression();
case POSTGRES:
+ case YUGABYTE:
+ case COCKROACH:
return new PostgresDbExpression();
case MARIADB:
return new MariaDbExpression();
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/DefaultPersister.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/DefaultPersister.java
index eaa1e1c95..07410134b 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/DefaultPersister.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/DefaultPersister.java
@@ -1,7 +1,6 @@
package io.ebeaninternal.server.persist;
import io.ebean.*;
-import io.ebean.annotation.Platform;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.BeanCollection.ModifyListenMode;
import io.ebean.bean.EntityBean;
@@ -42,15 +41,15 @@ public final class DefaultPersister implements Persister {
* Actually does the persisting work.
*/
private final PersistExecute persistExecute;
-
private final SpiEbeanServer server;
-
private final BeanDescriptorManager beanDescriptorManager;
+ private final int maxInBinding;
public DefaultPersister(SpiEbeanServer server, Binder binder, BeanDescriptorManager descMgr) {
this.server = server;
this.beanDescriptorManager = descMgr;
this.persistExecute = new DefaultPersistExecute(binder, server.config().getPersistBatchSize());
+ this.maxInBinding = server.databasePlatform().getMaxInBinding();
}
@Override
@@ -645,18 +644,18 @@ public final class DefaultPersister implements Persister {
DeleteMode deleteMode = (permanent || !descriptor.isSoftDelete()) ? DeleteMode.HARD : DeleteMode.SOFT;
return delete(descriptor, null, idList, transaction, deleteMode);
}
-
+
/**
* Delete by Id or a List of Id's.
*/
private int delete(BeanDescriptor> descriptor, Object id, List