Compare commits

..
Author SHA1 Message Date
rob bygrave 08d241d946 #1966 Remove sequence batching - postgres opps 2020-03-05 00:34:05 +13:00
rob bygrave 1c24ebf6dc Remove Sequence batching in favour of JPA increment 50
Also remove h2sqldb in favour of always using H2
2020-03-05 00:22:20 +13:00
rob bygrave 0ce39c73f1 Test only update postgres DDL review 2020-03-05 00:21:36 +13:00
rob bygrave 82d6294dcb Test only update h2 DDL review 2020-03-05 00:13:41 +13:00
rob bygrave ba01103f48 #1965 @Identity with sequence ddl generation 2020-03-05 00:13:08 +13:00
Rob BygraveandGitHub 3e14ec6a5a Merge pull request #1965 from ebean-orm/feature/identityMode2
Support for @Identity - with cache, generated by (ALWAYS/BY DEFAULT), start, increment options
2020-03-04 22:56:44 +13:00
rob bygrave b9d5d35850 Set testing default back to H2 2020-03-04 22:55:51 +13:00
rob bygrave 04adc35ccd Support for @Identity - Fix sequence name propagation 2020-03-04 22:49:08 +13:00
rob bygrave bfc2b57df5 Support for @Identity - use identity options with sequence 2020-03-04 21:29:56 +13:00
rob bygrave a5880fca97 Support for @Identity - update sql server ddl review 2020-03-04 21:23:50 +13:00
rob bygrave 8685f2056a Support for @Identity - update ddl review 2020-03-04 17:18:01 +13:00
rob bygrave 2ac19bea1a Support for @Identity - with start, increment, cache 2020-03-04 16:59:42 +13:00
rob bygrave 9410f82f5a Support for @Identity - update default sequence increment 2020-03-03 22:36:59 +13:00
rob bygrave 21382a52ed Support for @Identity - split IdentityMode and DeployIdentityMode 2020-03-03 22:36:27 +13:00
rob bygrave f871259f20 Support for @Identity - split IdentityMode and DeployIdentityMode 2020-03-03 22:36:27 +13:00
rob bygrave eb765056fd Support for @Identity - initial refactor to add IdentityMode 2020-03-03 22:36:27 +13:00
rob bygrave ddf3c753d6 Ability to use order(<orderByClause>) on filterMany() (related to #1943) #1964 2020-03-03 22:31:01 +13:00
rob bygrave 4767aff046 Test only update sql server DDL review 2020-03-03 22:03:24 +13:00
rob bygrave 75846edd97 Test only update H2 DDL review 2020-03-03 21:20:19 +13:00
rob bygrave f7c6745de1 Ci Testing - remove test/resources/dbmigration/migrationtest/postgres9 2020-03-01 13:09:37 +13:00
rob bygrave f080f16e63 #1962 - Refactor simplify Postgres platforms to POSTGRES and POSTGRES9 2020-03-01 12:53:34 +13:00
rob bygrave 9135f87253 CI Testing - ParentQueryTest cascade on parent.data 2020-03-01 12:42:03 +13:00
rob bygrave 90ccf2d8b4 CI Testing - ParentQueryTest cascade on parent.data 2020-03-01 12:29:54 +13:00
rob bygrave 11409f8c09 CI Testing - ParentQueryTest cascade on parent.data 2020-03-01 12:23:16 +13:00
rob bygrave fb472a0304 CI Testing - ParentQueryTest truncate 2020-03-01 12:12:01 +13:00
rob bygrave 04c9007d22 CI Testing - Update travis to not both with oraclejdk8 which is no longer available 2020-03-01 11:55:31 +13:00
rob bygrave 8f75a04ad0 CI Testing - Update travis to xenial & postgresql: 10 2020-03-01 11:45:56 +13:00
Rob BygraveandGitHub 2236e4bfd7 Refactor PlatformDdl separate call to asIdentityColumn() (#1961) 2020-03-01 10:42:41 +13:00
rob bygrave 6d1c3738de #1960 - CVEs - Update postgresql and h2 jdbc drivers (provided scope only) 2020-02-28 16:22:48 +13:00
rob bygrave 20160144f5 No effective change - remove unused snakeyaml.version from pom 2020-02-22 14:42:00 +13:00
rob bygrave 891166507d Test only - restore postgres9 and postgres10 migration resources for CI postgres 2020-02-22 13:36:33 +13:00
rob bygrave bce97b1063 Test only - add dummy files for pg9 and pg10 DbMigrationTest for running on CI (with old postgres) 2020-02-22 12:54:16 +13:00
Rob BygraveandGitHub c5b960c588 Merge pull request #1959 from ebean-orm/feature/1955
#1953 - Do we need SqlServer2005Platform and SqlServer2005SqlLimiter?
2020-02-22 12:47:13 +13:00
rob bygrave 009b400ed3 Test only - delete test/resources/dbmigration/migrationtest/postgres10 and postgres9 2020-02-22 12:45:07 +13:00
rob bygrave b6ee6ade8c Test only - update ddl review for postgres 2020-02-22 12:44:00 +13:00
rob bygrave 0891c067d5 #1953 - Do we need SqlServer2005Platform and SqlServer2005SqlLimiter? 2020-02-22 11:48:16 +13:00
Rob BygraveandGitHub 7b3b32f0ff Merge pull request #1957 from ebean-orm/feature/1955
#1955 #1956  SqlQuery.mapToScalar() and SqlQuery.mapTo(RowMapper)
2020-02-22 11:25:48 +13:00
rob bygrave 3878b02cff #1956 - Add SqlQuery - mapTo( RowMapper) ... to deprecate findOne(RowMapper) 2020-02-22 10:50:23 +13:00
rob bygrave 92995b807e #1955 - mapToScalar - Add SqlQuery.mapToScalar( <class> ).findOne() / findList() ... to deprecate findSingleAttribute() 2020-02-22 10:09:44 +13:00
Rob BygraveandGitHub 907d36123d OraclePlatform update (ANSI row limiting with OFFSET), Oracle11Platform for old behaviour (#1951)
* #1947 - Oracle change to use concat operator rather than function

* #1947 - setMaxRows(1).findIds() under Oracle returns wrong result

* #1950 - Change OraclePlatform to use ANSI row limiting - OFFSET and NEXT ROWS, old platform as Oracle11Platform

* Revert test default to H2

* #1947 - Oracle & Oracle11 platform
2020-02-22 09:28:34 +13:00
Rob BygraveandGitHub 2db95f909d Merge pull request #1944 from ebean-orm/feature/1943
#1943 - Use limit & offset via setFirstRow() and setMaxRows() on filterMany()
2020-02-22 09:26:03 +13:00
rob bygrave 5213e8b653 Fix test merge for StartPostgres9 - using int for port 2020-02-22 09:24:58 +13:00
Rob BygraveandGitHub 2314fd267f Split Postgres platform into Postgres10 & Postgres9 (DDL - generated by default as identity) (#1921)
* Split Postgres platform into Postgres10 & Postgres9

- Use "generated by default as identity" for Postgres10 identity columns

* Add postgres9 migrationtest resources

* Set default back to h2

* Add postgres10 migration test resources
2020-02-22 09:18:05 +13:00
Rob BygraveandGitHub b24f093bbb Remove io.ebean.meta.MetricType as it is unnecessary (#1923) 2020-02-22 09:17:14 +13:00
Rob BygraveandGitHub 444c868124 Refactor to use avaje config (#1894)
* Move to use Avaje Config for properties loading
2020-02-22 09:11:37 +13:00
rob bygrave 81b16b8f73 [maven-release-plugin] prepare for next development iteration 2020-02-22 08:59:33 +13:00
rob bygrave e0d322d80a #1943 - Return ExpressionList<T> from setMaxRows() setFirstRow() to allow fluid style
This is to allow fluid style use of setMaxRows() setFirstRow() with filterMany query
2020-02-17 17:19:27 +13:00
rob bygrave 1dd0f3cbf8 #1943 - Use limit & offset via setFirstRow() and setMaxRows() on filterMany() 2020-02-17 17:05:01 +13:00
241 changed files with 5353 additions and 5356 deletions
+2 -3
View File
@@ -1,15 +1,14 @@
dist: trusty
dist: xenial
sudo: required
language: java
jdk:
- oraclejdk8
- openjdk11
git:
depth: 3
addons:
postgresql: "9.6"
postgresql: 10
# apt:
# packages:
# - mysql-server-5.7
+9 -13
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>12.1.13</version>
<version>12.1.14-SNAPSHOT</version>
<packaging>jar</packaging>
<name>ebean</name>
@@ -18,12 +18,11 @@
<properties>
<jackson-core.version>2.10.0</jackson-core.version>
<jackson-databind.version>2.10.0</jackson-databind.version>
<snakeyaml.version>1.21</snakeyaml.version>
</properties>
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-12.1.13</tag>
<tag>HEAD</tag>
</scm>
<profiles>
@@ -62,13 +61,10 @@
<scope>provided</scope>
</dependency>
<!--
Exclude snakeyaml if you desire - it is not strictly required.
-->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${snakeyaml.version}</version>
<groupId>io.avaje</groupId>
<artifactId>config</artifactId>
<version>1.0</version>
</dependency>
<!--
@@ -91,7 +87,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>6.4</version>
<version>6.9</version>
</dependency>
<dependency>
@@ -181,7 +177,7 @@
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.2</version>
<version>42.2.10</version>
<scope>provided</scope>
</dependency>
@@ -204,7 +200,7 @@
<dependency>
<groupId>io.ebean.test</groupId>
<artifactId>ebean-test-docker</artifactId>
<version>3.1.2</version>
<version>3.1.3</version>
<scope>test</scope>
</dependency>
@@ -220,7 +216,7 @@
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.197</version>
<version>1.4.199</version>
<scope>provided</scope>
</dependency>
+4 -4
View File
@@ -68,7 +68,7 @@ public interface ExpressionList<T> {
* optional asc and desc keywords representing ascending and descending order
* respectively.
*/
Query<T> order(String orderByClause);
ExpressionList<T> order(String orderByClause);
/**
* Return the OrderBy so that you can append an ascending or descending
@@ -506,7 +506,7 @@ public interface ExpressionList<T> {
* @param expressions Filter expressions with and, or and ? or ?1 type bind parameters
* @param params Bind parameters used in the expressions
*/
Query<T> filterMany(String manyProperty, String expressions, Object... params);
ExpressionList<T> filterMany(String manyProperty, String expressions, Object... params);
/**
* Specify specific properties to fetch on the main/root bean (aka partial
@@ -557,14 +557,14 @@ public interface ExpressionList<T> {
*
* @see Query#setFirstRow(int)
*/
Query<T> setFirstRow(int firstRow);
ExpressionList<T> setFirstRow(int firstRow);
/**
* Set the maximum number of rows to fetch.
*
* @see Query#setMaxRows(int)
*/
Query<T> setMaxRows(int maxRows);
ExpressionList<T> setMaxRows(int maxRows);
/**
* Set the name of the property which values become the key of a map.
+4 -16
View File
@@ -1,6 +1,6 @@
package io.ebean;
import io.ebean.config.properties.PropertiesLoader;
import io.avaje.config.Config;
import java.util.Properties;
@@ -11,8 +11,6 @@ import java.util.Properties;
*/
class PrimaryServer {
private static Properties globalProperties;
private static String defaultServerName;
private static boolean skip;
@@ -43,14 +41,10 @@ class PrimaryServer {
* Return the default configuration Properties.
*/
static synchronized Properties getProperties() {
if (globalProperties == null) {
globalProperties = PropertiesLoader.load();
}
if (defaultServerName == null) {
defaultServerName = determineDefaultServerName();
}
return globalProperties;
return Config.asProperties();
}
/**
@@ -62,15 +56,9 @@ class PrimaryServer {
defaultServerName = System.getProperty("db", defaultServerName);
defaultServerName = System.getProperty("ebean_db", defaultServerName);
if (isEmpty(defaultServerName)) {
defaultServerName = System.getProperty("datasource.default");
defaultServerName = Config.get("datasource.default", null);
if (isEmpty(defaultServerName)) {
defaultServerName = System.getProperty("ebean.default.datasource");
if (isEmpty(defaultServerName)) {
defaultServerName = globalProperties.getProperty("datasource.default");
if (isEmpty(defaultServerName)) {
defaultServerName = globalProperties.getProperty("ebean.default.datasource");
}
}
defaultServerName = Config.get("ebean.default.datasource", null);
}
}
if (defaultServerName == null) {
+3 -2
View File
@@ -18,7 +18,7 @@ import java.sql.SQLException;
* <pre>{@code
*
* //
* // A mapper from ResultSet into our CustomerDto bean
* // Map from ResultSet to CustomerDto bean
* //
* class CustomerMapper implements RowMapper<CustomerDto> {
*
@@ -42,7 +42,8 @@ import java.sql.SQLException;
*
* CustomerDto rob = DB.sqlQuery(sql)
* .setParameter(1, "Rob")
* .findOne(CUSTOMER_MAPPER);
* .mapTo(CUSTOMER_MAPPER)
* .findOne();
*
*
* }</pre>
+84 -48
View File
@@ -75,17 +75,15 @@ public interface SqlQuery extends Serializable {
SqlRow findOne();
/**
* Execute the query returning a single result using the mapper.
*
* @param mapper Used to map each ResultSet row into the result object.
* Deprecated migrate to use {@link #mapTo(RowMapper)}
*/
@Deprecated
<T> T findOne(RowMapper<T> mapper);
/**
* Execute the query returning a list using the mapper.
*
* @param mapper Used to map each ResultSet row into the result object.
* Deprecated migrate to use {@link #mapTo(RowMapper)}
*/
@Deprecated
<T> List<T> findList(RowMapper<T> mapper);
/**
@@ -125,64 +123,47 @@ public interface SqlQuery extends Serializable {
Optional<SqlRow> findOneOrEmpty();
/**
* Execute the query returning a single scalar attribute.
* <pre>@{code
* Deprecated - migrate to <code>.mapToScalar(attributeType).findOne()</code>.
* <pre>{@code
*
* String sql = "select max(unit_price) from o_order_detail where order_qty > ?";
*
* BigDecimal maxPrice = DB.sqlQuery(sql)
* .setParameter(42)
* .findSingleAttribute(BigDecimal.class);
*
* }</pre>
*
* <p>
* The attributeType can be any scalar type that Ebean supports (includes javax time types, Joda types etc).
* </p>
*
* @param attributeType The type of the returned value
* .mapToScalar(BigDecimal.class)
* .findOne();
* }
*/
@Deprecated
<T> T findSingleAttribute(Class<T> attributeType);
/**
* Execute the query returning a single BigDecimal value.
* <p>
* This is an alias for <code>findSingleAttribute(BigDecimal.class)</code>
* </p>
* Deprecated - migrate to <code>.mapToScalar(BigDecimal.class).findOne()</code>.
* <pre>{@code
*
* .mapToScalar(BigDecimal.class)
* .findOne();
* }
*/
@Deprecated
BigDecimal findSingleDecimal();
/**
* Execute the query returning a single Long value.
* <p>
* This is an alias for <code>findSingleAttribute(Long.class)</code>
* </p>
* Deprecated - migrate to <code>.mapToScalar(Long.class).findOne()</code>.
* <pre>{@code
*
* .mapToScalar(Long.class)
* .findOne();
* }
*/
@Deprecated
Long findSingleLong();
/**
* Execute the query returning a list of scalar attribute values.
*
* Deprecated - migrate to <code>.mapToScalar(Long.class).findList()</code>.
* <pre>{@code
*
* String sql =
* " select (unit_price * order_qty) " +
* " from o_order_detail " +
* " where unit_price > ? " +
* " order by (unit_price * order_qty) desc";
*
* List<BigDecimal> lineAmounts =
* DB.sqlQuery(sql)
* .setParameter(42)
* .findSingleAttributeList(BigDecimal.class);
*
* }</pre>
*
* <p>
* The attributeType can be any scalar type that Ebean supports (includes javax time types, Joda types etc).
*
* @param attributeType The type of the returned value
* .mapToScalar(Long.class)
* .findList();
* }
*/
@Deprecated
<T> List<T> findSingleAttributeList(Class<T> attributeType);
/**
@@ -293,4 +274,59 @@ public interface SqlQuery extends Serializable {
*/
SqlQuery setBufferFetchSizeHint(int bufferFetchSizeHint);
/**
* The query result maps to a single scalar value like Long, BigDecimal,
* String, UUID, OffsetDateTime etc.
* <p>
* Any scalar type Ebean is aware of can be used including java time
* types like Instant, LocalDate, OffsetDateTime, UUID, Inet, Cdir etc.
*
* <pre>{@code
*
* String sql = " select min(updtime) from o_order_detail " +
* " where unit_price > ? and updtime is not null ";
*
* OffsetDateTime minCreated = DB.sqlQuery(sql)
* .setParameter(42)
* .mapToScalar(OffsetDateTime.class)
* .findOne();
*
* }</pre>
*
* @param attributeType The type the result is returned as
* @return The query to execute via findOne() findList() etc
*/
<T> TypeQuery<T> mapToScalar(Class<T> attributeType);
/**
* Use a RowMapper to map the result to beans.
*
* @param mapper Maps rows to beans
* @param <T> The type of beans mapped to
* @return The query to execute by findOne() findList() etc
*/
<T> TypeQuery<T> mapTo(RowMapper<T> mapper);
/**
* Query mapping to single scalar values.
*
* @param <T> The type of the scalar values
*/
interface TypeQuery<T> {
/**
* Return the single value.
*/
T findOne();
/**
* Return the single value that is optional.
*/
Optional<T> findOneOrEmpty();
/**
* Return the list of values.
*/
List<T> findList();
}
}
@@ -34,11 +34,6 @@ public class PlatformConfig {
*/
private String databaseBooleanFalse;
/**
* For DB's using sequences this is the number of sequence values prefetched.
*/
private int databaseSequenceBatchSize = 20;
/**
* Set for DB's that support both Sequence and Identity (and the default choice is not desired).
*/
@@ -79,7 +74,6 @@ public class PlatformConfig {
public PlatformConfig(PlatformConfig platformConfig) {
this.databaseBooleanFalse = platformConfig.databaseBooleanFalse;
this.databaseBooleanTrue = platformConfig.databaseBooleanTrue;
this.databaseSequenceBatchSize = platformConfig.databaseSequenceBatchSize;
this.idType = platformConfig.idType;
this.geometrySRID = platformConfig.geometrySRID;
this.dbUuid = platformConfig.dbUuid;
@@ -173,20 +167,6 @@ public class PlatformConfig {
this.databaseBooleanFalse = databaseBooleanFalse;
}
/**
* Return the number of DB sequence values that should be preallocated.
*/
public int getDatabaseSequenceBatchSize() {
return databaseSequenceBatchSize;
}
/**
* Set the number of DB sequence values that should be preallocated.
*/
public void setDatabaseSequenceBatchSize(int databaseSequenceBatchSize) {
this.databaseSequenceBatchSize = databaseSequenceBatchSize;
}
/**
* Return the Geometry SRID.
*/
@@ -294,7 +274,6 @@ public class PlatformConfig {
public void loadSettings(PropertiesWrapper p) {
idType = p.getEnum(IdType.class, "idType", idType);
databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", databaseSequenceBatchSize);
databaseBooleanTrue = p.get("databaseBooleanTrue", databaseBooleanTrue);
databaseBooleanFalse = p.get("databaseBooleanFalse", databaseBooleanFalse);
databaseInetAddressVarchar = p.getBoolean("databaseInetAddressVarchar", databaseInetAddressVarchar);
@@ -1,6 +1,7 @@
package io.ebean.config;
import com.fasterxml.jackson.core.JsonFactory;
import io.avaje.config.Config;
import io.ebean.DatabaseFactory;
import io.ebean.PersistenceContextScope;
import io.ebean.Query;
@@ -13,7 +14,6 @@ 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.config.properties.PropertiesLoader;
import io.ebean.datasource.DataSourceConfig;
import io.ebean.event.BeanFindController;
import io.ebean.event.BeanPersistController;
@@ -1050,18 +1050,6 @@ public class ServerConfig {
this.lazyLoadBatchSize = lazyLoadBatchSize;
}
/**
* Set the number of sequences to fetch/preallocate when using DB sequences.
* <p>
* This is a performance optimisation to reduce the number times Ebean
* requests a sequence to be used as an Id for a bean (aka reduce network
* chatter).
* </p>
*/
public void setDatabaseSequenceBatchSize(int databaseSequenceBatchSize) {
platformConfig.setDatabaseSequenceBatchSize(databaseSequenceBatchSize);
}
/**
* Return the default JDBC fetchSize hint for findList queries.
*/
@@ -1828,32 +1816,6 @@ public class ServerConfig {
this.platformConfig.setDatabaseBooleanFalse(databaseFalse);
}
/**
* Return the number of DB sequence values that should be preallocated.
*/
public int getDatabaseSequenceBatchSize() {
return platformConfig.getDatabaseSequenceBatchSize();
}
/**
* Set the number of DB sequence values that should be preallocated and cached
* by Ebean.
* <p>
* This is only used for DB's that use sequences and is a performance
* optimisation. This reduces the number of times Ebean needs to get a
* sequence value from the Database reducing network chatter.
* </p>
* <p>
* By default this value is 10 so when we need another Id (and don't have one
* in our cache) Ebean will fetch 10 id's from the database. Note that when
* the cache drops to have full (which is 5 by default) Ebean will fetch
* another batch of Id's in a background thread.
* </p>
*/
public void setDatabaseSequenceBatch(int databaseSequenceBatchSize) {
this.platformConfig.setDatabaseSequenceBatchSize(databaseSequenceBatchSize);
}
/**
* Return the database platform name (can be null).
* <p>
@@ -2746,7 +2708,7 @@ public class ServerConfig {
* Load settings from ebean.properties.
*/
public void loadFromProperties() {
this.properties = PropertiesLoader.load();
this.properties = Config.asProperties();
configureFromProperties();
}
@@ -2755,7 +2717,7 @@ public class ServerConfig {
*/
public void loadFromProperties(Properties properties) {
// keep the properties used for configuration so that these are available for plugins
this.properties = PropertiesLoader.eval(properties);
this.properties = Config.asConfiguration().eval(properties);
configureFromProperties();
}
@@ -110,10 +110,6 @@ public class DatabasePlatform {
*/
protected DbIdentity dbIdentity = new DbIdentity();
protected boolean sequenceBatchMode = true;
protected int sequenceBatchSize = 20;
/**
* The history support for this database platform.
*/
@@ -235,7 +231,6 @@ public class DatabasePlatform {
* Configure the platform given the server configuration.
*/
public void configure(PlatformConfig config) {
this.sequenceBatchSize = config.getDatabaseSequenceBatchSize();
this.caseSensitiveCollation = config.isCaseSensitiveCollation();
configureIdType(config.getIdType());
configure(config, config.isAllQuotedIdentifiers());
@@ -278,7 +273,7 @@ public class DatabasePlatform {
* Return true if this matches the given platform.
*/
public boolean isPlatform(Platform platform) {
return this.platform == platform;
return this.platform.base() == platform;
}
/**
@@ -298,20 +293,6 @@ 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.
*/
@@ -381,13 +362,13 @@ public class DatabasePlatform {
/**
* Return a DB Sequence based IdGenerator.
*
* @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 be the BackgroundExecutor that can be used to load the sequence if
* desired
* @param ds the DataSource
* @param increment the sequence allocation size as defined by mapping (defaults to 50)
* @param seqName the name of the sequence
*/
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return null;
}
@@ -35,6 +35,11 @@ public enum IdType {
* Used when the key is a compound key or lookup table code.
* </p>
*/
EXTERNAL
EXTERNAL,
/**
* Auto mapping to platform preferred identity strategy.
*/
AUTO
}
@@ -40,8 +40,7 @@ public class LimitOffsetSqlLimiter implements SqlLimiter {
}
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
return new SqlLimitResponse(sql, false);
return new SqlLimitResponse(sql);
}
}
@@ -1,75 +0,0 @@
package io.ebean.config.dbplatform;
/**
* Adds the ROW_NUMBER() OVER function to a query.
*/
public class RowNumberSqlLimiter implements SqlLimiter {
/**
* ROW_NUMBER() OVER (ORDER BY
*/
private static final String ROW_NUMBER_OVER = "row_number() over (order by ";
/**
* ) as rn,
*/
private static final String ROW_NUMBER_AS = ") as rn, ";
private final String rowNumberWindowAlias;
/**
* Specify the name of the rowNumberWindowAlias.
*/
public RowNumberSqlLimiter(String rowNumberWindowAlias) {
this.rowNumberWindowAlias = rowNumberWindowAlias;
}
public RowNumberSqlLimiter() {
this("as limitresult");
}
@Override
public SqlLimitResponse limit(SqlLimitRequest request) {
String dbSql = request.getDbSql();
StringBuilder sb = new StringBuilder(60 + dbSql.length());
int firstRow = request.getFirstRow();
int lastRow = request.getMaxRows();
if (lastRow > 0) {
lastRow += firstRow;
}
sb.append("select * from ( ");
sb.append("select ");
if (request.isDistinct()) {
sb.append("distinct ");
}
sb.append(ROW_NUMBER_OVER);
sb.append(request.getDbOrderBy());
sb.append(ROW_NUMBER_AS);
sb.append(dbSql);
sb.append(" ) ");
sb.append(rowNumberWindowAlias);
sb.append(" where ");
if (firstRow > 0) {
sb.append(" rn > ").append(firstRow);
if (lastRow > 0) {
sb.append(" and ");
}
}
if (lastRow > 0) {
sb.append(" rn <= ").append(lastRow);
}
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
return new SqlLimitResponse(sql, true);
}
}
@@ -1,81 +0,0 @@
package io.ebean.config.dbplatform;
/**
* Add ROWNUM column etc around SQL query to limit results.
*/
public class RownumSqlLimiter implements SqlLimiter {
private final String rnum;
private final boolean useFirstRowsHint;
/**
* Create with default inner rownum column alias and used FIRST_ROWS hint.
*/
public RownumSqlLimiter() {
this("rn_", true);
}
/**
* Specify the inner rownum column alias and whether to include the FIRST_ROWS
* hint.
*/
public RownumSqlLimiter(String rnum, boolean useFirstRowsHint) {
this.rnum = rnum;
this.useFirstRowsHint = useFirstRowsHint;
}
@Override
public SqlLimitResponse limit(SqlLimitRequest request) {
// select *
// from ( select /*+ FIRST_ROWS(n) */ ROWNUM rnum, a.*
// from ( your_query_goes_here,
// with order by ) a
// where ROWNUM <=
// :MAX_ROW_TO_FETCH )
// where rnum >= :MIN_ROW_TO_FETCH;
String dbSql = request.getDbSql();
StringBuilder sb = new StringBuilder(60 + dbSql.length());
int firstRow = request.getFirstRow();
int lastRow = request.getMaxRows();
if (lastRow > 0) {
lastRow += firstRow;
}
sb.append("select * from ( ");
sb.append("select ");
if (useFirstRowsHint && request.getMaxRows() > 0) {
sb.append("/*+ FIRST_ROWS(").append(request.getMaxRows()).append(") */ ");
}
sb.append("rownum ").append(rnum).append(", a.* ");
sb.append(" from (");
sb.append(" select ");
if (request.isDistinct()) {
sb.append("distinct ");
}
sb.append(dbSql);
sb.append(NEW_LINE).append(" ) a ");
if (lastRow > 0) {
sb.append(" where rownum <= ").append(lastRow);
}
sb.append(" ) ");
if (firstRow > 0) {
sb.append(" where ");
sb.append(rnum).append(" > ").append(firstRow);
}
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
return new SqlLimitResponse(sql, true);
}
}
@@ -1,57 +0,0 @@
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.
* <p>
* For example, when inserting a bean with a cascade on a OneToMany with many
* beans Ebean can call this to ensure .
* </p>
*/
@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.
*/
@Override
protected List<Long> readIds(ResultSet resultSet, int loadSize) throws SQLException {
List<Long> newIds = new ArrayList<>(loadSize);
while (resultSet.next()) {
newIds.add(resultSet.getLong(1));
}
return newIds;
}
}
@@ -59,7 +59,7 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
this.allocationSize = allocationSize;
}
public abstract String getSql(int batchSize);
public abstract String getSql();
/**
* Returns the sequence name.
@@ -102,7 +102,7 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
if (size > 0) {
maybeLoadMoreInBackground(size);
} else {
loadMore(allocationSize);
loadMore();
}
return idList.pollFirst();
}
@@ -116,8 +116,8 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
}
}
private void loadMore(int requestSize) {
List<Long> newIds = getMoreIds(requestSize);
private void loadMore() {
List<Long> newIds = getMoreIds();
synchronized (monitor) {
idList.addAll(newIds);
}
@@ -139,7 +139,7 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
currentlyBackgroundLoading = true;
backgroundExecutor.execute(() -> {
loadMore(requestSize);
loadMore();
synchronized (backgroundLoadMonitor) {
currentlyBackgroundLoading = false;
}
@@ -150,14 +150,14 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
/**
* Read the resultSet returning the list of Id values.
*/
protected abstract List<Long> readIds(ResultSet resultSet, int loadSize) throws SQLException;
protected abstract List<Long> readIds(ResultSet resultSet) throws SQLException;
/**
* Get more Id's by executing a query and reading the Id's returned.
*/
protected List<Long> getMoreIds(int requestSize) {
protected List<Long> getMoreIds() {
String sql = getSql(requestSize);
String sql = getSql();
Connection connection = null;
PreparedStatement statement = null;
@@ -168,7 +168,7 @@ public abstract class SequenceIdGenerator implements PlatformIdGenerator {
statement = connection.prepareStatement(sql);
resultSet = statement.executeQuery();
List<Long> newIds = readIds(resultSet, requestSize);
List<Long> newIds = readIds(resultSet);
if (logger.isTraceEnabled()) {
logger.trace("seq:{} loaded:{} sql:{}", seqName, newIds.size(), sql);
}
@@ -13,19 +13,25 @@ import java.util.List;
*/
public abstract class SequenceStepIdGenerator extends SequenceIdGenerator {
private final String nextSql;
/**
* Construct with stepSize (typically 50).
*/
protected SequenceStepIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
protected SequenceStepIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int stepSize, String nextSql) {
super(be, ds, seqName, stepSize);
this.nextSql = nextSql;
}
@Override
public String getSql() {
return nextSql;
}
/**
* Add the next set of Ids as the next value plus all the following numbers up to the step size.
*/
@Override
protected List<Long> readIds(ResultSet resultSet, int ignoreRequestSize) throws SQLException {
protected List<Long> readIds(ResultSet resultSet) throws SQLException {
List<Long> newIds = new ArrayList<>(allocationSize);
if (resultSet.next()) {
long start = resultSet.getLong(1);
@@ -7,14 +7,11 @@ public class SqlLimitResponse {
final String sql;
final boolean includesRowNumberColumn;
/**
* Create the response.
*/
public SqlLimitResponse(String sql, boolean includesRowNumberColumn) {
public SqlLimitResponse(String sql) {
this.sql = sql;
this.includesRowNumberColumn = includesRowNumberColumn;
}
/**
@@ -24,11 +21,4 @@ public class SqlLimitResponse {
return sql;
}
/**
* Returns true if a ROW_NUMBER column is used in the query.
*/
public boolean isIncludesRowNumberColumn() {
return includesRowNumberColumn;
}
}
@@ -52,9 +52,8 @@ public class DB2Platform extends DatabasePlatform {
* sequence values.
*/
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new DB2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return new DB2Sequence(be, ds, seqName, increment);
}
}
@@ -0,0 +1,17 @@
package io.ebean.config.dbplatform.db2;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
/**
* DB2 specific sequence Id Generator.
*/
class DB2Sequence extends SequenceStepIdGenerator {
DB2Sequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize, "values nextval for " + seqName);
}
}
@@ -1,35 +0,0 @@
package io.ebean.config.dbplatform.db2;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
import javax.sql.DataSource;
/**
* DB2 specific sequence Id Generator.
*/
public class DB2SequenceIdGenerator extends SequenceBatchIdGenerator {
private final String baseSql;
private final String unionBaseSql;
/**
* Construct given a dataSource and sql to return the next sequence value.
*/
public DB2SequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
super(be, ds, seqName, batchSize);
this.baseSql = "values nextval for " + seqName;
this.unionBaseSql = " union " + baseSql;
}
@Override
public String getSql(int batchSize) {
StringBuilder sb = new StringBuilder();
sb.append(baseSql);
for (int i = 1; i < batchSize; i++) {
sb.append(unionBaseSql);
}
return sb.toString();
}
}
@@ -22,6 +22,6 @@ public class Db2SqlLimiter implements SqlLimiter {
}
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
return new SqlLimitResponse(sql, false);
return new SqlLimitResponse(sql);
}
}
@@ -0,0 +1,14 @@
package io.ebean.config.dbplatform.h2;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
class H2DbSequence extends SequenceStepIdGenerator {
H2DbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize, "select " + seqName + ".nextval");
}
}
@@ -42,13 +42,9 @@ public class H2Platform extends DatabasePlatform {
dbTypeMap.put(DbType.UUID, new DbPlatformType("uuid", false));
}
/**
* Return a H2 specific sequence IdGenerator that supports batch fetching
* sequence values.
*/
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new H2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return new H2DbSequence(be, ds, seqName, increment);
}
@Override
@@ -1,35 +0,0 @@
package io.ebean.config.dbplatform.h2;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
import javax.sql.DataSource;
/**
* H2 specific sequence Id Generator.
*/
public class H2SequenceIdGenerator extends SequenceBatchIdGenerator {
private final String baseSql;
private final String unionBaseSql;
/**
* Construct given a dataSource and sql to return the next sequence value.
*/
public H2SequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
super(be, ds, seqName, batchSize);
this.baseSql = "select " + seqName + ".nextval";
this.unionBaseSql = " union " + baseSql;
}
@Override
public String getSql(int batchSize) {
StringBuilder sb = new StringBuilder();
sb.append(baseSql);
for (int i = 1; i < batchSize; i++) {
sb.append(unionBaseSql);
}
return sb.toString();
}
}
@@ -29,7 +29,6 @@ public class HanaSqlLimiter implements SqlLimiter {
}
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
return new SqlLimitResponse(sql, false);
return new SqlLimitResponse(sql);
}
}
}
@@ -1,38 +0,0 @@
package io.ebean.config.dbplatform.hsqldb;
import io.ebean.BackgroundExecutor;
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.h2.H2DbEncrypt;
import io.ebean.config.dbplatform.h2.H2SequenceIdGenerator;
import javax.sql.DataSource;
/**
* H2 specific platform.
*/
public class HsqldbPlatform extends DatabasePlatform {
public HsqldbPlatform() {
super();
this.platform = Platform.HSQLDB;
this.dbEncrypt = new H2DbEncrypt();
this.truncateTable = "delete from %s";
this.dbIdentity.setIdType(IdType.IDENTITY);
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsSequence(true);
this.dbIdentity.setSupportsIdentity(true);
dbTypeMap.put(DbType.INTEGER, new DbPlatformType("integer", false));
}
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new H2SequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
}
}
@@ -1,4 +0,0 @@
/**
* Hsqldb specific support.
*/
package io.ebean.config.dbplatform.hsqldb;
@@ -44,8 +44,8 @@ public class NuoDbPlatform extends DatabasePlatform {
}
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new NuoDbSequence(be, ds, seqName, stepSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return new NuoDbSequence(be, ds, seqName, increment);
}
@Override
@@ -5,20 +5,10 @@ import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
public class NuoDbSequence extends SequenceStepIdGenerator {
class NuoDbSequence extends SequenceStepIdGenerator {
private final String nextSql;
/**
* Construct where batchSize is the sequence step size.
*/
public NuoDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize);
this.nextSql = "select next value for " + seqName + " from dual";
NuoDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize, "select next value for " + seqName + " from dual");
}
@Override
public String getSql(int batchSize) {
return nextSql;
}
}
@@ -0,0 +1,16 @@
package io.ebean.config.dbplatform.oracle;
import io.ebean.annotation.Platform;
/**
* Oracle 11 platform using rownum sql limiting.
*/
public class Oracle11Platform extends OraclePlatform {
public Oracle11Platform() {
super();
this.platform = Platform.ORACLE11;
this.columnAliasPrefix = "c";
this.sqlLimiter = new OracleRownumSqlLimiter();
}
}
@@ -0,0 +1,34 @@
package io.ebean.config.dbplatform.oracle;
import io.ebean.config.dbplatform.SqlLimitRequest;
import io.ebean.config.dbplatform.SqlLimitResponse;
import io.ebean.config.dbplatform.SqlLimiter;
/**
* Use ANSI offset rows syntax.
*/
class OracleAnsiSqlRowsLimiter implements SqlLimiter {
@Override
public SqlLimitResponse limit(SqlLimitRequest request) {
String dbSql = request.getDbSql();
StringBuilder sb = new StringBuilder(50 + dbSql.length());
int firstRow = request.getFirstRow();
int maxRows = request.getMaxRows();
sb.append("select ");
if (request.isDistinct()) {
sb.append("distinct ");
}
sb.append(dbSql);
if (firstRow > 0) {
sb.append(" offset ").append(firstRow).append(" rows");
}
if (maxRows > 0) {
sb.append(" fetch next ").append(maxRows).append(" rows only");
}
return new SqlLimitResponse(sb.toString());
}
}
@@ -0,0 +1,14 @@
package io.ebean.config.dbplatform.oracle;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
class OracleDbSequence extends SequenceStepIdGenerator {
OracleDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize, "select " + seqName + ".nextval from dual");
}
}
@@ -9,7 +9,6 @@ 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.RownumSqlLimiter;
import io.ebean.config.dbplatform.SqlErrorCodes;
import javax.sql.DataSource;
@@ -23,11 +22,12 @@ public class OraclePlatform extends DatabasePlatform {
public OraclePlatform() {
super();
this.platform = Platform.ORACLE;
this.columnAliasPrefix = null;
this.supportsDeleteTableAlias = true;
this.maxTableNameLength = 30;
this.maxConstraintNameLength = 30;
this.dbEncrypt = new OracleDbEncrypt();
this.sqlLimiter = new RownumSqlLimiter();
this.sqlLimiter = new OracleAnsiSqlRowsLimiter();
this.basicSqlLimiter = new BasicSqlAnsiLimiter();
this.historySupport = new OracleDbHistorySupport();
this.truncateTable = "truncate table %s cascade";
@@ -76,8 +76,8 @@ public class OraclePlatform extends DatabasePlatform {
}
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new OracleSequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return new OracleDbSequence(be, ds, seqName, increment);
}
@Override
@@ -0,0 +1,54 @@
package io.ebean.config.dbplatform.oracle;
import io.ebean.config.dbplatform.SqlLimitRequest;
import io.ebean.config.dbplatform.SqlLimitResponse;
import io.ebean.config.dbplatform.SqlLimiter;
/**
* Add ROWNUM column etc around SQL query to limit results.
*/
class OracleRownumSqlLimiter implements SqlLimiter {
@Override
public SqlLimitResponse limit(SqlLimitRequest request) {
// select *
// from ( select /*+ FIRST_ROWS(n) */ a.*, ROWNUM rnum
// from ( your_query_goes_here,
// with order by ) a
// where ROWNUM <=
// :MAX_ROW_TO_FETCH )
// where rnum >= :MIN_ROW_TO_FETCH;
String dbSql = request.getDbSql();
StringBuilder sb = new StringBuilder(60 + dbSql.length());
int firstRow = request.getFirstRow();
int lastRow = request.getMaxRows();
if (lastRow > 0) {
lastRow += firstRow;
}
sb.append("select * from (select ");
if (request.getMaxRows() > 0) {
sb.append("/*+ FIRST_ROWS(").append(request.getMaxRows()).append(") */ ");
}
sb.append("a.*, rownum rn_ from (select ");
if (request.isDistinct()) {
sb.append("distinct ");
}
sb.append(dbSql).append(") a ");
if (lastRow > 0) {
sb.append(" where rownum <= ").append(lastRow);
}
sb.append(") ");
if (firstRow > 0) {
sb.append(" where rn_ > ").append(firstRow);
}
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
return new SqlLimitResponse(sql);
}
}
@@ -1,27 +0,0 @@
package io.ebean.config.dbplatform.oracle;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
import javax.sql.DataSource;
/**
* Oracle specific sequence Id Generator.
*/
public class OracleSequenceIdGenerator extends SequenceBatchIdGenerator {
private final String baseSql;
/**
* Construct given a dataSource and sql to return the next sequence value.
*/
public OracleSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
super(be, ds, seqName, batchSize);
this.baseSql = "select " + seqName + ".nextval, a from (select level as a FROM dual CONNECT BY level <= ";
}
@Override
public String getSql(int batchSize) {
return baseSql + batchSize + ")";
}
}
@@ -1,20 +0,0 @@
package io.ebean.config.dbplatform.postgres;
import io.ebean.config.dbplatform.IdType;
/**
* Postgres v8.3 specific platform.
* <p>
* No support for getGeneratedKeys.
* </p>
*/
public class Postgres8Platform extends PostgresPlatform {
public Postgres8Platform() {
super();
this.dbIdentity.setSupportsGetGeneratedKeys(false);
this.dbIdentity.setIdType(IdType.SEQUENCE);
this.columnAliasPrefix = "as c";
}
}
@@ -0,0 +1,14 @@
package io.ebean.config.dbplatform.postgres;
import io.ebean.annotation.Platform;
/**
* Postgres9 platform - uses serial type for identity columns.
*/
public class Postgres9Platform extends PostgresPlatform {
public Postgres9Platform() {
super();
this.platform = Platform.POSTGRES9;
}
}
@@ -0,0 +1,14 @@
package io.ebean.config.dbplatform.postgres;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
class PostgresDbSequence extends SequenceStepIdGenerator {
PostgresDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize, "select nextval('" + seqName + "')");
}
}
@@ -19,10 +19,9 @@ import java.sql.SQLException;
import java.sql.Types;
/**
* Postgres v9 specific platform.
* Postgres 10+ platform.
* <p>
* Uses serial types and getGeneratedKeys.
* </p>
* Defaults to use "generated by default as identity".
*/
public class PostgresPlatform extends DatabasePlatform {
@@ -110,9 +109,8 @@ public class PostgresPlatform extends DatabasePlatform {
* Create a Postgres specific sequence IdGenerator.
*/
@Override
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
return new PostgresSequenceIdGenerator(be, ds, seqName, sequenceBatchSize);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return new PostgresDbSequence(be, ds, seqName, increment);
}
@Override
@@ -1,27 +0,0 @@
package io.ebean.config.dbplatform.postgres;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceBatchIdGenerator;
import javax.sql.DataSource;
/**
* Postgres specific sequence Id Generator.
*/
public class PostgresSequenceIdGenerator extends SequenceBatchIdGenerator {
private final String baseSql;
/**
* Construct given a dataSource and sql to return the next sequence value.
*/
public PostgresSequenceIdGenerator(BackgroundExecutor be, DataSource ds, String seqName, int batchSize) {
super(be, ds, seqName, batchSize);
this.baseSql = "select nextval('" + seqName + "'), s.generate_series from (select generate_series from generate_series(1,";
}
@Override
public String getSql(int batchSize) {
return baseSql + batchSize + ") ) as s";
}
}
@@ -35,8 +35,7 @@ public class SqlAnywhereLimiter implements SqlLimiter {
sb.append(dbSql);
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
return new SqlLimitResponse(sql, false);
return new SqlLimitResponse(sql);
}
}
@@ -1,11 +0,0 @@
package io.ebean.config.dbplatform.sqlserver;
/**
* SQL Server platform using the older ROW_NUMBER() mechanism.
*/
public class SqlServer2005Platform extends SqlServer16Platform {
public SqlServer2005Platform() {
this.sqlLimiter = new SqlServer2005SqlLimiter();
}
}
@@ -1,79 +0,0 @@
package io.ebean.config.dbplatform.sqlserver;
import io.ebean.config.dbplatform.SqlLimitRequest;
import io.ebean.config.dbplatform.SqlLimitResponse;
import io.ebean.config.dbplatform.SqlLimiter;
/**
* Use top and row_number() function to limit sql results.
*/
public class SqlServer2005SqlLimiter implements SqlLimiter {
final String rowNumberWindowAlias;
/**
* Specify the name of the rowNumberWindowAlias.
*/
public SqlServer2005SqlLimiter(String rowNumberWindowAlias) {
this.rowNumberWindowAlias = rowNumberWindowAlias;
}
public SqlServer2005SqlLimiter() {
this("as limitresult");
}
@Override
public SqlLimitResponse limit(SqlLimitRequest request) {
StringBuilder sb = new StringBuilder(500);
int firstRow = request.getFirstRow();
int lastRow = request.getMaxRows();
if (lastRow > 0) {
lastRow += firstRow;
}
if (firstRow < 1) {
// just use top n
sb.append("select ");
if (request.isDistinct()) {
sb.append("distinct ");
}
sb.append("top ").append(lastRow).append(" ");
sb.append(request.getDbSql());
return new SqlLimitResponse(sb.toString(), false);
}
/*
* SELECT * FROM (SELECT TOP 20 ROW_NUMBER() OVER (ORDER BY ...) AS rn, ...)
* AS limitresult WHERE rn >= 11 AND rn <= 20
*/
sb.append("select * ").append(NEW_LINE).append("from ( ");
sb.append("select ");
if (request.isDistinct()) {
sb.append("distinct ");
}
sb.append("top ").append(lastRow);
sb.append(" row_number() over (order by ");
sb.append(request.getDbOrderBy());
sb.append(") as rn, ");
sb.append(request.getDbSql());
sb.append(NEW_LINE).append(") ");
sb.append(rowNumberWindowAlias);
sb.append(" where ");
sb.append(" rn > ").append(firstRow);
if (lastRow > 0) {
sb.append(" and ");
sb.append(" rn <= ").append(lastRow);
}
String sql = request.getDbPlatform().completeSql(sb.toString(), request.getOrmQuery());
return new SqlLimitResponse(sql, true);
}
}
@@ -36,7 +36,6 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
this.dbIdentity.setSupportsGetGeneratedKeys(true);
this.dbIdentity.setSupportsIdentity(true);
this.dbIdentity.setSupportsSequence(true);
this.sequenceBatchMode = false;
this.exceptionTranslator =
new SqlErrorCodes()
@@ -95,12 +94,9 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
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);
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int increment, String seqName) {
return new SqlServerDbSequence(be, ds, seqName, increment);
}
/**
@@ -0,0 +1,13 @@
package io.ebean.config.dbplatform.sqlserver;
import io.ebean.BackgroundExecutor;
import io.ebean.config.dbplatform.SequenceStepIdGenerator;
import javax.sql.DataSource;
class SqlServerDbSequence extends SequenceStepIdGenerator {
SqlServerDbSequence(BackgroundExecutor be, DataSource ds, String seqName, int stepSize) {
super(be, ds, seqName, stepSize, "select next value for " + seqName);
}
}
@@ -29,7 +29,7 @@ public class SqlServerSqlLimiter implements SqlLimiter {
}
sb.append("top ").append(maxRows).append(" ");
sb.append(dbSql);
return new SqlLimitResponse(sb.toString(), false);
return new SqlLimitResponse(sb.toString());
}
sb.append("select ");
@@ -43,8 +43,7 @@ public class SqlServerSqlLimiter implements SqlLimiter {
if (maxRows > 0) {
sb.append(" fetch next ").append(maxRows).append(" rows only");
}
String sql = sb.toString();
return new SqlLimitResponse(sql, false);
return new SqlLimitResponse(sb.toString());
}
}
@@ -1,24 +0,0 @@
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;
}
}
@@ -1,106 +0,0 @@
package io.ebean.config.properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* Manages the underlying map of properties we are gathering.
*/
class LoadContext {
private static final Logger log = LoggerFactory.getLogger(LoadContext.class);
/**
* Map we are loading the properties into.
*/
private final Map<String, String> map = new LinkedHashMap<>();
/**
* Names of resources/files that were loaded.
*/
private final Set<String> loadedResources = new LinkedHashSet<>();
/**
* Return the input stream (maybe null) for the given source.
*/
InputStream resource(String resourcePath, Loader.Source source) {
InputStream is = null;
if (source == Loader.Source.RESOURCE) {
is = resourceStream(resourcePath);
if (is != null) {
loadedResources.add(resourcePath);
}
} else {
File file = new File(resourcePath);
if (file.exists()) {
try {
is = new FileInputStream(file);
loadedResources.add("file:" + resourcePath);
} catch (FileNotFoundException e) {
throw new IllegalStateException(e);
}
}
}
return is;
}
private InputStream resourceStream(String resourcePath) {
InputStream is = getClass().getResourceAsStream("/" + resourcePath);
if (is == null) {
// search the module path for top level resource
is = ClassLoader.getSystemResourceAsStream(resourcePath);
}
return is;
}
/**
* Add a property entry.
*/
void put(String key, String val) {
if (val != null) {
val = val.trim();
}
map.put(key, val);
}
/**
* Evaluate all the expressions and return as a Properties object.
*/
Properties eval() {
log.info("loaded properties from {}", loadedResources);
Properties properties = new Properties();
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = PropertyEval.eval(entry.getValue());
properties.setProperty(key, value);
}
return properties;
}
/**
* Read the special properties that can point to an external properties source.
*/
String indirectLocation() {
String indirectLocation = map.get("load.properties");
if (indirectLocation == null) {
indirectLocation = map.get("load.properties.override");
}
return indirectLocation;
}
}
@@ -1,181 +0,0 @@
package io.ebean.config.properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Properties;
/**
* Loads the configuration from known/expected locations.
* <p>
* Defines the loading order of resources and files.
* </p>
*/
class Loader {
private static final Logger log = LoggerFactory.getLogger(Loader.class);
enum Source {
RESOURCE,
FILE
}
private final LoadContext loadContext = new LoadContext();
private YamlLoader yamlLoader;
Loader() {
String skipYaml = System.getProperty("ebeanSkipYaml");
if (!"true".equals(skipYaml)) {
try {
Class<?> exists = Class.forName("org.yaml.snakeyaml.Yaml");
if (exists != null) {
yamlLoader = new YamlLoader(loadContext);
}
} catch (Exception e) {
// ignored
}
}
}
/**
* Load the configuration with the expected ordering.
*/
void load() {
loadMain(Source.RESOURCE);
// external file configuration overrides the resources configuration
loadMain(Source.FILE);
loadViaSystemProperty();
loadViaIndirection();
// test configuration (if found) overrides main configuration
// we should only find these resources when running tests
loadTest();
}
/**
* Load test configuration.
*/
private void loadTest() {
loadProperties("application-test.properties", Source.RESOURCE);
loadYaml("application-test.yaml", Source.RESOURCE);
loadYaml("application-test.yml", Source.RESOURCE);
loadProperties("test-ebean.properties", Source.RESOURCE);
}
/**
* Load configuration defined by a <em>load.properties</em> entry in properties file.
*/
private void loadViaIndirection() {
String location = loadContext.indirectLocation();
if (location != null) {
location = PropertyEval.eval(location);
loadWithExtensionCheck(location);
}
}
/**
* Load the main configuration for the given source.
*/
private void loadMain(Source source) {
loadYaml("application.yaml", source);
loadYaml("application.yml", source);
loadProperties("application.properties", source);
loadProperties("ebean.properties", source);
}
private void loadViaSystemProperty() {
String fileName = System.getenv("EBEAN_PROPS_FILE");
if (fileName == null) {
fileName = System.getProperty("ebean.props.file");
if (fileName != null) {
loadWithExtensionCheck(fileName);
}
} else {
loadWithExtensionCheck(fileName);
}
}
void loadWithExtensionCheck(String fileName) {
if (fileName.endsWith("yaml") || fileName.endsWith("yml")) {
loadYaml(fileName, Source.FILE);
} else if (fileName.endsWith("properties")) {
loadProperties(fileName, Source.FILE);
} else {
throw new IllegalArgumentException("Expecting only yml or properties file but got [" + fileName + "]");
}
}
/**
* Evaluate all the configuration entries and return as properties.
*/
Properties eval() {
return loadContext.eval();
}
void loadYaml(String resourcePath, Source source) {
if (yamlLoader != null) {
try {
try (InputStream is = resource(resourcePath, source)) {
yamlLoader.load(is);
}
} catch (Exception e) {
log.warn("Failed to read yml from:" + resourcePath, e);
}
}
}
void loadProperties(String resourcePath, Source source) {
try {
try (InputStream is = resource(resourcePath, source)) {
if (is != null) {
loadProperties(is);
}
}
} catch (Exception e) {
log.warn("Failed to read properties from:" + resourcePath, e);
}
}
private InputStream resource(String resourcePath, Source source) {
return loadContext.resource(resourcePath, source);
}
private void loadProperties(InputStream is) {
if (is != null) {
try {
Properties properties = new Properties();
properties.load(is);
put(properties);
} catch (IOException e) {
throw new RuntimeException("Failed to load properties?", e);
} finally {
close(is);
}
}
}
private void put(Properties properties) {
Enumeration<?> enumeration = properties.propertyNames();
while (enumeration.hasMoreElements()) {
String key = (String) enumeration.nextElement();
String property = properties.getProperty(key);
loadContext.put(key, property);
}
}
private void close(InputStream is) {
try {
is.close();
} catch (IOException e) {
log.warn("Error closing input stream for properties", e);
}
}
}
@@ -1,74 +0,0 @@
package io.ebean.config.properties;
import java.util.Enumeration;
import java.util.Properties;
/**
* Loads and evaluates properties and yml configuration.
*/
public class PropertiesLoader {
private static Properties properties;
/**
* Provides properties by reading known locations.
* <p>
* <h3>Main configuration</h3>
* <p>
* <p>Firstly loads from main resources</p>
* <pre>
* - application.properties
* - application.yaml
* - ebean.properties
* </pre>
* <p>
* <p>Then loads from local files</p>
* <pre>
* - application.properties
* - application.yaml
* - ebean.properties
* </pre>
* <p>
* <p>Then loads from environment variable <em>EBEAN_PROPS_FILE</em></p>
* <p>Then loads from system property <em>ebean.props.file</em></p>
* <p>Then loads from <em>load.properties</em></p>
* <p>
* <h3>Test configuration</h3>
* <p>
* Once the main configuration is read it will try to read common test configuration.
* This will only be successful if the test resources are available (i.e. running tests).
* </p>
* <p>Loads from test resources</p>
* <pre>
* - application-test.properties
* - application-test.yaml
* - test-ebean.properties
* </pre>
*/
public static synchronized Properties load() {
if (properties == null) {
Loader loader = new Loader();
loader.load();
properties = loader.eval();
}
return properties;
}
/**
* Return a copy of the properties with 'eval' run on all the values.
* This resolves expressions like ${HOME} etc.
*/
public static Properties eval(Properties properties) {
Properties evalCopy = new Properties();
Enumeration<?> names = properties.propertyNames();
while (names.hasMoreElements()) {
String name = (String)names.nextElement();
String value = PropertyEval.eval(properties.getProperty(name));
evalCopy.setProperty(name, value);
}
return evalCopy;
}
}
@@ -1,205 +0,0 @@
package io.ebean.config.properties;
import javax.naming.InitialContext;
import javax.naming.NamingException;
/**
* Helper used to evaluate expressions such as ${CATALINA_HOME}.
* <p>
* The expressions can contain environment variables, system properties or JNDI
* properties. JNDI expressions take the form ${jndi:propertyName} where you
* substitute propertyName with the name of the jndi property you wish to
* evaluate.
* </p>
*/
final class PropertyEval {
/**
* Prefix for looking up JNDI Environment variable.
*/
private static final String JAVA_COMP_ENV = "java:comp/env/";
/**
* Used to detect the start of an expression.
*/
private static final String START = "${";
/**
* Used to detect the end of an expression.
*/
private static final String END = "}";
/**
* Specify the PropertyHolder.
*/
private PropertyEval() {
}
/**
* Return the property value evaluating and replacing any expressions such as
* ${CATALINA_HOME}.
*/
static String eval(String val) {
if (val == null) {
return null;
}
int sp = val.indexOf(START);
if (sp > -1) {
int ep = val.indexOf(END, sp + 1);
if (ep > -1) {
return eval(val, sp, ep);
}
}
return val;
}
/**
* Convert the expression using JNDI, Environment variables, System Properties
* or existing an property in SystemProperties itself.
*/
private static String evaluateExpression(String exp) {
if (exp.isEmpty()) {
return null;
}
if (isJndiExpression(exp)) {
// JNDI property lookup...
String val = getJndiProperty(exp);
if (val != null) {
return val;
}
}
String val = System.getProperty(exp);
if (val == null) {
val = System.getenv(exp);
}
return val;
}
private static String eval(String val, int sp, int ep) {
return new EvalBuffer(val, sp, ep).process();
}
private static boolean isJndiExpression(String exp) {
return exp.startsWith("JNDI:") || exp.startsWith("jndi:");
}
/**
* Returns null if JNDI is not setup or if the property is not found.
*
* @param key the key of the JNDI Environment property including a JNDI: prefix.
*/
private static String getJndiProperty(String key) {
try {
// remove the JNDI: prefix
key = key.substring(5);
return (String) getJndiObject(key);
} catch (NamingException ex) {
return null;
}
}
/**
* Similar to getProperty but throws NamingException if JNDI is not setup or
* if the property is not found.
*/
private static Object getJndiObject(String key) throws NamingException {
InitialContext ctx = new InitialContext();
return ctx.lookup(JAVA_COMP_ENV + key);
}
private static class EvalBuffer {
private final StringBuilder buf = new StringBuilder();
private final String original;
private int position;
private int start;
private int end;
private String expression;
private String defaultValue;
EvalBuffer(String val, int start, int ep) {
this.original = val;
this.start = start;
this.end = ep;
this.position = 0;
moveToStart();
}
void moveToStart() {
if (start > position) {
buf.append(original.substring(position, start));
position = start;
}
}
void parseForDefault() {
int colonPos = original.lastIndexOf(':', end);
if (colonPos > start) {
expression = original.substring(start + START.length(), colonPos);
defaultValue = original.substring(colonPos + 1, end);
} else {
expression = original.substring(start + START.length(), end);
}
}
void evaluate() {
String eval = evaluateExpression(expression);
if (eval != null) {
buf.append(eval);
} else {
if (defaultValue != null) {
buf.append(defaultValue);
} else {
buf.append(START).append(expression).append(END);
}
}
}
String end() {
if (end < original.length() - 1) {
buf.append(original.substring(end + 1));
}
return buf.toString();
}
boolean next() {
if (end < original.length()) {
int startPos = original.indexOf(START, end + 1);
if (startPos > -1) {
int endPos = original.indexOf(END, startPos + 1);
if (endPos > -1) {
if (startPos > end + 1) {
buf.append(original.substring(end + 1, startPos));
}
this.start = startPos;
this.end = endPos;
return true;
}
}
}
return false;
}
private void evalNext() {
parseForDefault();
evaluate();
}
String process() {
evalNext();
while (next()) {
evalNext();
}
return end();
}
}
}
@@ -1,58 +0,0 @@
package io.ebean.config.properties;
import org.yaml.snakeyaml.Yaml;
import java.io.InputStream;
import java.util.Map;
/**
* Loads configuration from Yml into the load context.
* <p>
* Note that this ignores 'lists' so just reads 'maps' and scalar values.
* </p>
*/
class YamlLoader {
private final Yaml yaml = new Yaml();
private final LoadContext loadContext;
YamlLoader(LoadContext loadContext) {
this.loadContext = loadContext;
}
@SuppressWarnings("unchecked")
void load(InputStream is) {
if (is != null) {
for (Object map : yaml.loadAll(is)) {
loadMap((Map<String, Object>)map, null);
}
}
}
@SuppressWarnings("unchecked")
void loadMap(Map<String, Object> map, String path) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
if (path != null) {
key = path + "." + key;
}
Object val = entry.getValue();
if (val instanceof Map) {
loadMap((Map<String, Object>) val, key);
} else {
addScalar(key, val);
}
}
}
private void addScalar(String key, Object val) {
if (val instanceof String) {
loadContext.put(key, (String) val);
} else if (val instanceof Number || val instanceof Boolean) {
loadContext.put(key, val.toString());
}
}
}
@@ -5,11 +5,6 @@ package io.ebean.meta;
*/
public interface MetaMetric {
/**
* Return the metric type.
*/
MetricType getMetricType();
/**
* Return the metric name.
*/
+1 -11
View File
@@ -6,7 +6,6 @@ package io.ebean.meta;
public class MetricData {
private String name;
private String type;
private String hash;
private String loc;
private String sql;
@@ -16,9 +15,8 @@ public class MetricData {
private Long max;
private Long total;
public MetricData(String name, String type) {
public MetricData(String name) {
this.name = name;
this.type = type;
}
public MetricData() {
@@ -32,14 +30,6 @@ public class MetricData {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getHash() {
return hash;
}
@@ -1,40 +0,0 @@
package io.ebean.meta;
/**
* The type of Metric.
*/
public enum MetricType {
/**
* Transactions.
*/
TXN,
/**
* ORM Insert Update or Delete.
*/
IUD,
/**
* ORM queries.
*/
ORM,
/**
* DTO queries.
*/
DTO,
/**
* SQL queries with a label will have metrics collected.
* <p>
* SqlQuery and SqlUpdate without a label have no metrics collected.
*/
SQL,
/**
* L2 cache metrics.
*/
L2
}
@@ -1,7 +1,6 @@
package io.ebean.metric;
import io.ebean.ProfileLocation;
import io.ebean.meta.MetricType;
/**
* Factory to create timed metric counters.
@@ -18,21 +17,21 @@ public interface MetricFactory {
/**
* Create a timed metric group.
*/
TimedMetricMap createTimedMetricMap(MetricType metricType, String name);
TimedMetricMap createTimedMetricMap(String name);
/**
* Create a Timed metric.
*/
TimedMetric createTimedMetric(MetricType metricType, String name);
TimedMetric createTimedMetric(String name);
/**
* Create a counter metric.
*/
CountMetric createCountMetric(MetricType metricType, String name);
CountMetric createCountMetric(String name);
/**
* Create a Timed metric.
*/
QueryPlanMetric createQueryPlanMetric(MetricType metricType, Class<?> type, String label, ProfileLocation profileLocation, String sql);
QueryPlanMetric createQueryPlanMetric(Class<?> type, String label, ProfileLocation profileLocation, String sql);
}
+1 -7
View File
@@ -8,12 +8,11 @@ import io.ebean.event.BeanPersistListener;
import io.ebean.event.BeanQueryAdapter;
import io.ebeanservice.docstore.api.mapping.DocumentMapping;
import javax.annotation.Nonnull;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import javax.annotation.Nonnull;
/**
* Information and methods on BeanDescriptors made available to plugins.
*/
@@ -156,11 +155,6 @@ public interface BeanType<T> {
*/
IdType getIdType();
/**
* Return the sequence name associated to this entity bean type (if there is one).
*/
String getSequenceName();
/**
* Return true if this bean type has doc store backing.
*/
@@ -1,6 +1,5 @@
package io.ebeaninternal.api;
import io.ebean.meta.MetricType;
import io.ebean.meta.MetricVisitor;
import io.ebean.metric.CountMetric;
import io.ebean.metric.MetricFactory;
@@ -22,11 +21,11 @@ public class ExtraMetrics {
*/
public ExtraMetrics() {
final MetricFactory factory = MetricFactory.get();
this.bindCapture = factory.createTimedMetric(MetricType.ORM, "ebean.queryplan.bindcapture");
this.planCollect = factory.createTimedMetric(MetricType.ORM, "ebean.queryplan.collect");
this.loadOneL2 = factory.createCountMetric(MetricType.ORM, "loadone.l2");
this.loadOneRef = factory.createCountMetric(MetricType.ORM, "loadone.ref");
this.loadOneNoLoader = factory.createCountMetric(MetricType.ORM, "loadone.noloader");
this.bindCapture = factory.createTimedMetric("ebean.queryplan.bindcapture");
this.planCollect = factory.createTimedMetric("ebean.queryplan.collect");
this.loadOneL2 = factory.createCountMetric("loadone.l2");
this.loadOneRef = factory.createCountMetric("loadone.ref");
this.loadOneNoLoader = factory.createCountMetric("loadone.noloader");
}
/**
@@ -0,0 +1,27 @@
package io.ebeaninternal.api;
import io.ebean.annotation.Platform;
import io.ebean.util.StringHelper;
public class PlatformMatch {
/**
* Return true if the script platforms is a match/supported for the given platform.
*
* @param platform The database platform we are generating/running DDL for
* @param platforms The platforms (comma delimited) this script should run for
*/
public static boolean matchPlatform(Platform platform, String platforms) {
if (platforms == null || platforms.trim().isEmpty()) {
return true;
}
// match on base platform name and platform name
for (String name : StringHelper.splitNames(platforms)) {
if (name.equalsIgnoreCase(platform.base().name()) || name.equalsIgnoreCase(platform.name())) {
return true;
}
}
return false;
}
}
@@ -36,4 +36,11 @@ public interface SpiExpressionList<T> extends ExpressionList<T>, SpiExpression {
* Write the top level where expressions taking into account possible extra idEquals expression.
*/
void writeDocQuery(DocQueryContext context, SpiExpression idEquals) throws IOException;
/**
* Apply firstRow maxRows limits on the filterMany query.
*/
default void applyRowLimits(SpiQuery<?> query) {
// do nothing by default
}
}
@@ -1,9 +1,9 @@
package io.ebeaninternal.dbmigration;
import io.ebean.annotation.Platform;
import io.ebean.config.DbMigrationConfig;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.migration.ddl.DdlAutoCommit;
import io.ebean.migration.ddl.DdlRunner;
import io.ebean.migration.runner.ScriptTransform;
import io.ebean.util.JdbcClose;
@@ -48,6 +48,7 @@ public class DdlGenerator {
private final boolean ddlAutoCommit;
private final String dbSchema;
private final ScriptTransform scriptTransform;
private final Platform platform;
private final String platformName;
private CurrentModel currentModel;
@@ -63,7 +64,8 @@ public class DdlGenerator {
this.createOnly = serverConfig.isDdlCreateOnly();
this.dbSchema = serverConfig.getDbSchema();
final DatabasePlatform databasePlatform = server.getDatabasePlatform();
this.platformName = databasePlatform.getPlatform().base().name();
this.platform = databasePlatform.getPlatform();
this.platformName = platform.base().name();
if (!serverConfig.getTenantMode().isDdlEnabled() && serverConfig.isDdlRun()) {
log.warn("DDL can't be run on startup with TenantMode " + serverConfig.getTenantMode());
this.runDdl = false;
@@ -188,7 +190,7 @@ public class DdlGenerator {
protected void runDropSql(Connection connection) throws IOException {
if (!createOnly) {
if (extraDdl && jaxbPresent) {
String extraApply = ExtraDdlXmlReader.buildExtra(server.getDatabasePlatform().getName(), true);
String extraApply = ExtraDdlXmlReader.buildExtra(platform, true);
if (extraApply != null) {
runScript(connection, false, extraApply, "extra-ddl");
}
@@ -209,13 +211,13 @@ public class DdlGenerator {
if (extraDdl && jaxbPresent) {
if (currentModel().isTablePartitioning()) {
String extraPartitioning = ExtraDdlXmlReader.buildPartitioning(server.getDatabasePlatform().getName());
String extraPartitioning = ExtraDdlXmlReader.buildPartitioning(platform);
if (extraPartitioning != null && !extraPartitioning.isEmpty()) {
runScript(connection, false, extraPartitioning, "builtin-partitioning-ddl");
}
}
String extraApply = ExtraDdlXmlReader.buildExtra(server.getDatabasePlatform().getName(), false);
String extraApply = ExtraDdlXmlReader.buildExtra(platform, false);
if (extraApply != null) {
runScript(connection, false, extraApply, "extra-ddl");
}
@@ -14,11 +14,12 @@ import io.ebean.config.dbplatform.cockroach.CockroachPlatform;
import io.ebean.config.dbplatform.db2.DB2Platform;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hana.HanaPlatform;
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
import io.ebean.config.dbplatform.mysql.MySql55Platform;
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
import io.ebean.config.dbplatform.nuodb.NuoDbPlatform;
import io.ebean.config.dbplatform.oracle.Oracle11Platform;
import io.ebean.config.dbplatform.oracle.OraclePlatform;
import io.ebean.config.dbplatform.postgres.Postgres9Platform;
import io.ebean.config.dbplatform.postgres.PostgresPlatform;
import io.ebean.config.dbplatform.sqlanywhere.SqlAnywherePlatform;
import io.ebean.config.dbplatform.sqlite.SQLitePlatform;
@@ -49,6 +50,8 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static io.ebeaninternal.api.PlatformMatch.matchPlatform;
/**
* Generates DB Migration xml and sql scripts.
* <p>
@@ -406,7 +409,7 @@ public class DefaultDbMigration implements DbMigration {
if (extraDdl != null) {
List<DdlScript> ddlScript = extraDdl.getDdlScript();
for (DdlScript script : ddlScript) {
if (!script.isDrop() && ExtraDdlXmlReader.matchPlatform(dbPlatform.getName(), script.getPlatforms())) {
if (!script.isDrop() && matchPlatform(dbPlatform.getPlatform(), script.getPlatforms())) {
writeExtraDdl(migrationDir, script);
}
}
@@ -810,8 +813,8 @@ public class DefaultDbMigration implements DbMigration {
switch (platform) {
case H2:
return new H2Platform();
case HSQLDB:
return new HsqldbPlatform();
case POSTGRES9:
return new Postgres9Platform();
case POSTGRES:
return new PostgresPlatform();
case MYSQL55:
@@ -820,6 +823,8 @@ public class DefaultDbMigration implements DbMigration {
return new MySqlPlatform();
case ORACLE:
return new OraclePlatform();
case ORACLE11:
return new Oracle11Platform();
case SQLANYWHERE:
return new SqlAnywherePlatform();
case SQLSERVER16:
@@ -44,8 +44,8 @@ public abstract class AbstractHanaDdl extends PlatformDdl {
String columnName = alter.getColumnName();
String currentType = alter.getCurrentType();
String type = alter.getType() != null ? alter.getType() : currentType;
type = convert(type, false);
currentType = convert(currentType, false);
type = convert(type);
currentType = convert(currentType);
boolean notnull = (alter.isNotnull() != null) ? alter.isNotnull() : Boolean.TRUE.equals(alter.isCurrentNotnull());
String notnullClause = notnull ? " not null" : "";
String defaultValue = DdlHelp.isDropDefault(alter.getDefaultValue()) ? "null"
@@ -103,7 +103,7 @@ public abstract class AbstractHanaDdl extends PlatformDdl {
protected String convertArrayType(String logicalArrayType) {
Matcher matcher = ARRAY_PATTERN.matcher(logicalArrayType);
if (matcher.matches()) {
return convert(matcher.group(1), false) + " array" + (matcher.group(2) == null ? "" : matcher.group(2));
return convert(matcher.group(1)) + " array" + (matcher.group(2) == null ? "" : matcher.group(2));
} else {
return fallbackArrayType;
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.annotation.Platform;
import io.ebean.config.DbConstraintNaming;
import io.ebean.config.NamingConvention;
import io.ebean.config.ServerConfig;
@@ -28,6 +29,8 @@ import io.ebeaninternal.dbmigration.migration.DropTable;
import io.ebeaninternal.dbmigration.migration.ForeignKey;
import io.ebeaninternal.dbmigration.migration.UniqueConstraint;
import io.ebeaninternal.dbmigration.model.MTable;
import io.ebeaninternal.dbmigration.model.MTableIdentity;
import io.ebeaninternal.server.deploy.IdentityMode;
import java.io.IOException;
import java.math.BigInteger;
@@ -37,6 +40,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static io.ebeaninternal.api.PlatformMatch.matchPlatform;
import static io.ebeaninternal.dbmigration.ddlgeneration.platform.SplitColumns.split;
/**
@@ -168,18 +172,14 @@ public class BaseTableDdl implements TableDdl {
}
private List<String> getScriptsForPlatform(List<DdlScript> scripts) {
String searchPlatform = platformDdl.getPlatform().getName();
List<String> ret = Collections.emptyList();
Platform searchPlatform = platformDdl.getPlatform().getPlatform();
for (DdlScript script : scripts) {
if (script.getPlatforms() == null || script.getPlatforms().isEmpty()) {
ret = script.getDdl();
} else for (String platform : StringHelper.splitNames(script.getPlatforms())) {
if (platform.equals(searchPlatform)) {
return script.getDdl();
}
if (matchPlatform(searchPlatform, script.getPlatforms())) {
// just returns the first match (rather than appends them)
return script.getDdl();
}
}
return ret;
return Collections.emptyList();
}
/**
@@ -248,21 +248,22 @@ public class BaseTableDdl implements TableDdl {
List<Column> columns = createTable.getColumn();
List<Column> pk = determinePrimaryKeyColumns(columns);
boolean singleColumnPrimaryKey = (pk.size() == 1);
boolean useIdentity = false;
boolean useSequence = false;
if (singleColumnPrimaryKey) {
IdType useDbIdentityType = platformDdl.useIdentityType(createTable.getIdentityType());
useIdentity = (IdType.IDENTITY == useDbIdentityType);
useSequence = (IdType.SEQUENCE == useDbIdentityType);
DdlIdentity identity = DdlIdentity.NONE;
if ((pk.size() == 1)) {
final IdentityMode identityMode = MTableIdentity.fromCreateTable(createTable);
IdType idType = platformDdl.useIdentityType(identityMode.getIdType());
String sequenceName = identityMode.getSequenceName();
if (IdType.SEQUENCE == idType && (sequenceName == null || sequenceName.isEmpty())) {
sequenceName = sequenceName(createTable, pk);
}
identity = new DdlIdentity(idType, identityMode, sequenceName);
}
String partitionMode = createTable.getPartitionMode();
DdlBuffer apply = writer.apply();
apply.append(platformDdl.getCreateTableCommandPrefix()).append(" ").append(tableName).append(" (");
writeTableColumns(apply, columns, useIdentity);
writeTableColumns(apply, columns, identity);
writeUniqueConstraints(apply, createTable);
writeCompoundUniqueConstraints(apply, createTable);
if (!pk.isEmpty()) {
@@ -297,9 +298,8 @@ public class BaseTableDdl implements TableDdl {
// we drop the related sequence (if sequences are used)
dropTable(writer.dropAll(), tableName);
if (useSequence) {
String pkCol = pk.get(0).getName();
writeSequence(writer, createTable, pkCol);
if (identity.useSequence()) {
writeSequence(writer, identity);
}
// add blank line for a bit of whitespace between tables
@@ -311,6 +311,10 @@ public class BaseTableDdl implements TableDdl {
}
}
private String sequenceName(CreateTable createTable, List<Column> pk) {
return namingConvention.getSequenceName(createTable.getName(), pk.get(0).getName());
}
/**
* Add table and column comments (separate from the create table statement).
*/
@@ -350,8 +354,8 @@ public class BaseTableDdl implements TableDdl {
}
}
private void writeTableColumns(DdlBuffer apply, List<Column> columns, boolean useIdentity) throws IOException {
platformDdl.writeTableColumns(apply, columns, useIdentity);
private void writeTableColumns(DdlBuffer apply, List<Column> columns, DdlIdentity identity) throws IOException {
platformDdl.writeTableColumns(apply, columns, identity);
}
/**
@@ -380,18 +384,9 @@ public class BaseTableDdl implements TableDdl {
}
}
protected void writeSequence(DdlWrite writer, CreateTable createTable, String pk) throws IOException {
// explicit sequence use or platform decides
String explicitSequenceName = createTable.getSequenceName();
int initial = toInt(createTable.getSequenceInitial());
int allocate = toInt(createTable.getSequenceAllocate());
String seqName = explicitSequenceName;
if (seqName == null) {
seqName = namingConvention.getSequenceName(createTable.getName(), pk);
}
String createSeq = platformDdl.createSequence(seqName, initial, allocate);
protected void writeSequence(DdlWrite writer, DdlIdentity identity) throws IOException {
String seqName = identity.getSequenceName();
String createSeq = platformDdl.createSequence(seqName, identity);
if (hasValue(createSeq)) {
writer.apply().append(createSeq).newLine();
writer.dropAll().appendStatement(platformDdl.dropSequence(seqName));
@@ -511,16 +506,7 @@ public class BaseTableDdl implements TableDdl {
}
private boolean platformInclude(String platforms) {
if (platforms == null || platforms.isEmpty()) {
return true;
}
String currentPlatform = platformDdl.getPlatform().getPlatform().name();
for (String name : StringHelper.splitNames(platforms)) {
if (currentPlatform.equalsIgnoreCase(name)) {
return true;
}
}
return false;
return matchPlatform(platformDdl.getPlatform().getPlatform(), platforms);
}
/**
@@ -25,8 +25,7 @@ public class CockroachDdl extends PlatformDdl {
* Map bigint, integer and smallint all into serial.
*/
@Override
public String asIdentityColumn(String columnDefn) {
public String asIdentityColumn(String columnDefn, DdlIdentity identity) {
if ("bigint".equalsIgnoreCase(columnDefn)) {
return "serial";
}
@@ -195,7 +195,7 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
*/
protected void writeColumnDefinition(DdlBuffer buffer, String columnName, String type) throws IOException {
String platformType = platformDdl.convert(type, false);
String platformType = platformDdl.convert(type);
buffer.append(" ");
buffer.append(platformDdl.lowerColumnName(columnName), 29);
buffer.append(platformType);
@@ -0,0 +1,98 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.annotation.IdentityGenerated;
import io.ebean.config.dbplatform.IdType;
import io.ebeaninternal.server.deploy.IdentityMode;
public class DdlIdentity {
public static final DdlIdentity NONE = new DdlIdentity();
private final IdType idType;
private final IdentityMode identityMode;
private final String sequenceName;
public DdlIdentity(IdType idType, IdentityMode identityMode, String sequenceName) {
this.idType = idType;
this.identityMode = identityMode;
this.sequenceName = sequenceName;
}
private DdlIdentity() {
this.idType = null;
this.identityMode = null;
this.sequenceName = null;
}
public boolean useSequence() {
return idType == IdType.SEQUENCE;
}
public boolean useIdentity() {
return idType == IdType.IDENTITY;
}
public String getSequenceName() {
return sequenceName;
}
private String generatedBy() {
return isAlways() ? "always" : "by default";
}
private boolean isAlways() {
return IdentityGenerated.ALWAYS == identityMode.getGenerated();
}
public String optionGenerated() {
return " generated " + generatedBy() +" as identity";
}
public String identityOptions(String startWith, String incrementBy, String cache) {
return options(true, startWith, incrementBy, cache);
}
public String sequenceOptions(String startWith, String incrementBy, String cache) {
return options(false, startWith, incrementBy, cache);
}
private String options(boolean brackets, String startWith, String incrementBy, String cache) {
if (!identityMode.hasOptions()) {
return "";
}
StringBuilder sb = new StringBuilder(40);
if (brackets) {
sb.append(" (");
} else {
sb.append(" ");
}
optionFor(sb, startWith, identityMode.getStart());
optionFor(sb, incrementBy, identityMode.getIncrement());
optionFor(sb, cache, identityMode.getCache());
if (brackets) {
sb.append(")");
}
return sb.toString();
}
private void optionFor(StringBuilder sb, String prefix, int val) {
if (val > 0 && prefix != null) {
if (sb.length() > 2) {
sb.append(" ");
}
sb.append(prefix).append(" ").append(val);
}
}
public int getStart() {
return identityMode.getStart();
}
public int getIncrement() {
return identityMode.getIncrement();
}
public int getCache() {
return identityMode.getCache();
}
}
@@ -12,6 +12,14 @@ public class H2Ddl extends PlatformDdl {
this.historyDdl = new H2HistoryDdl();
}
/**
* Modify and return the column definition for autoincrement or identity definition.
*/
@Override
public String asIdentityColumn(String columnDefn, DdlIdentity identity) {
return asIdentityStandardOptions(columnDefn, identity);
}
@Override
protected String convertArrayType(String logicalArrayType) {
return "array";
@@ -109,7 +109,7 @@ public class HanaHistoryDdl implements PlatformHistoryDdl {
protected void writeColumnDefinition(DdlBuffer buffer, String columnName, String type, String defaultValue,
boolean isNotNull, String generated) throws IOException {
String platformType = platformDdl.convert(type, false);
String platformType = platformDdl.convert(type);
buffer.append(" ").append(platformDdl.lowerColumnName(columnName));
buffer.append(" ").append(platformType);
if (defaultValue != null) {
@@ -107,7 +107,7 @@ public class MySqlDdl extends PlatformDdl {
String tableName = alter.getTableName();
String columnName = alter.getColumnName();
String type = alter.getType() != null ? alter.getType() : alter.getCurrentType();
type = convert(type, false);
type = convert(type);
boolean notnull = (alter.isNotnull() != null) ? alter.isNotnull() : Boolean.TRUE.equals(alter.isCurrentNotnull());
String notnullClause = notnull ? " not null" : "";
@@ -115,8 +115,8 @@ public class MySqlDdl extends PlatformDdl {
}
@Override
protected void writeColumnDefinition(DdlBuffer buffer, Column column, boolean useIdentity) throws IOException {
super.writeColumnDefinition(buffer, column, useIdentity);
protected void writeColumnDefinition(DdlBuffer buffer, Column column, DdlIdentity identity) throws IOException {
super.writeColumnDefinition(buffer, column, identity);
String comment = column.getComment();
if (!StringHelper.isNull(comment)) {
// in mysql 5.5 column comment save in information_schema.COLUMNS.COLUMN_COMMENT(VARCHAR 1024)
@@ -19,7 +19,7 @@ public class Oracle10Ddl extends PlatformDdl {
this.columnSetNotnull = "not null";
this.columnSetNull = "null";
this.columnSetDefault = "default";
this.identitySuffix = " generated always as identity";
this.identitySuffix = " generated by default as identity";
}
@Override
@@ -19,10 +19,7 @@ import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
import io.ebeaninternal.dbmigration.migration.AlterColumn;
import io.ebeaninternal.dbmigration.migration.Column;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.migration.IdentityType;
import io.ebeaninternal.dbmigration.model.MTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Collection;
@@ -33,8 +30,6 @@ import java.util.List;
*/
public class PlatformDdl {
private static final Logger logger = LoggerFactory.getLogger(PlatformDdl.class);
protected final DatabasePlatform platform;
protected PlatformHistoryDdl historyDdl = new NoHistorySupportDdl();
@@ -70,6 +65,12 @@ public class PlatformDdl {
protected String foreignKeyOnUpdate = "on update";
protected String identitySuffix = " auto_increment";
protected String identityStartWith = "start with";
protected String identityIncrementBy = "increment by";
protected String identityCache = "cache";
protected String sequenceStartWith = "start with";
protected String sequenceIncrementBy = "increment by";
protected String sequenceCache = "cache";
protected String alterTableIfExists = "";
@@ -156,8 +157,7 @@ public class PlatformDdl {
* Return the identity type to use given the support in the underlying database
* platform for sequences and identity/autoincrement.
*/
public IdType useIdentityType(IdentityType modelIdentity) {
public IdType useIdentityType(IdType modelIdentity) {
if (modelIdentity == null) {
// use the default
return dbIdentity.getIdType();
@@ -169,8 +169,7 @@ public class PlatformDdl {
* Determine the id type to use based on requested identityType and
* the support for that in the database platform.
*/
private IdType identityType(IdentityType modelIdentity, IdType platformIdType, boolean supportsSequence, boolean supportsIdentity) {
private IdType identityType(IdType modelIdentity, IdType platformIdType, boolean supportsSequence, boolean supportsIdentity) {
switch (modelIdentity) {
case GENERATOR:
return IdType.GENERATOR;
@@ -188,10 +187,21 @@ public class PlatformDdl {
/**
* Modify and return the column definition for autoincrement or identity definition.
*/
public String asIdentityColumn(String columnDefn) {
public String asIdentityColumn(String columnDefn, DdlIdentity identity) {
return columnDefn + identitySuffix;
}
/**
* SQl2003 standard identity definition.
*/
protected String asIdentityStandardOptions(String columnDefn, DdlIdentity identity) {
StringBuilder sb = new StringBuilder(columnDefn.length() + 60);
sb.append(columnDefn).append(identity.optionGenerated());
sb.append(identity.identityOptions(identityStartWith, identityIncrementBy, identityCache));
return sb.toString();
}
/**
* Return true if the table and column comments are included inline.
*/
@@ -224,13 +234,13 @@ public class PlatformDdl {
/**
* Write all the table columns converting to platform types as necessary.
*/
public void writeTableColumns(DdlBuffer apply, List<Column> columns, boolean useIdentity) throws IOException {
public void writeTableColumns(DdlBuffer apply, List<Column> columns, DdlIdentity identity) throws IOException {
for (int i = 0; i < columns.size(); i++) {
if (i > 0) {
apply.append(",");
}
apply.newLine();
writeColumnDefinition(apply, columns.get(i), useIdentity);
writeColumnDefinition(apply, columns.get(i), identity);
}
for (Column column : columns) {
@@ -248,14 +258,16 @@ public class PlatformDdl {
/**
* Write the column definition to the create table statement.
*/
protected void writeColumnDefinition(DdlBuffer buffer, Column column, boolean useIdentity) throws IOException {
protected void writeColumnDefinition(DdlBuffer buffer, Column column, DdlIdentity identity) throws IOException {
boolean identityColumn = useIdentity && isTrue(column.isPrimaryKey());
String platformType = convert(column.getType(), identityColumn);
String columnDefn = convert(column.getType());
if (identity.useIdentity() && isTrue(column.isPrimaryKey())) {
columnDefn = asIdentityColumn(columnDefn, identity);
}
buffer.append(" ");
buffer.append(lowerColumnName(column.getName()), 29);
buffer.append(platformType);
buffer.append(columnDefn);
if (!Boolean.TRUE.equals(column.isPrimaryKey())) {
String defaultValue = convertDefaultValue(column.getDefaultValue());
if (defaultValue != null) {
@@ -301,15 +313,14 @@ public class PlatformDdl {
/**
* Convert the standard type to the platform specific type.
*/
public String convert(String type, boolean identity) {
public String convert(String type) {
if (type == null) {
return null;
}
if (type.contains("[]")) {
return convertArrayType(type);
}
String platformType = typeConverter.convert(type);
return identity ? asIdentityColumn(platformType) : platformType;
return typeConverter.convert(type);
}
/**
@@ -354,16 +365,10 @@ public class PlatformDdl {
/**
* Generate and return the create sequence DDL.
*/
public String createSequence(String sequenceName, int initialValue, int allocationSize) {
public String createSequence(String sequenceName, DdlIdentity identity) {
StringBuilder sb = new StringBuilder("create sequence ");
sb.append(sequenceName);
if (initialValue > 1) {
sb.append(" start with ").append(initialValue);
}
if (allocationSize > 1) {
sb.append(" increment by ").append(allocationSize);
}
sb.append(identity.sequenceOptions(sequenceStartWith, sequenceIncrementBy, sequenceCache));
sb.append(";");
return sb.toString();
}
@@ -515,13 +520,12 @@ public class PlatformDdl {
public void alterTableAddColumn(DdlBuffer buffer, String tableName, Column column, boolean onHistoryTable, String defaultValue) throws IOException {
String convertedType = convert(column.getType(), false);
String convertedType = convert(column.getType());
buffer.append("alter table ").append(tableName)
.append(" ").append(addColumn).append(" ").append(column.getName())
.append(" ").append(convertedType);
// Add default value also to history table if it is not excluded
if (defaultValue != null) {
if (!onHistoryTable || !isTrue(column.isHistoryExclude())) {
@@ -572,8 +576,7 @@ public class PlatformDdl {
* </p>
*/
public String alterColumnType(String tableName, String columnName, String type) {
return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + columnSetType + convert(type, false) + alterColumnSuffix;
return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + columnSetType + convert(type) + alterColumnSuffix;
}
/**
@@ -583,7 +586,6 @@ public class PlatformDdl {
* </p>
*/
public String alterColumnNotnull(String tableName, String columnName, boolean notnull) {
String suffix = notnull ? columnSetNotnull : columnSetNull;
return "alter table " + tableName + " " + alterColumn + " " + columnName + " " + suffix + alterColumnSuffix;
}
@@ -592,7 +594,6 @@ public class PlatformDdl {
* Alter table adding the check constraint.
*/
public String alterTableAddCheckConstraint(String tableName, String checkConstraintName, String checkConstraint) {
return "alter table " + tableName + " " + addConstraint + " " + maxConstraintName(checkConstraintName) + " " + checkConstraint;
}
@@ -627,12 +628,6 @@ public class PlatformDdl {
buffer.append(")");
}
protected void appendWithSpace(String content, StringBuilder buffer) {
if (hasValue(content)) {
buffer.append(" ").append(content);
}
}
/**
* Convert the table to lower case.
* <p>
@@ -0,0 +1,27 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.dbplatform.DatabasePlatform;
public class Postgres9Ddl extends PostgresDdl {
public Postgres9Ddl(DatabasePlatform platform) {
super(platform);
}
/**
* Map bigint, integer and smallint into their equivalent serial types.
*/
@Override
public String asIdentityColumn(String columnDefn, DdlIdentity identity) {
if ("bigint".equalsIgnoreCase(columnDefn)) {
return "bigserial";
}
if ("integer".equalsIgnoreCase(columnDefn)) {
return "serial";
}
if ("smallint".equalsIgnoreCase(columnDefn)) {
return "smallserial";
}
return columnDefn;
}
}
@@ -37,23 +37,6 @@ public class PostgresDdl extends PlatformDdl {
return NativeDbArray.logicalToNative(logicalArrayType);
}
/**
* Map bigint, integer and smallint into their equivalent serial types.
*/
@Override
public String asIdentityColumn(String columnDefn) {
if ("bigint".equalsIgnoreCase(columnDefn)) {
return "bigserial";
}
if ("integer".equalsIgnoreCase(columnDefn)) {
return "serial";
}
if ("smallint".equalsIgnoreCase(columnDefn)) {
return "smallserial";
}
return columnDefn;
}
@Override
public void addTablePartition(DdlBuffer apply, String partitionMode, String partitionColumn) throws IOException {
apply.append(" partition by range (").append(partitionColumn).append(")");
@@ -63,4 +46,12 @@ public class PostgresDdl extends PlatformDdl {
public String dropIndex(String indexName, String tableName, boolean concurrent) {
return (concurrent ? dropIndexConcurrentlyIfExists : dropIndexIfExists) + maxConstraintName(indexName);
}
/**
* Modify and return the column definition for autoincrement or identity definition.
*/
@Override
public String asIdentityColumn(String columnDefn, DdlIdentity identity) {
return asIdentityStandardOptions(columnDefn, identity);
}
}
@@ -114,18 +114,22 @@ public class SqlServerDdl extends PlatformDdl {
* Generate and return the create sequence DDL.
*/
@Override
public String createSequence(String sequenceName, int initialValue, int allocationSize) {
StringBuilder sb = new StringBuilder("create sequence ");
sb.append(sequenceName);
sb.append(" as bigint ");
if (initialValue > 1) {
sb.append(" start with ").append(initialValue);
public String createSequence(String sequenceName, DdlIdentity identity) {
StringBuilder sb = new StringBuilder(80);
sb.append("create sequence ").append(sequenceName).append(" as bigint");
final int start = identity.getStart();
if (start > 1) {
sb.append(" start with ").append(start);
} else {
sb.append(" start with 1 ");
sb.append(" start with 1");
}
if (allocationSize > 1) {
sb.append(" increment by ").append(allocationSize);
final int increment = identity.getIncrement();
if (increment > 1) {
sb.append(" increment by ").append(increment);
}
final int cache = identity.getCache();
if (cache > 1) {
sb.append(" cache ").append(increment);
}
sb.append(";");
return sb.toString();
@@ -157,7 +161,7 @@ public class SqlServerDdl extends PlatformDdl {
String tableName = alter.getTableName();
String columnName = alter.getColumnName();
String type = alter.getType() != null ? alter.getType() : alter.getCurrentType();
type = convert(type, false);
type = convert(type);
boolean notnull = (alter.isNotnull() != null) ? alter.isNotnull() : Boolean.TRUE.equals(alter.isCurrentNotnull());
String notnullClause = notnull ? " not null" : "";
@@ -33,6 +33,10 @@ import java.util.List;
* &lt;attribute name="withHistory" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="draft" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="identityType" type="{http://ebean-orm.github.io/xml/ns/dbmigration}identityType" />
* &lt;attribute name="identityStart" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" />
* &lt;attribute name="identityIncrement" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" />
* &lt;attribute name="identityCache" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" />
* &lt;attribute name="identityGenerated" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="sequenceName" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="sequenceInitial" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" />
* &lt;attribute name="sequenceAllocate" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" />
@@ -68,6 +72,17 @@ public class CreateTable {
protected Boolean draft;
@XmlAttribute(name = "identityType")
protected IdentityType identityType;
@XmlAttribute(name = "identityStart")
@XmlSchemaType(name = "positiveInteger")
protected BigInteger identityStart;
@XmlAttribute(name = "identityIncrement")
@XmlSchemaType(name = "positiveInteger")
protected BigInteger identityIncrement;
@XmlAttribute(name = "identityCache")
@XmlSchemaType(name = "positiveInteger")
protected BigInteger identityCache;
@XmlAttribute(name = "identityGenerated")
protected String identityGenerated;
@XmlAttribute(name = "sequenceName")
protected String sequenceName;
@XmlAttribute(name = "sequenceInitial")
@@ -288,6 +303,86 @@ public class CreateTable {
this.identityType = value;
}
/**
* Gets the value of the identityStart property.
*
* @return possible object is
* {@link BigInteger }
*/
public BigInteger getIdentityStart() {
return identityStart;
}
/**
* Sets the value of the identityStart property.
*
* @param value allowed object is
* {@link BigInteger }
*/
public void setIdentityStart(BigInteger value) {
this.identityStart = value;
}
/**
* Gets the value of the identityIncrement property.
*
* @return possible object is
* {@link BigInteger }
*/
public BigInteger getIdentityIncrement() {
return identityIncrement;
}
/**
* Sets the value of the identityIncrement property.
*
* @param value allowed object is
* {@link BigInteger }
*/
public void setIdentityIncrement(BigInteger value) {
this.identityIncrement = value;
}
/**
* Gets the value of the identityCache property.
*
* @return possible object is
* {@link BigInteger }
*/
public BigInteger getIdentityCache() {
return identityCache;
}
/**
* Sets the value of the identityCache property.
*
* @param value allowed object is
* {@link BigInteger }
*/
public void setIdentityCache(BigInteger value) {
this.identityCache = value;
}
/**
* Gets the value of the identityGenerated property.
*
* @return possible object is
* {@link String }
*/
public String getIdentityGenerated() {
return identityGenerated;
}
/**
* Sets the value of the identityGenerated property.
*
* @param value allowed object is
* {@link String }
*/
public void setIdentityGenerated(String value) {
this.identityGenerated = value;
}
/**
* Gets the value of the sequenceName property.
*
@@ -62,11 +62,7 @@ public class DdlScript {
if (ddl == null) {
ddl = new ArrayList<>();
}
return ddl;
}
public void setDdl(List<String> ddl) {
this.ddl = ddl;
return this.ddl;
}
/**
@@ -17,6 +17,8 @@ import io.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
import java.io.IOException;
import java.util.List;
import static io.ebeaninternal.api.PlatformMatch.matchPlatform;
/**
* Reads EbeanServer bean descriptors to build the current model.
*/
@@ -146,7 +148,7 @@ public class CurrentModel {
if (extraDdl != null) {
List<DdlScript> ddlScript = extraDdl.getDdlScript();
for (DdlScript script : ddlScript) {
if (script.isInit() && ExtraDdlXmlReader.matchPlatform(server.getDatabasePlatform().getName(), script.getPlatforms())) {
if (script.isInit() && matchPlatform(server.getDatabasePlatform().getPlatform(), script.getPlatforms())) {
ddl.append(prefix).append(script.getName()).append('\n');
ddl.append(script.getValue());
}
@@ -295,14 +295,14 @@ public class MColumn {
for (DbMigrationInfo info : dbMigrationInfos) {
if (!info.getPreAdd().isEmpty()) {
DdlScript script = new DdlScript();
script.setDdl(info.getPreAdd());
script.getDdl().addAll(info.getPreAdd());
script.setPlatforms(info.joinPlatforms());
c.getBefore().add(script);
}
if (!info.getPostAdd().isEmpty()) {
DdlScript script = new DdlScript();
script.setDdl(info.getPostAdd());
script.getDdl().addAll(info.getPostAdd());
script.setPlatforms(info.joinPlatforms());
c.getAfter().add(script);
}
@@ -341,14 +341,14 @@ public class MColumn {
for (DbMigrationInfo info : dbMigrationInfos) {
if (!info.getPreAlter().isEmpty()) {
DdlScript script = new DdlScript();
script.setDdl(info.getPreAlter());
script.getDdl().addAll(info.getPreAlter());
script.setPlatforms(info.joinPlatforms());
alterColumn.getBefore().add(script);
}
if (!info.getPostAlter().isEmpty()) {
DdlScript script = new DdlScript();
script.setDdl(info.getPostAlter());
script.getDdl().addAll(info.getPostAlter());
script.setPlatforms(info.joinPlatforms());
alterColumn.getAfter().add(script);
}
@@ -11,13 +11,14 @@ import io.ebeaninternal.dbmigration.migration.DropColumn;
import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
import io.ebeaninternal.dbmigration.migration.DropTable;
import io.ebeaninternal.dbmigration.migration.ForeignKey;
import io.ebeaninternal.dbmigration.migration.IdentityType;
import io.ebeaninternal.dbmigration.migration.UniqueConstraint;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.IdentityMode;
import io.ebeaninternal.server.deploy.PartitionMeta;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
@@ -27,6 +28,8 @@ import java.util.Map;
import java.util.Set;
import static io.ebeaninternal.dbmigration.ddlgeneration.platform.SplitColumns.split;
import static io.ebeaninternal.dbmigration.model.MTableIdentity.fromCreateTable;
import static io.ebeaninternal.dbmigration.model.MTableIdentity.toCreateTable;
/**
* Holds the logical model for a given Table and everything associated to it.
@@ -84,18 +87,7 @@ public class MTable {
*/
private String indexTablespace;
/**
* If set then this overrides the platform default so for UUID generated values
* or DB's supporting both sequences and autoincrement.
*/
private IdentityType identityType;
/**
* DB sequence name.
*/
private String sequenceName;
private int sequenceInitial;
private int sequenceAllocate;
private IdentityMode identityMode;
/**
* If set to true this table should has history support.
@@ -130,6 +122,29 @@ public class MTable {
private List<String> droppedColumns = new ArrayList<>();
public MTable(BeanDescriptor<?> descriptor) {
this.name = descriptor.getBaseTable();
this.identityMode = descriptor.getIdentityMode();
this.storageEngine = descriptor.getStorageEngine();
this.partitionMeta = descriptor.getPartitionMeta();
this.comment = descriptor.getDbComment();
if (descriptor.isHistorySupport()) {
withHistory = true;
BeanProperty whenCreated = descriptor.getWhenCreatedProperty();
if (whenCreated != null) {
whenCreatedColumn = whenCreated.getDbColumn();
}
}
}
/**
* Construct for element collection or intersection table.
*/
public MTable(String name) {
this.name = name;
this.identityMode = IdentityMode.NONE;
}
/**
* Create a copy of this table structure as a 'draft' table.
* <p>
@@ -143,7 +158,7 @@ public class MTable {
draftTable.whenCreatedColumn = whenCreatedColumn;
// compoundKeys
// compoundUniqueConstraints
draftTable.identityType = identityType;
draftTable.identityMode = identityMode;
for (MColumn col : allColumns()) {
draftTable.addColumn(col.copyForDraft());
@@ -164,9 +179,7 @@ public class MTable {
this.indexTablespace = createTable.getIndexTablespace();
this.withHistory = Boolean.TRUE.equals(createTable.isWithHistory());
this.draft = Boolean.TRUE.equals(createTable.isDraft());
this.sequenceName = createTable.getSequenceName();
this.sequenceInitial = toInt(createTable.getSequenceInitial());
this.sequenceAllocate = toInt(createTable.getSequenceAllocate());
this.identityMode = fromCreateTable(createTable);
List<Column> cols = createTable.getColumn();
for (Column column : cols) {
addColumn(column);
@@ -194,13 +207,6 @@ public class MTable {
addForeignKey(foreignKey);
}
/**
* Construct typically from EbeanServer meta data.
*/
public MTable(String name) {
this.name = name;
}
/**
* Return the DropTable migration for this table.
*/
@@ -208,7 +214,7 @@ public class MTable {
DropTable dropTable = new DropTable();
dropTable.setName(name);
// we must add pk col name & sequence name, as we have to delete the sequence also.
if (identityType != IdentityType.GENERATOR && identityType != IdentityType.EXTERNAL) {
if (identityMode.isDatabaseIdentity()) {
String pkCol = null;
for (MColumn column : columns.values()) {
if (column.isPrimaryKey()) {
@@ -222,7 +228,7 @@ public class MTable {
}
if (pkCol != null) {
dropTable.setSequenceCol(pkCol);
dropTable.setSequenceName(sequenceName);
dropTable.setSequenceName(identityMode.getSequenceName());
}
}
return dropTable;
@@ -244,10 +250,7 @@ public class MTable {
createTable.setStorageEngine(storageEngine);
createTable.setTablespace(tablespace);
createTable.setIndexTablespace(indexTablespace);
createTable.setSequenceName(sequenceName);
createTable.setSequenceInitial(toBigInteger(sequenceInitial));
createTable.setSequenceAllocate(toBigInteger(sequenceAllocate));
createTable.setIdentityType(identityType);
toCreateTable(identityMode, createTable);
if (withHistory) {
createTable.setWithHistory(Boolean.TRUE);
}
@@ -458,10 +461,6 @@ public class MTable {
this.comment = comment;
}
public void setStorageEngine(String storageEngine) {
this.storageEngine = storageEngine;
}
public String getTablespace() {
return tablespace;
}
@@ -519,36 +518,10 @@ public class MTable {
return compoundKeys;
}
public void setSequenceName(String sequenceName) {
this.sequenceName = sequenceName;
}
public void setSequenceInitial(int sequenceInitial) {
this.sequenceInitial = sequenceInitial;
}
public void setSequenceAllocate(int sequenceAllocate) {
this.sequenceAllocate = sequenceAllocate;
}
public void setWhenCreatedColumn(String whenCreatedColumn) {
this.whenCreatedColumn = whenCreatedColumn;
}
public String getWhenCreatedColumn() {
return whenCreatedColumn;
}
/**
* Set the identity type to use for this table.
* <p>
* If set then this overrides the platform default so for UUID generated values
* or DB's supporting both sequences and autoincrement.
*/
public void setIdentityType(IdentityType identityType) {
this.identityType = identityType;
}
/**
* Return the list of columns that make the primary key.
*/
@@ -671,14 +644,6 @@ public class MTable {
droppedColumns.add(columnName);
}
private int toInt(BigInteger value) {
return (value == null) ? 0 : value.intValue();
}
private BigInteger toBigInteger(int value) {
return (value == 0) ? null : BigInteger.valueOf(value);
}
/**
* Check if there are duplicate foreign keys.
* <p>
@@ -774,10 +739,6 @@ public class MTable {
}
}
public void setPartitionMeta(PartitionMeta partitionMeta) {
this.partitionMeta = partitionMeta;
}
/**
* Clear foreign key as this element collection table logically references
* back to multiple tables.
@@ -0,0 +1,108 @@
package io.ebeaninternal.dbmigration.model;
import io.ebean.annotation.IdentityGenerated;
import io.ebean.config.dbplatform.IdType;
import io.ebeaninternal.dbmigration.migration.CreateTable;
import io.ebeaninternal.dbmigration.migration.IdentityType;
import io.ebeaninternal.server.deploy.IdentityMode;
import java.math.BigInteger;
/**
* Helper to convert between IdentityMode and CreateTable
*/
public class MTableIdentity {
/**
* Return the IdentityMode from CreateTable.
*/
public static IdentityMode fromCreateTable(CreateTable createTable) {
IdType type = fromType(createTable.getIdentityType());
IdentityGenerated generated = fromGenerated(createTable.getIdentityGenerated());
int start = toInt(createTable.getIdentityStart(), createTable.getSequenceInitial());
int increment = toInt(createTable.getIdentityIncrement(), createTable.getSequenceAllocate());
int cache = toInt(createTable.getIdentityCache(), null);
String seqName = createTable.getSequenceName();
return new IdentityMode(type, generated, start, increment, cache, seqName);
}
private static IdentityGenerated fromGenerated(String identityGenerated) {
if (identityGenerated == null) {
return IdentityGenerated.AUTO;
}
return IdentityGenerated.valueOf(identityGenerated.toUpperCase());
}
/**
* Set the IdentityMode to the CreateTable model.
*/
public static void toCreateTable(IdentityMode identityMode, CreateTable createTable) {
if (!identityMode.isPlatformDefault()) {
createTable.setIdentityType(toType(identityMode.getIdType()));
}
final String seqName = identityMode.getSequenceName();
if (seqName != null && !seqName.isEmpty()) {
createTable.setSequenceName(seqName);
}
createTable.setIdentityStart(toBigInteger(identityMode.getStart()));
createTable.setIdentityIncrement(toBigInteger(identityMode.getIncrement()));
createTable.setIdentityCache(toBigInteger(identityMode.getCache()));
final IdentityGenerated generated = identityMode.getGenerated();
if (generated != null && generated != IdentityGenerated.AUTO) {
createTable.setIdentityGenerated(generated.name().toLowerCase());
}
}
private static IdType fromType(IdentityType type) {
if (type == null) {
return IdType.AUTO;
}
switch (type) {
case DEFAULT:
return IdType.AUTO;
case SEQUENCE:
return IdType.SEQUENCE;
case IDENTITY:
return IdType.IDENTITY;
case GENERATOR:
return IdType.GENERATOR;
case EXTERNAL:
return IdType.EXTERNAL;
}
return IdType.AUTO;
}
private static IdentityType toType(IdType type) {
if (type == null) {
// intersection or element collection table
return null;
}
switch (type) {
case SEQUENCE:
return IdentityType.SEQUENCE;
case IDENTITY:
return IdentityType.IDENTITY;
case EXTERNAL:
return IdentityType.EXTERNAL;
case GENERATOR:
return IdentityType.GENERATOR;
}
return null;
}
private static int toInt(BigInteger firstVal, BigInteger secVal) {
if (firstVal != null) {
return firstVal.intValue();
}
return (secVal == null) ? 0 : secVal.intValue();
}
private static BigInteger toBigInteger(int value) {
return (value == 0) ? null : BigInteger.valueOf(value);
}
}
@@ -1,13 +1,10 @@
package io.ebeaninternal.dbmigration.model.build;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.IdType;
import io.ebeaninternal.dbmigration.migration.IdentityType;
import io.ebeaninternal.dbmigration.model.MColumn;
import io.ebeaninternal.dbmigration.model.MTable;
import io.ebeaninternal.dbmigration.model.visitor.BeanVisitor;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.InheritInfo;
/**
@@ -35,19 +32,7 @@ public class ModelBuildBeanVisitor implements BeanVisitor {
return null;
}
MTable table = new MTable(descriptor.getBaseTable());
table.setStorageEngine(descriptor.getStorageEngine());
table.setPartitionMeta(descriptor.getPartitionMeta());
table.setComment(descriptor.getDbComment());
if (descriptor.isHistorySupport()) {
table.setWithHistory(true);
BeanProperty whenCreated = descriptor.getWhenCreatedProperty();
if (whenCreated != null) {
table.setWhenCreatedColumn(whenCreated.getDbColumn());
}
}
setIdentity(descriptor, table);
MTable table = new MTable(descriptor);
// add the table to the model
ctx.addTable(table);
@@ -66,43 +51,4 @@ public class ModelBuildBeanVisitor implements BeanVisitor {
return new ModelBuildPropertyVisitor(ctx, table, descriptor);
}
/**
* Set the identity type to use for this table.
* <p>
* Takes into account the requested identity type and the underlying support in the
* database platform.
* </p>
*/
private void setIdentity(BeanDescriptor<?> descriptor, MTable table) {
if (IdType.GENERATOR == descriptor.getIdType()) {
// explicit generator like UUID
table.setIdentityType(IdentityType.GENERATOR);
return;
}
if (IdType.EXTERNAL == descriptor.getIdType()) {
// externally defined code (lookup table, ISO country code etc)
table.setIdentityType(IdentityType.EXTERNAL);
return;
}
int initialValue = descriptor.getSequenceInitialValue();
int allocationSize = descriptor.getSequenceAllocationSize();
if (!descriptor.isIdTypePlatformDefault() || initialValue > 0 || allocationSize > 0) {
// explicitly set to use sequence or identity (generally not recommended practice)
if (IdType.IDENTITY == descriptor.getIdType()) {
if (!descriptor.isIdTypePlatformDefault()) {
table.setIdentityType(IdentityType.IDENTITY);
}
} else {
// explicit sequence defined
table.setIdentityType(IdentityType.SEQUENCE);
table.setSequenceName(descriptor.getSequenceName());
table.setSequenceInitial(initialValue);
table.setSequenceAllocate(allocationSize);
}
}
}
}
@@ -1,7 +1,6 @@
package io.ebeaninternal.extraddl.model;
import io.ebean.annotation.Platform;
import io.ebean.util.StringHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -11,6 +10,8 @@ import javax.xml.bind.Unmarshaller;
import java.io.IOException;
import java.io.InputStream;
import static io.ebeaninternal.api.PlatformMatch.matchPlatform;
/**
* Read ExtraDdl from an XML document.
*/
@@ -18,25 +19,21 @@ public class ExtraDdlXmlReader {
private static final Logger logger = LoggerFactory.getLogger(ExtraDdlXmlReader.class);
private static final String SQLSERVER = Platform.SQLSERVER.name().toLowerCase();
/**
* Return the combined extra DDL that should be run given the platform name.
*/
public static String buildExtra(String platformName, boolean drops) {
ExtraDdl read = ExtraDdlXmlReader.read("/extra-ddl.xml");
return buildExtra(platformName, drops, read);
public static String buildExtra(Platform platform, boolean drops) {
return buildExtra(platform, drops, read("/extra-ddl.xml"));
}
/**
* Return any extra DDL for supporting partitioning given the database platform.
*/
public static String buildPartitioning(String platformName) {
return buildExtra(platformName, false, readBuiltinTablePartitioning());
public static String buildPartitioning(Platform platform) {
return buildExtra(platform, false, readBuiltinTablePartitioning());
}
private static String buildExtra(String platformName, boolean drops, ExtraDdl read) {
private static String buildExtra(Platform platform, boolean drops, ExtraDdl read) {
if (read == null) {
return null;
@@ -44,7 +41,7 @@ public class ExtraDdlXmlReader {
StringBuilder sb = new StringBuilder(300);
for (DdlScript script : read.getDdlScript()) {
if (script.isDrop() == drops && matchPlatform(platformName, script.getPlatforms())) {
if (script.isDrop() == drops && matchPlatform(platform, script.getPlatforms())) {
logger.debug("include script {}", script.getName());
String value = script.getValue();
sb.append(value);
@@ -58,44 +55,6 @@ public class ExtraDdlXmlReader {
return sb.toString();
}
/**
* Return true if the script platforms is a match/supported for the given platform.
*
* @param platformName The database platform we are generating/running DDL for
* @param platforms The platforms (comma delimited) this script should run for
*/
public static boolean matchPlatform(String platformName, String platforms) {
if (platforms == null || platforms.trim().isEmpty()) {
return true;
}
String genericMatch = genericPlatformMatch(platformName);
for (String name : StringHelper.splitNames(platforms)) {
if (name.toLowerCase().contains(platformName)) {
return true;
} else if (genericMatch != null && genericMatch.equals(name.toLowerCase())) {
// allow sqlserver ... to match sqlserver17 and sqlserver16 platforms
return true;
}
}
return false;
}
/**
* Return a "generic" platform name that can be used. e.g. sqlserver17 -> sqlserver.
*/
private static String genericPlatformMatch(String platformName) {
switch (platformName) {
case "sqlserver17":
return SQLSERVER;
case "sqlserver16":
return SQLSERVER;
default:
return null;
}
}
/**
* Read the builtin extra ddl. (Stored procedures, tvp types etc)
*/
@@ -18,8 +18,6 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static io.ebean.meta.MetricType.L2;
/**
* The default cache implementation.
* <p>
@@ -74,12 +72,12 @@ public class DefaultServerCache implements ServerCache {
MetricFactory factory = MetricFactory.get();
String prefix = "l2n.";
this.hitCount = factory.createCountMetric(L2, prefix + shortName + ".hit");
this.missCount = factory.createCountMetric(L2, prefix + shortName + ".miss");
this.putCount = factory.createCountMetric(L2, prefix + shortName + ".put");
this.removeCount = factory.createCountMetric(L2, prefix + shortName + ".remove");
this.clearCount = factory.createCountMetric(L2, prefix + shortName + ".clear");
this.evictCount = factory.createCountMetric(L2, prefix + shortName + ".evict");
this.hitCount = factory.createCountMetric(prefix + shortName + ".hit");
this.missCount = factory.createCountMetric(prefix + shortName + ".miss");
this.putCount = factory.createCountMetric(prefix + shortName + ".put");
this.removeCount = factory.createCountMetric(prefix + shortName + ".remove");
this.clearCount = factory.createCountMetric(prefix + shortName + ".clear");
this.evictCount = factory.createCountMetric(prefix + shortName + ".evict");
}
public void periodicTrim(BackgroundExecutor executor) {
@@ -7,12 +7,12 @@ import io.ebean.config.dbplatform.cockroach.CockroachPlatform;
import io.ebean.config.dbplatform.db2.DB2Platform;
import io.ebean.config.dbplatform.h2.H2Platform;
import io.ebean.config.dbplatform.hana.HanaPlatform;
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
import io.ebean.config.dbplatform.mysql.MySql55Platform;
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
import io.ebean.config.dbplatform.nuodb.NuoDbPlatform;
import io.ebean.config.dbplatform.oracle.Oracle11Platform;
import io.ebean.config.dbplatform.oracle.OraclePlatform;
import io.ebean.config.dbplatform.postgres.Postgres8Platform;
import io.ebean.config.dbplatform.postgres.Postgres9Platform;
import io.ebean.config.dbplatform.postgres.PostgresPlatform;
import io.ebean.config.dbplatform.sqlanywhere.SqlAnywherePlatform;
import io.ebean.config.dbplatform.sqlite.SQLitePlatform;
@@ -89,10 +89,10 @@ public class DatabasePlatformFactory {
if (dbName.equals("postgres") || dbName.equals("postgres9")) {
return new PostgresPlatform();
}
if (dbName.equals("postgres8") || dbName.equals("postgres83")) {
return new Postgres8Platform();
if (dbName.equals("oracle11") || dbName.equals("oracle10") || dbName.equals("oracle9")) {
return new Oracle11Platform();
}
if (dbName.equals("oracle") || dbName.equals("oracle10") || dbName.equals("oracle9")) {
if (dbName.equals("oracle")) {
return new OraclePlatform();
}
if (dbName.equals("sqlserver16")) {
@@ -163,10 +163,8 @@ public class DatabasePlatformFactory {
return mysqlVersion(majorVersion, minorVersion);
} else if (dbProductName.contains("h2")) {
return new H2Platform();
} else if (dbProductName.contains("hsql database engine")) {
return new HsqldbPlatform();
} else if (dbProductName.contains("postgres")) {
return readPostgres(connection);
return readPostgres(connection, majorVersion);
} else if (dbProductName.contains("nuo")) {
return new NuoDbPlatform();
} else if (dbProductName.contains("sqlite")) {
@@ -195,28 +193,23 @@ public class DatabasePlatformFactory {
/**
* Use a select version() query as it could be Postgres or CockroachDB.
*/
private static DatabasePlatform readPostgres(Connection connection) {
// Postgres driver uses a hardcoded product name so use version() query
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
statement = connection.prepareStatement("SELECT version() AS \"version\"");
resultSet = statement.executeQuery();
if (resultSet.next()) {
String productVersion = resultSet.getString("version").toLowerCase();
if (productVersion.contains("cockroach")) {
return new CockroachPlatform();
private static DatabasePlatform readPostgres(Connection connection, int majorVersion) {
try (PreparedStatement statement = connection.prepareStatement("select version() as \"version\"")) {
try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
String productVersion = resultSet.getString("version").toLowerCase();
if (productVersion.contains("cockroach")) {
return new CockroachPlatform();
}
}
}
} catch (SQLException e) {
logger.warn("Error running detection query on Postgres", e);
} finally {
JdbcClose.close(resultSet);
JdbcClose.close(statement);
}
// Real Postgres
if (majorVersion <= 9) {
return new Postgres9Platform();
}
return new PostgresPlatform();
}
}
@@ -47,7 +47,7 @@ class DumpMetricsData {
}
private MetricData create(MetaMetric metric) {
MetricData data = new MetricData(metric.getName(), metric.getMetricType().name());
MetricData data = new MetricData(metric.getName());
list.add(data);
return data;
}
@@ -343,7 +343,7 @@ public class InternalConfiguration {
private MultiValueBind createMultiValueBind(Platform platform) {
// only Postgres at this stage
if (platform == Platform.POSTGRES) {
if (platform.base() == Platform.POSTGRES) {
return new PostgresMultiValueBind();
}
return new MultiValueBind();
@@ -533,7 +533,7 @@ public class InternalConfiguration {
}
return new NoDataTimeZone();
}
if (getPlatform() == Platform.ORACLE) {
if (getPlatform().base() == Platform.ORACLE) {
return new CloneDataTimeZone(tz);
} else {
return new SimpleDataTimeZone(tz);
@@ -11,6 +11,7 @@ import io.ebeaninternal.dbmigration.ddlgeneration.platform.MySqlDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.NuoDbDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.Oracle10Ddl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.PlatformDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.Postgres9Ddl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.PostgresDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.SQLiteDdl;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.SqlServerDdl;
@@ -38,17 +39,16 @@ public class PlatformDdlBuilder {
case NUODB:
return new NuoDbDdl(platform);
case ORACLE:
case ORACLE11:
return new Oracle10Ddl(platform);
case SQLITE:
return new SQLiteDdl(platform);
case GENERIC:
return new PlatformDdl(platform);
case POSTGRES9:
return new Postgres9Ddl(platform);
case POSTGRES:
return new PostgresDdl(platform);
case COCKROACH:
return new CockroachDdl(platform);
case SQLANYWHERE:
return new PlatformDdl(platform);
case SQLSERVER16:
case SQLSERVER17:
case SQLSERVER:
@@ -154,28 +154,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
*/
private final EntityType entityType;
/**
* Type of Identity generation strategy used.
*/
private final IdType idType;
/**
* Set when Id property is marked with GeneratedValue annotation.
*/
private final boolean idGeneratedValue;
private final boolean idTypePlatformDefault;
private final PlatformIdGenerator idGenerator;
/**
* The database sequence name (optional).
*/
private final String sequenceName;
private final int sequenceInitialValue;
private final int sequenceAllocationSize;
private final IdentityMode identityMode;
private final IdType idType;
/**
* SQL used to return last inserted id. Used for Identity columns where
@@ -456,13 +441,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
this.changeLogFilter = deploy.getChangeLogFilter();
this.defaultSelectClause = deploy.getDefaultSelectClause();
this.idType = deploy.getIdType();
this.identityMode = deploy.buildIdentityMode();
this.idType = identityMode.getIdType();
this.idGeneratedValue = deploy.isIdGeneratedValue();
this.idTypePlatformDefault = deploy.isIdTypePlatformDefault();
this.idGenerator = deploy.getIdGenerator();
this.sequenceName = deploy.getSequenceName();
this.sequenceInitialValue = deploy.getSequenceInitialValue();
this.sequenceAllocationSize = deploy.getSequenceAllocationSize();
this.selectLastInsertedId = deploy.getSelectLastInsertedId();
this.selectLastInsertedIdDraft = deploy.getSelectLastInsertedIdDraft();
this.concurrencyMode = deploy.getConcurrencyMode();
@@ -3158,33 +3140,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return idGeneratedValue;
}
/**
* Return true if the identity is the platform default (not explicitly set).
*/
public boolean isIdTypePlatformDefault() {
return idTypePlatformDefault;
}
/**
* Return the sequence name.
*/
@Override
public String getSequenceName() {
return sequenceName;
}
/**
* Return the sequence initial value.
*/
public int getSequenceInitialValue() {
return sequenceInitialValue;
}
/**
* Return the sequence allocation size.
*/
public int getSequenceAllocationSize() {
return sequenceAllocationSize;
public IdentityMode getIdentityMode() {
return identityMode;
}
/**
@@ -46,6 +46,7 @@ import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.meta.DeployBeanTable;
import io.ebeaninternal.server.deploy.meta.DeployOrderColumn;
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
import io.ebeaninternal.server.deploy.meta.DeployIdentityMode;
import io.ebeaninternal.server.deploy.parse.DeployBeanInfo;
import io.ebeaninternal.server.deploy.parse.DeployCreateProperties;
import io.ebeaninternal.server.deploy.parse.DeployInherit;
@@ -1354,42 +1355,39 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
return;
}
if (IdType.SEQUENCE == desc.getIdType() && !dbIdentity.isSupportsSequence()) {
final DeployIdentityMode identityMode = desc.getIdentityMode();
if (identityMode.isSequence() && !dbIdentity.isSupportsSequence()) {
// explicit sequence but not supported by the DatabasePlatform
logger.info("Explicit sequence on " + desc.getFullName() + " but not supported by DB Platform - ignored");
desc.setIdType(null);
identityMode.setIdType(IdType.AUTO);
}
if (IdType.IDENTITY == desc.getIdType() && !dbIdentity.isSupportsIdentity()) {
if (identityMode.isIdentity() && !dbIdentity.isSupportsIdentity()) {
// explicit identity but not supported by the DatabasePlatform
logger.info("Explicit Identity on " + desc.getFullName() + " but not supported by DB Platform - ignored");
desc.setIdType(null);
identityMode.setIdType(IdType.AUTO);
}
if (desc.getIdType() == null) {
if (identityMode.isAuto()) {
if (desc.isPrimaryKeyCompoundOrNonNumeric()) {
// assuming that this is a user supplied key like ISO country code or ISO currency code or lookup table code
logger.debug("Expecting user defined identity on {} - not using db sequence or autoincrement", desc.getFullName());
desc.setIdType(IdType.EXTERNAL);
identityMode.setIdType(IdType.EXTERNAL);
return;
}
if (desc.isIdGeneratedValue() || serverConfig.isIdGeneratorAutomatic()) {
// use IDENTITY or SEQUENCE based on platform
desc.setIdType(dbIdentity.getIdType());
desc.setIdTypePlatformDefault();
identityMode.setPlatformType(dbIdentity.getIdType());
} else {
// externally/application supplied Id values
desc.setIdType(IdType.EXTERNAL);
identityMode.setIdType(IdType.EXTERNAL);
return;
}
}
if (desc.getBaseTable() == null) {
// no base table so not going to set Identity
// of sequence information
// no base table so not going to set Identity or sequence information
return;
}
if (IdType.IDENTITY == desc.getIdType()) {
if (identityMode.isIdentity()) {
// used when getGeneratedKeys is not supported (SQL Server 2000, SAP Hana)
String selectLastInsertedId = dbIdentity.getSelectLastInsertedId(desc.getBaseTable());
String selectLastInsertedIdDraft = (!desc.isDraftable()) ? selectLastInsertedId : dbIdentity.getSelectLastInsertedId(desc.getDraftTable());
@@ -1397,27 +1395,19 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
return;
}
if (IdType.SEQUENCE == desc.getIdType()) {
String seqName = desc.getIdGeneratorName();
if (seqName != null) {
logger.debug("explicit sequence {} on {}", seqName, desc.getFullName());
} else {
if (identityMode.isSequence()) {
String seqName = identityMode.getSequenceName();
if (seqName == null || seqName.isEmpty()) {
String primaryKeyColumn = desc.getSinglePrimaryKeyColumn();
// use namingConvention to define sequence name
seqName = namingConvention.getSequenceName(desc.getBaseTable(), primaryKeyColumn);
}
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));
int increment = desc.setIdentitySequenceDefaultIncrement();
desc.setIdGenerator(createSequenceIdGenerator(seqName, increment));
}
}
private PlatformIdGenerator createSequenceIdGenerator(String seqName, int stepSize) {
return databasePlatform.createSequenceIdGenerator(backgroundExecutor, dataSource, stepSize, seqName);
private PlatformIdGenerator createSequenceIdGenerator(String seqName, int increment) {
return databasePlatform.createSequenceIdGenerator(backgroundExecutor, dataSource, increment, seqName);
}
private void createByteCode(DeployBeanDescriptor<?> deploy) {
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.deploy;
import io.ebean.meta.MetricType;
import io.ebean.meta.MetricVisitor;
import io.ebean.metric.MetricFactory;
import io.ebean.metric.TimedMetric;
@@ -25,12 +24,12 @@ class BeanIudMetrics {
MetricFactory metricFactory = MetricFactory.get();
String prefix = "iud." + beanShortName;
this.insert = metricFactory.createTimedMetric(MetricType.IUD, prefix + ".insert");
this.update = metricFactory.createTimedMetric(MetricType.IUD, prefix + ".update");
this.delete = metricFactory.createTimedMetric(MetricType.IUD, prefix + ".delete");
this.insertBatch = metricFactory.createTimedMetric(MetricType.IUD, prefix + ".insertBatch");
this.updateBatch = metricFactory.createTimedMetric(MetricType.IUD, prefix + ".updateBatch");
this.deleteBatch = metricFactory.createTimedMetric(MetricType.IUD, prefix + ".deleteBatch");
this.insert = metricFactory.createTimedMetric(prefix + ".insert");
this.update = metricFactory.createTimedMetric(prefix + ".update");
this.delete = metricFactory.createTimedMetric(prefix + ".delete");
this.insertBatch = metricFactory.createTimedMetric(prefix + ".insertBatch");
this.updateBatch = metricFactory.createTimedMetric(prefix + ".updateBatch");
this.deleteBatch = metricFactory.createTimedMetric(prefix + ".deleteBatch");
}
/**
@@ -0,0 +1,109 @@
package io.ebeaninternal.server.deploy;
import io.ebean.annotation.IdentityGenerated;
import io.ebean.config.dbplatform.IdType;
import io.ebeaninternal.server.deploy.meta.DeployIdentityMode;
public class IdentityMode {
public static IdentityMode NONE = new IdentityMode();
private final IdType type;
private final IdentityGenerated generated;
private final int start;
private final int increment;
private final int cache;
private final String sequenceName;
private final boolean platformDefault;
/**
* Create from deployment.
*/
public IdentityMode(DeployIdentityMode deploy) {
this.type = deploy.getIdType();
this.generated = deploy.getGenerated();
this.start = deploy.getStart();
this.increment = deploy.getIncrement();
this.cache = deploy.getCache();
this.sequenceName = deploy.getSequenceName();
this.platformDefault = deploy.isPlatformDefault();
}
/**
* Create from migration model CreateTable.
*/
public IdentityMode(IdType type, IdentityGenerated auto, int start, int increment, int cache, String seqName) {
this.type = type;
this.generated = auto;
this.start = start;
this.increment = increment;
this.cache = cache;
this.sequenceName = seqName;
this.platformDefault = false;
}
/**
* NONE constructor.
*/
private IdentityMode() {
this.type = null;
this.generated = IdentityGenerated.AUTO;
this.start = 0;
this.increment = 0;
this.cache = 0;
this.sequenceName = "";
this.platformDefault = false;
}
public boolean isPlatformDefault() {
return platformDefault;
}
public IdType getIdType() {
return type;
}
public IdentityGenerated getGenerated() {
return generated;
}
public int getStart() {
return start;
}
public int getIncrement() {
return increment;
}
public int getCache() {
return cache;
}
public String getSequenceName() {
return sequenceName;
}
public boolean isSequence() {
return type == IdType.SEQUENCE;
}
public boolean isIdentity() {
return type == IdType.IDENTITY;
}
public boolean isExternal() {
return type == IdType.EXTERNAL;
}
public boolean isAuto() {
return type == IdType.AUTO;
}
public boolean isDatabaseIdentity() {
return type != IdType.EXTERNAL && type != IdType.GENERATOR;
}
public boolean hasOptions() {
return start > 0 || cache > 0 || increment > 0;
}
}
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.deploy.meta;
import io.ebean.annotation.Cache;
import io.ebean.annotation.DocStore;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.Identity;
import io.ebean.config.ServerConfig;
import io.ebean.config.TableName;
import io.ebean.config.dbplatform.IdType;
@@ -17,6 +18,7 @@ import io.ebean.event.changelog.ChangeLogFilter;
import io.ebean.text.PathProperties;
import io.ebeaninternal.api.ConcurrencyMode;
import io.ebeaninternal.server.core.CacheOptions;
import io.ebeaninternal.server.deploy.IdentityMode;
import io.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
import io.ebeaninternal.server.deploy.ChainedBeanPersistController;
@@ -85,24 +87,11 @@ public class DeployBeanDescriptor<T> {
private DeployBeanProperty orderColumn;
/**
* Type of Identity generation strategy used.
*/
private IdType idType;
private Class<?> idClass;
private DeployBeanPropertyAssocOne<?> idClassProperty;
/**
* Set to true if the identity is default for the platform.
*/
private boolean idTypePlatformDefault;
/**
* The name of an IdGenerator (optional).
*/
private String idGeneratorName;
private DeployIdentityMode identityMode = DeployIdentityMode.auto();
private PlatformIdGenerator idGenerator;
@@ -111,15 +100,6 @@ public class DeployBeanDescriptor<T> {
*/
private boolean idGeneratedValue;
/**
* The database sequence name (optional).
*/
private String sequenceName;
private int sequenceInitialValue;
private int sequenceAllocationSize = 50;
/**
* Used with Identity columns but no getGeneratedKeys support.
*/
@@ -244,8 +224,7 @@ public class DeployBeanDescriptor<T> {
*/
public void setPrimaryKeyJoin(TableJoin join) {
this.primaryKeyJoin = join;
this.idType = IdType.EXTERNAL;
this.idGeneratorName = null;
this.identityMode.setIdType(IdType.EXTERNAL);
this.idGenerator = null;
}
@@ -399,20 +378,41 @@ public class DeployBeanDescriptor<T> {
return entityType;
}
public void setSequenceInitialValue(int sequenceInitialValue) {
this.sequenceInitialValue = sequenceInitialValue;
/**
* Return the immutable IdentityMode.
*/
public IdentityMode buildIdentityMode() {
return new IdentityMode(identityMode);
}
public void setSequenceAllocationSize(int sequenceAllocationSize) {
this.sequenceAllocationSize = sequenceAllocationSize;
public DeployIdentityMode getIdentityMode() {
return identityMode;
}
public int getSequenceInitialValue() {
return sequenceInitialValue;
public void setIdentityMode(Identity identity) {
this.identityMode = new DeployIdentityMode(identity);
}
public int getSequenceAllocationSize() {
return sequenceAllocationSize;
/**
* Set from <code>@Sequence</code>
*/
public void setIdentitySequence(int initialValue, int allocationSize, String seqName) {
identityMode.setSequence(initialValue, allocationSize, seqName);
}
/**
* Potentially set sequence name from <code>@GeneratedValue</code>.
*/
public void setIdentitySequenceGenerator(String genName) {
identityMode.setSequenceGenerator(genName);
}
public int setIdentitySequenceDefaultIncrement() {
return identityMode.setSequenceDefaultIncrement();
}
public void setIdentityType(IdType type) {
this.identityMode.setIdType(type);
}
public String[] getProperties() {
@@ -760,48 +760,6 @@ public class DeployBeanDescriptor<T> {
this.name = name;
}
/**
* Return the identity generation type.
*/
public IdType getIdType() {
return idType;
}
/**
* Set the identity generation type.
*/
public void setIdType(IdType idType) {
this.idType = idType;
}
/**
* Set when the identity type is the platform default.
*/
public void setIdTypePlatformDefault() {
this.idTypePlatformDefault = true;
}
/**
* Return true when the identity is the platform default.
*/
public boolean isIdTypePlatformDefault() {
return idTypePlatformDefault;
}
/**
* Return the DB sequence name (can be null).
*/
public String getSequenceName() {
return sequenceName;
}
/**
* Set the DB sequence name.
*/
private void setSequenceName(String sequenceName) {
this.sequenceName = sequenceName;
}
/**
* Return the SQL used to return the last inserted Id.
* <p>
@@ -824,21 +782,6 @@ public class DeployBeanDescriptor<T> {
this.selectLastInsertedIdDraft = selectLastInsertedIdDraft;
}
/**
* Return the name of the IdGenerator that should be used with this type of
* bean. A null value could be used to specify the 'default' IdGenerator.
*/
public String getIdGeneratorName() {
return idGeneratorName;
}
/**
* Set the name of the IdGenerator that should be used with this type of bean.
*/
public void setIdGeneratorName(String idGeneratorName) {
this.idGeneratorName = idGeneratorName;
}
/**
* Return the actual IdGenerator for this bean type (can be null).
*/
@@ -851,9 +794,6 @@ public class DeployBeanDescriptor<T> {
*/
public void setIdGenerator(PlatformIdGenerator idGenerator) {
this.idGenerator = idGenerator;
if (idGenerator != null && idGenerator.isDbSequence()) {
setSequenceName(idGenerator.getName());
}
}
/**
@@ -871,25 +811,25 @@ public class DeployBeanDescriptor<T> {
}
/**
* Assign the standard UUID generator.
* Assign the standard UUID generator if one has not been set.
*/
public void setUuidGenerator() {
this.idType = IdType.EXTERNAL;
this.idGeneratorName = PlatformIdGenerator.AUTO_UUID;
if (idGenerator == null) {
this.identityMode.setIdType(IdType.EXTERNAL);
switch (serverConfig.getUuidVersion()) {
case VERSION1:
this.idGenerator = UuidV1IdGenerator.getInstance(serverConfig.getUuidStateFile());
break;
switch (serverConfig.getUuidVersion()) {
case VERSION1:
this.idGenerator = UuidV1IdGenerator.getInstance(serverConfig.getUuidStateFile());
break;
case VERSION1RND:
this.idGenerator = UuidV1RndIdGenerator.INSTANCE;
break;
case VERSION1RND:
this.idGenerator = UuidV1RndIdGenerator.INSTANCE;
break;
case VERSION4:
default:
this.idGenerator = UuidV4IdGenerator.INSTANCE;
break;
case VERSION4:
default:
this.idGenerator = UuidV4IdGenerator.INSTANCE;
break;
}
}
}
@@ -897,8 +837,7 @@ public class DeployBeanDescriptor<T> {
* Assign a custom external IdGenerator.
*/
public void setCustomIdGenerator(PlatformIdGenerator idGenerator) {
this.idType = IdType.EXTERNAL;
this.idGeneratorName = idGenerator.getName();
this.identityMode.setIdType(IdType.EXTERNAL);
this.idGenerator = idGenerator;
}
@@ -951,7 +890,6 @@ public class DeployBeanDescriptor<T> {
* is non-numeric (and hence not suitable for db identity or sequence.
*/
public boolean isPrimaryKeyCompoundOrNonNumeric() {
DeployBeanProperty id = idProperty();
if (id == null) {
return false;
@@ -0,0 +1,139 @@
package io.ebeaninternal.server.deploy.meta;
import io.ebean.annotation.Identity;
import io.ebean.annotation.IdentityGenerated;
import io.ebean.annotation.IdentityType;
import io.ebean.config.dbplatform.IdType;
public class DeployIdentityMode {
public static DeployIdentityMode auto() {
return new DeployIdentityMode(IdType.AUTO);
}
private IdType type;
private IdentityGenerated generated;
private int start;
private int increment;
private int cache;
private String sequenceName;
private boolean platformDefault;
public DeployIdentityMode(Identity id) {
this.type = idType(id.type());
this.generated = id.generated();
this.start = id.start();
this.increment = id.increment();
this.cache = id.cache();
this.sequenceName = id.sequenceName();
}
private DeployIdentityMode(IdType type) {
this.type = type;
this.generated = IdentityGenerated.AUTO;
this.sequenceName = "";
this.start = 0;
this.increment = 0;
this.cache = 0;
}
public void setPlatformType(IdType type) {
this.type = type;
this.platformDefault = true;
}
public void setSequence(int initialValue, int allocationSize, String sequenceName) {
this.start = initialValue;
this.increment = allocationSize;
this.sequenceName = sequenceName;
}
public void setSequenceGenerator(String genName) {
if (sequenceName == null || sequenceName.isEmpty()) {
sequenceName = genName;
}
}
public int setSequenceDefaultIncrement() {
if (increment == 0) {
// Use JPA default of 50
increment = 50;
}
return increment;
}
public void setIdType(IdType type) {
this.type = type;
}
public void setStart(int start) {
this.start = start;
}
public void setIncrement(int increment) {
this.increment = increment;
}
public void setSequenceName(String sequenceName) {
this.sequenceName = sequenceName;
}
public boolean isPlatformDefault() {
return platformDefault;
}
public IdType getIdType() {
return type;
}
public IdentityGenerated getGenerated() {
return generated;
}
public int getStart() {
return start;
}
public int getIncrement() {
return increment;
}
public int getCache() {
return cache;
}
public String getSequenceName() {
return sequenceName;
}
public boolean isSequence() {
return type == IdType.SEQUENCE;
}
public boolean isIdentity() {
return type == IdType.IDENTITY;
}
public boolean isExternal() {
return type == IdType.EXTERNAL;
}
public boolean isAuto() {
return type == IdType.AUTO;
}
private static IdType idType(IdentityType type) {
switch (type) {
case AUTO:
return IdType.AUTO;
case SEQUENCE:
return IdType.SEQUENCE;
case IDENTITY:
return IdType.IDENTITY;
case APPLICATION:
return IdType.EXTERNAL;
default:
throw new IllegalStateException("type " + type + " not expected?");
}
}
}
@@ -7,6 +7,7 @@ import io.ebean.annotation.DocStore;
import io.ebean.annotation.Draftable;
import io.ebean.annotation.DraftableElement;
import io.ebean.annotation.History;
import io.ebean.annotation.Identity;
import io.ebean.annotation.Index;
import io.ebean.annotation.InvalidateQueryCache;
import io.ebean.annotation.ReadAudit;
@@ -122,6 +123,11 @@ public class AnnotationClass extends AnnotationParser {
}
}
Identity identity = findAnnotationRecursive(cls, Identity.class);
if (identity != null) {
descriptor.setIdentityMode(identity);
}
IdClass idClass = findAnnotationRecursive(cls, IdClass.class);
if (idClass != null) {
descriptor.setIdClass(idClass.value());
@@ -5,9 +5,9 @@ import io.ebean.annotation.CreatedTimestamp;
import io.ebean.annotation.DbArray;
import io.ebean.annotation.DbComment;
import io.ebean.annotation.DbDefault;
import io.ebean.annotation.DbMap;
import io.ebean.annotation.DbJson;
import io.ebean.annotation.DbJsonB;
import io.ebean.annotation.DbMap;
import io.ebean.annotation.DbMigration;
import io.ebean.annotation.DocCode;
import io.ebean.annotation.DocEmbedded;
@@ -21,11 +21,11 @@ import io.ebean.annotation.Encrypted;
import io.ebean.annotation.Expose;
import io.ebean.annotation.Formula;
import io.ebean.annotation.HistoryExclude;
import io.ebean.annotation.Identity;
import io.ebean.annotation.Index;
import io.ebean.annotation.JsonIgnore;
import io.ebean.annotation.Length;
import io.ebean.annotation.SoftDelete;
import io.ebean.annotation.Sum;
import io.ebean.annotation.TenantId;
import io.ebean.annotation.UnmappedJson;
import io.ebean.annotation.UpdatedTimestamp;
@@ -196,10 +196,8 @@ public class AnnotationFields extends AnnotationParser {
readJsonAnnotations(prop);
if (prop.getDbColumn() == null) {
// No @Column annotation or @Column.name() not set
// Use the NamingConvention to set the DB column name
String dbColumn = namingConvention.getColumnFromProperty(beanType, prop.getName());
prop.setDbColumn(dbColumn);
// No @Column or @Column.name() so use NamingConvention
prop.setDbColumn(namingConvention.getColumnFromProperty(beanType, prop.getName()));
}
Id id = get(prop, Id.class);
@@ -210,6 +208,10 @@ public class AnnotationFields extends AnnotationParser {
if (id != null) {
readIdScalar(prop);
}
Identity identity = get(prop, Identity.class);
if (identity != null) {
readIdentity(identity);
}
// determine the JDBC type using Lob/Temporal
// otherwise based on the property Class
@@ -381,6 +383,10 @@ public class AnnotationFields extends AnnotationParser {
}
}
private void readIdentity(Identity identity) {
descriptor.setIdentityMode(identity);
}
private void readDbMigration(DeployBeanProperty prop) {
DbDefault dbDefault = get(prop, DbDefault.class);
if (dbDefault != null) {
@@ -521,34 +527,32 @@ public class AnnotationFields extends AnnotationParser {
}
}
descriptor.setIdGeneratedValue();
String genName = gen.generator();
SequenceGenerator sequenceGenerator = find(prop, SequenceGenerator.class);
if (sequenceGenerator != null) {
if (sequenceGenerator.name().equals(genName)) {
genName = sequenceGenerator.sequenceName();
SequenceGenerator seq = find(prop, SequenceGenerator.class);
if (seq != null) {
String seqName = seq.sequenceName();
if (seqName.isEmpty()) {
seqName = namingConvention.getSequenceName(descriptor.getBaseTable(), prop.getDbColumn());
}
descriptor.setSequenceInitialValue(sequenceGenerator.initialValue());
descriptor.setSequenceAllocationSize(sequenceGenerator.allocationSize());
descriptor.setIdentitySequence(seq.initialValue(), seq.allocationSize(), seqName);
}
GenerationType strategy = gen.strategy();
if (strategy == GenerationType.IDENTITY) {
descriptor.setIdType(IdType.IDENTITY);
descriptor.setIdentityType(IdType.IDENTITY);
} else if (strategy == GenerationType.SEQUENCE) {
descriptor.setIdType(IdType.SEQUENCE);
if (!genName.isEmpty()) {
descriptor.setIdGeneratorName(genName);
descriptor.setIdentityType(IdType.SEQUENCE);
if (!gen.generator().isEmpty()) {
descriptor.setIdentitySequenceGenerator(gen.generator());
}
} else if (strategy == GenerationType.AUTO) {
if (!genName.isEmpty()) {
if (!gen.generator().isEmpty()) {
// use a custom IdGenerator
PlatformIdGenerator idGenerator = generatedPropFactory.getIdGenerator(genName);
PlatformIdGenerator idGenerator = generatedPropFactory.getIdGenerator(gen.generator());
if (idGenerator == null) {
throw new IllegalStateException("No custom IdGenerator registered with name " + genName);
throw new IllegalStateException("No custom IdGenerator registered with name " + gen.generator());
}
descriptor.setCustomIdGenerator(idGenerator);
} else if (prop.getPropertyType().equals(UUID.class)) {
@@ -67,10 +67,8 @@ public abstract class AnnotationParser extends AnnotationBase {
prop.setImportedPrimaryKey();
} else {
prop.setId();
if (prop.getPropertyType().equals(UUID.class)) {
if (readConfig.isIdGeneratorAutomatic() && descriptor.getIdGeneratorName() == null) {
descriptor.setUuidGenerator();
}
if (prop.getPropertyType().equals(UUID.class) && readConfig.isIdGeneratorAutomatic()) {
descriptor.setUuidGenerator();
}
}
}
@@ -1,7 +1,6 @@
package io.ebeaninternal.server.dto;
import io.ebean.ProfileLocation;
import io.ebean.meta.MetricType;
import io.ebean.metric.MetricFactory;
import io.ebean.metric.QueryPlanMetric;
import io.ebeaninternal.api.SpiDtoQuery;
@@ -49,6 +48,6 @@ public class DtoMappingRequest {
}
public QueryPlanMetric createMetric() {
return MetricFactory.get().createQueryPlanMetric(MetricType.DTO, type, label, profileLocation, sql);
return MetricFactory.get().createQueryPlanMetric(type, label, profileLocation, sql);
}
}
@@ -340,8 +340,9 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
}
@Override
public Query<T> order(String orderByClause) {
return query.order(orderByClause);
public ExpressionList<T> order(String orderByClause) {
query.order(orderByClause);
return this;
}
@Override
@@ -480,9 +481,8 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
}
@Override
public Query<T> filterMany(String manyProperty, String expressions, Object... params) {
query.filterMany(manyProperty).where(expressions, params);
return query;
public ExpressionList<T> filterMany(String manyProperty, String expressions, Object... params) {
return query.filterMany(manyProperty).where(expressions, params);
}
@Override
@@ -521,13 +521,15 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
}
@Override
public Query<T> setFirstRow(int firstRow) {
return query.setFirstRow(firstRow);
public ExpressionList<T> setFirstRow(int firstRow) {
query.setFirstRow(firstRow);
return this;
}
@Override
public Query<T> setMaxRows(int maxRows) {
return query.setMaxRows(maxRows);
public ExpressionList<T> setMaxRows(int maxRows) {
query.setMaxRows(maxRows);
return this;
}
@Override
@@ -9,6 +9,7 @@ import io.ebean.Junction;
import io.ebean.OrderBy;
import io.ebean.Query;
import io.ebeaninternal.api.SpiExpressionList;
import io.ebeaninternal.api.SpiQuery;
import javax.persistence.PersistenceException;
import java.util.Collection;
@@ -25,6 +26,10 @@ public class FilterExpressionList<T> extends DefaultExpressionList<T> {
private final FilterExprPath pathPrefix;
private int firstRow;
private int maxRows;
private String orderByClause;
public FilterExpressionList(FilterExprPath pathPrefix, FilterExpressionList<T> original) {
super(null, original.expr, null, original.getUnderlyingList());
this.pathPrefix = pathPrefix;
@@ -119,21 +124,6 @@ public class FilterExpressionList<T> extends DefaultExpressionList<T> {
throw new PersistenceException(notAllowedMessage);
}
@Override
public OrderBy<T> order() {
return rootQuery.order();
}
@Override
public Query<T> order(String orderByClause) {
return rootQuery.order(orderByClause);
}
@Override
public Query<T> orderBy(String orderBy) {
return rootQuery.orderBy(orderBy);
}
@Override
public Query<T> query() {
return rootQuery;
@@ -144,19 +134,37 @@ public class FilterExpressionList<T> extends DefaultExpressionList<T> {
throw new PersistenceException(notAllowedMessage);
}
@Override
public Query<T> setFirstRow(int firstRow) {
return rootQuery.setFirstRow(firstRow);
}
@Override
public Query<T> setMapKey(String mapKey) {
return rootQuery.setMapKey(mapKey);
}
@Override
public Query<T> setMaxRows(int maxRows) {
return rootQuery.setMaxRows(maxRows);
public OrderBy<T> order() {
return rootQuery.order();
}
@Override
public Query<T> orderBy(String orderBy) {
return rootQuery.orderBy(orderBy);
}
@Override
public ExpressionList<T> order(String orderByClause) {
this.orderByClause = orderByClause;
return this;
}
@Override
public ExpressionList<T> setMaxRows(int maxRows) {
this.maxRows = maxRows;
return this;
}
@Override
public ExpressionList<T> setFirstRow(int firstRow) {
this.firstRow = firstRow;
return this;
}
@Override
@@ -169,5 +177,17 @@ public class FilterExpressionList<T> extends DefaultExpressionList<T> {
return rootQuery.where();
}
@Override
public void applyRowLimits(SpiQuery<?> query) {
if (firstRow > 0) {
query.setFirstRow(firstRow);
}
if (maxRows > 0) {
query.setMaxRows(maxRows);
}
if (orderByClause != null) {
query.order(orderByClause);
}
}
}

Some files were not shown because too many files have changed in this diff Show More