Compare commits

...
Author SHA1 Message Date
rob bygrave 5b09443cdf [maven-release-plugin] prepare release ebean-11.22.7 2018-10-04 22:10:06 +13:00
rob bygrave 31093babe3 #1498 - findIds() with @SoftDelete bean returns all Ids (when soft deleted ones should not return)
And fix delete permanent that cascades
2018-10-04 20:49:30 +13:00
rob bygrave f8b9ca2034 #1498 - findIds() with @SoftDelete bean returns all Ids (when soft deleted ones should not return) 2018-10-04 20:26:00 +13:00
rob bygrave c27749ae2d #1497 - SQL Error with @History + @SoftDelete + @OneToOne fetch=FetchType.EAGER 2018-10-04 19:59:36 +13:00
rob bygrave 2c04430185 No effective change - add testSoftDelete_includeSoftDeletes_findOne 2018-10-04 17:14:53 +13:00
rob bygrave dcde47daf6 Merge branch 'master' of github.com:ebean-orm/ebean 2018-09-28 16:34:08 +12:00
rob bygrave 57056d7abd #1496 - Query label lost for findId() 2018-09-28 16:33:54 +12:00
Rob Bygrave 3409f264ec [maven-release-plugin] prepare for next development iteration 2018-09-26 23:38:57 +12:00
Rob Bygrave 6a61614430 [maven-release-plugin] prepare release ebean-11.22.6 2018-09-26 23:38:42 +12:00
Rob Bygrave 9734ae5787 #1490 - FIX: withQuote_when_match for different platforms 2018-09-26 21:33:27 +12:00
Roland PramlandRob Bygrave b817d5ee4d FIX: withQuote_when:_match for different platforms (#1490) 2018-09-26 21:26:29 +12:00
Rob Bygrave 0036473971 #1489 - removed the concat-operator "||" and replaced with "concat()" 2018-09-26 21:23:14 +12:00
Roland PramlandRob Bygrave a020bb15a1 removed the concat-operator "||" and replaced with "concat()" (#1489)
* removed the concat-operator "||" and replaced with "concat()"

* using || in postgres

* FIX: Assert-statement
2018-09-26 21:19:28 +12:00
Rob Bygrave efa86043bf #1492 - Update for NEW: collation support for orderBy 2018-09-26 21:15:41 +12:00
Roland PramlandRob Bygrave eb1ea5ddd2 NEW: collation support for orderBy (#1492) 2018-09-26 20:57:33 +12:00
Roland PramlandRob Bygrave a83a4fa1c4 FIX: check platform on multiple annotations like '@Formula(platform=...)' (#1495) 2018-09-26 20:55:39 +12:00
Roland PramlandRob Bygrave fe71c7cc4e FIX: tableName detection for native statements on SQLServer (#1494) 2018-09-26 20:54:22 +12:00
Roland PramlandRob Bygrave 5522d56e8d ignored tests for sqlserver, because there is no nested transaction support, yet (#1493) 2018-09-26 20:48:49 +12:00
Roland PramlandRob Bygrave 7e995efb62 Add methods th the plugin API to access type and inheritance information (#1491) 2018-09-26 20:48:19 +12:00
Roland PramlandRob Bygrave ef46bc86cc Original Value and bean change was not computed correctly in changelog (#1488)
* Bug in Changelog: It takes the first non null value as oldValue and does not detect if oldValue == newValue

* CHANGE: EntityBeanIntercept uses bitflags instead of separate boolean arrays - new flag 'origValueSet' introduced - getDirtyValues checks, if values are really different.
2018-09-26 20:40:56 +12:00
rob bygrave 0e75c50be5 #1487 - @OneToOne Postgres with different Id types - error PSQLException: ERROR: operator does not exist: uuid = character varying
Fix
2018-09-24 22:30:45 +12:00
rob bygrave cd1cdb027f #1487 - @OneToOne Postgres with different Id types - error PSQLException: ERROR: operator does not exist: uuid = character varying
Test
2018-09-24 22:30:28 +12:00
Roland PramlandRob Bygrave 66703a3a29 FIX: recursive BatchedPstmtHolder.flush, when BeanPersistController or ChangeLogFilter hit the database (#1486) 2018-09-21 15:38:21 +12:00
Roland PramlandRob Bygrave 56e65d7463 Fix issue in UUID-v1 generator on AWS Beanstalk (#1485) 2018-09-21 15:35:53 +12:00
Roland PramlandRob Bygrave eb7277aa13 Wrong behaviour in BeanMap.put/putAll (#1484) 2018-09-21 15:35:01 +12:00
Rob Bygrave 42f9e74d91 #1483 - Invalid SQL with allQuotedIdentifiers true - where t0.[t0.[COND]] in (?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) 2018-09-20 23:12:26 +12:00
Rob Bygrave be24e11b91 [maven-release-plugin] prepare for next development iteration 2018-09-20 21:35:24 +12:00
Rob Bygrave 42d3e9eb91 [maven-release-plugin] prepare release ebean-11.22.5 2018-09-20 21:35:08 +12:00
Rob Bygrave bb164a0763 OneToOne inserts with idType=SEQUENCE will fail #1480 2018-09-20 21:32:48 +12:00
Rob Bygrave 3a50c264b1 No effective change - add test with softDelete on bean with history 2018-09-20 21:10:41 +12:00
Rob Bygrave 0e7dc4209c #1482 - Improve error message for incorrect mapping - @JoinColumn without @ManyToOne etc 2018-09-20 21:09:01 +12:00
Roland PramlandRob Bygrave 72845acbad FIX: JunctionExpression.notExists (#1481) 2018-09-20 20:24:33 +12:00
Roland PramlandRob Bygrave 29d88d7de8 FIX: NPE in StalarTypeLocalTime and StalarTypeMonthDay (#1479) 2018-09-20 20:23:46 +12:00
rob bygrave c81d329100 [maven-release-plugin] prepare for next development iteration 2018-08-31 21:56:34 +12:00
rob bygrave 7c1c144af5 [maven-release-plugin] prepare release ebean-11.22.4 2018-08-31 21:56:19 +12:00
rob bygrave 7809858634 #1478 - MySql DDL, invalid comment included resulting in PersistenceException: Failed to run script 2018-08-31 21:54:48 +12:00
rob bygrave bd3cb3c547 [maven-release-plugin] prepare for next development iteration 2018-08-27 21:44:54 +12:00
rob bygrave db25cb7bfb [maven-release-plugin] prepare release ebean-11.22.3 2018-08-27 21:44:38 +12:00
rob bygrave 55a9ba7170 #1475 - @OneToOne child with @SoftDelete that is deleted is still loaded by parent 2018-08-27 21:35:00 +12:00
rob bygrave 2df2224286 No effective change - add a new test for OneToOne with fetchType LAZY 2018-08-27 20:00:44 +12:00
rob bygrave e1e8092144 [maven-release-plugin] prepare for next development iteration 2018-08-21 21:15:23 +12:00
rob bygrave 4d4886d808 [maven-release-plugin] prepare release ebean-11.22.2 2018-08-21 21:15:07 +12:00
rob bygrave d11bb2c4e5 No effective change - add a new test for OneToMany orphanRemoval=true 2018-08-21 21:08:17 +12:00
rob bygrave 663b1bb4aa #1474 - IllegalStateException: Table [some_table] does not exists in model? ... when applying drop table migration and table is no longer in the model 2018-08-21 20:45:31 +12:00
SnōwballandRob Bygrave f3d26e32ec Possible fix error with multiple uses of enum (#1473)
When an enum is exists in the type cache and isn't String, @DbArray
fails. This includes a test that should fail everywhere (order of
reading fields isn't guaranteed afaik and the column needs to be read
before the @DbArray for the test to fail).
2018-08-21 20:26:08 +12:00
rob bygrave 5f83584315 [maven-release-plugin] prepare for next development iteration 2018-08-16 00:05:31 +12:00
rob bygrave d28e3ee456 [maven-release-plugin] prepare release ebean-11.22.1 2018-08-16 00:05:16 +12:00
rob bygrave e1b7161b22 Bump version to 11.22.1-SNAPSHOT 2018-08-16 00:03:15 +12:00
rob bygrave f63a3cb926 Bump ebean-migration to 11.9.1 2018-08-16 00:02:10 +12:00
rob bygrave 29c5a17202 #1472 - Cascade insert with managed id ... results in update rather than insert statement 2018-08-15 23:12:06 +12:00
rob bygrave 340b100a1a No effective change - Add test for OneToOne orphanRemoval=true and String Ids (non generated ids) 2018-08-14 22:09:01 +12:00
rob bygrave 80975acfbf #1471 - Change stateless update on bean with no @Version property that updates 0 rows to throw EntityNotFoundException 2018-08-14 14:23:28 +12:00
rob bygrave 95dab97b1b No effective change - adjust test adding @Draft property to @Draftable Document 2018-08-14 14:16:08 +12:00
rob bygrave ce10dd79f9 [maven-release-plugin] prepare for next development iteration 2018-08-13 22:06:41 +12:00
rob bygrave 3f12739e71 [maven-release-plugin] prepare release ebean-11.21.1 2018-08-13 22:06:26 +12:00
rob bygrave cd1ae20015 Bump to 11.21.1-SNAPSHOT 2018-08-13 22:00:15 +12:00
rob bygrave 6d1e3a08e1 #1470 - Change OnQueryOnly to default to OnQueryOnly.COMMIT 2018-08-13 21:59:33 +12:00
rob bygrave 94e6fe90e5 No effective change - white space format 2018-08-13 21:54:19 +12:00
SnōwballandRob Bygrave 9bbe99657a Let enum arrays be saved as integers or strings (#1467)
* Let enums be saved as integers or strings as needed

* Add tests for int and varchar @DbArray enums
2018-08-13 21:26:14 +12:00
rob bygrave 0faf9f54ea [maven-release-plugin] prepare for next development iteration 2018-08-10 15:54:42 +12:00
rob bygrave 046a9b30b0 [maven-release-plugin] prepare release ebean-11.20.2 2018-08-10 15:54:29 +12:00
Rob BygraveandGitHub afb7679302 #1468 - ENH: Add built-in DDL generation for Postgres table partitioning (by day, week, month and year) (#1469) 2018-08-10 14:52:42 +12:00
rob bygrave db35f0f5ed #1466 - With ebean.dbSchema property (aka default db schema) we should not connection.setSchema() when running migrations 2018-08-09 21:34:23 +12:00
rob bygrave 19756421ad [maven-release-plugin] prepare for next development iteration 2018-08-08 23:34:11 +12:00
rob bygrave 1d678576f7 [maven-release-plugin] prepare release ebean-11.20.1 2018-08-08 23:33:57 +12:00
Rob BygraveandGitHub ded7d44974 Add support for Postgres range partition DDL plus refactor xsd definitions that had got out of sync (#1465)
* Add partition meta mapping

* Fix to allow schema prefix on select dynamic formula

* Add partition meta mapping

* Migration XSD Update

* Fix to allow schema prefix on select dynamic formula

* Bump ebean-migration to 11.8.1
2018-08-08 23:29:29 +12:00
rob bygrave c9497fd654 [maven-release-plugin] prepare for next development iteration 2018-08-08 12:06:03 +12:00
rob bygrave 23a9cf9bb3 [maven-release-plugin] prepare release ebean-11.19.3 2018-08-08 12:05:50 +12:00
rob bygrave 75395a8481 #1464 - NPE in PropertiesWrapper.internalGet(PropertiesWrapper.java:74) running multi-platform DB migration generation with ServerConfig that has no properties
Note that newPlatformConfig() is only used for multi-platform DB migration generation.
2018-08-07 11:55:06 +12:00
rob bygrave a161d0c01b #1463 - NPE in MigrationModel.readMigrations() when running using File resources with no migration directory 2018-08-07 11:36:26 +12:00
rob bygrave 1dd7720605 #1462 - DB Migration generation with Postgis types gives us non-strict geometry column DDL 2018-08-07 11:19:09 +12:00
rob bygrave 3d2f885e87 [maven-release-plugin] prepare for next development iteration 2018-08-06 21:42:56 +12:00
rob bygrave 2b3e485beb [maven-release-plugin] prepare release ebean-11.19.2 2018-08-06 21:42:41 +12:00
rob bygrave d4be0718a8 #1460 - ENH: Add FetchGroup ... as a mechanism to specify select() + fetch() separately from Query
Change to add generics for FetchGroup
2018-08-06 21:30:36 +12:00
rob bygrave 6d0556a120 #1460 - ENH: Add FetchGroup ... as a mechanism to specify select() + fetch() separately from Query
Change to add generics for FetchGroup
2018-08-02 23:18:44 +12:00
rob bygrave ec7e4e4935 #1461 - @OneToMany with @JoinTable without table name does not default join table name 2018-08-02 22:25:48 +12:00
rob bygrave 471605619a #1460 - ENH: Add FetchGroup ... as a mechanism to specify select() + fetch() separately from Query 2018-07-30 23:51:52 +12:00
rob bygrave 02fe956056 #1459 - findNative with join to a @OneToMany ... does not map properly 2018-07-27 12:31:56 +12:00
rob bygrave db13034fec No effective change - update code style in test 2018-07-27 12:31:27 +12:00
rob bygrave c2489beb82 [maven-release-plugin] prepare for next development iteration 2018-07-18 22:25:00 +12:00
rob bygrave 9a424d7d6d [maven-release-plugin] prepare release ebean-11.19.1 2018-07-18 22:24:45 +12:00
rob bygrave 9b74602617 Bump pom to 11.19.1-SNAPSHOT 2018-07-18 22:22:48 +12:00
Roland PramlandRob Bygrave 5c99986476 Migration with procedures (#1451)
DB Migration with helper stored procedures (for MySql and SQL Server)
2018-07-18 22:21:47 +12:00
rob bygrave 7d38933e9d Refactor remove dead code - BindableRequest registerGeneratedVersion()
No effective change
2018-07-18 22:16:15 +12:00
rob bygrave 7668fb11a9 [maven-release-plugin] prepare for next development iteration 2018-07-18 22:05:02 +12:00
220 changed files with 4477 additions and 1078 deletions
+4 -4
View File
@@ -9,7 +9,7 @@
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.18.6</version>
<version>11.22.7</version>
<packaging>jar</packaging>
<name>ebean</name>
@@ -22,7 +22,7 @@
<scm>
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
<tag>ebean-11.18.6</tag>
<tag>ebean-11.22.7</tag>
</scm>
<profiles>
@@ -117,7 +117,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-annotation</artifactId>
<version>4.1</version>
<version>4.2</version>
</dependency>
<dependency>
@@ -135,7 +135,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-migration</artifactId>
<version>11.7.1</version>
<version>11.9.1</version>
</dependency>
<dependency>
@@ -412,6 +412,11 @@ public interface ExpressionList<T> {
*/
Query<T> select(String properties);
/**
* Apply the fetchGroup which defines what part of the object graph to load.
*/
Query<T> select(FetchGroup<T> fetchGroup);
/**
* Set whether this query uses DISTINCT.
* <p>
+114
View File
@@ -0,0 +1,114 @@
package io.ebean;
import javax.annotation.Nonnull;
/**
* Defines what part of the object graph to load (select and fetch clauses).
* <p>
* Using a FetchGroup effectively sets the select() and fetch() clauses for a query. It is alternative
* to specifying the select() and fetch() clauses on the query allowing for more re-use of "what to load"
* that can be defined separately from the query and combined with other FetchGroups.
* </p>
*
* <h3>Select example</h3>*
* <pre>{@code
*
* FetchGroup<Customer> fetchGroup = FetchGroup.of(Customer.class, "name, status");
*
* Customer.query()
* .select(fetchGroup)
* .findList();
*
* }</pre>
*
* <h3>Select and fetch example</h3>
* <pre>{@code
*
* FetchGroup<Customer> fetchGroup = FetchGroup.of(Customer.class)
* .select("name, status")
* .fetch("contacts", "firstName, lastName, email")
* .build();
*
* Customer.query()
* .select(fetchGroup)
* .findList();
*
* }</pre>
*
* <h3>Combining FetchGroups</h3>
* <p>
* FetchGroups can be combined together to form another FetchGroup.
* </p>
* <pre>{@code
*
* FetchGroup<Address> FG_ADDRESS = FetchGroup.of(Address.class)
* .select("line1, line2, city")
* .fetch("country", "name")
* .build();
*
* FetchGroup<Customer> FG_CUSTOMER = FetchGroup.of(Customer.class)
* .select("name, version")
* .fetch("billingAddress", FG_ADDRESS)
* .build();
*
*
* Customer.query()
* .select(FG_CUSTOMER)
* .findList();
*
* }</pre>
*
* @param <T> The bean type the Fetch group can be applied to
*/
public interface FetchGroup<T> {
/**
* Return the FetchGroup with the given select clause.
* <p>
* We use this for simple FetchGroup that only select() properties and do not have additional fetch() clause.
* </p>
* <pre>{@code
*
* FetchGroup<Customer> fetchGroup = FetchGroup.of(Customer.class, "name, status");
*
* Customer.query()
* .select(fetchGroup)
* .findList();
*
* }</pre>
*
* @param select The select clause of the FetchGroup
*
* @return The FetchGroup with the given select clause
*/
@Nonnull
static <T> FetchGroup<T> of(Class<T> cls, String select) {
return XServiceProvider.fetchGroupOf(cls, select);
}
/**
* Return the FetchGroupBuilder with the given select clause that we can add fetch clauses to.
* <p>
* We chain select() with one or more fetch() clauses to define the object graph to load.
* </p>
* <pre>{@code
*
* FetchGroup<Customer> fetchGroup = FetchGroup.of(Customer.class)
* .select("name, status")
* .fetch("contacts", "firstName, lastName, email")
* .build();
*
* Customer.query()
* .select(fetchGroup)
* .findList();
*
* }</pre>
*
* @return The FetchGroupBuilder with the given select clause which we will add fetch clauses to
*/
@Nonnull
static <T> FetchGroupBuilder<T> of(Class<T> cls) {
return XServiceProvider.fetchGroupOf(cls);
}
}
@@ -0,0 +1,93 @@
package io.ebean;
import javax.annotation.Nonnull;
/**
* Builds a FetchGroup by adding fetch clauses.
* <p>
* We add select() and fetch() clauses to define the object graph we want to load.
* </p>
*
* <pre>{@code
*
* FetchGroup fetchGroup = FetchGroup
* .select("name, status")
* .fetch("contacts", "firstName, lastName, email")
* .build();
*
* Customer.query()
* .select(fetchGroup)
* .where()
* ...
* .findList();
*
* }</pre>
*/
public interface FetchGroupBuilder<T> {
/**
* Specify specific properties to select (top level properties).
*/
@Nonnull
FetchGroupBuilder<T> select(String select);
/**
* Fetch all the properties at the given path.
*/
@Nonnull
FetchGroupBuilder<T> fetch(String path);
/**
* Fetch the path with the nested fetch group.
*/
@Nonnull
FetchGroupBuilder<T> fetch(String path, FetchGroup<?> nestedGroup);
/**
* Fetch the path using a query join with the nested fetch group.
*/
@Nonnull
FetchGroupBuilder<T> fetchQuery(String path, FetchGroup<?> nestedGroup);
/**
* Fetch the path lazily with the nested fetch group.
*/
@Nonnull
FetchGroupBuilder<T> fetchLazy(String path, FetchGroup<?> nestedGroup);
/**
* Fetch the path including specified properties.
*/
@Nonnull
FetchGroupBuilder<T> fetch(String path, String properties);
/**
* Fetch the path including all its properties using a query join.
*/
@Nonnull
FetchGroupBuilder<T> fetchQuery(String path);
/**
* Fetch the path including specified properties using a query join.
*/
@Nonnull
FetchGroupBuilder<T> fetchQuery(String path, String properties);
/**
* Fetch the path including all its properties lazily.
*/
@Nonnull
FetchGroupBuilder<T> fetchLazy(String path);
/**
* Fetch the path including specified properties lazily.
*/
@Nonnull
FetchGroupBuilder<T> fetchLazy(String path, String properties);
/**
* Build and return the FetchGroup.
*/
@Nonnull
FetchGroup<T> build();
}
+59 -7
View File
@@ -1,9 +1,12 @@
package io.ebean;
import io.ebean.util.StringHelper;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* Represents an Order By for a Query.
@@ -74,6 +77,15 @@ public final class OrderBy<T> implements Serializable {
return query;
}
/**
* Add a property with ascending order to this OrderBy.
*/
public Query<T> asc(String propertyName, String collation) {
list.add(new Property(propertyName, true, collation));
return query;
}
/**
* Add a property with descending order to this OrderBy.
*/
@@ -83,6 +95,16 @@ public final class OrderBy<T> implements Serializable {
return query;
}
/**
* Add a property with descending order to this OrderBy.
*/
public Query<T> desc(String propertyName, String collation) {
list.add(new Property(propertyName, false, collation));
return query;
}
/**
* Return true if the property is known to be contained in the order by clause.
*/
@@ -231,6 +253,8 @@ public final class OrderBy<T> implements Serializable {
private boolean ascending;
private String collation;
private String nulls;
private String highLow;
@@ -247,17 +271,32 @@ public final class OrderBy<T> implements Serializable {
this.highLow = highLow;
}
public Property(String property, boolean ascending, String collation) {
this.property = property;
this.ascending = ascending;
this.collation = collation;
}
public Property(String property, boolean ascending, String collation, String nulls, String highLow) {
this.property = property;
this.ascending = ascending;
this.collation = collation;
this.nulls = nulls;
this.highLow = highLow;
}
/**
* Return a copy of this Property with the path trimmed.
*/
public Property copyWithTrim(String path) {
return new Property(property.substring(path.length() + 1), ascending, nulls, highLow);
return new Property(property.substring(path.length() + 1), ascending, collation, nulls, highLow);
}
@Override
public int hashCode() {
int hc = property.hashCode();
hc = hc * 92821 + (ascending ? 0 : 1);
hc = hc * 92821 + (collation == null ? 0 : collation.hashCode());
hc = hc * 92821 + (nulls == null ? 0 : nulls.hashCode());
hc = hc * 92821 + (highLow == null ? 0 : highLow.hashCode());
return hc;
@@ -274,8 +313,9 @@ public final class OrderBy<T> implements Serializable {
Property e = (Property) obj;
if (ascending != e.ascending) return false;
if (!property.equals(e.property)) return false;
if (nulls != null ? !nulls.equals(e.nulls) : e.nulls != null) return false;
return highLow != null ? highLow.equals(e.highLow) : e.highLow == null;
if (!Objects.equals(collation, e.collation)) return false;
if (!Objects.equals(nulls, e.nulls)) return false;
return Objects.equals(highLow, e.highLow);
}
@Override
@@ -284,7 +324,7 @@ public final class OrderBy<T> implements Serializable {
}
public String toStringFormat() {
if (nulls == null) {
if (nulls == null && collation == null) {
if (ascending) {
return property;
} else {
@@ -292,11 +332,23 @@ public final class OrderBy<T> implements Serializable {
}
} else {
StringBuilder sb = new StringBuilder();
sb.append(property);
if (collation != null) {
if (collation.contains("${}")) {
// this is a complex collation, e.g. DB2 - we must replace the property
sb.append(StringHelper.replaceString(collation, "${}", property));
} else {
sb.append(property);
sb.append(" collate ").append(collation);
}
} else {
sb.append(property);
}
if (!ascending) {
sb.append(" ").append("desc");
}
sb.append(" ").append(nulls).append(" ").append(highLow);
if (nulls != null) {
sb.append(" ").append(nulls).append(" ").append(highLow);
}
return sb.toString();
}
}
@@ -319,7 +371,7 @@ public final class OrderBy<T> implements Serializable {
* Return a copy of this property.
*/
public Property copy() {
return new Property(property, ascending, nulls, highLow);
return new Property(property, ascending, collation, nulls, highLow);
}
/**
+5
View File
@@ -397,6 +397,11 @@ public interface Query<T> {
*/
Query<T> select(String fetchProperties);
/**
* Apply the fetchGroup which defines what part of the object graph to load.
*/
Query<T> select(FetchGroup<T> fetchGroup);
/**
* Specify a path to fetch eagerly including specific properties.
* <p>
@@ -1,5 +1,6 @@
package io.ebean;
import io.ebean.service.SpiFetchGroupService;
import io.ebean.service.SpiProfileLocationFactory;
import io.ebean.service.SpiRawSqlService;
@@ -15,6 +16,16 @@ class XServiceProvider {
private static SpiProfileLocationFactory profileLocationFactory = initProfileLocation();
private static SpiFetchGroupService fetchGroupService = initSpiFetchGroupService();
private static SpiFetchGroupService initSpiFetchGroupService() {
Iterator<SpiFetchGroupService> loader = ServiceLoader.load(SpiFetchGroupService.class).iterator();
if (loader.hasNext()) {
return loader.next();
}
throw new IllegalStateException("No service implementation found for SpiFetchGroupService?");
}
private static SpiRawSqlService initRawSql() {
Iterator<SpiRawSqlService> loader = ServiceLoader.load(SpiRawSqlService.class).iterator();
@@ -47,4 +58,17 @@ class XServiceProvider {
return profileLocationFactory;
}
/**
* Return the FetchGroup with the given select clause.
*/
static <T> FetchGroup<T> fetchGroupOf(Class<T> cls, String select) {
return fetchGroupService.of(cls, select);
}
/**
* Return the FetchGroupBuilder with the given select clause.
*/
static <T> FetchGroupBuilder<T> fetchGroupOf(Class<T> cls) {
return fetchGroupService.of(cls);
}
}
@@ -71,21 +71,30 @@ public final class EntityBeanIntercept implements Serializable {
/**
* Used when a bean is partially filled.
*/
private final boolean[] loadedProps;
private boolean fullyLoadedBean;
private static final byte FLAG_LOADED_PROP = 1;
/**
* Set of changed properties.
*/
private boolean[] changedProps;
private static final byte FLAG_CHANGED_PROP = 2;
/**
* Flags indicating if a property is a dirty embedded bean. Used to distingush
* between an embedded bean being completely overwritten and one of its
* embedded properties being made dirty.
*/
private boolean[] embeddedDirty;
private static final byte FLAG_EMBEDDED_DIRTY = 4;
/**
* Flags indicating if a property is a dirty embedded bean. Used to distingush
* between an embedded bean being completely overwritten and one of its
* embedded properties being made dirty.
*/
private static final byte FLAG_ORIG_VALUE_SET = 8;
private final byte[] flags;
private boolean fullyLoadedBean;
private Object[] origValues;
@@ -102,7 +111,7 @@ public final class EntityBeanIntercept implements Serializable {
*/
public EntityBeanIntercept(Object ownerBean) {
this.owner = (EntityBean) ownerBean;
this.loadedProps = new boolean[owner._ebean_getPropertyNames().length];
this.flags = new byte[owner._ebean_getPropertyNames().length];
}
/**
@@ -213,8 +222,8 @@ public final class EntityBeanIntercept implements Serializable {
* Check each property to see if the bean is partially loaded.
*/
public boolean isPartial() {
for (boolean loadedProp : loadedProps) {
if (!loadedProp) {
for (byte flag : flags) {
if ((flag & FLAG_LOADED_PROP) == 0) {
return true;
}
}
@@ -259,10 +268,10 @@ public final class EntityBeanIntercept implements Serializable {
* Return true if only the Id property has been loaded.
*/
public boolean hasIdOnly(int idIndex) {
for (int i = 0; i < loadedProps.length; i++) {
for (int i = 0; i < flags.length; i++) {
if (i == idIndex) {
if (!loadedProps[i]) return false;
} else if (loadedProps[i]) {
if ((flags[i] & FLAG_LOADED_PROP) == 0) return false;
} else if ((flags[i] & FLAG_LOADED_PROP) != 0) {
return false;
}
}
@@ -284,9 +293,9 @@ public final class EntityBeanIntercept implements Serializable {
if (idPos > -1) {
// For cases where properties are set on constructor
// set every non Id property to unloaded (for lazy loading)
for (int i = 0; i < loadedProps.length; i++) {
for (int i = 0; i < flags.length; i++) {
if (i != idPos) {
loadedProps[i] = false;
flags[i] &= ~FLAG_LOADED_PROP;
}
}
}
@@ -352,7 +361,9 @@ public final class EntityBeanIntercept implements Serializable {
this.owner._ebean_setEmbeddedLoaded();
this.lazyLoadProperty = -1;
this.origValues = null;
this.changedProps = null;
for (int i = 0; i < flags.length; i++) {
flags[i] &= ~(FLAG_CHANGED_PROP + FLAG_ORIG_VALUE_SET);
}
this.dirty = false;
}
@@ -475,7 +486,11 @@ public final class EntityBeanIntercept implements Serializable {
if (position == -1) {
throw new IllegalArgumentException("Property " + propertyName + " not found");
}
loadedProps[position] = loaded;
if (loaded) {
flags[position] |= FLAG_LOADED_PROP;
} else {
flags[position] &= ~FLAG_LOADED_PROP;
}
}
/**
@@ -483,22 +498,22 @@ public final class EntityBeanIntercept implements Serializable {
* constructor.
*/
public void setPropertyUnloaded(int propertyIndex) {
loadedProps[propertyIndex] = false;
flags[propertyIndex] &= ~FLAG_LOADED_PROP;
}
/**
* Set the property to be loaded.
*/
public void setLoadedProperty(int propertyIndex) {
loadedProps[propertyIndex] = true;
flags[propertyIndex] |= FLAG_LOADED_PROP;
}
/**
* Set all properties to be loaded (post insert).
*/
public void setLoadedPropertyAll() {
for (int i = 0; i < loadedProps.length; i++) {
loadedProps[i] = true;
for (int i = 0; i < flags.length; i++) {
flags[i] |= FLAG_LOADED_PROP;
}
}
@@ -506,14 +521,14 @@ public final class EntityBeanIntercept implements Serializable {
* Return true if the property is loaded.
*/
public boolean isLoadedProperty(int propertyIndex) {
return loadedProps[propertyIndex];
return (flags[propertyIndex] & FLAG_LOADED_PROP) != 0;
}
/**
* Return true if the property is considered changed.
*/
public boolean isChangedProperty(int propertyIndex) {
return (changedProps != null && changedProps[propertyIndex]);
return (flags[propertyIndex] & FLAG_CHANGED_PROP) != 0;
}
/**
@@ -521,8 +536,7 @@ public final class EntityBeanIntercept implements Serializable {
* embedded properties is dirty.
*/
public boolean isDirtyProperty(int propertyIndex) {
return (changedProps != null && changedProps[propertyIndex]
|| embeddedDirty != null && embeddedDirty[propertyIndex]);
return (flags[propertyIndex] & (FLAG_CHANGED_PROP + FLAG_EMBEDDED_DIRTY)) != 0;
}
/**
@@ -534,27 +548,22 @@ public final class EntityBeanIntercept implements Serializable {
}
public void setChangedProperty(int propertyIndex) {
if (changedProps == null) {
changedProps = new boolean[owner._ebean_getPropertyNames().length];
}
changedProps[propertyIndex] = true;
flags[propertyIndex] |= FLAG_CHANGED_PROP;
}
/**
* Set that an embedded bean has had one of its properties changed.
*/
private void setEmbeddedPropertyDirty(int propertyIndex) {
if (embeddedDirty == null) {
embeddedDirty = new boolean[owner._ebean_getPropertyNames().length];
}
embeddedDirty[propertyIndex] = true;
flags[propertyIndex] |= FLAG_EMBEDDED_DIRTY;
}
private void setOriginalValue(int propertyIndex, Object value) {
if (origValues == null) {
origValues = new Object[owner._ebean_getPropertyNames().length];
}
if (origValues[propertyIndex] == null) {
if ((flags[propertyIndex] & FLAG_ORIG_VALUE_SET) == 0) {
flags[propertyIndex] |= FLAG_ORIG_VALUE_SET;
origValues[propertyIndex] = value;
}
}
@@ -574,13 +583,9 @@ public final class EntityBeanIntercept implements Serializable {
*/
public void setNewBeanForUpdate() {
if (changedProps == null) {
changedProps = new boolean[owner._ebean_getPropertyNames().length];
}
for (int i = 0; i < loadedProps.length; i++) {
if (loadedProps[i]) {
changedProps[i] = true;
for (int i = 0; i < flags.length; i++) {
if ((flags[i] & FLAG_LOADED_PROP) != 0) {
flags[i] |= FLAG_CHANGED_PROP;
}
}
setDirty(true);
@@ -594,8 +599,8 @@ public final class EntityBeanIntercept implements Serializable {
return null;
}
Set<String> props = new LinkedHashSet<>();
for (int i = 0; i < loadedProps.length; i++) {
if (loadedProps[i]) {
for (int i = 0; i < flags.length; i++) {
if ((flags[i] & FLAG_LOADED_PROP) != 0) {
props.add(getProperty(i));
}
}
@@ -609,12 +614,8 @@ public final class EntityBeanIntercept implements Serializable {
int len = getPropertyLength();
boolean[] dirties = new boolean[len];
for (int i = 0; i < len; i++) {
if (changedProps != null && changedProps[i]) {
dirties[i] = true;
} else if (embeddedDirty != null && embeddedDirty[i]) {
// an embedded property has been changed - recurse
dirties[i] = true;
}
// this, or an embedded property has been changed - recurse
dirties[i] = (flags[i] & (FLAG_CHANGED_PROP + FLAG_EMBEDDED_DIRTY)) != 0;
}
return dirties;
}
@@ -634,11 +635,11 @@ public final class EntityBeanIntercept implements Serializable {
public void addDirtyPropertyNames(Set<String> props, String prefix) {
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
if (changedProps != null && changedProps[i]) {
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
// the property has been changed on this bean
String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i));
props.add(propName);
} else if (embeddedDirty != null && embeddedDirty[i]) {
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
// an embedded property has been changed - recurse
EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
embeddedBean._ebean_getIntercept().addDirtyPropertyNames(props, getProperty(i) + ".");
@@ -654,12 +655,12 @@ public final class EntityBeanIntercept implements Serializable {
String[] names = owner._ebean_getPropertyNames();
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
if (changedProps != null && changedProps[i]) {
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
// the property has been changed on this bean
if (propertyNames.contains(names[i])) {
return true;
}
} else if (embeddedDirty != null && embeddedDirty[i]) {
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
if (propertyNames.contains(names[i])) {
return true;
}
@@ -683,15 +684,16 @@ public final class EntityBeanIntercept implements Serializable {
public void addDirtyPropertyValues(Map<String, ValuePair> dirtyValues, String prefix) {
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
if (changedProps != null && changedProps[i]) {
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
// the property has been changed on this bean
String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i));
Object newVal = owner._ebean_getField(i);
Object oldVal = getOrigValue(i);
if (!areEqual(oldVal, newVal)) {
dirtyValues.put(propName, new ValuePair(newVal, oldVal));
}
dirtyValues.put(propName, new ValuePair(newVal, oldVal));
} else if (embeddedDirty != null && embeddedDirty[i]) {
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
// an embedded property has been changed - recurse
EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
embeddedBean._ebean_getIntercept().addDirtyPropertyValues(dirtyValues, getProperty(i) + ".");
@@ -705,13 +707,15 @@ public final class EntityBeanIntercept implements Serializable {
public void addDirtyPropertyValues(BeanDiffVisitor visitor) {
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
if (changedProps != null && changedProps[i]) {
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
// the property has been changed on this bean
Object newVal = owner._ebean_getField(i);
Object oldVal = getOrigValue(i);
visitor.visit(i, newVal, oldVal);
if (!areEqual(oldVal, newVal)) {
visitor.visit(i, newVal, oldVal);
}
} else if (embeddedDirty != null && embeddedDirty[i]) {
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
// an embedded property has been changed - recurse
EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
visitor.visitPush(i);
@@ -739,9 +743,9 @@ public final class EntityBeanIntercept implements Serializable {
}
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
if (changedProps != null && changedProps[i]) {
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
sb.append(i).append(',');
} else if (embeddedDirty != null && embeddedDirty[i]) {
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
// an embedded property has been changed - recurse
EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
sb.append(i).append('[');
@@ -765,15 +769,12 @@ public final class EntityBeanIntercept implements Serializable {
return sb;
}
/**
* Return the set of property names for changed properties.
*/
public boolean[] getChanged() {
return changedProps;
}
public boolean[] getLoaded() {
return loadedProps;
boolean[] ret= new boolean[flags.length];
for (int i = 0; i < ret.length; i++) {
ret[i] = (flags[i] & FLAG_LOADED_PROP) != 0;
}
return ret;
}
/**
@@ -821,7 +822,7 @@ public final class EntityBeanIntercept implements Serializable {
*/
private void loadBeanInternal(int loadProperty, BeanLoader loader) {
if (loadedProps == null || loadedProps[loadProperty]) {
if ((flags[loadProperty] & FLAG_LOADED_PROP) != 0) {
// race condition where multiple threads calling preGetter concurrently
return;
}
@@ -887,7 +888,7 @@ public final class EntityBeanIntercept implements Serializable {
* Called when a BeanCollection is initialised automatically.
*/
public void initialisedMany(int propertyIndex) {
loadedProps[propertyIndex] = true;
flags[propertyIndex] |= FLAG_LOADED_PROP;
}
private void preGetterCallback(int propertyIndex) {
+6 -3
View File
@@ -273,14 +273,16 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
checkReadOnly();
init();
if (modifyListening) {
Object oldBean = map.put(key, value);
E oldBean = map.put(key, value);
if (value != oldBean) {
// register the add of the new and the removal of the old
modifyAddition(value);
modifyRemoval(oldBean);
}
return oldBean;
} else {
return map.put(key, value);
}
return map.put(key, value);
}
@Override
@@ -295,8 +297,9 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
modifyRemoval(oldBean);
}
}
} else {
map.putAll(puts);
}
map.putAll(puts);
}
@Override
@@ -107,6 +107,11 @@ public class DbMigrationConfig {
*/
protected String dbSchema;
/**
* Set to true if we consider this the 'default schema' (Postgres schema that matches DB username)
*/
protected boolean defaultDbSchema;
/**
* DB user used to run the DB migration.
*/
@@ -400,9 +405,15 @@ public class DbMigrationConfig {
* Set the Db schema if it hasn't already been defined.
*/
public void setDefaultDbSchema(String dbSchema) {
if (this.dbSchema == null) {
this.dbSchema = dbSchema;
}
this.defaultDbSchema = true;
this.dbSchema = dbSchema;
}
/**
* Return true if this is considered the default DB schema (Postgres schema matching DB username).
*/
public boolean isDefaultDbSchema() {
return defaultDbSchema;
}
/**
@@ -591,6 +602,9 @@ public class DbMigrationConfig {
runnerConfig.setDbUsername(getDbUsername());
runnerConfig.setDbPassword(getDbPassword());
runnerConfig.setDbSchema(getDbSchema());
if (defaultDbSchema) {
runnerConfig.setSetCurrentSchema(false);
}
runnerConfig.setClassLoader(classLoader);
if (patchInsertOn != null) {
runnerConfig.setPatchInsertOn(patchInsertOn);
@@ -1965,6 +1965,15 @@ public class ServerConfig {
* Return the UUID state file.
*/
public String getUuidStateFile() {
if (uuidStateFile == null || uuidStateFile.isEmpty()) {
// by default, add servername...
uuidStateFile = name + "-uuid.state";
// and store it in the user's home directory
String homeDir = System.getProperty("user.home");
if (homeDir != null && homeDir.isEmpty()) {
uuidStateFile = homeDir + "/.ebean/" + uuidStateFile;
}
}
return uuidStateFile;
}
@@ -3130,9 +3139,10 @@ public class ServerConfig {
* @return A copy of the PlatformConfig with overridden properties
*/
public PlatformConfig newPlatformConfig(String propertiesPath, String platformPrefix) {
if (properties == null) {
properties = new Properties();
}
PropertiesWrapper p = new PropertiesWrapper(propertiesPath, platformPrefix, properties, classLoadConfig);
PlatformConfig config = new PlatformConfig(platformConfig);
config.loadSettings(p);
return config;
@@ -2,6 +2,7 @@ package io.ebean.config.dbplatform;
import io.ebean.BackgroundExecutor;
import io.ebean.Query;
import io.ebean.annotation.PartitionMode;
import io.ebean.annotation.PersistBatch;
import io.ebean.annotation.Platform;
import io.ebean.config.CustomDbTypeMapping;
@@ -51,7 +52,7 @@ public class DatabasePlatform {
/**
* The behaviour used when ending a read only transaction at read committed isolation level.
*/
protected OnQueryOnly onQueryOnly = OnQueryOnly.ROLLBACK;
protected OnQueryOnly onQueryOnly = OnQueryOnly.COMMIT;
/**
* The open quote used by quoted identifiers.
@@ -63,8 +64,6 @@ public class DatabasePlatform {
*/
protected String closeQuote = "\"";
protected String concatOperator = "||";
/**
* When set to true all db column names and table names use quoted identifiers.
*/
@@ -454,13 +453,6 @@ public class DatabasePlatform {
return openQuote;
}
/**
* Return the DB concat operator.
*/
public String getConcatOperator() {
return concatOperator;
}
/**
* Return the JDBC type used to store booleans.
*/
@@ -706,6 +698,20 @@ public class DatabasePlatform {
}
}
/**
* Return true if partitions exist for the given table.
*/
public boolean tablePartitionsExist(Connection connection, String table) throws SQLException {
return true;
}
/**
* Return the SQL to create an initial partition for the given table.
*/
public String tablePartitionInit(String tableName, PartitionMode mode, String property, String singlePrimaryKey) {
return null;
}
/**
* Escapes the like string for this DB-Platform
*/
@@ -2,6 +2,7 @@ package io.ebean.config.dbplatform.postgres;
import io.ebean.BackgroundExecutor;
import io.ebean.Query;
import io.ebean.annotation.PartitionMode;
import io.ebean.annotation.Platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
@@ -11,6 +12,10 @@ import io.ebean.config.dbplatform.PlatformIdGenerator;
import io.ebean.config.dbplatform.SqlErrorCodes;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
/**
@@ -116,4 +121,31 @@ public class PostgresPlatform extends DatabasePlatform {
return sql + " for update";
}
}
@Override
public boolean tablePartitionsExist(Connection connection, String table) throws SQLException {
try (PreparedStatement statement = connection.prepareStatement("select count(*) from pg_inherits i WHERE i.inhparent = ?::regclass")) {
statement.setString(1, table);
try (ResultSet resultSet = statement.executeQuery()) {
return resultSet.next() && resultSet.getInt(1) > 0;
}
}
}
/**
* Return SQL using built in partition helper functions to create some initial partitions.
*
* Only use this if extra-dll doesn't have some initial partitions defined (which it should).
*/
public String tablePartitionInit(String tableName, PartitionMode mode, String property, String pkey) {
if (property == null) {
property = "";
}
if (pkey == null) {
pkey = "";
}
return "select partition('" + mode.name().toLowerCase() + "','" + tableName + "','" + pkey + "','" + property + "',1);";
}
}
@@ -98,6 +98,11 @@ public interface DbMigration {
*/
void setIncludeGeneratedFileComment(boolean includeGeneratedFileComment);
/**
* Set this to false to exclude the builtin support for table partitioning (with @DbPartition).
*/
void setIncludeBuiltInPartitioning(boolean includeBuiltInPartitioning);
/**
* Set the header that is included in the generated DDL script.
*/
@@ -9,6 +9,10 @@ import io.ebean.event.BeanQueryAdapter;
import io.ebeanservice.docstore.api.mapping.DocumentMapping;
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.
@@ -18,6 +22,7 @@ public interface BeanType<T> {
/**
* Return the short name of the bean type.
*/
@Nonnull
String getName();
/**
@@ -28,11 +33,13 @@ public interface BeanType<T> {
/**
* Return the full name of the bean type.
*/
@Nonnull
String getFullName();
/**
* Return the class type this BeanDescriptor describes.
*/
@Nonnull
Class<T> getBeanType();
/**
@@ -43,6 +50,7 @@ public interface BeanType<T> {
/**
* Return all the properties for this bean type.
*/
@Nonnull
Collection<? extends Property> allProperties();
/**
@@ -197,6 +205,28 @@ public interface BeanType<T> {
*/
boolean hasInheritance();
/**
* Return true if this object is the root level object in its entity
* inheritance.
*/
boolean isInheritanceRoot();
/**
* Returns all direct children of this beantype
*/
List<BeanType<?>> getInheritanceChildren();
/**
* Returns the parent in inheritance hiearchy
*/
BeanType<?> getInheritanceParent();
/**
* Visit all children recursively
* @param visitor
*/
void visitAllInheritanceChildren(Consumer<BeanType<?>> visitor);
/**
* Return the discriminator column.
*/
@@ -1,5 +1,7 @@
package io.ebean.plugin;
import javax.annotation.Nonnull;
/**
* Property of a entity bean that can be read.
*/
@@ -8,8 +10,15 @@ public interface Property {
/**
* Return the name of the property.
*/
@Nonnull
String getName();
/**
* Return the type of the property.
*/
@Nonnull
Class<?> getPropertyType();
/**
* Return the value of the property on the given bean.
*/
@@ -0,0 +1,26 @@
package io.ebean.service;
import io.ebean.FetchGroup;
import io.ebean.FetchGroupBuilder;
/**
* Service that parses FetchGroup expressions.
*/
public interface SpiFetchGroupService {
/**
* Return the FetchGroup with the given select clause.
*
* @param beanType The type of entity bean the fetch group is for
* @param select The properties to select (top level properties)
*/
<T> FetchGroup<T> of(Class<T> beanType, String select);
/**
* Create and return a FetchGroupBuilder starting with a select() clause.
*
* @param beanType The type of entity bean the fetch group is for
* @return The FetchGroupBuilder to add additional select and fetch clauses
*/
<T> FetchGroupBuilder<T> of(Class<T> beanType);
}
@@ -134,27 +134,40 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
/**
* Includes soft deletes rows in the result.
*/
SOFT_DELETED,
SOFT_DELETED(false),
/**
* Query runs against draft tables.
*/
DRAFT,
DRAFT(false),
/**
* Query runs against current data (normal).
*/
CURRENT,
CURRENT(false),
/**
* Query runs potentially returning many versions of the same bean.
*/
VERSIONS,
VERSIONS(true),
/**
* Query runs 'As Of' a given date time.
*/
AS_OF;
AS_OF(true);
private final boolean history;
TemporalMode(boolean history) {
this.history = history;
}
/**
* Return true if this is a history query.
*/
public boolean isHistory() {
return history;
}
/**
* Return the mode of the query of if null return CURRENT mode.
@@ -1,11 +1,14 @@
package io.ebeaninternal.dbmigration;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.migration.ddl.DdlRunner;
import io.ebean.util.JdbcClose;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.dbmigration.model.CurrentModel;
import io.ebeaninternal.dbmigration.model.MTable;
import io.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
import io.ebeaninternal.server.deploy.PartitionMeta;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -180,10 +183,50 @@ public class DdlGenerator {
String ignoreExtraDdl = System.getProperty("ebean.ignoreExtraDdl");
if (!"true".equalsIgnoreCase(ignoreExtraDdl) && jaxbPresent) {
if (currentModel.isTablePartitioning()) {
String extraPartitioning = ExtraDdlXmlReader.buildPartitioning(server.getDatabasePlatform().getName());
if (extraPartitioning != null && !extraPartitioning.isEmpty()) {
runScript(connection, false, extraPartitioning, "builtin-partitioning-dll");
}
}
String extraApply = ExtraDdlXmlReader.buildExtra(server.getDatabasePlatform().getName(), false);
if (extraApply != null) {
runScript(connection, false, extraApply, "extra-dll");
}
if (currentModel.isTablePartitioning()) {
checkInitialTablePartitions(connection);
}
}
}
/**
* Check if table partitions exist and if not create some. The expectation is that
* extra-dll.xml should have some partition initialisation but this helps people get going.
*/
private void checkInitialTablePartitions(Connection connection) {
DatabasePlatform databasePlatform = server.getDatabasePlatform();
try {
StringBuilder sb = new StringBuilder();
for (MTable table : currentModel.getPartitionedTables()) {
String tableName = table.getName();
if (!databasePlatform.tablePartitionsExist(connection, tableName)) {
log.info("No table partitions for table {}", tableName);
PartitionMeta meta = table.getPartitionMeta();
String initPart = databasePlatform.tablePartitionInit(tableName, meta.getMode(), meta.getProperty(), table.singlePrimaryKey());
sb.append(initPart).append("\n");
}
}
String initialPartitionSql = sb.toString();
if (!initialPartitionSql.isEmpty()) {
runScript(connection, false, initialPartitionSql, "initial table partitions");
}
} catch (SQLException e) {
log.error("Error checking initial table partitions", e);
}
}
@@ -84,6 +84,8 @@ public class DefaultDbMigration implements DbMigration {
protected DatabasePlatform databasePlatform;
private boolean vanillaPlatform;
protected List<Pair> platforms = new ArrayList<>();
protected ServerConfig serverConfig;
@@ -98,6 +100,8 @@ public class DefaultDbMigration implements DbMigration {
protected String name;
protected String generatePendingDrop;
protected boolean includeBuiltInPartitioning = true;
/**
* Create for offline migration generation.
*/
@@ -179,6 +183,11 @@ public class DefaultDbMigration implements DbMigration {
this.includeGeneratedFileComment = includeGeneratedFileComment;
}
@Override
public void setIncludeBuiltInPartitioning(boolean includeBuiltInPartitioning) {
this.includeBuiltInPartitioning = includeBuiltInPartitioning;
}
@Override
public void setHeader(String header) {
this.header = header;
@@ -192,6 +201,7 @@ public class DefaultDbMigration implements DbMigration {
*/
@Override
public void setPlatform(Platform platform) {
vanillaPlatform = true;
setPlatform(getPlatform(platform));
}
@@ -276,7 +286,7 @@ public class DefaultDbMigration implements DbMigration {
try {
Request request = createRequest();
if (platforms.isEmpty()) {
generateExtraDdl(request.migrationDir, databasePlatform);
generateExtraDdl(request.migrationDir, databasePlatform, request.isTablePartitioning());
}
String pendingVersion = generatePendingDrop();
@@ -311,16 +321,23 @@ public class DefaultDbMigration implements DbMigration {
* migration runner.
* </p>
*/
private void generateExtraDdl(File migrationDir, DatabasePlatform dbPlatform) throws IOException {
private void generateExtraDdl(File migrationDir, DatabasePlatform dbPlatform, boolean tablePartitioning) throws IOException {
if (dbPlatform != null) {
ExtraDdl extraDdl = ExtraDdlXmlReader.read("/extra-ddl.xml");
if (extraDdl != null) {
List<DdlScript> ddlScript = extraDdl.getDdlScript();
for (DdlScript script : ddlScript) {
if (!script.isDrop() && ExtraDdlXmlReader.matchPlatform(dbPlatform.getName(), script.getPlatforms())) {
writeExtraDdl(migrationDir, script);
}
if (tablePartitioning && includeBuiltInPartitioning) {
generateExtraDdlFor(migrationDir, dbPlatform, ExtraDdlXmlReader.readBuiltinTablePartitioning());
}
generateExtraDdlFor(migrationDir, dbPlatform, ExtraDdlXmlReader.readBuiltin());
generateExtraDdlFor(migrationDir, dbPlatform, ExtraDdlXmlReader.read());
}
}
private void generateExtraDdlFor(File migrationDir, DatabasePlatform dbPlatform, ExtraDdl extraDdl) throws IOException {
if (extraDdl != null) {
List<DdlScript> ddlScript = extraDdl.getDdlScript();
for (DdlScript script : ddlScript) {
if (!script.isDrop() && ExtraDdlXmlReader.matchPlatform(dbPlatform.getName(), script.getPlatforms())) {
writeExtraDdl(migrationDir, script);
}
}
}
@@ -331,7 +348,7 @@ public class DefaultDbMigration implements DbMigration {
*/
private void writeExtraDdl(File migrationDir, DdlScript script) throws IOException {
String fullName = repeatableMigrationName(script.getName());
String fullName = repeatableMigrationName(script.isInit(), script.getName());
logger.info("writing repeatable script {}", fullName);
@@ -342,8 +359,16 @@ public class DefaultDbMigration implements DbMigration {
}
}
private String repeatableMigrationName(String scriptName) {
return "R__" + scriptName.replace(' ', '_') + migrationConfig.getApplySuffix();
private String repeatableMigrationName(boolean init, String scriptName) {
StringBuilder sb = new StringBuilder();
if (init) {
sb.append("I__");
} else {
sb.append("R__");
}
sb.append(scriptName.replace(' ', '_'));
sb.append(migrationConfig.getApplySuffix());
return sb.toString();
}
/**
@@ -404,6 +429,10 @@ public class DefaultDbMigration implements DbMigration {
this.current = currentModel.read();
}
boolean isTablePartitioning() {
return current.isTablePartitioning();
}
/**
* Return the migration for the pending drops for a given version.
*/
@@ -511,7 +540,7 @@ public class DefaultDbMigration implements DbMigration {
File subPath = platformWriter.subPath(writePath, pair.prefix);
platformWriter.processMigration(dbMigration, platformBuffer, subPath, fullVersion);
generateExtraDdl(subPath, pair.platform);
generateExtraDdl(subPath, pair.platform, currentModel.isTablePartitioning());
}
}
@@ -542,11 +571,10 @@ public class DefaultDbMigration implements DbMigration {
if (server == null) {
setServer(Ebean.getDefaultServer());
}
if (databasePlatform == null && platforms.isEmpty()) {
// not explicitly set not set a list of platforms so
// default to the platform of the default server
if (vanillaPlatform || databasePlatform == null) {
// not explicitly set so use the platform of the server
databasePlatform = server.getDatabasePlatform();
logger.debug("set platform to {}", databasePlatform.getName());
logger.trace("set platform to {}", databasePlatform.getName());
}
if (migrationConfig != null) {
if (strictMode != null) {
@@ -257,6 +257,8 @@ public class BaseTableDdl implements TableDdl {
useSequence = (IdType.SEQUENCE == useDbIdentityType);
}
String partitionMode = createTable.getPartitionMode();
DdlBuffer apply = writer.apply();
apply.append("create table ").append(tableName).append(" (");
writeTableColumns(apply, columns, useIdentity);
@@ -265,7 +267,9 @@ public class BaseTableDdl implements TableDdl {
writeCompoundUniqueConstraints(apply, createTable);
if (!pk.isEmpty()) {
// defined on the columns
writePrimaryKeyConstraint(apply, createTable.getPkName(), toColumnNames(pk));
if (partitionMode == null || !platformDdl.suppressPrimaryKeyOnPartition()) {
writePrimaryKeyConstraint(apply, createTable.getPkName(), toColumnNames(pk));
}
}
if (platformDdl.isInlineForeignKeys()) {
writeInlineForeignKeys(writer, createTable);
@@ -273,6 +277,9 @@ public class BaseTableDdl implements TableDdl {
apply.newLine().append(")");
addTableCommentInline(apply, createTable);
if (partitionMode != null) {
platformDdl.addTablePartition(apply, partitionMode, createTable.getPartitionColumn());
}
apply.endOfStatement();
addComments(apply, createTable);
@@ -38,6 +38,16 @@ public class MySqlDdl extends PlatformDdl {
return "alter table " + tableName + " drop foreign key " + maxConstraintName(fkName);
}
/**
* It is rather complex to delete a column on MySql as there must not exist any foreign keys.
* That's why we call a user stored procedure here
*/
@Override
public void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) throws IOException {
buffer.append("CALL usp_ebean_drop_column('").append(tableName).append("', '").append(columnName).append("')").endOfStatement();
}
@Override
public String alterTableDropConstraint(String tableName, String constraintName) {
// drop constraint not supported in MySQL 5.7 and 8.0 but starting with MariaDB 10.2.1 CHECK is evaluated
@@ -61,7 +71,7 @@ public class MySqlDdl extends PlatformDdl {
@Override
public String alterColumnDefaultValue(String tableName, String columnName, String defaultValue) {
String suffix = DdlHelp.isDropDefault(defaultValue) ? columnDropDefault : columnSetDefault + " " + defaultValue;
String suffix = DdlHelp.isDropDefault(defaultValue) ? columnDropDefault : columnSetDefault + " " + convertDefaultValue(defaultValue);
// use alter
return "alter table " + tableName + " alter " + columnName + " " + suffix;
@@ -662,4 +662,12 @@ public class PlatformDdl {
public void unlockTables(DdlBuffer buffer, Collection<String> tables) throws IOException {
}
public boolean suppressPrimaryKeyOnPartition() {
return false;
}
public void addTablePartition(DdlBuffer apply, String partitionMode, String partitionColumn) throws IOException {
// only supported by postgres initially
}
}
@@ -1,6 +1,9 @@
package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import java.io.IOException;
/**
* Postgres specific DDL.
@@ -16,6 +19,11 @@ public class PostgresDdl extends PlatformDdl {
this.columnSetNull = "drop not null";
}
@Override
public boolean suppressPrimaryKeyOnPartition() {
return true;
}
@Override
protected String convertArrayType(String logicalArrayType) {
return NativeDbArray.logicalToNative(logicalArrayType);
@@ -38,4 +46,9 @@ public class PostgresDdl extends PlatformDdl {
}
return columnDefn;
}
@Override
public void addTablePartition(DdlBuffer apply, String partitionMode, String partitionColumn) throws IOException {
apply.append(" partition by range (").append(partitionColumn).append(")");
}
}
@@ -3,7 +3,9 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform;
import io.ebean.annotation.ConstraintMode;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
import io.ebeaninternal.dbmigration.migration.AlterColumn;
import io.ebeaninternal.server.persist.platform.MultiValueBind;
import java.io.IOException;
@@ -137,13 +139,7 @@ public class SqlServerDdl extends PlatformDdl {
// a rather complex statement.
StringBuilder sb = new StringBuilder();
if (DdlHelp.isDropDefault(defaultValue)) {
sb.append("delimiter $$\n");
sb.append("DECLARE @Tmp nvarchar(200);");
sb.append("select @Tmp = t1.name from sys.default_constraints t1\n");
sb.append(" join sys.columns t2 on t1.object_id = t2.default_object_id\n");
sb.append(" where t1.parent_object_id = OBJECT_ID('").append(tableName)
.append("') and t2.name = '").append(columnName).append("';\n");
sb.append("if @Tmp is not null EXEC('alter table ").append(tableName).append(" drop constraint ' + @Tmp)$$");
sb.append("EXEC usp_ebean_drop_default_constraint ").append(tableName).append(", ").append(columnName);
} else {
sb.append("alter table ").append(tableName);
sb.append(" add default ").append(convertDefaultValue(defaultValue)).append(" for ").append(columnName);
@@ -200,25 +196,50 @@ public class SqlServerDdl extends PlatformDdl {
/**
* It is rather complex to delete a column on SqlServer as there must not exist any references
* (constraints, default values, indices and foreign keys). The list is not yet complete, as
* indices over multiple columns will not yet deleted.
* (This may be changed to delete all refering objects by using the sys.* tables later)
* (constraints, default values, indices and foreign keys). That's why we call a user stored procedure here
*/
@Override
public void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) throws IOException {
buffer.append("-- drop column ").append(tableName).append(".").append(columnName).endOfStatement();
buffer.append(alterTableDropUniqueConstraint(tableName, naming.uniqueConstraintName(tableName, columnName)));
buffer.endOfStatement();
buffer.append(alterColumnDefaultValue(tableName, columnName, DdlHelp.DROP_DEFAULT));
buffer.endOfStatement();
buffer.append(alterTableDropConstraint(tableName, naming.checkConstraintName(tableName, columnName)));
buffer.endOfStatement();
buffer.append(dropIndex(naming.indexName(tableName, columnName), tableName));
buffer.endOfStatement();
buffer.append(alterTableDropForeignKey(tableName, naming.foreignKeyConstraintName(tableName, columnName)));
buffer.endOfStatement();
super.alterTableDropColumn(buffer, tableName, columnName);
buffer.append("EXEC usp_ebean_drop_column ").append(tableName).append(", ").append(columnName).endOfStatement();
}
/**
* This writes the multi value datatypes needed for {@link MultiValueBind}
*/
@Override
public void generateProlog(DdlWrite write) throws IOException {
super.generateProlog(write);
generateTVPDefinitions(write, "bigint");
generateTVPDefinitions(write, "float");
generateTVPDefinitions(write, "bit");
generateTVPDefinitions(write, "date");
generateTVPDefinitions(write, "time");
//generateTVPDefinitions(write, "datetime2");
generateTVPDefinitions(write, "uniqueidentifier");
generateTVPDefinitions(write, "nvarchar(max)");
}
private void generateTVPDefinitions(DdlWrite write, String definition) throws IOException {
int pos = definition.indexOf('(');
String name = pos == -1 ? definition : definition.substring(0, pos);
dropTVP(write.dropAll(), name);
//TVPs are included in "I__create_procs.sql"
//createTVP(write.apply(), name, definition);
}
private void dropTVP(DdlBuffer ddl, String name) throws IOException {
ddl.append("if exists (select name from sys.types where name = 'ebean_").append(name)
.append("_tvp') drop type ebean_").append(name).append("_tvp").endOfStatement();
}
private void createTVP(DdlBuffer ddl, String name, String definition) throws IOException {
ddl.append("if not exists (select name from sys.types where name = 'ebean_").append(name)
.append("_tvp') create type ebean_").append(name).append("_tvp as table (c1 ").append(definition).append(")")
.endOfStatement();
}
}
@@ -12,9 +12,9 @@ import java.util.List;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -45,20 +45,20 @@ public class AddColumn {
/**
* Gets the value of the column property.
* <p>
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the column property.
* <p>
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getColumn().add(newItem);
* </pre>
* <p>
* <p>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link Column }
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,11 +9,21 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* TODO
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;attribute name="constraintName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="columnNames" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="oneToOne" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="nullableColumns" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*/
@XmlAccessorType(XmlAccessType.FIELD)
@@ -23,18 +33,14 @@ public class AddUniqueConstraint {
@XmlAttribute(name = "constraintName", required = true)
protected String constraintName;
@XmlAttribute(name = "tableName", required = true)
protected String tableName;
@XmlAttribute(name = "columnNames", required = true)
protected String columnNames;
@XmlAttribute(name = "nullableColumns", required = true)
protected String nullableColumns;
@XmlAttribute(name = "oneToOne", required = false)
@XmlAttribute(name = "oneToOne")
protected Boolean oneToOne;
@XmlAttribute(name = "nullableColumns")
protected String nullableColumns;
/**
* Gets the value of the constraintName property.
@@ -55,7 +61,7 @@ public class AddUniqueConstraint {
public void setConstraintName(String value) {
this.constraintName = value;
}
/**
* Gets the value of the tableName property.
*
@@ -65,15 +71,15 @@ public class AddUniqueConstraint {
public String getTableName() {
return tableName;
}
/**
* Sets the value of the tableName property.
*
* @param value allowed object is
* {@link String }
*/
public void setTableName(String tableName) {
this.tableName = tableName;
public void setTableName(String value) {
this.tableName = value;
}
/**
@@ -96,6 +102,26 @@ public class AddUniqueConstraint {
this.columnNames = value;
}
/**
* Gets the value of the oneToOne property.
*
* @return possible object is
* {@link Boolean }
*/
public Boolean isOneToOne() {
return oneToOne;
}
/**
* Sets the value of the oneToOne property.
*
* @param value allowed object is
* {@link Boolean }
*/
public void setOneToOne(Boolean value) {
this.oneToOne = value;
}
/**
* Gets the value of the nullableColumns property.
*
@@ -115,23 +141,5 @@ public class AddUniqueConstraint {
public void setNullableColumns(String value) {
this.nullableColumns = value;
}
/**
* Gets the value of the oneToOne property.
*
* @return true if oneToOne was set
*/
public boolean isOneToOne() {
return Boolean.TRUE.equals(oneToOne);
}
/**
* Sets the value of the oneToOne property.
*
* @param value boolean
*/
public void setOneToOne(boolean oneToOne) {
this.oneToOne = oneToOne;
}
}
@@ -3,7 +3,6 @@ package io.ebeaninternal.dbmigration.migration;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import java.util.ArrayList;
@@ -12,13 +11,17 @@ import java.util.List;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element name="before" type="{http://ebean-orm.github.io/xml/ns/dbmigration}ddl-script" maxOccurs="unbounded" minOccurs="0"/>
* &lt;element name="after" type="{http://ebean-orm.github.io/xml/ns/dbmigration}ddl-script" maxOccurs="unbounded" minOccurs="0"/>
* &lt;/sequence>
* &lt;attribute name="columnName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="withHistory" type="{http://www.w3.org/2001/XMLSchema}boolean" />
@@ -50,16 +53,14 @@ import java.util.List;
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"before", "after"
"before",
"after"
})
@XmlRootElement(name = "alterColumn")
public class AlterColumn {
@XmlElement(required = false)
protected List<DdlScript> before;
@XmlElement(required = false)
protected List<DdlScript> after;
@XmlAttribute(name = "columnName", required = true)
protected String columnName;
@XmlAttribute(name = "tableName", required = true)
@@ -109,6 +110,60 @@ public class AlterColumn {
@XmlAttribute(name = "dropForeignKeyIndex")
protected String dropForeignKeyIndex;
/**
* Gets the value of the before property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the before property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getBefore().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link DdlScript }
*/
public List<DdlScript> getBefore() {
if (before == null) {
before = new ArrayList<>();
}
return this.before;
}
/**
* Gets the value of the after property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the after property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getAfter().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link DdlScript }
*/
public List<DdlScript> getAfter() {
if (after == null) {
after = new ArrayList<>();
}
return this.after;
}
/**
* Gets the value of the columnName property.
*
@@ -589,17 +644,4 @@ public class AlterColumn {
this.dropForeignKeyIndex = value;
}
public List<DdlScript> getBefore() {
if (before == null) {
before = new ArrayList<>();
}
return before;
}
public List<DdlScript> getAfter() {
if (after == null) {
after = new ArrayList<>();
}
return after;
}
}
@@ -9,21 +9,34 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* TODO
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="columnNames" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="refColumnNames" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="refTableName" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="indexName" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="onDelete" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="onUpdate" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "")
@XmlRootElement(name = "foreignKey")
@XmlRootElement(name = "alterForeignKey")
public class AlterForeignKey {
@XmlAttribute(name = "name", required = true)
protected String name;
@XmlAttribute(name = "columnNames", required = true)
@XmlAttribute(name = "columnNames")
protected String columnNames;
@XmlAttribute(name = "refColumnNames")
protected String refColumnNames;
@@ -37,6 +50,7 @@ public class AlterForeignKey {
protected String onDelete;
@XmlAttribute(name = "onUpdate")
protected String onUpdate;
/**
* Gets the value of the name property.
*
@@ -196,4 +210,5 @@ public class AlterForeignKey {
public void setOnUpdate(String value) {
this.onUpdate = value;
}
}
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlValue;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;simpleContent>
@@ -13,9 +13,9 @@ import java.util.List;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -50,16 +50,16 @@ public class ChangeSet {
@XmlElement(name = "dropTable", type = DropTable.class),
@XmlElement(name = "renameTable", type = RenameTable.class),
@XmlElement(name = "addTableComment", type = AddTableComment.class),
@XmlElement(name = "addUniqueConstraint", type = AddUniqueConstraint.class),
@XmlElement(name = "addHistoryTable", type = AddHistoryTable.class),
@XmlElement(name = "dropHistoryTable", type = DropHistoryTable.class),
@XmlElement(name = "alterForeignKey", type = AlterForeignKey.class),
@XmlElement(name = "addColumn", type = AddColumn.class),
@XmlElement(name = "dropColumn", type = DropColumn.class),
@XmlElement(name = "alterColumn", type = AlterColumn.class),
@XmlElement(name = "renameColumn", type = RenameColumn.class),
@XmlElement(name = "createIndex", type = CreateIndex.class),
@XmlElement(name = "dropIndex", type = DropIndex.class),
@XmlElement(name = "addUniqueConstraint", type = AddUniqueConstraint.class),
@XmlElement(name = "alterForeignKey", type = AlterForeignKey.class),
@XmlElement(name = "dropIndex", type = DropIndex.class)
})
protected List<Object> changeSetChildren;
@XmlAttribute(name = "type", required = true)
@@ -77,20 +77,20 @@ public class ChangeSet {
/**
* Gets the value of the changeSetChildren property.
* <p>
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the changeSetChildren property.
* <p>
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getChangeSetChildren().add(newItem);
* </pre>
* <p>
* <p>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link Configuration }
@@ -99,8 +99,10 @@ public class ChangeSet {
* {@link DropTable }
* {@link RenameTable }
* {@link AddTableComment }
* {@link AddUniqueConstraint }
* {@link AddHistoryTable }
* {@link DropHistoryTable }
* {@link AlterForeignKey }
* {@link AddColumn }
* {@link DropColumn }
* {@link AlterColumn }
@@ -7,7 +7,7 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for changeSetType.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
* <pre>
@@ -16,6 +16,7 @@ import javax.xml.bind.annotation.XmlType;
* &lt;enumeration value="apply"/>
* &lt;enumeration value="pendingDrops"/>
* &lt;enumeration value="baseline"/>
* &lt;enumeration value="drop"/>
* &lt;/restriction>
* &lt;/simpleType>
* </pre>
@@ -29,7 +30,9 @@ public enum ChangeSetType {
@XmlEnumValue("pendingDrops")
PENDING_DROPS("pendingDrops"),
@XmlEnumValue("baseline")
BASELINE("baseline");
BASELINE("baseline"),
@XmlEnumValue("drop")
DROP("drop");
private final String value;
ChangeSetType(String v) {
@@ -3,7 +3,6 @@ package io.ebeaninternal.dbmigration.migration;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import java.util.ArrayList;
@@ -12,13 +11,17 @@ import java.util.List;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element name="before" type="{http://ebean-orm.github.io/xml/ns/dbmigration}ddl-script" maxOccurs="unbounded" minOccurs="0"/>
* &lt;element name="after" type="{http://ebean-orm.github.io/xml/ns/dbmigration}ddl-script" maxOccurs="unbounded" minOccurs="0"/>
* &lt;/sequence>
* &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="type" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="defaultValue" type="{http://www.w3.org/2001/XMLSchema}string" />
@@ -43,16 +46,14 @@ import java.util.List;
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"before", "after"
"before",
"after"
})
@XmlRootElement(name = "column")
public class Column {
@XmlElement(required = false)
protected List<DdlScript> before;
@XmlElement(required = false)
protected List<DdlScript> after;
@XmlAttribute(name = "name", required = true)
protected String name;
@XmlAttribute(name = "type", required = true)
@@ -88,19 +89,58 @@ public class Column {
@XmlAttribute(name = "comment")
protected String comment;
/**
* Gets the value of the before property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the before property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getBefore().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link DdlScript }
*/
public List<DdlScript> getBefore() {
if (before == null) {
before = new ArrayList<>();
}
return before;
return this.before;
}
/**
* Gets the value of the after property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the after property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getAfter().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link DdlScript }
*/
public List<DdlScript> getAfter() {
if (after == null) {
after = new ArrayList<>();
}
return after;
return this.after;
}
/**
@@ -384,7 +424,7 @@ public class Column {
}
/**
* Gets the value of the foreignOnDelete property.
* Gets the value of the foreignKeyOnDelete property.
*
* @return possible object is
* {@link String }
@@ -404,7 +444,7 @@ public class Column {
}
/**
* Gets the value of the foreignOnUpdate property.
* Gets the value of the foreignKeyOnUpdate property.
*
* @return possible object is
* {@link String }
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -14,9 +14,9 @@ import java.util.List;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -28,6 +28,8 @@ import java.util.List;
* &lt;/sequence>
* &lt;attGroup ref="{http://ebean-orm.github.io/xml/ns/dbmigration}tablespaceAttributes"/>
* &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="partitionMode" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="partitionColumn" type="{http://www.w3.org/2001/XMLSchema}string" />
* &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" />
@@ -55,6 +57,10 @@ public class CreateTable {
protected List<ForeignKey> foreignKey;
@XmlAttribute(name = "name", required = true)
protected String name;
@XmlAttribute(name = "partitionMode")
protected String partitionMode;
@XmlAttribute(name = "partitionColumn")
protected String partitionColumn;
@XmlAttribute(name = "withHistory")
protected Boolean withHistory;
@XmlAttribute(name = "draft")
@@ -80,20 +86,20 @@ public class CreateTable {
/**
* Gets the value of the column property.
* <p>
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the column property.
* <p>
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getColumn().add(newItem);
* </pre>
* <p>
* <p>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link Column }
@@ -107,20 +113,20 @@ public class CreateTable {
/**
* Gets the value of the uniqueConstraint property.
* <p>
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the uniqueConstraint property.
* <p>
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getUniqueConstraint().add(newItem);
* </pre>
* <p>
* <p>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link UniqueConstraint }
@@ -134,20 +140,20 @@ public class CreateTable {
/**
* Gets the value of the foreignKey property.
* <p>
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the foreignKey property.
* <p>
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getForeignKey().add(newItem);
* </pre>
* <p>
* <p>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link ForeignKey }
@@ -179,6 +185,46 @@ public class CreateTable {
this.name = value;
}
/**
* Gets the value of the partitionMode property.
*
* @return possible object is
* {@link String }
*/
public String getPartitionMode() {
return partitionMode;
}
/**
* Sets the value of the partitionMode property.
*
* @param value allowed object is
* {@link String }
*/
public void setPartitionMode(String value) {
this.partitionMode = value;
}
/**
* Gets the value of the partitionColumn property.
*
* @return possible object is
* {@link String }
*/
public String getPartitionColumn() {
return partitionColumn;
}
/**
* Sets the value of the partitionColumn property.
*
* @param value allowed object is
* {@link String }
*/
public void setPartitionColumn(String value) {
this.partitionColumn = value;
}
/**
* Gets the value of the withHistory property.
*
@@ -1,42 +1,61 @@
package io.ebeaninternal.dbmigration.migration;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
import java.util.ArrayList;
import java.util.List;
/**
* <p>Java class for anonymous complex type.
* <p>
* <p>Java class for ddl-script complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* TODO @Rob: Can this generated automatically?
* &lt;complexType name="ddl-script">
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element name="ddl" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded"/>
* &lt;/sequence>
* &lt;attribute name="platforms" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
@XmlType(name = "ddl-script", propOrder = {
"ddl"
})
@XmlRootElement(name = "ddl-script")
public class DdlScript {
@XmlValue
@XmlElement(required = true)
protected List<String> ddl;
@XmlAttribute(name = "platforms")
protected String platforms;
/**
* Gets the value of the value property.
* Gets the value of the ddl property.
*
* @return possible object is
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the ddl property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getDdl().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link String }
*/
public List<String> getDdl() {
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,14 +9,16 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="sequenceCol" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="sequenceName" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
@@ -64,16 +66,6 @@ public class DropTable {
return sequenceCol;
}
/**
* Gets the value of the sequenceName property.
*
* @return possible object is
* {@link String }
*/
public String getSequenceName() {
return sequenceName;
}
/**
* Sets the value of the sequenceCol property.
*
@@ -84,6 +76,16 @@ public class DropTable {
this.sequenceCol = value;
}
/**
* Gets the value of the sequenceName property.
*
* @return possible object is
* {@link String }
*/
public String getSequenceName() {
return sequenceName;
}
/**
* Sets the value of the sequenceName property.
*
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -47,6 +47,7 @@ public class ForeignKey {
protected String onDelete;
@XmlAttribute(name = "onUpdate")
protected String onUpdate;
/**
* Gets the value of the name property.
*
@@ -7,7 +7,7 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for identityType.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
* <pre>
@@ -11,9 +11,9 @@ import java.util.List;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -38,20 +38,20 @@ public class Migration {
/**
* Gets the value of the changeSet property.
* <p>
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the changeSet property.
* <p>
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getChangeSet().add(newItem);
* </pre>
* <p>
* <p>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link ChangeSet }
@@ -6,7 +6,7 @@ import javax.xml.bind.annotation.XmlRegistry;
/**
* This object contains factory methods for each
* Java content interface and Java element interface
* generated in the io.ebean.dbmigration.migration package.
* generated in the io.ebeaninternal.dbmigration.migration package.
* <p>An ObjectFactory allows you to programatically
* construct new instances of the Java representation
* for XML content. The Java representation of XML
@@ -21,23 +21,23 @@ public class ObjectFactory {
/**
* Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: io.ebean.dbmigration.migration
* Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: io.ebeaninternal.dbmigration.migration
*/
public ObjectFactory() {
}
/**
* Create an instance of {@link Rollback }
* Create an instance of {@link AddUniqueConstraint }
*/
public Rollback createRollback() {
return new Rollback();
public AddUniqueConstraint createAddUniqueConstraint() {
return new AddUniqueConstraint();
}
/**
* Create an instance of {@link AddColumn }
* Create an instance of {@link CreateTable }
*/
public AddColumn createAddColumn() {
return new AddColumn();
public CreateTable createCreateTable() {
return new CreateTable();
}
/**
@@ -48,10 +48,10 @@ public class ObjectFactory {
}
/**
* Create an instance of {@link CreateTable }
* Create an instance of {@link DdlScript }
*/
public CreateTable createCreateTable() {
return new CreateTable();
public DdlScript createDdlScript() {
return new DdlScript();
}
/**
@@ -68,13 +68,6 @@ public class ObjectFactory {
return new ForeignKey();
}
/**
* Create an instance of {@link Apply }
*/
public Apply createApply() {
return new Apply();
}
/**
* Create an instance of {@link Configuration }
*/
@@ -89,13 +82,6 @@ public class ObjectFactory {
return new DefaultTablespace();
}
/**
* Create an instance of {@link AddTableComment }
*/
public AddTableComment createAddTableComment() {
return new AddTableComment();
}
/**
* Create an instance of {@link RenameTable }
*/
@@ -111,10 +97,10 @@ public class ObjectFactory {
}
/**
* Create an instance of {@link AlterColumn }
* Create an instance of {@link AlterForeignKey }
*/
public AlterColumn createAlterColumn() {
return new AlterColumn();
public AlterForeignKey createAlterForeignKey() {
return new AlterForeignKey();
}
/**
@@ -124,6 +110,69 @@ public class ObjectFactory {
return new DropColumn();
}
/**
* Create an instance of {@link Sql }
*/
public Sql createSql() {
return new Sql();
}
/**
* Create an instance of {@link Apply }
*/
public Apply createApply() {
return new Apply();
}
/**
* Create an instance of {@link Rollback }
*/
public Rollback createRollback() {
return new Rollback();
}
/**
* Create an instance of {@link DropIndex }
*/
public DropIndex createDropIndex() {
return new DropIndex();
}
/**
* Create an instance of {@link RenameColumn }
*/
public RenameColumn createRenameColumn() {
return new RenameColumn();
}
/**
* Create an instance of {@link DropTable }
*/
public DropTable createDropTable() {
return new DropTable();
}
/**
* Create an instance of {@link AddColumn }
*/
public AddColumn createAddColumn() {
return new AddColumn();
}
/**
* Create an instance of {@link AddTableComment }
*/
public AddTableComment createAddTableComment() {
return new AddTableComment();
}
/**
* Create an instance of {@link AlterColumn }
*/
public AlterColumn createAlterColumn() {
return new AlterColumn();
}
/**
* Create an instance of {@link CreateIndex }
*/
@@ -138,20 +187,6 @@ public class ObjectFactory {
return new ChangeSet();
}
/**
* Create an instance of {@link Sql }
*/
public Sql createSql() {
return new Sql();
}
/**
* Create an instance of {@link DropTable }
*/
public DropTable createDropTable() {
return new DropTable();
}
/**
* Create an instance of {@link AddHistoryTable }
*/
@@ -159,20 +194,6 @@ public class ObjectFactory {
return new AddHistoryTable();
}
/**
* Create an instance of {@link RenameColumn }
*/
public RenameColumn createRenameColumn() {
return new RenameColumn();
}
/**
* Create an instance of {@link DropIndex }
*/
public DropIndex createDropIndex() {
return new DropIndex();
}
/**
* Create an instance of {@link AlterHistoryTable }
*/
@@ -187,24 +208,4 @@ public class ObjectFactory {
return new Migration();
}
/**
* Create an instance of {@link DdlScript }
*/
public DdlScript createDdlScript() {
return new DdlScript();
}
/**
* Create an instance of {@link AddUniqueConstraint }
*/
public AddUniqueConstraint createAddUniqueConstraint() {
return new AddUniqueConstraint();
}
/**
* Create an instance of {@link AddUniqueConstraint }
*/
public AlterForeignKey createAlterForeignKey() {
return new AlterForeignKey();
}
}
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlValue;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;simpleContent>
@@ -9,9 +9,9 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
@@ -9,15 +9,17 @@ import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
* <p>
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
*
* <pre>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="columnNames" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;attribute name="oneToOne" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* &lt;attribute name="nullableColumns" type="{http://www.w3.org/2001/XMLSchema}string" />
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
@@ -30,16 +32,13 @@ public class UniqueConstraint {
@XmlAttribute(name = "name", required = true)
protected String name;
@XmlAttribute(name = "columnNames", required = true)
protected String columnNames;
@XmlAttribute(name = "oneToOne", required = false)
@XmlAttribute(name = "oneToOne")
protected Boolean oneToOne;
@XmlAttribute(name = "nullableColumns", required = true)
@XmlAttribute(name = "nullableColumns")
protected String nullableColumns;
/**
* Gets the value of the name property.
*
@@ -79,23 +78,25 @@ public class UniqueConstraint {
public void setColumnNames(String value) {
this.columnNames = value;
}
/**
* Gets the value of the oneToOne property.
*
* @return true if oneToOne was set
* @return possible object is
* {@link Boolean }
*/
public boolean isOneToOne() {
return Boolean.TRUE.equals(oneToOne);
public Boolean isOneToOne() {
return oneToOne;
}
/**
* Sets the value of the oneToOne property.
*
* @param value boolean
* @param value allowed object is
* {@link Boolean }
*/
public void setOneToOne(boolean oneToOne) {
this.oneToOne = oneToOne;
public void setOneToOne(Boolean value) {
this.oneToOne = value;
}
/**
@@ -9,8 +9,12 @@ import io.ebeaninternal.dbmigration.migration.ChangeSet;
import io.ebeaninternal.dbmigration.model.build.ModelBuildBeanVisitor;
import io.ebeaninternal.dbmigration.model.build.ModelBuildContext;
import io.ebeaninternal.dbmigration.model.visitor.VisitAllUsing;
import io.ebeaninternal.extraddl.model.DdlScript;
import io.ebeaninternal.extraddl.model.ExtraDdl;
import io.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
import java.io.IOException;
import java.util.List;
/**
* Reads EbeanServer bean descriptors to build the current model.
@@ -56,6 +60,20 @@ public class CurrentModel {
this.platformTypes = platformTypes;
}
/**
* Return true if the model contains tables that are partitioned.
*/
public boolean isTablePartitioning() {
return model.isTablePartitioning();
}
/**
* Return the tables that have partitioning.
*/
public List<MTable> getPartitionedTables() {
return model.getPartitionedTables();
}
private static DbConstraintNaming.MaxLength maxLength(SpiEbeanServer server, DbConstraintNaming naming) {
if (naming.getMaxLength() != null) {
@@ -111,6 +129,9 @@ public class CurrentModel {
if (header != null && !header.isEmpty()) {
ddl.append(header).append('\n');
}
addExtraDdl(ddl, ExtraDdlXmlReader.readBuiltin(), "-- init script ");
ddl.append(write.apply().getBuffer());
ddl.append(write.applyForeignKeys().getBuffer());
ddl.append(write.applyHistoryView().getBuffer());
@@ -119,6 +140,18 @@ public class CurrentModel {
return ddl.toString();
}
private void addExtraDdl(StringBuilder ddl, ExtraDdl extraDdl, String prefix) {
if (extraDdl != null) {
List<DdlScript> ddlScript = extraDdl.getDdlScript();
for (DdlScript script : ddlScript) {
if (script.isInit() && ExtraDdlXmlReader.matchPlatform(server.getDatabasePlatform().getName(), script.getPlatforms())) {
ddl.append(prefix + script.getName()).append('\n');
ddl.append(script.getValue());
}
}
}
}
/**
* Return the 'Drop' DDL.
*/
@@ -1,11 +1,12 @@
package io.ebeaninternal.dbmigration.model;
import java.util.Arrays;
import java.util.Objects;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.DdlHelp;
import io.ebeaninternal.dbmigration.migration.AddUniqueConstraint;
import io.ebeaninternal.dbmigration.migration.UniqueConstraint;
import java.util.Arrays;
import java.util.Objects;
/**
* A unique constraint for multiple columns.
* <p>
@@ -26,13 +27,13 @@ public class MCompoundUniqueConstraint {
* The columns combined to be unique.
*/
private final String[] columns;
private String[] nullableColumns;
public MCompoundUniqueConstraint(String[] columns, boolean oneToOne, String name) {
public MCompoundUniqueConstraint(String[] columns, Boolean oneToOne, String name) {
this.name = name;
this.columns = columns;
this.oneToOne = oneToOne;
this.oneToOne = Boolean.TRUE.equals(oneToOne);
}
/**
@@ -55,6 +56,7 @@ public class MCompoundUniqueConstraint {
public String getName() {
return name;
}
public UniqueConstraint getUniqueConstraint() {
UniqueConstraint uq = new UniqueConstraint();
uq.setName(getName());
@@ -63,6 +65,7 @@ public class MCompoundUniqueConstraint {
uq.setOneToOne(isOneToOne());
return uq;
}
/**
* Return a AddUniqueConstraint migration for this constraint.
*/
@@ -87,7 +90,7 @@ public class MCompoundUniqueConstraint {
dropUniqueConstraint.setNullableColumns(join(nullableColumns));
return dropUniqueConstraint;
}
public void setNullableColumns(String[] nullableColumns) {
if (nullableColumns != null && nullableColumns.length == 0) {
this.nullableColumns = null;
@@ -109,12 +112,12 @@ public class MCompoundUniqueConstraint {
}
return sb.toString();
}
@Override
public int hashCode() {
return Arrays.hashCode(columns) + 31 * Objects.hash(name, oneToOne);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
@@ -125,8 +128,8 @@ public class MCompoundUniqueConstraint {
}
MCompoundUniqueConstraint other = (MCompoundUniqueConstraint) obj;
return Arrays.equals(columns, other.columns)
&& Arrays.equals(nullableColumns, other.nullableColumns)
&& Objects.equals(name, other.name)
&& oneToOne == other.oneToOne;
&& Arrays.equals(nullableColumns, other.nullableColumns)
&& Objects.equals(name, other.name)
&& oneToOne == other.oneToOne;
}
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.dbmigration.model;
import io.ebean.annotation.PartitionMode;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.DdlHelp;
import io.ebeaninternal.dbmigration.ddlgeneration.platform.SplitColumns;
import io.ebeaninternal.dbmigration.migration.AddColumn;
@@ -14,6 +15,7 @@ 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.PartitionMeta;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -61,6 +63,8 @@ public class MTable {
*/
private boolean draft;
private PartitionMeta partitionMeta;
/**
* Primary key name.
*/
@@ -237,6 +241,10 @@ public class MTable {
createTable.setName(name);
createTable.setPkName(pkName);
createTable.setComment(comment);
if (partitionMeta != null) {
createTable.setPartitionMode(partitionMeta.getMode().name());
createTable.setPartitionColumn(partitionMeta.getProperty());
}
createTable.setTablespace(tablespace);
createTable.setIndexTablespace(indexTablespace);
createTable.setSequenceName(sequenceName);
@@ -419,6 +427,20 @@ public class MTable {
return draft;
}
/**
* Return true if this table is partitioned.
*/
public boolean isPartitioned() {
return partitionMeta != null;
}
/**
* Return the partition meta for this table.
*/
public PartitionMeta getPartitionMeta() {
return partitionMeta;
}
public void setPkName(String pkName) {
this.pkName = pkName;
}
@@ -531,6 +553,17 @@ public class MTable {
return pk;
}
/**
* Return the primary key column if it is a simple primary key.
*/
public String singlePrimaryKey() {
List<MColumn> columns = primaryKeyColumns();
if (columns.size() == 1) {
return columns.get(0).getName();
}
return null;
}
private void checkTableName(String tableName) {
if (!name.equals(tableName)) {
throw new IllegalArgumentException("addColumn tableName [" + tableName + "] does not match [" + name + "]");
@@ -745,4 +778,8 @@ public class MTable {
compoundKey.setIndexName(null);
}
}
public void setPartitionMeta(PartitionMeta partitionMeta) {
this.partitionMeta = partitionMeta;
}
}
@@ -42,9 +42,10 @@ public class MigrationModel {
// find all the migration xml files
File[] xmlFiles = modelDirectory.listFiles(pathname -> pathname.getName().toLowerCase().endsWith(modelSuffix));
if (xmlFiles == null || xmlFiles.length == 0) {
return;
}
List<MigrationResource> resources = new ArrayList<>(xmlFiles.length);
for (File xmlFile : xmlFiles) {
resources.add(new MigrationResource(xmlFile, createVersion(xmlFile)));
}
@@ -19,6 +19,7 @@ import io.ebeaninternal.dbmigration.migration.DropTable;
import io.ebeaninternal.dbmigration.migration.Migration;
import io.ebeaninternal.dbmigration.migration.Sql;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -43,9 +44,25 @@ public class ModelContainer {
private final PendingDrops pendingDrops = new PendingDrops();
private final List<MTable> partitionedTables = new ArrayList<>();
public ModelContainer() {
}
/**
* Return true if the model contains tables that are partitioned.
*/
public boolean isTablePartitioning() {
return !partitionedTables.isEmpty();
}
/**
* Return the list of partitioned tables.
*/
public List<MTable> getPartitionedTables() {
return partitionedTables;
}
/**
* Adjust the FK references on all the draft tables.
*/
@@ -167,13 +184,12 @@ public class ModelContainer {
/**
* Unset the withHistory flag on the associated base table.
*/
private void applyChange(DropHistoryTable change) {
protected void applyChange(DropHistoryTable change) {
MTable table = tables.get(change.getBaseTable());
if (table == null) {
throw new IllegalStateException("Table [" + change.getBaseTable() + "] does not exist in model?");
if (table != null) {
table.setWithHistory(false);
}
table.setWithHistory(false);
}
private void applyChange(AddUniqueConstraint change) {
@@ -224,19 +240,14 @@ public class ModelContainer {
if (tables.containsKey(tableName)) {
throw new IllegalStateException("Table [" + tableName + "] already exists in model?");
}
MTable table = new MTable(createTable);
tables.put(tableName, table);
tables.put(tableName, new MTable(createTable));
}
/**
* Apply a DropTable change to the model.
*/
protected void applyChange(DropTable dropTable) {
String tableName = dropTable.getName();
if (!tables.containsKey(tableName)) {
throw new IllegalStateException("Table [" + tableName + "] does not exists in model?");
}
tables.remove(tableName);
tables.remove(dropTable.getName());
}
/**
@@ -247,22 +258,16 @@ public class ModelContainer {
if (indexes.containsKey(indexName)) {
throw new IllegalStateException("Index [" + indexName + "] already exists in model?");
}
MIndex index = new MIndex(createIndex);
indexes.put(createIndex.getIndexName(), index);
indexes.put(createIndex.getIndexName(), new MIndex(createIndex));
}
/**
* Apply a DropTable change to the model.
*/
protected void applyChange(DropIndex dropIndex) {
String name = dropIndex.getIndexName();
if (!indexes.containsKey(name)) {
throw new IllegalStateException("Index [" + name + "] does not exist in model?");
}
indexes.remove(name);
indexes.remove(dropIndex.getIndexName());
}
/**
* Apply a AddColumn change to the model.
*/
@@ -300,6 +305,9 @@ public class ModelContainer {
* Add a table (typically from reading EbeanServer meta data).
*/
public MTable addTable(MTable table) {
if (table.isPartitioned()) {
partitionedTables.add(table);
}
return tables.put(table.getName(), table);
}
@@ -36,6 +36,7 @@ public class ModelBuildBeanVisitor implements BeanVisitor {
}
MTable table = new MTable(descriptor.getBaseTable());
table.setPartitionMeta(descriptor.getPartitionMeta());
table.setComment(descriptor.getDbComment());
if (descriptor.isHistorySupport()) {
table.setWithHistory(true);
@@ -192,7 +192,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
col.setDbMigrationInfos(p.getDbMigrationInfos());
col.setDefaultValue(p.getDbColumnDefault());
if (columns.length == 1) {
if (p.hasForeignKey()) {
if (p.hasForeignKey() && !importedProperty.getBeanDescriptor().suppressForeignKey()) {
// single references column (put it on the column)
String refTable = importedProperty.getBeanDescriptor().getBaseTable();
if (refTable == null) {
@@ -251,19 +251,20 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
col.setIdentity(true);
}
TableJoin primaryKeyJoin = p.getBeanDescriptor().getPrimaryKeyJoin();
if (primaryKeyJoin != null) {
if (primaryKeyJoin != null && !table.isPartitioned()) {
TableJoinColumn[] columns = primaryKeyJoin.columns();
col.setReferences(primaryKeyJoin.getTable() + "." + columns[0].getForeignDbColumn());
col.setForeignKeyName(determineForeignKeyConstraintName(col.getName()));
}
} else {
col.setDefaultValue(p.getDbColumnDefault());
col.setDbMigrationInfos(p.getDbMigrationInfos());
if (!p.isNullable() || p.isDDLNotNull()) {
col.setNotnull(true);
}
}
col.setDbMigrationInfos(p.getDbMigrationInfos());
if (p.isUnique() && !p.isId()) {
col.setUnique(determineUniqueConstraintName(col.getName()));
indexSetAdd(col.getName());
@@ -39,7 +39,8 @@ public class DdlScript {
protected String platforms;
@XmlAttribute(name = "drop")
protected boolean drop;
@XmlAttribute(name = "init")
protected boolean init;
/**
* Gets the value of the value property.
*
@@ -114,4 +115,17 @@ public class DdlScript {
this.drop = drop;
}
/**
* Return if this a init script.
*/
public boolean isInit() {
return init;
}
/**
* Sets that this is a init script.
*/
public void setInit(boolean init) {
this.init = init;
}
}
@@ -26,9 +26,22 @@ public class ExtraDdlXmlReader {
public static String buildExtra(String platformName, boolean drops) {
ExtraDdl read = ExtraDdlXmlReader.read("/extra-ddl.xml");
return buildExtra(platformName, drops, read);
}
/**
* Return any extra DDL for supporting partitioning given the database platform.
*/
public static String buildPartitioning(String platformName) {
return buildExtra(platformName, false, readBuiltinTablePartitioning());
}
private static String buildExtra(String platformName, boolean drops, ExtraDdl read) {
if (read == null) {
return null;
}
StringBuilder sb = new StringBuilder(300);
for (DdlScript script : read.getDdlScript()) {
if (script.isDrop() == drops && matchPlatform(platformName, script.getPlatforms())) {
@@ -83,10 +96,31 @@ public class ExtraDdlXmlReader {
}
}
/**
* Read the builtin extra ddl. (Stored procedures, tvp types etc)
*/
public static ExtraDdl readBuiltin() {
return read("/io/ebeaninternal/dbmigration/builtin-extra-ddl.xml");
}
/**
* Read the builtin extra ddl to support table partitioning.
*/
public static ExtraDdl readBuiltinTablePartitioning() {
return read("/io/ebeaninternal/dbmigration/builtin-extra-ddl-partitioning.xml");
}
/**
* Read the extra ddl.
*/
public static ExtraDdl read() {
return read("/extra-ddl.xml");
}
/**
* Read and return a ExtraDdl from an xml document at the given resource path.
*/
public static ExtraDdl read(String resourcePath) {
private static ExtraDdl read(String resourcePath) {
try (InputStream is = ExtraDdlXmlReader.class.getResourceAsStream(resourcePath)) {
if (is == null) {
@@ -31,6 +31,7 @@ import io.ebeanservice.docstore.api.DocStoreUpdate;
import io.ebeanservice.docstore.api.DocStoreUpdateContext;
import io.ebeanservice.docstore.api.DocStoreUpdates;
import javax.persistence.EntityNotFoundException;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import java.io.IOException;
@@ -854,12 +855,11 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
*/
@Override
public final void checkRowCount(int rowCount) {
if (ConcurrencyMode.VERSION == concurrencyMode && rowCount != 1) {
// fix for oracle.
// see: https://stackoverflow.com/questions/19022175/executebatch-method-return-array-of-value-2-in-java
if (rowCount != Statement.SUCCESS_NO_INFO) {
String m = Message.msg("persist.conc2", String.valueOf(rowCount));
throw new OptimisticLockException(m, null, bean);
if (rowCount != 1 && rowCount != Statement.SUCCESS_NO_INFO) {
if (ConcurrencyMode.VERSION == concurrencyMode) {
throw new OptimisticLockException(Message.msg("persist.conc2", String.valueOf(rowCount)), null, bean);
} else if (rowCount == 0 && type == Type.UPDATE) {
throw new EntityNotFoundException("No rows updated");
}
}
switch (type) {
@@ -1202,7 +1202,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
/**
* Set the value of the Version property on the bean.
*/
public void setVersionValue(Object versionValue) {
private void setVersionValue(Object versionValue) {
version = beanDescriptor.setVersion(entityBean, versionValue);
}
@@ -1307,14 +1307,22 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
* Set the request flags indicating this is an insert.
*/
public void flagInsert() {
flags = Flags.setInsert(flags);
if (intercept.isNew()) {
flags = Flags.setInsertNormal(flags);
} else {
flags = Flags.setInsert(flags);
}
}
/**
* Unset the request insert flag indicating this is an update.
*/
public void flagUpdate() {
flags = Flags.unsetInsert(flags);
if (intercept.isLoaded()) {
flags = Flags.setUpdateNormal(flags);
} else {
flags = Flags.setUpdate(flags);
}
}
/**
@@ -7,8 +7,14 @@ import io.ebeaninternal.server.query.SqlJoinType;
*/
class AssocOneHelpRefExported extends AssocOneHelp {
public AssocOneHelpRefExported(BeanPropertyAssocOne<?> property) {
private final boolean softDelete;
private final String softDeletePredicate;
AssocOneHelpRefExported(BeanPropertyAssocOne<?> property) {
super(property);
this.softDelete = property.targetDescriptor.isSoftDelete();
this.softDeletePredicate = (softDelete) ? property.targetDescriptor.getSoftDeletePredicate("") : null;
}
/**
@@ -28,6 +34,10 @@ class AssocOneHelpRefExported extends AssocOneHelp {
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
String relativePrefix = ctx.getRelativePrefix(property.getName());
property.tableJoin.addJoin(joinType, relativePrefix, ctx);
if (softDelete && !ctx.isIncludeSoftDelete()) {
property.tableJoin.addJoin(joinType, relativePrefix, ctx, softDeletePredicate);
} else {
property.tableJoin.addJoin(joinType, relativePrefix, ctx);
}
}
}
@@ -7,6 +7,7 @@ import io.ebean.SqlUpdate;
import io.ebean.Transaction;
import io.ebean.ValuePair;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.PartitionMode;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
@@ -99,6 +100,7 @@ import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
@@ -106,6 +108,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
* Describes Beans including their deployment information.
@@ -204,6 +208,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
private final String draftTable;
private final PartitionMeta partitionMeta;
/**
* DB table comment.
*/
@@ -482,6 +488,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
this.baseTableVersionsBetween = deploy.getBaseTableVersionsBetween();
this.dependentTables = deploy.getDependentTables();
this.dbComment = deploy.getDbComment();
this.partitionMeta = deploy.getPartitionMeta();
this.autoTunable = EntityType.ORM == entityType && (beanFinder == null);
// helper object used to derive lists of properties
@@ -1129,6 +1136,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
* Return true if this object is the root level object in its entity
* inheritance.
*/
@Override
public boolean isInheritanceRoot() {
return inheritInfo == null || inheritInfo.isRoot();
}
@@ -2816,6 +2824,20 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return dbComment;
}
/**
* Return true if foreign keys to the base table should be suppressed.
*/
public boolean suppressForeignKey() {
return partitionMeta != null;
}
/**
* Return the partition details of the bean.
*/
public PartitionMeta getPartitionMeta() {
return partitionMeta;
}
/**
* Return the dependent tables for a view based entity.
* <p>
@@ -3160,7 +3182,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
public boolean hasIdPropertyOnly(EntityBeanIntercept ebi) {
return ebi.hasIdOnly(idPropertyIndex);
}
public boolean isIdLoaded(EntityBeanIntercept ebi) {
return ebi.isLoadedProperty(idPropertyIndex);
}
@@ -3457,4 +3479,29 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
public List<BeanProperty[]> getUniqueProps() {
return propertiesUnique;
}
@Override
public List<BeanType<?>> getInheritanceChildren() {
if (hasInheritance()) {
return getInheritInfo().getChildren()
.stream()
.map(InheritInfo::desc)
.collect(Collectors.toList());
} else {
return Collections.emptyList();
}
}
@Override
public BeanType<?> getInheritanceParent() {
return getInheritInfo() == null ? null : getInheritInfo().getParent().desc();
}
@Override
public void visitAllInheritanceChildren(Consumer<BeanType<?>> visitor) {
if (hasInheritance()) {
getInheritInfo().visitChildren(info -> visitor.accept(info.desc()));
}
}
}
@@ -979,6 +979,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
/**
* Return the full name of this property.
*/
@Override
public String getFullBeanName() {
return descriptor.getFullName() + "." + name;
}
@@ -994,6 +995,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
/**
* Return the scalarType.
*/
@Override
@SuppressWarnings(value = "unchecked")
public ScalarType<Object> getScalarType() {
return scalarType;
@@ -1369,6 +1371,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
/**
* Return the property type.
*/
@Override
public Class<?> getPropertyType() {
return propertyType;
}
@@ -213,6 +213,12 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
// by default not including "Many" properties in document store
}
@Override
public void registerColumn(BeanDescriptor<?> desc, String prefix) {
if (targetDescriptor != null) {
desc.registerTable(targetDescriptor.getBaseTable(), this);
}
}
/**
* Return the underlying collection of beans.
*/
@@ -312,11 +318,11 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
/**
* Find the Id's of detail beans given a parent Id or list of parent Id's.
*/
public List<Object> findIdsByParentId(Object parentId, List<Object> parentIdList, Transaction t, List<Object> excludeDetailIds) {
public List<Object> findIdsByParentId(Object parentId, List<Object> parentIdList, Transaction t, List<Object> excludeDetailIds, boolean hard) {
if (parentId != null) {
return sqlHelp.findIdsByParentId(parentId, t, excludeDetailIds);
return sqlHelp.findIdsByParentId(parentId, t, excludeDetailIds, hard);
} else {
return sqlHelp.findIdsByParentIdList(parentIdList, t, excludeDetailIds);
return sqlHelp.findIdsByParentIdList(parentIdList, t, excludeDetailIds, hard);
}
}
@@ -127,14 +127,16 @@ class BeanPropertyAssocManySqlHelp<T> {
many.bindParentIdsIn(expr, parentIds, query);
}
List<Object> findIdsByParentId(Object parentId, Transaction t, List<Object> excludeDetailIds) {
List<Object> findIdsByParentId(Object parentId, Transaction t, List<Object> excludeDetailIds, boolean hard) {
String rawWhere = deriveWhereParentIdSql(false, "");
SpiEbeanServer server = descriptor.getEbeanServer();
SpiQuery<?> q = many.newQuery(server);
many.bindParentIdEq(rawWhere, parentId, q);
if (hard) {
q.setIncludeSoftDeletes();
}
if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) {
q.where().not(q.getExpressionFactory().idIn(excludeDetailIds));
}
@@ -142,7 +144,7 @@ class BeanPropertyAssocManySqlHelp<T> {
return server.findIds(q, t);
}
List<Object> findIdsByParentIdList(List<Object> parentIds, Transaction t, List<Object> excludeDetailIds) {
List<Object> findIdsByParentIdList(List<Object> parentIds, Transaction t, List<Object> excludeDetailIds, boolean hard) {
String rawWhere = deriveWhereParentIdSql(true, "");
String inClause = buildInClauseBinding(parentIds.size(), exportedPropertyBindProto);
@@ -153,7 +155,9 @@ class BeanPropertyAssocManySqlHelp<T> {
SpiQuery<?> q = many.newQuery(server);
//Query<?> q = server.find(propertyType);
many.bindParentIdsIn(expr, parentIds, q);
if (hard) {
q.setIncludeSoftDeletes();
}
if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) {
q.where().not(q.getExpressionFactory().idIn(excludeDetailIds));
}
@@ -261,7 +261,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
private List<Object> findIdsByParentIdList(List<Object> parentIds, Transaction t) {
String rawWhere = deriveWhereParentIdSql(true);
String inClause = targetIdBinder.getIdInValueExpr(false, parentIds.size());
String inClause = getIdBinder().getIdInValueExpr(false, parentIds.size());
String expr = rawWhere + inClause;
SpiEbeanServer server = server();
@@ -537,8 +537,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
BeanProperty[] emIds = targetDesc.propertiesBaseScalar();
try {
for (BeanProperty emId : emIds) {
ExportedProperty expProp = findMatch(true, emId);
list.add(expProp);
list.add(findMatch(true, emId));
}
} catch (PersistenceException e) {
// not found as individual scalar properties
@@ -547,8 +546,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
} else {
if (idProp != null) {
ExportedProperty expProp = findMatch(false, idProp);
list.add(expProp);
list.add(findMatch(false, idProp));
}
}
@@ -12,8 +12,6 @@ public interface DbSqlContext {
*/
void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2, String inheritance);
void pushSecondaryTableAlias(String alias);
/**
* Push the current table alias onto the stack.
*/
@@ -91,11 +89,6 @@ public interface DbSqlContext {
*/
String getContent();
/**
* Return the current join node.
*/
String peekJoin();
/**
* Push a join node onto the stack.
*/
@@ -125,6 +118,11 @@ public interface DbSqlContext {
*/
void appendHistorySysPeriod();
/**
* Return true if the query includes soft deleted rows.
*/
boolean isIncludeSoftDelete();
/**
* Return true if the query is a 'asDraft' query.
*/
@@ -139,4 +137,5 @@ public interface DbSqlContext {
* Append 'for update' lock hints on FROM clause (sql server only).
*/
void appendFromForUpdate();
}
@@ -22,6 +22,11 @@ public abstract class DeployParser {
*/
protected static final char UNDERSCORE = '_';
protected static final char OPEN_SQUARE_BRACKET = '[';
protected static final char CLOSE_SQUARE_BRACKET = ']';
protected static final char DOUBLE_QUOTE = '\"';
protected static final char BACK_QUOTE = '`';
/**
* Used to determine when a column name terminates.
*/
@@ -183,10 +188,10 @@ public abstract class DeployParser {
wordBuffer.append(ch);
return false;
}
return Character.isLetterOrDigit(ch) || ch == UNDERSCORE || ch == PERIOD;
return Character.isLetterOrDigit(ch) || ch == UNDERSCORE || ch == PERIOD || ch == DOUBLE_QUOTE || ch == CLOSE_SQUARE_BRACKET || ch == BACK_QUOTE;
}
private boolean isWordStart(char ch) {
return Character.isLetter(ch) || ch == UNDERSCORE;
return Character.isLetter(ch) || ch == UNDERSCORE || ch == DOUBLE_QUOTE || ch == OPEN_SQUARE_BRACKET || ch == BACK_QUOTE;
}
}
@@ -0,0 +1,27 @@
package io.ebeaninternal.server.deploy;
import io.ebean.annotation.PartitionMode;
public class PartitionMeta {
private final PartitionMode mode;
private String property;
public PartitionMeta(PartitionMode mode, String property) {
this.mode = mode;
this.property = property;
}
public PartitionMode getMode() {
return mode;
}
public String getProperty() {
return property;
}
public void setProperty(String dbColumn) {
this.property = dbColumn;
}
}
@@ -126,6 +126,16 @@ public final class TableJoin {
return type;
}
public SqlJoinType addJoin(SqlJoinType joinType, String prefix, DbSqlContext ctx, String predicate) {
String[] names = SplitName.split(prefix);
String a1 = ctx.getTableAlias(names[0]);
String a2 = ctx.getTableAlias(prefix);
SqlJoinType returnJoinType = addJoin(joinType, a1, a2, ctx);
ctx.append("and ").append(a2).append(predicate);
return returnJoinType;
}
public SqlJoinType addJoin(SqlJoinType joinType, String prefix, DbSqlContext ctx) {
String[] names = SplitName.split(prefix);
@@ -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.PartitionMode;
import io.ebean.config.ServerConfig;
import io.ebean.config.TableName;
import io.ebean.config.dbplatform.IdType;
@@ -27,6 +28,7 @@ import io.ebeaninternal.server.deploy.ChainedBeanQueryAdapter;
import io.ebeaninternal.server.deploy.DeployPropertyParserMap;
import io.ebeaninternal.server.deploy.IndexDefinition;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.PartitionMeta;
import io.ebeaninternal.server.deploy.TableJoin;
import io.ebeaninternal.server.deploy.parse.DeployBeanInfo;
import io.ebeaninternal.server.idgen.UuidV1IdGenerator;
@@ -193,6 +195,8 @@ public class DeployBeanDescriptor<T> {
private String dbComment;
private PartitionMeta partitionMeta;
/**
* One of NONE, INDEX or EMBEDDED.
*/
@@ -305,6 +309,20 @@ public class DeployBeanDescriptor<T> {
return dbComment;
}
public void setPartitionMeta(PartitionMeta partitionMeta) {
this.partitionMeta = partitionMeta;
}
public PartitionMeta getPartitionMeta() {
if (partitionMeta != null) {
DeployBeanProperty beanProperty = getBeanProperty(partitionMeta.getProperty());
if (beanProperty != null) {
partitionMeta.setProperty(beanProperty.getDbColumn());
}
}
return partitionMeta;
}
public void setDraftable() {
draftable = true;
}
@@ -304,6 +304,12 @@ class AnnotationAssocManys extends AnnotationParser {
private void readJoinTable(JoinTable joinTable, DeployBeanPropertyAssocMany<?> prop) {
String intTableName = getFullTableName(joinTable);
if (intTableName.isEmpty()) {
BeanTable localTable = factory.getBeanTable(descriptor.getBeanType());
BeanTable otherTable = factory.getBeanTable(prop.getTargetType());
intTableName = getM2MJoinTableName(localTable, otherTable);
}
// set the intersection table
DeployTableJoin intJoin = new DeployTableJoin();
intJoin.setTable(intTableName);
@@ -132,6 +132,9 @@ public class AnnotationAssocOnes extends AnnotationParser {
// check for manually defined joins
BeanTable beanTable = prop.getBeanTable();
for (JoinColumn joinColumn : getAll(prop, JoinColumn.class)) {
if (beanTable == null) {
throw new IllegalStateException("Looks like a missing @ManyToOne or @OneToOne on property " + prop.getFullBeanName()+" - no related 'BeanTable'");
}
prop.getTableJoin().addJoinColumn(false, joinColumn, beanTable);
if (!joinColumn.updatable()) {
prop.setDbUpdateable(false);
@@ -146,6 +149,9 @@ public class AnnotationAssocOnes extends AnnotationParser {
JoinTable joinTable = get(prop, JoinTable.class);
if (joinTable != null) {
for (JoinColumn joinColumn : joinTable.joinColumns()) {
if (beanTable == null) {
throw new IllegalStateException("Looks like a missing @ManyToOne or @OneToOne on property " + prop.getFullBeanName()+" - no related 'BeanTable'");
}
prop.getTableJoin().addJoinColumn(false, joinColumn, beanTable);
if (!joinColumn.updatable()) {
prop.setDbUpdateable(false);
@@ -82,12 +82,12 @@ public abstract class AnnotationBase {
T a = null;
Field field = prop.getField();
if (field != null) {
a = AnnotationUtil.findAnnotation(field, annClass);
a = AnnotationUtil.findAnnotation(field, annClass, platform);
}
if (a == null) {
Method method = prop.getReadMethod();
if (method != null) {
a = AnnotationUtil.findAnnotation(method, annClass);
a = AnnotationUtil.findAnnotation(method, annClass, platform);
}
}
return a;
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.deploy.parse;
import io.ebean.annotation.Cache;
import io.ebean.annotation.DbComment;
import io.ebean.annotation.DbPartition;
import io.ebean.annotation.DocStore;
import io.ebean.annotation.Draftable;
import io.ebean.annotation.DraftableElement;
@@ -16,6 +17,7 @@ import io.ebean.util.AnnotationUtil;
import io.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import io.ebeaninternal.server.deploy.IndexDefinition;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.PartitionMeta;
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -152,6 +154,11 @@ public class AnnotationClass extends AnnotationParser {
}
}
DbPartition partition = AnnotationUtil.findAnnotationRecursive(cls, DbPartition.class);
if (partition != null) {
descriptor.setPartitionMeta(new PartitionMeta(partition.mode(), partition.property()));
}
Draftable draftable = AnnotationUtil.findAnnotationRecursive(cls, Draftable.class);
if (draftable != null) {
descriptor.setDraftable();
@@ -6,6 +6,7 @@ import io.ebean.DtoQuery;
import io.ebean.Expression;
import io.ebean.ExpressionFactory;
import io.ebean.ExpressionList;
import io.ebean.FetchGroup;
import io.ebean.FetchPath;
import io.ebean.FutureIds;
import io.ebean.FutureList;
@@ -460,6 +461,11 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return query.select(fetchProperties);
}
@Override
public Query<T> select(FetchGroup fetchGroup) {
return query.select(fetchGroup);
}
@Override
public Query<T> setDistinct(boolean distinct) {
return query.setDistinct(distinct);
@@ -92,14 +92,7 @@ class InPairsExpression extends AbstractExpression {
return;
}
String concat = request.getDbPlatformHandler().getConcatOperator();
String concatFormula = "(" + property0 + concat + "'" + separator + "'" + concat + property1;
if (suffix != null && !suffix.isEmpty()) {
concatFormula += concat + "'" + suffix + "'";
}
concatFormula += ")";
request.append(concatFormula);
request.append(request.getDbPlatformHandler().concat(property0, separator, property1, suffix));
request.appendInExpression(not, concatBindValues);
}
@@ -5,6 +5,7 @@ import io.ebean.CountDistinctOrder;
import io.ebean.DtoQuery;
import io.ebean.Expression;
import io.ebean.ExpressionList;
import io.ebean.FetchGroup;
import io.ebean.FetchPath;
import io.ebean.FutureIds;
import io.ebean.FutureList;
@@ -666,7 +667,7 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
@Override
public ExpressionList<T> notExists(Query<?> subQuery) {
return exprList.exists(subQuery);
return exprList.notExists(subQuery);
}
@Override
@@ -759,6 +760,11 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
return exprList.select(properties);
}
@Override
public Query<T> select(FetchGroup fetchGroup) {
return exprList.select(fetchGroup);
}
@Override
public Query<T> setDistinct(boolean distinct) {
return exprList.setDistinct(distinct);
@@ -8,17 +8,6 @@ import io.ebeaninternal.server.expression.BitwiseOp;
*/
abstract class BaseDbExpression implements DbExpressionHandler {
private final String concatOperator;
BaseDbExpression(String concatOperator) {
this.concatOperator = concatOperator;
}
@Override
public String getConcatOperator() {
return concatOperator;
}
@Override
public void bitwise(SpiExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, long match) {
@@ -57,4 +46,14 @@ abstract class BaseDbExpression implements DbExpressionHandler {
}
}
@Override
public String concat(String property0, String separator, String property1, String suffix) {
StringBuilder sb = new StringBuilder();
sb.append("concat(").append(property0).append(",'").append(separator).append("',").append(property1);
if (suffix != null && !suffix.isEmpty()) {
sb.append(",'").append(suffix).append('\'');
}
sb.append(')');
return sb.toString();
}
}
@@ -8,10 +8,6 @@ import io.ebeaninternal.server.expression.Op;
*/
public class BasicDbExpression extends BaseDbExpression {
BasicDbExpression(String concatOperator) {
super(concatOperator);
}
@Override
public void json(SpiExpressionRequest request, String propName, String path, Op operator, Object value) {
throw new RuntimeException("JSON expressions only supported on Postgres and Oracle");
@@ -9,11 +9,6 @@ import io.ebeaninternal.server.expression.Op;
*/
public interface DbExpressionHandler {
/**
* Return the DB concat operator (Usually SQL standard "||").
*/
String getConcatOperator();
/**
* Write the db platform specific json expression.
*/
@@ -33,4 +28,9 @@ public interface DbExpressionHandler {
* Add the bitwise expression.
*/
void bitwise(SpiExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, long match);
/**
* Performs a "CONCAT" operation for that platform.
*/
String concat(String property0, String separator, String property1, String suffix);
}
@@ -12,20 +12,21 @@ public class DbExpressionHandlerFactory {
public static DbExpressionHandler from(DatabasePlatform databasePlatform) {
Platform platform = databasePlatform.getPlatform();
String concatOperator = databasePlatform.getConcatOperator();
switch (platform) {
case H2:
return new H2DbExpression(concatOperator);
return new H2DbExpression();
case POSTGRES:
return new PostgresDbExpression(concatOperator);
return new PostgresDbExpression();
case MYSQL:
return new MySqlDbExpression(concatOperator);
return new MySqlDbExpression();
case ORACLE:
return new OracleDbExpression(concatOperator);
return new OracleDbExpression();
case SQLSERVER16:
case SQLSERVER17:
case SQLSERVER:
return new SqlServerDbExpression(concatOperator);
return new SqlServerDbExpression();
default:
return new BasicDbExpression(concatOperator);
return new BasicDbExpression();
}
}
}
@@ -8,10 +8,6 @@ import io.ebeaninternal.server.expression.BitwiseOp;
*/
class H2DbExpression extends BasicDbExpression {
H2DbExpression(String concatOperator) {
super(concatOperator);
}
@Override
public void bitwise(SpiExpressionRequest request, String propName, BitwiseOp operator, long flags, String compare, long match) {
@@ -5,8 +5,4 @@ package io.ebeaninternal.server.expression.platform;
*/
class MySqlDbExpression extends BasicDbExpression {
MySqlDbExpression(String concatOperator) {
super(concatOperator);
}
}
@@ -9,10 +9,6 @@ import io.ebeaninternal.server.expression.Op;
*/
public class OracleDbExpression extends BaseDbExpression {
OracleDbExpression(String concatOperator) {
super(concatOperator);
}
@Override
public void json(SpiExpressionRequest request, String propName, String path, Op operator, Object value) {
@@ -8,10 +8,6 @@ import io.ebeaninternal.server.expression.Op;
*/
public class PostgresDbExpression extends BaseDbExpression {
PostgresDbExpression(String concatOperator) {
super(concatOperator);
}
@Override
public void json(SpiExpressionRequest request, String propName, String path, Op operator, Object value) {
@@ -65,4 +61,16 @@ public class PostgresDbExpression extends BaseDbExpression {
request.append(" <> 0");
}
}
@Override
public String concat(String property0, String separator, String property1, String suffix) {
StringBuilder sb = new StringBuilder();
sb.append("(").append(property0).append("||'").append(separator).append("'||").append(property1);
if (suffix != null && !suffix.isEmpty()) {
sb.append("||'").append(suffix).append('\'');
}
sb.append(')');
return sb.toString();
}
}
@@ -8,10 +8,6 @@ import io.ebeaninternal.server.expression.Op;
*/
public class SqlServerDbExpression extends BaseDbExpression {
SqlServerDbExpression(String concatOperator) {
super(concatOperator);
}
@Override
public void json(final SpiExpressionRequest request, final String propName,
final String path, final Op operator, final Object value) {
@@ -82,8 +82,17 @@ public class UuidV1IdGenerator extends UuidV1RndIdGenerator {
final Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
while (e.hasMoreElements()) {
NetworkInterface network = e.nextElement();
if (!network.isLoopback()) {
return network.getHardwareAddress();
try {
logger.trace("Probing interface {}", network);
if (!network.isLoopback()) {
byte[] addr = network.getHardwareAddress();
if (addr != null) {
logger.debug("Using interface {}", network);
return addr;
}
}
} catch (SocketException ex) {
logger.debug("Skipping {}", network, ex);
}
}
return null;
@@ -181,6 +190,10 @@ public class UuidV1IdGenerator extends UuidV1RndIdGenerator {
prop.setProperty("nodeId", getNodeIdentifier());
prop.setProperty("clockSeq", String.valueOf(clockSeq.get()));
prop.setProperty("timeStamp", String.valueOf(timeStamp.get()));
File dir = stateFile.getParentFile();
if (dir != null) {
dir.mkdirs();
}
try (OutputStream os = new FileOutputStream(stateFile)) {
prop.store(os, "ebean uuid state file");
logger.debug("Persisted state to '{}'", stateFile);
@@ -110,7 +110,15 @@ public class BatchedPstmtHolder {
// but still need to close PreparedStatements.
boolean isError = false;
for (BatchedPstmt bs : stmtMap.values()) {
// if there are Listeners/Controllers that interact with the database,
// the flush may get called recursively in executeBatch/postExecute.
// which leads that we process stmtMap.values() twice in the loop.
// So we copy the values, that we want to flush and clear it immediately.
BatchedPstmt[] values = stmtMap.values().toArray(new BatchedPstmt[stmtMap.values().size()]);
clear();
// this loop
for (BatchedPstmt bs : values) {
try {
if (!isError) {
bs.executeBatch(getGeneratedKeys);
@@ -139,8 +147,6 @@ public class BatchedPstmtHolder {
}
}
// clear the batch cache
clear();
if (firstError != null) {
String msg = "Error when batch flush on sql: " + errorSql;
@@ -456,8 +456,7 @@ public final class DefaultPersister implements Persister {
void saveRecurse(EntityBean bean, Transaction t, Object parentBean, int flags) {
// determine insert or update taking into account stateless updates
PersistRequestBean<?> request = createRequestRecurse(bean, t, parentBean, flags);
saveRecurse(request);
saveRecurse(createRequestRecurse(bean, t, parentBean, flags));
}
private void saveRecurse(PersistRequestBean<?> request) {
@@ -755,7 +754,7 @@ public final class DefaultPersister implements Persister {
executeSqlUpdate(sqlDelete, t);
} else {
// we need to fetch the Id's to delete (recurse or notify L2 cache)
List<Object> childIds = many.findIdsByParentId(id, idList, t, null);
List<Object> childIds = many.findIdsByParentId(id, idList, t, null, deleteMode.isHard());
if (!childIds.isEmpty()) {
delete(targetDesc, null, childIds, t, deleteMode);
}
@@ -1053,7 +1052,7 @@ public final class DefaultPersister implements Persister {
} else {
// Delete recurse using the Id values of the children
Object parentId = desc.getId(parentBean);
List<Object> idsByParentId = many.findIdsByParentId(parentId, null, t, excludeDetailIds);
List<Object> idsByParentId = many.findIdsByParentId(parentId, null, t, excludeDetailIds, deleteMode.isHard());
if (!idsByParentId.isEmpty()) {
deleteChildrenById(t, targetDesc, idsByParentId, deleteMode);
}
@@ -1229,8 +1228,8 @@ public final class DefaultPersister implements Persister {
BeanDescriptor<T> desc = mgr.getBeanDescriptor();
EntityBean entityBean = (EntityBean) bean;
PersistRequest.Type type;
if (Flags.isPublishOrMerge(flags)) {
// insert if it is a new bean (as publish created it)
if (Flags.isPublishMergeOrNormal(flags)) {
// just use bean state to determine insert or update
type = entityBean._ebean_getIntercept().isUpdate() ? Type.UPDATE : Type.INSERT;
} else {
// determine Insert or Update based on bean state and insert flag
@@ -5,7 +5,7 @@ package io.ebeaninternal.server.persist;
* <p>
* Allows passing of flag state when recursively persisting.
*/
public class Flags {
public final class Flags {
/**
* Indicates the bean is being inserted.
@@ -27,6 +27,11 @@ public class Flags {
*/
public static final int MERGE = 0x00000008;
/**
* Indicates Normal insert or update (not forced).
*/
public static final int NORMAL = 0x00000010;
/**
* No flags set.
*/
@@ -34,7 +39,9 @@ public class Flags {
public static final int PUBLISH_RECURSE = PUBLISH + RECURSE;
private static final int PUBLISH_MERGE = PUBLISH + MERGE;
private static final int PUBLISH_MERGE_NORMAL = PUBLISH + MERGE + NORMAL;
private static final int INSERT_NORMAL = INSERT + NORMAL;
/**
* Return true if the bean is being inserted.
@@ -65,10 +72,10 @@ public class Flags {
}
/**
* Return true if part of a Merge or Publish.
* Return true if part of a Merge or Publish or Normal (bean state matches persist).
*/
public static boolean isPublishOrMerge(long state) {
return (state & PUBLISH_MERGE) != 0;
public static boolean isPublishMergeOrNormal(int state) {
return (state & PUBLISH_MERGE_NORMAL) != 0;
}
/**
@@ -82,54 +89,70 @@ public class Flags {
* Set Insert flag.
*/
public static int setInsert(int state) {
return set(state, INSERT, true);
return set(state, INSERT);
}
/**
* Insert flag and normal in that bean is in NEW state (for insert).
*/
public static int setInsertNormal(int state) {
return set(state, INSERT_NORMAL);
}
/**
* Parent was not inserted.
*/
public static int unsetInsert(int state) {
return set(state, INSERT, false);
public static int setUpdate(int state) {
return unset(state, INSERT);
}
/**
* Not Insert and normal in that bean is in LOADED state (for update).
*/
public static int setUpdateNormal(int state) {
state &= ~INSERT;
state |= NORMAL;
return state;
}
/**
* Set Recurse flag.
*/
public static int setRecurse(int state) {
return set(state, RECURSE, true);
return set(state, RECURSE);
}
public static int unsetRecuse(int state) {
return set(state, RECURSE, false);
return unset(state, RECURSE);
}
/**
* Set Publish flag.
*/
public static int setPublish(int state) {
return set(state, PUBLISH, true);
return set(state, PUBLISH);
}
public static int unsetPublish(int state) {
return set(state, PUBLISH, false);
return unset(state, PUBLISH);
}
/**
* Set Merge flag.
*/
public static int setMerge(int state) {
return set(state, MERGE, true);
return set(state, MERGE);
}
public static int unsetMerge(int state) {
return set(state, MERGE, false);
return unset(state, MERGE);
}
private static int set(int state, int flag, boolean setFlag) {
if (setFlag) {
return (state |= flag);
} else {
return state &= ~flag;
}
private static int set(int state, int flag) {
return (state |= flag);
}
private static int unset(int state, int flag) {
return state &= ~flag;
}
}
@@ -14,7 +14,7 @@ public class DeleteHandler extends DmlHandler {
private final DeleteMeta meta;
public DeleteHandler(PersistRequestBean<?> persist, DeleteMeta meta) {
DeleteHandler(PersistRequestBean<?> persist, DeleteMeta meta) {
super(persist, meta.isEmptyStringAsNull());
this.meta = meta;
}
@@ -7,7 +7,6 @@ import io.ebeaninternal.server.lib.util.Str;
import io.ebeaninternal.server.persist.BatchedPstmt;
import io.ebeaninternal.server.persist.BatchedPstmtHolder;
import io.ebeaninternal.server.persist.dmlbind.BindableRequest;
import io.ebeaninternal.server.transaction.TransactionManager;
import io.ebeaninternal.server.type.DataBind;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -50,11 +49,6 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
protected String sql;
/**
* The generated value for the @Version property. Must be set after where clause is bound.
*/
protected Object versionValue;
protected DmlHandler(PersistRequestBean<?> persistRequest, boolean emptyStringToNull) {
this.now = System.currentTimeMillis();
this.persistRequest = persistRequest;
@@ -231,30 +225,6 @@ public abstract class DmlHandler implements PersistHandler, BindableRequest {
prop.bind(dataBind, value);
}
/**
* Register a generated value on a update. This can not be set to the bean
* until after the where clause has been bound for concurrency checking.
* <p>
* GeneratedProperty values are likely going to be used for optimistic
* concurrency checking. This includes 'counter' and 'update timestamp'
* generation.
* </p>
*/
@Override
public void registerGeneratedVersion(Object versionValue) {
this.versionValue = versionValue;
}
/**
* Set any update generated values to the bean. Must be called after where
* clause has been bound.
*/
public void setUpdateGenValues() {
if (versionValue != null) {
persistRequest.setVersionValue(versionValue);
}
}
/**
* Check with useGeneratedKeys to get appropriate PreparedStatement.
*/
@@ -17,7 +17,7 @@ public class UpdateHandler extends DmlHandler {
private boolean emptySetClause;
public UpdateHandler(PersistRequestBean<?> persist, UpdateMeta meta) {
UpdateHandler(PersistRequestBean<?> persist, UpdateMeta meta) {
super(persist, meta.isEmptyStringAsNull());
this.meta = meta;
}
@@ -55,8 +55,6 @@ public class UpdateHandler extends DmlHandler {
if (persistRequest.isBatched()) {
batchedPstmt.registerInputStreams(dataBind.getInputStreams());
}
setUpdateGenValues();
logSql(sql);
}
@@ -42,12 +42,6 @@ public interface BindableRequest {
*/
void bindNoLog(Object value, BeanProperty prop) throws SQLException;
/**
* Register the value from a update GeneratedValue. This can only be set to
* the bean property after the where clause has bean built.
*/
void registerGeneratedVersion(Object value);
/**
* Return the original PersistRequest.
*/
@@ -138,11 +138,14 @@ class CQueryBuilder {
// wrap as - delete from table where id in (select id ...)
String sql = buildSql(null, request, predicates, sqlTree).getSql();
sql = request.getBeanDescriptor().getDeleteByIdInSql() + "in (" + sql + ")";
String alias = (rootTableAlias == null) ? "t0" : rootTableAlias;
sql = aliasReplace(sql, alias);
sql = aliasReplace(sql, alias(rootTableAlias));
return sql;
}
private String alias(String rootTableAlias) {
return (rootTableAlias == null) ? "t0" : rootTableAlias;
}
private <T> String buildUpdateSql(OrmQueryRequest<T> request, String rootTableAlias, CQueryPredicates predicates, SqlTree sqlTree) {
StringBuilder sb = new StringBuilder(200);
@@ -160,8 +163,7 @@ class CQueryBuilder {
// wrap as - update table set ... where id in (select id ...)
String sql = buildSqlUpdate(null, request, predicates, sqlTree).getSql();
sql = updateClause + " " + request.getBeanDescriptor().getWhereIdInSql() + "in (" + sql + ")";
String alias = (rootTableAlias == null) ? "t0" : rootTableAlias;
sql = aliasReplace(sql, alias);
sql = aliasReplace(sql, alias(rootTableAlias));
return sql;
}
@@ -209,7 +211,12 @@ class CQueryBuilder {
*/
<T> CQueryFetchSingleAttribute buildFetchIdsQuery(OrmQueryRequest<T> request) {
request.getQuery().setSelectId();
SpiQuery<T> query = request.getQuery();
query.setSelectId();
BeanDescriptor<T> desc = request.getBeanDescriptor();
if (!query.isIncludeSoftDeletes() && desc.isSoftDelete()) {
query.addSoftDeletePredicate(desc.getSoftDeletePredicate(alias(query.getAlias())));
}
return buildFetchAttributeQuery(request);
}
@@ -217,7 +224,7 @@ class CQueryBuilder {
* Return the history support if this query needs it (is a 'as of' type query).
*/
<T> CQueryHistorySupport getHistorySupport(SpiQuery<T> query) {
return query.getTemporalMode() != SpiQuery.TemporalMode.CURRENT ? historySupport : null;
return query.getTemporalMode().isHistory() ? historySupport : null;
}
/**
@@ -417,7 +424,9 @@ class CQueryBuilder {
BeanDescriptor<?> desc = request.getBeanDescriptor();
try {
PreparedStatement statement = connection.prepareStatement(sql);
// For SqlServer we need either "selectMethod=cursor" in the connection string or fetch explicitly a cursorable
// statement here by specifying ResultSet.CONCUR_UPDATABLE
PreparedStatement statement = connection.prepareStatement(sql,ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
predicates.bind(statement, connection);
ResultSet resultSet = statement.executeQuery();

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