mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
96
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20d538c428 | ||
|
|
01054bf470 | ||
|
|
affd720390 | ||
|
|
7d77df58c5 | ||
|
|
51fad2dec6 | ||
|
|
997378ce72 | ||
|
|
1a8a43603e | ||
|
|
d2c88bf7c6 | ||
|
|
0929147b08 | ||
|
|
ecab219635 | ||
|
|
7a974320ed | ||
|
|
1b7dc83a9a | ||
|
|
9827d9056f | ||
|
|
c8d5d9b552 | ||
|
|
dac55ab8e9 | ||
|
|
de75bcdaf9 | ||
|
|
5785124a2b | ||
|
|
4d1c0bfaa8 | ||
|
|
9a29c988cd | ||
|
|
80309f844e | ||
|
|
56ddc49ab4 | ||
|
|
1e7de83019 | ||
|
|
1db1f0312a | ||
|
|
d27bf14887 | ||
|
|
d4e7d4eebc | ||
|
|
30c44d0928 | ||
|
|
443c5dba58 | ||
|
|
f24b4dba71 | ||
|
|
a474880735 | ||
|
|
4cfab3a2de | ||
|
|
4807070147 | ||
|
|
e00aa479ae | ||
|
|
b665d8375c | ||
|
|
4e74c68603 | ||
|
|
2694aa3fe1 | ||
|
|
f7a28aa904 | ||
|
|
4bc2559e8e | ||
|
|
c2faf0e0a7 | ||
|
|
895b331e8f | ||
|
|
8890079b80 | ||
|
|
20dd33ebca | ||
|
|
e2a1489743 | ||
|
|
d6bab46ac3 | ||
|
|
573673ef2b | ||
|
|
eb69499ca3 | ||
|
|
d9323b8ab6 | ||
|
|
a01ba128e5 | ||
|
|
fdc67b3ce5 | ||
|
|
5d433ff603 | ||
|
|
a3f9c4c889 | ||
|
|
769fc485ec | ||
|
|
61ef4fa420 | ||
|
|
8cf37e2f0a | ||
|
|
80a764a448 | ||
|
|
6621c5dfbb | ||
|
|
5e7dda1ec8 | ||
|
|
7bc6368f69 | ||
|
|
fb5a69adea | ||
|
|
0e7607bb27 | ||
|
|
1098b572fe | ||
|
|
7a6662a2dc | ||
|
|
1c6c812d4d | ||
|
|
e58a85f0af | ||
|
|
e3551152bb | ||
|
|
5138d1723f | ||
|
|
9b5ab542d7 | ||
|
|
f0bc4c786c | ||
|
|
b98739bb49 | ||
|
|
fcadc63338 | ||
|
|
3574479627 | ||
|
|
cecf2fc650 | ||
|
|
a1dab24414 | ||
|
|
c12ea28ea9 | ||
|
|
a2b418df8b | ||
|
|
2fb533a606 | ||
|
|
5d89897bbd | ||
|
|
9640e02b6a | ||
|
|
8a0ef33f79 | ||
|
|
d2ece76ca8 | ||
|
|
3495d03706 | ||
|
|
bc90fa50d7 | ||
|
|
777fc0ff30 | ||
|
|
9413f72939 | ||
|
|
1416647d04 | ||
|
|
63bddb4d54 | ||
|
|
6fddd418a1 | ||
|
|
faba55d772 | ||
|
|
ac6db11e3d | ||
|
|
ca7d8ecfc1 | ||
|
|
46eeee68c9 | ||
|
|
c1487daea8 | ||
|
|
b329ae42db | ||
|
|
13429daddc | ||
|
|
98a80295fe | ||
|
|
f8587368ee | ||
|
|
8ae2ae7f8b |
@@ -44,4 +44,3 @@ Goto [https://ebean.io/docs/](https://ebean.io/docs/)
|
||||
## Other versions
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-agent) - ebean-agent
|
||||
* [](https://maven-badges.herokuapp.com/maven-central/io.ebean/ebean-maven-plugin) - ebean-maven-plugin
|
||||
|
||||
|
||||
@@ -3,27 +3,27 @@
|
||||
|
||||
<parent>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>java8-parent</artifactId>
|
||||
<version>1.3</version>
|
||||
<artifactId>java8-oss</artifactId>
|
||||
<version>2.1</version>
|
||||
</parent>
|
||||
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>11.44.1</version>
|
||||
<version>12.1.3</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ebean</name>
|
||||
<url>http://ebean-orm.github.io/</url>
|
||||
|
||||
<properties>
|
||||
<jackson-core.version>2.9.9</jackson-core.version>
|
||||
<jackson-databind.version>2.9.9.2</jackson-databind.version>
|
||||
<jackson-core.version>2.10.0</jackson-core.version>
|
||||
<jackson-databind.version>2.10.0</jackson-databind.version>
|
||||
<snakeyaml.version>1.21</snakeyaml.version>
|
||||
</properties>
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-11.44.1</tag>
|
||||
<tag>ebean-12.1.3</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
@@ -118,7 +118,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-annotation</artifactId>
|
||||
<version>4.12</version>
|
||||
<version>6.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -133,16 +133,10 @@
|
||||
<version>4.6.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>avaje-classpath-scanner</artifactId>
|
||||
<version>3.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-migration</artifactId>
|
||||
<version>11.20.1</version>
|
||||
<version>12.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -199,7 +193,7 @@
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jaxb</groupId>
|
||||
<artifactId>jaxb-runtime</artifactId>
|
||||
<version>2.4.0-b180830.0438</version>
|
||||
<version>2.3.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -301,10 +295,9 @@
|
||||
|
||||
<dependency>
|
||||
<groupId>org.avaje.composite</groupId>
|
||||
<artifactId>avaje-composite-testing</artifactId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>1.1</version>
|
||||
<scope>test</scope>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -330,7 +323,7 @@
|
||||
<plugin>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-maven-plugin</artifactId>
|
||||
<version>11.44.1</version>
|
||||
<version>12.1.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
@@ -387,9 +380,9 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9.1</version>
|
||||
<version>3.1.1</version>
|
||||
<configuration>
|
||||
<doctitle>Ebean 11</doctitle>
|
||||
<doctitle>Ebean 12</doctitle>
|
||||
<overview>src/main/java/io/ebean/overview.html</overview>
|
||||
<source>1.8</source>
|
||||
<doclet>org.avaje.doclet.PygmentsDoclet</doclet>
|
||||
@@ -399,6 +392,8 @@
|
||||
<artifactId>pygments-doclet</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</docletArtifact>
|
||||
<!-- for people using JDK11, our javadoc doclet isn't 11 compatible yet -->
|
||||
<!-- <javadocExecutable>${env.JAVA8_HOME}/bin/javadoc</javadocExecutable>-->
|
||||
<additionalparam>
|
||||
-Xdoclint:none
|
||||
-include-basedir ${project.basedir}
|
||||
|
||||
@@ -2,7 +2,6 @@ package io.ebean;
|
||||
|
||||
import io.ebean.annotation.TxIsolation;
|
||||
import io.ebean.cache.ServerCacheManager;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.plugin.Property;
|
||||
import io.ebean.text.csv.CsvReader;
|
||||
import io.ebean.text.json.JsonContext;
|
||||
@@ -396,15 +395,6 @@ public class DB {
|
||||
* called then no optimistic locking is performed (internally ConcurrencyMode.NONE is used).
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>{@link DatabaseConfig#setUpdatesDeleteMissingChildren(boolean)}: </b> When cascade saving to a
|
||||
* OneToMany or ManyToMany the updatesDeleteMissingChildren setting controls if any other children
|
||||
* that are in the database but are not in the collection are deleted.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>{@link DatabaseConfig#setUpdateChangesOnly(boolean)}: </b> The updateChangesOnly setting
|
||||
* controls if only the changed properties are included in the update or if all the loaded
|
||||
* properties are included instead.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // A 'stateless update' example
|
||||
@@ -414,9 +404,6 @@ public class DB {
|
||||
* database.update(customer);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see DatabaseConfig#setUpdatesDeleteMissingChildren(boolean)
|
||||
* @see DatabaseConfig#setUpdateChangesOnly(boolean)
|
||||
*/
|
||||
public static void update(Object bean) throws OptimisticLockException {
|
||||
getDefault().update(bean);
|
||||
@@ -1215,6 +1202,13 @@ public class DB {
|
||||
return getDefault().getBeanState(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the Id property for a given bean.
|
||||
*/
|
||||
public static Object getBeanId(Object bean) {
|
||||
return getDefault().getBeanId(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the manager of the level 2 cache ("L2" cache).
|
||||
*/
|
||||
|
||||
@@ -1196,17 +1196,6 @@ public interface Database {
|
||||
* <b>Optimistic Locking: </b> Note that if the version property is not set when update() is
|
||||
* called then no optimistic locking is performed (internally ConcurrencyMode.NONE is used).
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>{@link ServerConfig#setUpdatesDeleteMissingChildren(boolean)}: </b> When cascade saving to a
|
||||
* OneToMany or ManyToMany the updatesDeleteMissingChildren setting controls if any other children
|
||||
* that are in the database but are not in the collection are deleted.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>{@link ServerConfig#setUpdateChangesOnly(boolean)}: </b> The updateChangesOnly setting
|
||||
* controls if only the changed properties are included in the update or if all the loaded
|
||||
* properties are included instead.
|
||||
* </p>
|
||||
* <p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // A 'stateless update' example
|
||||
@@ -1216,9 +1205,6 @@ public interface Database {
|
||||
* database.update(customer);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see ServerConfig#setUpdatesDeleteMissingChildren(boolean)
|
||||
* @see ServerConfig#setUpdateChangesOnly(boolean)
|
||||
*/
|
||||
void update(Object bean) throws OptimisticLockException;
|
||||
|
||||
@@ -1227,16 +1213,6 @@ public interface Database {
|
||||
*/
|
||||
void update(Object bean, Transaction transaction) throws OptimisticLockException;
|
||||
|
||||
/**
|
||||
* Update a bean additionally specifying a transaction and the deleteMissingChildren setting.
|
||||
*
|
||||
* @param bean the bean to update
|
||||
* @param transaction the transaction to use (can be null).
|
||||
* @param deleteMissingChildren specify false if you do not want 'missing children' of a OneToMany
|
||||
* or ManyToMany to be automatically deleted.
|
||||
*/
|
||||
void update(Object bean, Transaction transaction, boolean deleteMissingChildren) throws OptimisticLockException;
|
||||
|
||||
/**
|
||||
* Update a collection of beans. If there is no current transaction one is created and used to
|
||||
* update all the beans in the collection.
|
||||
|
||||
@@ -548,9 +548,6 @@ public final class Ebean {
|
||||
* DB.update(customer);
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @see ServerConfig#setUpdatesDeleteMissingChildren(boolean)
|
||||
* @see ServerConfig#setUpdateChangesOnly(boolean)
|
||||
*/
|
||||
public static void update(Object bean) throws OptimisticLockException {
|
||||
serverMgr.getDefaultServer().update(bean);
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package io.ebean;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* An empty PagedList.
|
||||
* <p>
|
||||
* For use in application code when we need to return a PagedList but don't want to
|
||||
* execute a query.
|
||||
* </p>
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* PagedList<Customer> empty = PagedList.emptyList();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public class EmptyPagedList<T> implements PagedList<T> {
|
||||
|
||||
@Override
|
||||
public void loadCount() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Future<Integer> getFutureCount() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<T> getList() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTotalCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTotalPageCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPageSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPageIndex() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPrev() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayXtoYofZ(String to, String of) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* The extended API for Database.
|
||||
@@ -107,6 +108,24 @@ public interface ExtendedServer {
|
||||
@Nonnull
|
||||
<T> QueryIterator<T> findIterate(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Return the query result as a Stream using a single persistence context.
|
||||
* <p>
|
||||
* Note that the stream needs to be closed so use with try with resources.
|
||||
* </p>
|
||||
*/
|
||||
@Nonnull
|
||||
<T> Stream<T> findStream(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Return the query result as a Stream (with multiple persistence contexts).
|
||||
* <p>
|
||||
* Note that the stream needs to be closed so use with try with resources.
|
||||
* </p>
|
||||
*/
|
||||
@Nonnull
|
||||
<T> Stream<T> findLargeStream(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query visiting the each bean one at a time.
|
||||
* <p>
|
||||
|
||||
@@ -60,6 +60,13 @@ import java.util.concurrent.Future;
|
||||
*/
|
||||
public interface PagedList<T> {
|
||||
|
||||
/**
|
||||
* Return an empty PagedList.
|
||||
*/
|
||||
static <B> PagedList<B> emptyList() {
|
||||
return new EmptyPagedList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate the loading of the total row count in the background.
|
||||
* <pre>{@code
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Object relational query for finding a List, Set, Map or single entity bean.
|
||||
@@ -702,6 +703,49 @@ public interface Query<T> {
|
||||
@Nonnull
|
||||
QueryIterator<T> findIterate();
|
||||
|
||||
/**
|
||||
* Execute the query returning the result as a Stream.
|
||||
* <p>
|
||||
* Note that this will hold all resulting beans in memory using a single
|
||||
* persistence context. Use findLargeStream() for queries that expect to
|
||||
* return a large number of results.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // use try with resources to ensure Stream is closed
|
||||
*
|
||||
* try (Stream<Customer> stream = query.findStream()) {
|
||||
* stream
|
||||
* .map(...)
|
||||
* .collect(...);
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@Nonnull
|
||||
Stream<T> findStream();
|
||||
|
||||
/**
|
||||
* Execute the query returning the result as a Stream.
|
||||
* <p>
|
||||
* Note that this uses multiple persistence contexts such that we can use
|
||||
* it with a large number of results.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // use try with resources to ensure Stream is closed
|
||||
*
|
||||
* try (Stream<Customer> stream = query.findLargeStream()) {
|
||||
* stream
|
||||
* .map(...)
|
||||
* .collect(...);
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@Nonnull
|
||||
Stream<T> findLargeStream();
|
||||
|
||||
/**
|
||||
* Execute the query processing the beans one at a time.
|
||||
* <p>
|
||||
|
||||
@@ -20,29 +20,23 @@ class XServiceProvider {
|
||||
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?");
|
||||
return loadFirstService(SpiFetchGroupService.class);
|
||||
}
|
||||
|
||||
private static SpiRawSqlService initRawSql() {
|
||||
|
||||
Iterator<SpiRawSqlService> loader = ServiceLoader.load(SpiRawSqlService.class).iterator();
|
||||
if (loader.hasNext()) {
|
||||
return loader.next();
|
||||
}
|
||||
throw new IllegalStateException("No service implementation found for SpiRawSqlService?");
|
||||
return loadFirstService(SpiRawSqlService.class);
|
||||
}
|
||||
|
||||
private static SpiProfileLocationFactory initProfileLocation() {
|
||||
return loadFirstService(SpiProfileLocationFactory.class);
|
||||
}
|
||||
|
||||
Iterator<SpiProfileLocationFactory> loader = ServiceLoader.load(SpiProfileLocationFactory.class).iterator();
|
||||
private static <T> T loadFirstService(Class<T> cls) {
|
||||
Iterator<T> loader = ServiceLoader.load(cls).iterator();
|
||||
if (loader.hasNext()) {
|
||||
return loader.next();
|
||||
}
|
||||
throw new IllegalStateException("No service implementation found for SpiProfileLocationFactory?");
|
||||
throw new IllegalStateException("No service implementation found for " + cls);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -83,10 +83,13 @@ public abstract class AbstractNamingConvention implements NamingConvention {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSequenceName(String tableName, String pkColumn) {
|
||||
String s = TABLE_REPLACE.matcher(sequenceFormat).replaceAll(Matcher.quoteReplacement(tableName));
|
||||
public String getSequenceName(String rawTableName, String pkColumn) {
|
||||
final String tableNameUnquoted = databasePlatform.unQuote(rawTableName);
|
||||
String s = TABLE_REPLACE.matcher(sequenceFormat).replaceAll(Matcher.quoteReplacement(tableNameUnquoted));
|
||||
if (pkColumn == null) {
|
||||
pkColumn = "";
|
||||
} else {
|
||||
pkColumn = databasePlatform.unQuote(pkColumn);
|
||||
}
|
||||
return COLUMN_REPLACE.matcher(s).replaceAll(Matcher.quoteReplacement(pkColumn));
|
||||
}
|
||||
|
||||
@@ -36,8 +36,7 @@ public class MatchingNamingConvention extends AbstractNamingConvention {
|
||||
|
||||
@Override
|
||||
public TableName getTableNameByConvention(Class<?> beanClass) {
|
||||
|
||||
return new TableName(getCatalog(), getSchema(), beanClass.getSimpleName());
|
||||
return new TableName(quoteIdentifiers(getCatalog()), quoteIdentifiers(getSchema()), quoteIdentifiers(beanClass.getSimpleName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -350,21 +350,11 @@ public class ServerConfig {
|
||||
*/
|
||||
private DbConstraintNaming constraintNaming = new DbConstraintNaming();
|
||||
|
||||
/**
|
||||
* Behaviour of update to include on the change properties.
|
||||
*/
|
||||
private boolean updateChangesOnly = true;
|
||||
|
||||
/**
|
||||
* Behaviour of updates in JDBC batch to by default include all properties.
|
||||
*/
|
||||
private boolean updateAllPropertiesInBatch;
|
||||
|
||||
/**
|
||||
* Default behaviour for updates when cascade save on a O2M or M2M to delete any missing children.
|
||||
*/
|
||||
private boolean updatesDeleteMissingChildren = true;
|
||||
|
||||
/**
|
||||
* Database platform configuration.
|
||||
*/
|
||||
@@ -2409,20 +2399,6 @@ public class ServerConfig {
|
||||
this.skipCacheAfterWrite = skipCacheAfterWrite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true to only update changed properties.
|
||||
*/
|
||||
public boolean isUpdateChangesOnly() {
|
||||
return updateChangesOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to only update changed properties.
|
||||
*/
|
||||
public void setUpdateChangesOnly(boolean updateChangesOnly) {
|
||||
this.updateChangesOnly = updateChangesOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if updates in JDBC batch default to include all properties by default.
|
||||
*/
|
||||
@@ -2442,22 +2418,6 @@ public class ServerConfig {
|
||||
this.updateAllPropertiesInBatch = updateAllPropertiesInBatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if updates by default delete missing children when cascading save to a OneToMany or
|
||||
* ManyToMany. When not set this defaults to true.
|
||||
*/
|
||||
public boolean isUpdatesDeleteMissingChildren() {
|
||||
return updatesDeleteMissingChildren;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if updates by default delete missing children when cascading save to a OneToMany or
|
||||
* ManyToMany. When not set this defaults to true.
|
||||
*/
|
||||
public void setUpdatesDeleteMissingChildren(boolean updatesDeleteMissingChildren) {
|
||||
this.updatesDeleteMissingChildren = updatesDeleteMissingChildren;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if query statistics should be collected by ObjectGraphNode.
|
||||
*/
|
||||
@@ -2966,10 +2926,6 @@ public class ServerConfig {
|
||||
|
||||
skipCacheAfterWrite = p.getBoolean("skipCacheAfterWrite", skipCacheAfterWrite);
|
||||
updateAllPropertiesInBatch = p.getBoolean("updateAllPropertiesInBatch", updateAllPropertiesInBatch);
|
||||
updateChangesOnly = p.getBoolean("updateChangesOnly", updateChangesOnly);
|
||||
|
||||
boolean defaultDeleteMissingChildren = p.getBoolean("defaultDeleteMissingChildren", updatesDeleteMissingChildren);
|
||||
updatesDeleteMissingChildren = p.getBoolean("updatesDeleteMissingChildren", defaultDeleteMissingChildren);
|
||||
|
||||
if (p.get("batch.mode") != null || p.get("persistBatching") != null) {
|
||||
throw new IllegalArgumentException("Property 'batch.mode' or 'persistBatching' is being set but no longer used. Please change to use 'persistBatchMode'");
|
||||
|
||||
@@ -75,6 +75,11 @@ public class DatabasePlatform {
|
||||
|
||||
protected boolean caseSensitiveCollation = true;
|
||||
|
||||
/**
|
||||
* Set true if the Database support LIMIT clause on sql update.
|
||||
*/
|
||||
protected boolean inlineSqlUpdateLimit;
|
||||
|
||||
/**
|
||||
* For limit/offset, row_number etc limiting of SQL queries.
|
||||
*/
|
||||
@@ -143,8 +148,6 @@ public class DatabasePlatform {
|
||||
|
||||
protected String columnAliasPrefix = "c";
|
||||
|
||||
protected String tableAliasPlaceHolder = "${ta}";
|
||||
|
||||
/**
|
||||
* Use a BackTick ` at the beginning and end of table or column names that you
|
||||
* want to use quoted identifiers for. The backticks get converted to the
|
||||
@@ -171,13 +174,14 @@ public class DatabasePlatform {
|
||||
/**
|
||||
* Characters escaped for startsWith, endsWith and contains.
|
||||
*/
|
||||
protected char[] likeSpecialCharacters = { '%', '_', '|' };
|
||||
protected char[] likeSpecialCharacters = {'%', '_', '|'};
|
||||
|
||||
protected DbEncrypt dbEncrypt;
|
||||
|
||||
protected boolean idInExpandedForm;
|
||||
|
||||
protected boolean selectCountWithAlias;
|
||||
protected boolean selectCountWithColumnAlias;
|
||||
|
||||
/**
|
||||
* If set then use the FORWARD ONLY hint when creating ResultSets for
|
||||
@@ -187,7 +191,7 @@ public class DatabasePlatform {
|
||||
|
||||
/**
|
||||
* If set then use the CONCUR_UPDATABLE hint when creating ResultSets.
|
||||
*
|
||||
* <p>
|
||||
* This is {@code false} for HANA
|
||||
*/
|
||||
protected boolean supportsResultSetConcurrencyModeUpdatable = true;
|
||||
@@ -339,6 +343,13 @@ public class DatabasePlatform {
|
||||
return supportsSavepointId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the platform supports LIMIT with sql update.
|
||||
*/
|
||||
public boolean isInlineSqlUpdateLimit() {
|
||||
return inlineSqlUpdateLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum table name length.
|
||||
* <p>
|
||||
@@ -370,11 +381,11 @@ public class DatabasePlatform {
|
||||
/**
|
||||
* Return a DB Sequence based IdGenerator.
|
||||
*
|
||||
* @param be the BackgroundExecutor that can be used to load the sequence if
|
||||
* desired
|
||||
* @param ds the DataSource
|
||||
* @param stepSize the sequence allocation size as defined by mapping (defaults to 50)
|
||||
* @param seqName the name of the sequence
|
||||
* @param be the BackgroundExecutor that can be used to load the sequence if
|
||||
* desired
|
||||
* @param ds the DataSource
|
||||
* @param stepSize the sequence allocation size as defined by mapping (defaults to 50)
|
||||
* @param seqName the name of the sequence
|
||||
*/
|
||||
public PlatformIdGenerator createSequenceIdGenerator(BackgroundExecutor be, DataSource ds, int stepSize, String seqName) {
|
||||
return null;
|
||||
@@ -466,20 +477,6 @@ public class DatabasePlatform {
|
||||
this.columnAliasPrefix = columnAliasPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the table alias placeholder.
|
||||
*/
|
||||
public String getTableAliasPlaceHolder() {
|
||||
return tableAliasPlaceHolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the table alias placeholder.
|
||||
*/
|
||||
public void setTableAliasPlaceHolder(String tableAliasPlaceHolder) {
|
||||
this.tableAliasPlaceHolder = tableAliasPlaceHolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the close quote for quoted identifiers.
|
||||
*/
|
||||
@@ -659,7 +656,7 @@ public class DatabasePlatform {
|
||||
if (dbName != null && !dbName.isEmpty()) {
|
||||
if (dbName.startsWith(openQuote)) {
|
||||
// trim off the open and close quotes
|
||||
return dbName.substring(1, dbName.length()-1);
|
||||
return dbName.substring(1, dbName.length() - 1);
|
||||
}
|
||||
}
|
||||
return dbName;
|
||||
@@ -672,6 +669,14 @@ public class DatabasePlatform {
|
||||
return selectCountWithAlias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if select count with subquery needs column alias (SQL Server).
|
||||
*/
|
||||
public boolean isSelectCountWithColumnAlias() {
|
||||
return selectCountWithColumnAlias;
|
||||
}
|
||||
|
||||
|
||||
public String completeSql(String sql, Query<?> query) {
|
||||
if (query.isForUpdate()) {
|
||||
sql = withForUpdate(sql, query.getForUpdateMode());
|
||||
@@ -786,7 +791,7 @@ public class DatabasePlatform {
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
char ch = value.charAt(i);
|
||||
boolean escaped = false;
|
||||
for (char escapeChar: likeSpecialCharacters) {
|
||||
for (char escapeChar : likeSpecialCharacters) {
|
||||
if (ch == escapeChar) {
|
||||
if (sb == null) {
|
||||
sb = new StringBuilder(value.substring(0, i));
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Clickhouse DB specific support.
|
||||
*/
|
||||
package io.ebean.config.dbplatform.clickhouse;
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Cockroach DB specific support.
|
||||
*/
|
||||
package io.ebean.config.dbplatform.cockroach;
|
||||
@@ -24,6 +24,7 @@ public class H2Platform extends DatabasePlatform {
|
||||
this.historySupport = new H2HistorySupport();
|
||||
this.nativeUuidType = true;
|
||||
this.supportsDeleteTableAlias = true;
|
||||
this.inlineSqlUpdateLimit = true;
|
||||
this.dbDefaultValue.setNow("now()");
|
||||
this.columnAliasPrefix = null;
|
||||
this.exceptionTranslator =
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.ebean.config.dbplatform.mysql;
|
||||
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.config.dbplatform.DbType;
|
||||
|
||||
public class MySql55Platform extends MySqlPlatform {
|
||||
|
||||
public MySql55Platform() {
|
||||
super();
|
||||
this.platform = Platform.MYSQL55;
|
||||
dbTypeMap.put(DbType.TIMESTAMP, new DbPlatformType("datetime"));
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ public class MySqlPlatform extends DatabasePlatform {
|
||||
this.useExtraTransactionOnIterateSecondaryQueries = true;
|
||||
this.selectCountWithAlias = true;
|
||||
this.supportsSavepointId = false;
|
||||
this.inlineSqlUpdateLimit = true;
|
||||
this.dbEncrypt = new MySqlDbEncrypt();
|
||||
this.historySupport = new MySqlHistorySupport();
|
||||
this.columnAliasPrefix = null;
|
||||
@@ -51,8 +52,8 @@ public class MySqlPlatform extends DatabasePlatform {
|
||||
this.openQuote = "`";
|
||||
this.closeQuote = "`";
|
||||
// use pipe for escaping as it depends if mysql runs in no_backslash_escapes or not.
|
||||
this.likeClauseRaw = "like ? escape ''";
|
||||
this.likeClauseEscaped = "like ? escape '|'";
|
||||
this.likeClauseRaw = "like ? escape''";
|
||||
this.likeClauseEscaped = "like ? escape'|'";
|
||||
|
||||
this.forwardOnlyHintOnFindIterate = true;
|
||||
this.booleanDbType = Types.BIT;
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* NuoDB specific support.
|
||||
*/
|
||||
package io.ebean.config.dbplatform.nuodb;
|
||||
@@ -28,6 +28,7 @@ abstract class SqlServerBasePlatform extends DatabasePlatform {
|
||||
this.persistBatchOnCascade = PersistBatch.NONE;
|
||||
this.idInExpandedForm = true;
|
||||
this.selectCountWithAlias = true;
|
||||
this.selectCountWithColumnAlias = true;
|
||||
this.sqlLimiter = new SqlServerSqlLimiter();
|
||||
this.basicSqlLimiter = new SqlServerBasicSqlLimiter();
|
||||
this.historySupport = new SqlServerHistorySupport();
|
||||
|
||||
@@ -136,6 +136,23 @@ public interface DbMigration {
|
||||
*/
|
||||
void setGeneratePendingDrop(String generatePendingDrop);
|
||||
|
||||
/**
|
||||
* Set to true if ALTER TABLE ADD FOREIGN KEY should be generated with an option to skip validation.
|
||||
* <p>
|
||||
* Currently this is only useful for Postgres DDL adding the <code>NOT VALID</code> option.
|
||||
* </p>
|
||||
*/
|
||||
void setAddForeignKeySkipCheck(boolean addForeignKeySkipCheck);
|
||||
|
||||
/**
|
||||
* Set the lock timeout to be included with the DDL generation.
|
||||
* <p>
|
||||
* Currently this is only useful for Postgres migrations adding a <code>set lock_timeout</code>
|
||||
* statement to the generated database migration.
|
||||
* </p>
|
||||
*/
|
||||
void setLockTimeout(int seconds);
|
||||
|
||||
/**
|
||||
* Add an additional platform to write the migration DDL.
|
||||
* <p>
|
||||
@@ -198,6 +215,7 @@ public interface DbMigration {
|
||||
* An "init" migration can only be executed and used on a database that has had no
|
||||
* prior migrations run on it.
|
||||
* </p>
|
||||
*
|
||||
* @return the version of the generated migration
|
||||
*/
|
||||
String generateInitMigration() throws IOException;
|
||||
|
||||
@@ -41,6 +41,11 @@ public interface BeanPersistRequest<T> {
|
||||
*/
|
||||
Set<String> getUpdatedProperties();
|
||||
|
||||
/**
|
||||
* Flags set for dirty properties (used by ElasticSearch integration).
|
||||
*/
|
||||
boolean[] getDirtyProperties();
|
||||
|
||||
/**
|
||||
* Return true for an update request if at least one of dirty properties is contained
|
||||
* in the given set of property names.
|
||||
|
||||
@@ -16,6 +16,18 @@ public interface MetaInfoManager {
|
||||
*/
|
||||
ServerMetrics collectMetrics();
|
||||
|
||||
/**
|
||||
* Collect the metrics in raw JSON form.
|
||||
* <pre>{@code
|
||||
*
|
||||
* String metricsJson = database.getMetaInfoManager()
|
||||
* .collectMetricsAsJson()
|
||||
* .json();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
ServerMetricsAsJson collectMetricsAsJson();
|
||||
|
||||
/**
|
||||
* Collect query plans.
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,9 @@ package io.ebean.meta;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
|
||||
/**
|
||||
* Meta data for captured query plan.
|
||||
*/
|
||||
public interface MetaQueryPlan {
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Collect the metrics in raw JSON form.
|
||||
*/
|
||||
public interface ServerMetricsAsJson {
|
||||
|
||||
/**
|
||||
* Set to false to exclude profile location.
|
||||
*/
|
||||
ServerMetricsAsJson withLocation(boolean withLocation);
|
||||
|
||||
/**
|
||||
* Set to false to exclude SQL for query metrics.
|
||||
*/
|
||||
ServerMetricsAsJson withSql(boolean withSql);
|
||||
|
||||
/**
|
||||
* Set to false to exclude SQL hash.
|
||||
*/
|
||||
ServerMetricsAsJson withHash(boolean withHash);
|
||||
|
||||
/**
|
||||
* Set the sort property - see SortMetric
|
||||
* @see SortMetric
|
||||
*/
|
||||
ServerMetricsAsJson withSort(Comparator<MetaTimedMetric> sortBy);
|
||||
|
||||
/**
|
||||
* Set the new line character to use.
|
||||
*/
|
||||
ServerMetricsAsJson withNewLine(boolean withNewLine);
|
||||
|
||||
/**
|
||||
* Return the metrics in raw JSON.
|
||||
*/
|
||||
String json();
|
||||
}
|
||||
@@ -11,4 +11,9 @@ public interface TimedMetricStats extends MetaTimedMetric {
|
||||
* Additionally set the location.
|
||||
*/
|
||||
void setLocation(String location);
|
||||
|
||||
/**
|
||||
* Override the name based on profile location.
|
||||
*/
|
||||
void setName(String name);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import io.ebean.bean.EntityBeanIntercept;
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
|
||||
/**
|
||||
* Controls the loading of reference objects for a query instance.
|
||||
@@ -47,6 +48,11 @@ public interface LoadContext {
|
||||
*/
|
||||
void register(String path, EntityBeanIntercept ebi);
|
||||
|
||||
/**
|
||||
* Register a Bean with inheritance.
|
||||
*/
|
||||
void register(String path, EntityBeanIntercept ebi, BeanPropertyAssocOne<?> property);
|
||||
|
||||
/**
|
||||
* Register a collection for lazy loading.
|
||||
*/
|
||||
|
||||
@@ -169,7 +169,7 @@ public class LoadManyRequest extends LoadRequest {
|
||||
Object parentId = desc.getId(ownerBean);
|
||||
logger.debug("BeanCollection after lazy load was empty. type:" + ownerBean.getClass().getName() + " id:" + parentId + " owner:" + ownerBean);
|
||||
}
|
||||
} else if (isLoadCache()) {
|
||||
} else if (isLoadCache() && many.isUseCache()) {
|
||||
Object parentId = desc.getId(bc.getOwnerBean());
|
||||
desc.cacheManyPropPut(many, bc, parentId);
|
||||
}
|
||||
|
||||
@@ -458,7 +458,7 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
/**
|
||||
* Set the query to be a delete query.
|
||||
*/
|
||||
void setDelete();
|
||||
void setupForDeleteOrUpdate(boolean deleteRequest);
|
||||
|
||||
/**
|
||||
* Set the query to be delete by ids due to cascading delete.
|
||||
|
||||
@@ -33,4 +33,9 @@ public interface SpiSqlUpdate extends SqlUpdate {
|
||||
* Reset bind position to be ready for another bind execute.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Return a copy of the SqlUpdate with empty bind parameters.
|
||||
*/
|
||||
SpiSqlUpdate copy();
|
||||
}
|
||||
|
||||
@@ -75,9 +75,10 @@ public interface SpiTransaction extends Transaction {
|
||||
boolean isRegisteredDeleteBean(Integer hash);
|
||||
|
||||
/**
|
||||
* Unregister the persisted bean.
|
||||
* Unregister the persisted beans. Expected after persisting top level beans
|
||||
* and hence including all beans that were cascade persisted.
|
||||
*/
|
||||
void unregisterBean(Object bean);
|
||||
void unregisterBeans();
|
||||
|
||||
/**
|
||||
* Return true if this is a bean that has already been persisted in the
|
||||
|
||||
@@ -189,8 +189,8 @@ public abstract class SpiTransactionProxy implements SpiTransaction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterBean(Object bean) {
|
||||
transaction.unregisterBean(bean);
|
||||
public void unregisterBeans() {
|
||||
transaction.unregisterBeans();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -62,6 +62,16 @@ public interface SpiJsonWriter extends JsonWriter {
|
||||
*/
|
||||
void endAssocMany();
|
||||
|
||||
/**
|
||||
* Start a Many.
|
||||
*/
|
||||
void beginAssocManyMap(String name, boolean elementCollection);
|
||||
|
||||
/**
|
||||
* End a Many.
|
||||
*/
|
||||
void endAssocManyMap(boolean elementCollection);
|
||||
|
||||
/**
|
||||
* Write value using underlying Jaskson object mapper if available.
|
||||
*/
|
||||
|
||||
@@ -257,7 +257,9 @@ public class DdlGenerator {
|
||||
protected void runResourceScript(Connection connection, String sqlScript) throws IOException {
|
||||
if (sqlScript != null) {
|
||||
try (InputStream is = getClassLoader().getResourceAsStream(sqlScript)) {
|
||||
if (is != null) {
|
||||
if (is == null) {
|
||||
log.warn("sql script {} was not found as a resource", sqlScript);
|
||||
} else {
|
||||
String content = readContent(new InputStreamReader(is));
|
||||
runScript(connection, false, content, sqlScript);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import io.ebean.config.dbplatform.db2.DB2Platform;
|
||||
import io.ebean.config.dbplatform.h2.H2Platform;
|
||||
import io.ebean.config.dbplatform.hana.HanaPlatform;
|
||||
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
|
||||
import io.ebean.config.dbplatform.mysql.MySql55Platform;
|
||||
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
|
||||
import io.ebean.config.dbplatform.nuodb.NuoDbPlatform;
|
||||
import io.ebean.config.dbplatform.oracle.OraclePlatform;
|
||||
@@ -25,6 +26,7 @@ import io.ebean.config.dbplatform.sqlserver.SqlServer17Platform;
|
||||
import io.ebean.dbmigration.DbMigration;
|
||||
import io.ebean.migration.MigrationVersion;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
|
||||
import io.ebeaninternal.dbmigration.migration.Migration;
|
||||
import io.ebeaninternal.dbmigration.migrationreader.MigrationXmlWriter;
|
||||
@@ -105,6 +107,8 @@ public class DefaultDbMigration implements DbMigration {
|
||||
protected String version;
|
||||
protected String name;
|
||||
protected String generatePendingDrop;
|
||||
private boolean addForeignKeySkipCheck;
|
||||
private int lockTimeoutSeconds;
|
||||
|
||||
protected boolean includeBuiltInPartitioning = true;
|
||||
|
||||
@@ -179,6 +183,16 @@ public class DefaultDbMigration implements DbMigration {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAddForeignKeySkipCheck(boolean addForeignKeySkipCheck) {
|
||||
this.addForeignKeySkipCheck = addForeignKeySkipCheck;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLockTimeout(int seconds) {
|
||||
this.lockTimeoutSeconds = seconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGeneratePendingDrop(String generatePendingDrop) {
|
||||
this.generatePendingDrop = generatePendingDrop;
|
||||
@@ -564,7 +578,8 @@ public class DefaultDbMigration implements DbMigration {
|
||||
} else if (databasePlatform != null) {
|
||||
// writer needs the current model to provide table/column details for
|
||||
// history ddl generation (triggers, history tables etc)
|
||||
DdlWrite write = new DdlWrite(new MConfiguration(), request.current);
|
||||
DdlOptions options = new DdlOptions(addForeignKeySkipCheck);
|
||||
DdlWrite write = new DdlWrite(new MConfiguration(), request.current, options);
|
||||
PlatformDdlWriter writer = createDdlWriter(databasePlatform);
|
||||
writer.processMigration(dbMigration, write, request.migrationDir, fullVersion);
|
||||
}
|
||||
@@ -622,8 +637,9 @@ public class DefaultDbMigration implements DbMigration {
|
||||
*/
|
||||
private void writeExtraPlatformDdl(String fullVersion, CurrentModel currentModel, Migration dbMigration, File writePath) throws IOException {
|
||||
|
||||
DdlOptions options = new DdlOptions(addForeignKeySkipCheck);
|
||||
for (Pair pair : platforms) {
|
||||
DdlWrite platformBuffer = new DdlWrite(new MConfiguration(), currentModel.read());
|
||||
DdlWrite platformBuffer = new DdlWrite(new MConfiguration(), currentModel.read(), options);
|
||||
PlatformDdlWriter platformWriter = createDdlWriter(pair.platform);
|
||||
File subPath = platformWriter.subPath(writePath, pair.prefix);
|
||||
platformWriter.processMigration(dbMigration, platformBuffer, subPath, fullVersion);
|
||||
@@ -631,7 +647,7 @@ public class DefaultDbMigration implements DbMigration {
|
||||
}
|
||||
|
||||
private PlatformDdlWriter createDdlWriter(DatabasePlatform platform) {
|
||||
return new PlatformDdlWriter(platform, serverConfig, migrationConfig);
|
||||
return new PlatformDdlWriter(platform, serverConfig, migrationConfig, lockTimeoutSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -787,6 +803,8 @@ public class DefaultDbMigration implements DbMigration {
|
||||
return new HsqldbPlatform();
|
||||
case POSTGRES:
|
||||
return new PostgresPlatform();
|
||||
case MYSQL55:
|
||||
return new MySql55Platform();
|
||||
case MYSQL:
|
||||
return new MySqlPlatform();
|
||||
case ORACLE:
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package io.ebeaninternal.dbmigration.ddlgeneration;
|
||||
|
||||
/**
|
||||
* Options used when generated DDL.
|
||||
*/
|
||||
public class DdlOptions {
|
||||
|
||||
private boolean foreignKeySkipCheck;
|
||||
|
||||
public DdlOptions(boolean foreignKeySkipCheck) {
|
||||
this.foreignKeySkipCheck = foreignKeySkipCheck;
|
||||
}
|
||||
|
||||
public DdlOptions() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if ADD FOREIGN KEY should use a skip check option.
|
||||
*/
|
||||
public boolean isForeignKeySkipCheck() {
|
||||
return foreignKeySkipCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if ADD FOREIGN KEY should use a skip check option.
|
||||
*/
|
||||
public void setForeignKeySkipCheck(boolean foreignKeySkipCheck) {
|
||||
this.foreignKeySkipCheck = foreignKeySkipCheck;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,17 +26,19 @@ public class DdlWrite {
|
||||
|
||||
private final DdlBuffer dropAll;
|
||||
|
||||
private final DdlOptions options;
|
||||
|
||||
/**
|
||||
* Create without any configuration or current model (no history support).
|
||||
*/
|
||||
public DdlWrite() {
|
||||
this(new MConfiguration(), new ModelContainer());
|
||||
this(new MConfiguration(), new ModelContainer(), new DdlOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create with a configuration.
|
||||
*/
|
||||
public DdlWrite(MConfiguration configuration, ModelContainer currentModel) {
|
||||
public DdlWrite(MConfiguration configuration, ModelContainer currentModel, DdlOptions options) {
|
||||
this.currentModel = currentModel;
|
||||
this.applyDropDependencies = new BaseDdlBuffer(configuration);
|
||||
this.apply = new BaseDdlBuffer(configuration);
|
||||
@@ -45,6 +47,14 @@ public class DdlWrite {
|
||||
this.applyHistoryTrigger = new BaseDdlBuffer(configuration);
|
||||
this.dropAllForeignKeys = new BaseDdlBuffer(configuration);
|
||||
this.dropAll = new BaseDdlBuffer(configuration);
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DDL options.
|
||||
*/
|
||||
public DdlOptions getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,7 @@ import io.ebean.config.dbplatform.DbHistorySupport;
|
||||
import io.ebean.config.dbplatform.IdType;
|
||||
import io.ebean.util.StringHelper;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.TableDdl;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.util.IndexSet;
|
||||
@@ -31,7 +32,6 @@ import io.ebeaninternal.dbmigration.model.MTable;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -462,7 +462,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
fkeyBuffer.appendStatement(platformDdl.createIndex(request.indexName(), tableName, request.cols()));
|
||||
}
|
||||
|
||||
alterTableAddForeignKey(fkeyBuffer, request);
|
||||
alterTableAddForeignKey(write.getOptions(), fkeyBuffer, request);
|
||||
fkeyBuffer.end();
|
||||
|
||||
write.dropAllForeignKeys().appendStatement(platformDdl.alterTableDropForeignKey(tableName, request.fkName()));
|
||||
@@ -473,9 +473,9 @@ public class BaseTableDdl implements TableDdl {
|
||||
write.dropAllForeignKeys().end();
|
||||
}
|
||||
|
||||
protected void alterTableAddForeignKey(DdlBuffer buffer, WriteForeignKey request) throws IOException {
|
||||
protected void alterTableAddForeignKey(DdlOptions options, DdlBuffer buffer, WriteForeignKey request) throws IOException {
|
||||
|
||||
buffer.appendStatement(platformDdl.alterTableAddForeignKey(request));
|
||||
buffer.appendStatement(platformDdl.alterTableAddForeignKey(options, request));
|
||||
}
|
||||
|
||||
protected void appendColumns(String[] columns, DdlBuffer buffer) throws IOException {
|
||||
@@ -635,7 +635,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
if (DdlHelp.isDropForeignKey(alterForeignKey.getColumnNames())) {
|
||||
writer.apply().appendStatement(platformDdl.alterTableDropForeignKey(alterForeignKey.getTableName(), alterForeignKey.getName()));
|
||||
} else {
|
||||
writer.apply().appendStatement(platformDdl.alterTableAddForeignKey(new WriteForeignKey(alterForeignKey)));
|
||||
writer.apply().appendStatement(platformDdl.alterTableAddForeignKey(writer.getOptions(), new WriteForeignKey(alterForeignKey)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -899,7 +899,7 @@ public class BaseTableDdl implements TableDdl {
|
||||
|
||||
protected void alterColumnAddForeignKey(DdlWrite writer, AlterColumn alterColumn) throws IOException {
|
||||
|
||||
alterTableAddForeignKey(writer.apply(), new WriteForeignKey(alterColumn));
|
||||
alterTableAddForeignKey(writer.getOptions(), writer.apply(), new WriteForeignKey(alterColumn));
|
||||
}
|
||||
|
||||
protected void alterColumnDropForeignKey(DdlWrite writer, AlterColumn alter) throws IOException {
|
||||
|
||||
+2
-1
@@ -4,6 +4,7 @@ import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -40,7 +41,7 @@ public class ClickHouseDdl extends PlatformDdl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String alterTableAddForeignKey(WriteForeignKey request) {
|
||||
public String alterTableAddForeignKey(DdlOptions options, WriteForeignKey request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,16 +42,6 @@ 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 createCheckConstraint(String ckName, String checkConstraint) {
|
||||
if (USE_CHECK_CONSTRAINT) {
|
||||
@@ -60,6 +50,7 @@ public class MySqlDdl extends PlatformDdl {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String alterTableAddCheckConstraint(String tableName, String checkConstraintName, String checkConstraint) {
|
||||
if (USE_CHECK_CONSTRAINT) {
|
||||
@@ -96,17 +87,13 @@ public class MySqlDdl extends PlatformDdl {
|
||||
|
||||
@Override
|
||||
public String alterColumnNotnull(String tableName, String columnName, boolean notnull) {
|
||||
|
||||
// can't alter itself - done in alterColumnBaseAttributes()
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String alterColumnDefaultValue(String tableName, String columnName, String defaultValue) {
|
||||
|
||||
String suffix = DdlHelp.isDropDefault(defaultValue) ? columnDropDefault : columnSetDefault + " " + convertDefaultValue(defaultValue);
|
||||
|
||||
// use alter
|
||||
return "alter table " + tableName + " alter " + columnName + " " + suffix;
|
||||
}
|
||||
|
||||
@@ -124,7 +111,6 @@ public class MySqlDdl extends PlatformDdl {
|
||||
boolean notnull = (alter.isNotnull() != null) ? alter.isNotnull() : Boolean.TRUE.equals(alter.isCurrentNotnull());
|
||||
String notnullClause = notnull ? " not null" : "";
|
||||
|
||||
// use modify
|
||||
return "alter table " + tableName + " modify " + columnName + " " + type + notnullClause;
|
||||
}
|
||||
|
||||
@@ -161,7 +147,7 @@ public class MySqlDdl extends PlatformDdl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addColumnComment(DdlBuffer apply, String table, String column, String comment) throws IOException {
|
||||
public void addColumnComment(DdlBuffer apply, String table, String column, String comment) {
|
||||
// alter comment currently not supported as it requires to repeat whole column definition
|
||||
}
|
||||
|
||||
@@ -182,7 +168,6 @@ public class MySqlDdl extends PlatformDdl {
|
||||
i++;
|
||||
}
|
||||
buffer.endOfStatement();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import io.ebean.util.StringHelper;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.BaseDdlHandler;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.util.PlatformTypeConverter;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.util.VowelRemover;
|
||||
@@ -106,6 +107,8 @@ public class PlatformDdl {
|
||||
|
||||
protected String dropColumnSuffix = "";
|
||||
|
||||
protected String addForeignKeySkipCheck = "";
|
||||
|
||||
/**
|
||||
* Set false for MsSqlServer to allow multiple nulls for OneToOne mapping.
|
||||
*/
|
||||
@@ -208,6 +211,13 @@ public class PlatformDdl {
|
||||
return inlineForeignKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default this does nothing returning null / no lock timeout.
|
||||
*/
|
||||
public String setLockTimeout(int lockTimeoutSeconds) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write all the table columns converting to platform types as necessary.
|
||||
*/
|
||||
@@ -405,7 +415,7 @@ public class PlatformDdl {
|
||||
/**
|
||||
* Add foreign key.
|
||||
*/
|
||||
public String alterTableAddForeignKey(WriteForeignKey request) {
|
||||
public String alterTableAddForeignKey(DdlOptions options, WriteForeignKey request) {
|
||||
|
||||
StringBuilder buffer = new StringBuilder(90);
|
||||
buffer
|
||||
@@ -418,6 +428,9 @@ public class PlatformDdl {
|
||||
.append(lowerTableName(request.refTable()));
|
||||
appendColumns(request.refCols(), buffer);
|
||||
appendForeignKeySuffix(request, buffer);
|
||||
if (options.isForeignKeySkipCheck()) {
|
||||
buffer.append(addForeignKeySkipCheck);
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,11 @@ public class PostgresDdl extends PlatformDdl {
|
||||
this.columnSetType = "type ";
|
||||
this.alterTableIfExists = "if exists ";
|
||||
this.columnSetNull = "drop not null";
|
||||
this.addForeignKeySkipCheck = " not valid";
|
||||
}
|
||||
|
||||
public String setLockTimeout(int lockTimeoutSeconds) {
|
||||
return "set lock_timeout = " + (lockTimeoutSeconds * 1000);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform;
|
||||
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions;
|
||||
|
||||
/**
|
||||
* DB2 platform specific DDL.
|
||||
@@ -25,7 +26,7 @@ public class SQLiteDdl extends PlatformDdl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String alterTableAddForeignKey(WriteForeignKey request) {
|
||||
public String alterTableAddForeignKey(DdlOptions options, WriteForeignKey request) {
|
||||
// not supported
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.ebeaninternal.dbmigration.model;
|
||||
import io.ebean.config.DbConstraintNaming;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.platform.DefaultConstraintMaxLength;
|
||||
import io.ebeaninternal.dbmigration.migration.ChangeSet;
|
||||
@@ -37,6 +38,8 @@ public class CurrentModel {
|
||||
|
||||
private DdlWrite write;
|
||||
|
||||
private DdlOptions ddlOptions = new DdlOptions();
|
||||
|
||||
/**
|
||||
* Construct with a given EbeanServer instance for DDL create all generation, not migration.
|
||||
*/
|
||||
@@ -63,6 +66,10 @@ public class CurrentModel {
|
||||
this.jaxbPresent = server.getServerConfig().getClassLoadConfig().isJavaxJAXBPresent();
|
||||
}
|
||||
|
||||
public DdlOptions getDdlOptions() {
|
||||
return ddlOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the model contains tables that are partitioned.
|
||||
*/
|
||||
@@ -183,7 +190,7 @@ public class CurrentModel {
|
||||
if (write == null) {
|
||||
ChangeSet createChangeSet = getChangeSet();
|
||||
|
||||
write = new DdlWrite(new MConfiguration(), model);
|
||||
write = new DdlWrite(new MConfiguration(), model, ddlOptions);
|
||||
|
||||
DdlHandler handler = handler();
|
||||
handler.generateProlog(write);
|
||||
|
||||
@@ -261,6 +261,13 @@ public class MColumn {
|
||||
return !draftOnly && !historyExclude;
|
||||
}
|
||||
|
||||
public void clearForeignKey() {
|
||||
this.references = null;
|
||||
this.foreignKeyName = null;
|
||||
this.fkeyOnDelete = null;
|
||||
this.fkeyOnUpdate = null;
|
||||
}
|
||||
|
||||
public Column createColumn() {
|
||||
|
||||
Column c = new Column();
|
||||
|
||||
@@ -273,8 +273,7 @@ public class MTable {
|
||||
}
|
||||
|
||||
for (MCompoundUniqueConstraint constraint : uniqueConstraints) {
|
||||
UniqueConstraint uq = constraint.getUniqueConstraint();
|
||||
createTable.getUniqueConstraint().add(uq);
|
||||
createTable.getUniqueConstraint().add(constraint.getUniqueConstraint());
|
||||
}
|
||||
|
||||
return createTable;
|
||||
@@ -789,4 +788,20 @@ public class MTable {
|
||||
public void setPartitionMeta(PartitionMeta partitionMeta) {
|
||||
this.partitionMeta = partitionMeta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear foreign key as this element collection table logically references
|
||||
* back to multiple tables.
|
||||
*/
|
||||
public MIndex setReusedElementCollection() {
|
||||
MIndex index = null;
|
||||
for (MColumn column : columns.values()) {
|
||||
final String references = column.getReferences();
|
||||
if (references != null) {
|
||||
index = new MIndex(column.getForeignKeyIndex(), name, column.getName());
|
||||
column.clearForeignKey();
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,11 +312,29 @@ public class ModelContainer {
|
||||
return tables.put(table.getName(), table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an element table taking into account if it is reused/references back
|
||||
* to multiple bean types (and so can't have foreign key).
|
||||
*/
|
||||
public void addTableElementCollection(MTable table) {
|
||||
final MTable reusedElementCollection = tables.get(table.getName());
|
||||
if (reusedElementCollection != null) {
|
||||
final MIndex index = reusedElementCollection.setReusedElementCollection();
|
||||
if (index != null) {
|
||||
indexes.put(index.getIndexName(), index);
|
||||
}
|
||||
} else {
|
||||
if (table.isPartitioned()) {
|
||||
partitionedTables.add(table);
|
||||
}
|
||||
tables.put(table.getName(), table);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single column index.
|
||||
*/
|
||||
public void addIndex(String indexName, String tableName, String columnName) {
|
||||
|
||||
indexes.put(indexName, new MIndex(indexName, tableName, columnName));
|
||||
}
|
||||
|
||||
@@ -324,7 +342,6 @@ public class ModelContainer {
|
||||
* Add a multi column index.
|
||||
*/
|
||||
public void addIndex(String indexName, String tableName, String[] columnNames) {
|
||||
|
||||
indexes.put(indexName, new MIndex(indexName, tableName, columnNames));
|
||||
}
|
||||
|
||||
|
||||
@@ -33,10 +33,13 @@ public class PlatformDdlWriter {
|
||||
|
||||
private final PlatformDdl platformDdl;
|
||||
|
||||
public PlatformDdlWriter(DatabasePlatform platform, ServerConfig serverConfig, DbMigrationConfig config) {
|
||||
private final int lockTimeoutSeconds;
|
||||
|
||||
public PlatformDdlWriter(DatabasePlatform platform, ServerConfig serverConfig, DbMigrationConfig config, int lockTimeoutSeconds) {
|
||||
this.platformDdl = PlatformDdlBuilder.create(platform);
|
||||
this.serverConfig = serverConfig;
|
||||
this.config = config;
|
||||
this.lockTimeoutSeconds = lockTimeoutSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,6 +49,13 @@ public class PlatformDdlWriter {
|
||||
|
||||
DdlHandler handler = handler();
|
||||
handler.generateProlog(write);
|
||||
if (lockTimeoutSeconds > 0) {
|
||||
String lockSql = platformDdl.setLockTimeout(lockTimeoutSeconds);
|
||||
if (lockSql != null) {
|
||||
write.apply().append(lockSql).endOfStatement().newLine();
|
||||
}
|
||||
}
|
||||
|
||||
List<ChangeSet> changeSets = dbMigration.getChangeSet();
|
||||
for (ChangeSet changeSet : changeSets) {
|
||||
if (isApply(changeSet)) {
|
||||
|
||||
@@ -95,6 +95,10 @@ public class ModelBuildContext {
|
||||
return model.addTable(table);
|
||||
}
|
||||
|
||||
public void addTableElementCollection(MTable table) {
|
||||
model.addTableElementCollection(table);
|
||||
}
|
||||
|
||||
public void addIndex(String indexName, String tableName, String columnName) {
|
||||
model.addIndex(indexName, tableName, columnName);
|
||||
}
|
||||
|
||||
@@ -17,15 +17,11 @@ public class ModelBuildElementTable {
|
||||
public static void build(ModelBuildContext ctx, BeanPropertyAssocMany<?> manyProp) {
|
||||
|
||||
BeanTable beanTable = manyProp.getBeanTable();
|
||||
|
||||
BeanDescriptor<?> targetDescriptor = manyProp.getTargetDescriptor();
|
||||
|
||||
MTable table = new MTable(beanTable.getBaseTable());
|
||||
|
||||
VisitAllUsing.visitOne(targetDescriptor, new ModelBuildPropertyVisitor(ctx, table, targetDescriptor));
|
||||
|
||||
ctx.addTable(table);
|
||||
ctx.addTableElementCollection(table);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ public class CacheChangeSet {
|
||||
|
||||
final ManyKey key;
|
||||
|
||||
final List<Object> removes = new ArrayList<>();
|
||||
final Set<Object> removes = new HashSet<>();
|
||||
|
||||
final Map<Object, CachedManyIds> puts = new LinkedHashMap<>();
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import io.ebean.config.dbplatform.db2.DB2Platform;
|
||||
import io.ebean.config.dbplatform.h2.H2Platform;
|
||||
import io.ebean.config.dbplatform.hana.HanaPlatform;
|
||||
import io.ebean.config.dbplatform.hsqldb.HsqldbPlatform;
|
||||
import io.ebean.config.dbplatform.mysql.MySql55Platform;
|
||||
import io.ebean.config.dbplatform.mysql.MySqlPlatform;
|
||||
import io.ebean.config.dbplatform.nuodb.NuoDbPlatform;
|
||||
import io.ebean.config.dbplatform.oracle.OraclePlatform;
|
||||
@@ -82,6 +83,9 @@ public class DatabasePlatformFactory {
|
||||
if (dbName.equals("mysql")) {
|
||||
return new MySqlPlatform();
|
||||
}
|
||||
if (dbName.equals("mysql55")) {
|
||||
return new MySql55Platform();
|
||||
}
|
||||
if (dbName.equals("postgres") || dbName.equals("postgres9")) {
|
||||
return new PostgresPlatform();
|
||||
}
|
||||
@@ -147,15 +151,16 @@ public class DatabasePlatformFactory {
|
||||
*/
|
||||
private DatabasePlatform byDatabaseMeta(DatabaseMetaData metaData, Connection connection) throws SQLException {
|
||||
|
||||
String dbProductName = metaData.getDatabaseProductName();
|
||||
dbProductName = dbProductName.toLowerCase();
|
||||
String dbProductName = metaData.getDatabaseProductName().toLowerCase();
|
||||
final int majorVersion = metaData.getDatabaseMajorVersion();
|
||||
final int minorVersion = metaData.getDatabaseMinorVersion();
|
||||
|
||||
if (dbProductName.contains("oracle")) {
|
||||
return new OraclePlatform();
|
||||
} else if (dbProductName.contains("microsoft")) {
|
||||
throw new IllegalArgumentException("For SqlServer please explicitly choose either sqlserver16 or sqlserver17 as the platform via ServerConfig.setDatabasePlatformName. Refer to issue #1340 for more details");
|
||||
} else if (dbProductName.contains("mysql")) {
|
||||
return new MySqlPlatform();
|
||||
return mysqlVersion(majorVersion, minorVersion);
|
||||
} else if (dbProductName.contains("h2")) {
|
||||
return new H2Platform();
|
||||
} else if (dbProductName.contains("hsql database engine")) {
|
||||
@@ -180,6 +185,13 @@ public class DatabasePlatformFactory {
|
||||
return new DatabasePlatform();
|
||||
}
|
||||
|
||||
private DatabasePlatform mysqlVersion(int majorVersion, int minorVersion) {
|
||||
if (majorVersion <= 5 && minorVersion <= 5) {
|
||||
return new MySql55Platform();
|
||||
}
|
||||
return new MySqlPlatform();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a select version() query as it could be Postgres or CockroachDB.
|
||||
*/
|
||||
|
||||
@@ -82,7 +82,7 @@ class DefaultBeanLoader {
|
||||
parentDesc.contextPut(pc, parentId, parentBean);
|
||||
}
|
||||
|
||||
boolean useManyIdCache = beanCollection != null && parentDesc.isManyPropCaching();
|
||||
boolean useManyIdCache = beanCollection != null && parentDesc.isManyPropCaching() && many.isUseCache();
|
||||
if (useManyIdCache) {
|
||||
Boolean readOnly = null;
|
||||
if (ebi.isReadOnly()) {
|
||||
|
||||
@@ -12,6 +12,7 @@ import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.meta.QueryPlanRequest;
|
||||
import io.ebean.meta.ServerMetrics;
|
||||
import io.ebean.meta.ServerMetricsAsJson;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -42,6 +43,11 @@ public class DefaultMetaInfoManager implements MetaInfoManager {
|
||||
return visitBasic();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerMetricsAsJson collectMetricsAsJson() {
|
||||
return new DumpMetricsJson(server);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicMetricVisitor visitBasic() {
|
||||
BasicMetricVisitor basic = new BasicMetricVisitor();
|
||||
|
||||
@@ -98,11 +98,11 @@ import io.ebeaninternal.server.query.CQueryEngine;
|
||||
import io.ebeaninternal.server.query.CallableQueryCount;
|
||||
import io.ebeaninternal.server.query.CallableQueryIds;
|
||||
import io.ebeaninternal.server.query.CallableQueryList;
|
||||
import io.ebeaninternal.server.query.DtoQueryEngine;
|
||||
import io.ebeaninternal.server.query.LimitOffsetPagedList;
|
||||
import io.ebeaninternal.server.query.QueryFutureIds;
|
||||
import io.ebeaninternal.server.query.QueryFutureList;
|
||||
import io.ebeaninternal.server.query.QueryFutureRowCount;
|
||||
import io.ebeaninternal.server.query.DtoQueryEngine;
|
||||
import io.ebeaninternal.server.querydefn.DefaultDtoQuery;
|
||||
import io.ebeaninternal.server.querydefn.DefaultOrmQuery;
|
||||
import io.ebeaninternal.server.querydefn.DefaultOrmUpdate;
|
||||
@@ -136,11 +136,16 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.Spliterator;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.Spliterators.spliteratorUnknownSize;
|
||||
import static java.util.stream.StreamSupport.stream;
|
||||
|
||||
/**
|
||||
* The default server side implementation of EbeanServer.
|
||||
@@ -651,7 +656,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
executeSql(connection, databasePlatform.truncateStatement(table));
|
||||
}
|
||||
connection.commit();
|
||||
} catch(SQLException e) {
|
||||
} catch (SQLException e) {
|
||||
throw new PersistenceException("Error executing truncate", e);
|
||||
}
|
||||
}
|
||||
@@ -1346,8 +1351,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@Override
|
||||
public <T> boolean exists(SpiQuery<?> ormQuery, SpiTransaction transaction) {
|
||||
|
||||
ormQuery.setMaxRows(1);
|
||||
SpiOrmQueryRequest<?> request = createQueryRequest(Type.ID_LIST, ormQuery, transaction);
|
||||
Query<?> ormQueryCopy = ormQuery.copy();
|
||||
ormQueryCopy.setMaxRows(1);
|
||||
SpiOrmQueryRequest<?> request = createQueryRequest(Type.ID_LIST, ormQueryCopy, transaction);
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
List<Object> ids = request.findIds();
|
||||
@@ -1507,6 +1513,48 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Stream<T> findLargeStream(Query<T> query, Transaction transaction) {
|
||||
return findStreamWithSingleContext(false, query, transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Stream<T> findStream(Query<T> query, Transaction transaction) {
|
||||
return findStreamWithSingleContext(true, query, transaction);
|
||||
}
|
||||
|
||||
private <T> Stream<T> findStreamWithSingleContext(boolean singleContext, Query<T> query, Transaction transaction) {
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.ITERATE, query, transaction);
|
||||
if (singleContext) {
|
||||
request.setIterateSingleContext();
|
||||
}
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return toStream(request.findIterate());
|
||||
} catch (RuntimeException ex) {
|
||||
request.endTransIfRequired();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
private <T> Stream<T> toStream(QueryIterator<T> queryIterator) {
|
||||
return stream(spliteratorUnknownSize(queryIterator, Spliterator.ORDERED), false)
|
||||
.onClose(new QueryIteratorClose(queryIterator));
|
||||
}
|
||||
|
||||
private static class QueryIteratorClose implements Runnable {
|
||||
private final QueryIterator<?> iterator;
|
||||
|
||||
private QueryIteratorClose(QueryIterator<?> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
iterator.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void findEach(Query<T> query, Consumer<T> consumer, Transaction t) {
|
||||
|
||||
@@ -1736,30 +1784,16 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
((EntityBean) bean)._ebean_getIntercept().setDirty(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the bean using the default 'updatesDeleteMissingChildren' setting.
|
||||
*/
|
||||
@Override
|
||||
public void update(Object bean) {
|
||||
update(bean, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the bean using the default 'updatesDeleteMissingChildren' setting.
|
||||
*/
|
||||
@Override
|
||||
public void update(Object bean, Transaction t) {
|
||||
persister.update(checkEntityBean(bean), t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the bean specifying the deleteMissingChildren option.
|
||||
*/
|
||||
@Override
|
||||
public void update(Object bean, Transaction t, boolean deleteMissingChildren) {
|
||||
persister.update(checkEntityBean(bean), t, deleteMissingChildren);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAll(Collection<?> beans) throws OptimisticLockException {
|
||||
updateAll(beans, null);
|
||||
|
||||
@@ -117,6 +117,11 @@ public final class DefaultSqlUpdate implements Serializable, SpiSqlUpdate {
|
||||
this(null, sql, new BindParams());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiSqlUpdate copy() {
|
||||
return new DefaultSqlUpdate(server, origSql, new BindParams());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
addPos = 0;
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.Database;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.meta.MetaCountMetric;
|
||||
import io.ebean.meta.MetaOrmQueryMetric;
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.ServerMetrics;
|
||||
import io.ebean.meta.ServerMetricsAsJson;
|
||||
import io.ebean.meta.SortMetric;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
class DumpMetricsJson implements ServerMetricsAsJson {
|
||||
|
||||
private final Database database;
|
||||
|
||||
private final StringWriter writer = new StringWriter();
|
||||
|
||||
private boolean dumpHash = true;
|
||||
private boolean dumpSql = true;
|
||||
private boolean dumpLoc = true;
|
||||
|
||||
private Comparator<MetaTimedMetric> sortBy = SortMetric.NAME;
|
||||
|
||||
private int listCounter;
|
||||
private int objKeyCounter;
|
||||
|
||||
private String newLine = "\n";
|
||||
|
||||
DumpMetricsJson(Database database) {
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerMetricsAsJson withLocation(boolean withLocation) {
|
||||
this.dumpLoc = withLocation;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerMetricsAsJson withSql(boolean withSql) {
|
||||
this.dumpSql = withSql;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerMetricsAsJson withHash(boolean withHash) {
|
||||
this.dumpHash = withHash;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerMetricsAsJson withSort(Comparator<MetaTimedMetric> sortBy) {
|
||||
this.sortBy = sortBy;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerMetricsAsJson withNewLine(boolean withNewLine) {
|
||||
this.newLine = withNewLine ? "\n" : "";
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String json() {
|
||||
collect(database.getMetaInfoManager().collectMetrics());
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
|
||||
private void collect(ServerMetrics serverMetrics) {
|
||||
|
||||
start();
|
||||
for (MetaTimedMetric metric : serverMetrics.getTimedMetrics()) {
|
||||
log(metric);
|
||||
}
|
||||
|
||||
List<MetaCountMetric> countMetrics = serverMetrics.getCountMetrics();
|
||||
if (!countMetrics.isEmpty()) {
|
||||
if (sortBy != null) {
|
||||
countMetrics.sort(SortMetric.COUNT_NAME);
|
||||
}
|
||||
for (MetaCountMetric metric : countMetrics) {
|
||||
logCount(metric);
|
||||
}
|
||||
}
|
||||
|
||||
List<MetaOrmQueryMetric> ormQueryMetrics = serverMetrics.getOrmQueryMetrics();
|
||||
if (!ormQueryMetrics.isEmpty()) {
|
||||
if (sortBy != null) {
|
||||
ormQueryMetrics.sort(sortBy);
|
||||
}
|
||||
for (MetaOrmQueryMetric metric : ormQueryMetrics) {
|
||||
logQuery(metric);
|
||||
}
|
||||
}
|
||||
|
||||
List<MetaQueryMetric> dtoQueryMetrics = serverMetrics.getDtoQueryMetrics();
|
||||
if (!dtoQueryMetrics.isEmpty()) {
|
||||
if (sortBy != null) {
|
||||
dtoQueryMetrics.sort(sortBy);
|
||||
}
|
||||
for (MetaQueryMetric metric : dtoQueryMetrics) {
|
||||
logDtoQuery(metric);
|
||||
}
|
||||
}
|
||||
end();
|
||||
}
|
||||
|
||||
private void start() {
|
||||
objStart();
|
||||
key("db");
|
||||
val(database.getName());
|
||||
key("metrics");
|
||||
listStart();
|
||||
}
|
||||
|
||||
private void end() {
|
||||
listEnd();
|
||||
objEnd();
|
||||
}
|
||||
|
||||
private void objStart() {
|
||||
objKeyCounter = 0;
|
||||
writer.append("{");
|
||||
}
|
||||
|
||||
private void objEnd() {
|
||||
writer.append("}");
|
||||
}
|
||||
|
||||
private void listStart() {
|
||||
listCounter = 0;
|
||||
writer.append("[");
|
||||
writer.append(newLine);
|
||||
}
|
||||
|
||||
private void listEnd() {
|
||||
writer.append("]");
|
||||
}
|
||||
|
||||
private void key(String key) {
|
||||
if (objKeyCounter++ > 0) {
|
||||
writer.append(", ");
|
||||
}
|
||||
writer.append("\"").append(key).append("\":");
|
||||
}
|
||||
|
||||
private void val(long count) {
|
||||
writer.append(Long.toString(count));
|
||||
}
|
||||
|
||||
private void val(String val) {
|
||||
writer.append("\"").append(val).append("\"");
|
||||
}
|
||||
|
||||
private void log(MetaTimedMetric metric) {
|
||||
|
||||
metricStart();
|
||||
key("name");
|
||||
val(metric.getName());
|
||||
appendCounters(metric);
|
||||
objEnd();
|
||||
}
|
||||
|
||||
private void metricStart() {
|
||||
if (listCounter++ > 0) {
|
||||
writer.append(",").append(newLine);
|
||||
}
|
||||
objStart();
|
||||
}
|
||||
|
||||
private void logCount(MetaCountMetric metric) {
|
||||
metricStart();
|
||||
key("name");
|
||||
val(metric.getName());
|
||||
key("count");
|
||||
val(metric.getCount());
|
||||
objEnd();
|
||||
}
|
||||
|
||||
private void logQuery(MetaOrmQueryMetric metric) {
|
||||
|
||||
metricStart();
|
||||
key("name");
|
||||
val(metric.getName());
|
||||
appendCounters(metric);
|
||||
if (dumpHash) {
|
||||
key("hash");
|
||||
val(metric.getSqlHash());
|
||||
}
|
||||
appendProfileAndSql(metric);
|
||||
objEnd();
|
||||
}
|
||||
|
||||
private void logDtoQuery(MetaQueryMetric metric) {
|
||||
|
||||
metricStart();
|
||||
key("name");
|
||||
val(metric.getName());
|
||||
appendCounters(metric);
|
||||
appendProfileAndSql(metric);
|
||||
objEnd();
|
||||
}
|
||||
|
||||
private void appendProfileAndSql(MetaQueryMetric metric) {
|
||||
ProfileLocation profileLocation = metric.getProfileLocation();
|
||||
if (dumpLoc && profileLocation != null) {
|
||||
key("loc");
|
||||
val(profileLocation.shortDescription());
|
||||
}
|
||||
if (dumpSql) {
|
||||
key("sql");
|
||||
val(metric.getSql());
|
||||
}
|
||||
}
|
||||
|
||||
private void appendCounters(MetaTimedMetric timedMetric) {
|
||||
key("count");
|
||||
val(timedMetric.getCount());
|
||||
key("total");
|
||||
val(timedMetric.getTotal());
|
||||
key("mean");
|
||||
val(timedMetric.getMean());
|
||||
key("max");
|
||||
val(timedMetric.getMax());
|
||||
}
|
||||
}
|
||||
@@ -514,7 +514,7 @@ public class InternalConfiguration {
|
||||
|
||||
String tz = serverConfig.getDataTimeZone();
|
||||
if (tz == null) {
|
||||
if (getPlatform() == Platform.MYSQL) {
|
||||
if (isMySql(getPlatform())) {
|
||||
return new LocalDataTimeZone();
|
||||
}
|
||||
return new NoDataTimeZone();
|
||||
@@ -526,6 +526,10 @@ public class InternalConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isMySql(Platform platform) {
|
||||
return platform.base() == Platform.MYSQL;
|
||||
}
|
||||
|
||||
public DataTimeZone getDataTimeZone() {
|
||||
return dataTimeZone;
|
||||
}
|
||||
|
||||
@@ -87,6 +87,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
|
||||
private Set<String> dependentTables;
|
||||
|
||||
private boolean iterateSingleContext;
|
||||
|
||||
/**
|
||||
* Create the InternalQueryRequest.
|
||||
*/
|
||||
@@ -104,6 +106,15 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
return queryEngine.translate(this, bindLog, sql, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIterateSingleContext() {
|
||||
this.iterateSingleContext = true;
|
||||
}
|
||||
|
||||
public boolean isIterateSingleContext() {
|
||||
return iterateSingleContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeleteByStatement() {
|
||||
if (!transaction.isPersistCascade() || beanDescriptor.isDeleteByStatement()) {
|
||||
@@ -322,11 +333,13 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
* For iterate queries reset the persistenceContext and loadContext.
|
||||
*/
|
||||
public void flushPersistenceContextOnIterate() {
|
||||
persistenceContext = new DefaultPersistenceContext();
|
||||
loadContext.resetPersistenceContext(persistenceContext);
|
||||
if (jsonRead != null) {
|
||||
jsonRead.setPersistenceContext(persistenceContext);
|
||||
jsonRead.setLoadContext(loadContext);
|
||||
if (!iterateSingleContext) {
|
||||
persistenceContext = new DefaultPersistenceContext();
|
||||
loadContext.resetPersistenceContext(persistenceContext);
|
||||
if (jsonRead != null) {
|
||||
jsonRead.setPersistenceContext(persistenceContext);
|
||||
jsonRead.setLoadContext(loadContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -782,4 +795,11 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
dependentTables.addAll(tables);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if no MaxRows or use LIMIT in SQL update.
|
||||
*/
|
||||
public boolean isInlineSqlUpdateLimit() {
|
||||
return query.getMaxRows() < 1 || ebeanServer.getDatabasePlatform().isInlineSqlUpdateLimit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ public class PersistDeferredRelationship {
|
||||
private final EntityBean bean;
|
||||
|
||||
public PersistDeferredRelationship(SpiEbeanServer ebeanServer, BeanDescriptor<?> beanDescriptor, EntityBean assocBean, ImportedId importedId, EntityBean bean) {
|
||||
|
||||
this.ebeanServer = ebeanServer;
|
||||
this.beanDescriptor = beanDescriptor;
|
||||
this.assocBean = assocBean;
|
||||
@@ -41,13 +40,11 @@ public class PersistDeferredRelationship {
|
||||
|
||||
// bind the set clause for the importedId
|
||||
int pos = importedId.bind(1, sqlUpdate, assocBean);
|
||||
|
||||
// bind the where clause for the bean
|
||||
Object[] idValues = beanDescriptor.getIdBinder().getIdValues(bean);
|
||||
for (int j = 0; j < idValues.length; j++) {
|
||||
sqlUpdate.setParameter(pos + j, idValues[j]);
|
||||
}
|
||||
|
||||
ebeanServer.execute(sqlUpdate, transaction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,11 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFlushQueue() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a the request or queue/batch it for later execution.
|
||||
*/
|
||||
|
||||
@@ -27,7 +27,7 @@ import io.ebeaninternal.server.persist.BatchedSqlException;
|
||||
import io.ebeaninternal.server.persist.DeleteMode;
|
||||
import io.ebeaninternal.server.persist.Flags;
|
||||
import io.ebeaninternal.server.persist.PersistExecute;
|
||||
import io.ebeaninternal.server.persist.SaveManyBeans;
|
||||
import io.ebeaninternal.server.persist.SaveMany;
|
||||
import io.ebeaninternal.server.transaction.BeanPersistIdMap;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdate;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdateContext;
|
||||
@@ -83,6 +83,8 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
|
||||
private int flags;
|
||||
|
||||
private boolean saveRecurse;
|
||||
|
||||
private DocStoreMode docStoreMode;
|
||||
|
||||
private final ConcurrencyMode concurrencyMode;
|
||||
@@ -104,8 +106,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
|
||||
private boolean notifyCache;
|
||||
|
||||
private boolean deleteMissingChildren;
|
||||
|
||||
/**
|
||||
* Flag used to detect when only many properties where updated via a cascade. Used to ensure
|
||||
* appropriate caches are updated in that case.
|
||||
@@ -169,10 +169,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
*/
|
||||
private boolean getterCallback;
|
||||
|
||||
/**
|
||||
* postUpdate notifications. Used to combine bean and element update updates into single postUpdate event.
|
||||
*/
|
||||
private int pendingPostUpdateNotify;
|
||||
private boolean pendingPostUpdateNotify;
|
||||
|
||||
/**
|
||||
* Set to true when post execute has occurred (so includes batch flush).
|
||||
@@ -187,7 +184,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
/**
|
||||
* Many to many intersection table changes that are held for later batch processing.
|
||||
*/
|
||||
private List<SaveManyBeans> saveManyIntersections;
|
||||
private List<SaveMany> saveMany;
|
||||
|
||||
public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t,
|
||||
PersistExecute persistExecute, PersistRequest.Type type, int flags) {
|
||||
@@ -283,10 +280,12 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
* Check for batch escalation on cascade.
|
||||
*/
|
||||
public void checkBatchEscalationOnCascade() {
|
||||
if (transaction.checkBatchEscalationOnCascade(this)) {
|
||||
// we escalated to use batch mode so flush when done
|
||||
// but if createdTransaction then commit will flush it
|
||||
batchOnCascadeSet = !createdTransaction;
|
||||
if (type == Type.UPDATE || beanDescriptor.isBatchEscalateOnCascade(type)) {
|
||||
if (transaction.checkBatchEscalationOnCascade(this)) {
|
||||
// we escalated to use batch mode so flush when done
|
||||
// but if createdTransaction then commit will flush it
|
||||
batchOnCascadeSet = !createdTransaction;
|
||||
}
|
||||
}
|
||||
persistCascade = transaction.isPersistCascade();
|
||||
}
|
||||
@@ -425,6 +424,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
/**
|
||||
* Return the dirty properties on this request.
|
||||
*/
|
||||
@Override
|
||||
public boolean[] getDirtyProperties() {
|
||||
return dirtyProperties;
|
||||
}
|
||||
@@ -483,7 +483,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
/**
|
||||
* Collect L2 cache changes to be applied after the transaction has successfully committed.
|
||||
*/
|
||||
public void notifyCache(CacheChangeSet changeSet) {
|
||||
private void notifyCache(CacheChangeSet changeSet) {
|
||||
if (notifyCache) {
|
||||
switch (type) {
|
||||
case INSERT:
|
||||
@@ -531,8 +531,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
public void addToQueue(DocStoreUpdates docStoreUpdates) {
|
||||
switch (type) {
|
||||
case INSERT:
|
||||
docStoreUpdates.queueIndex(beanDescriptor.getDocStoreQueueId(), idValue);
|
||||
break;
|
||||
case UPDATE:
|
||||
case DELETE_SOFT:
|
||||
docStoreUpdates.queueIndex(beanDescriptor.getDocStoreQueueId(), idValue);
|
||||
@@ -587,7 +585,10 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
|
||||
public void unRegisterBean() {
|
||||
transaction.unregisterBean(bean);
|
||||
if (!saveRecurse) {
|
||||
// only clear all persisted beans when persisting at the top level
|
||||
transaction.unregisterBeans();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -629,20 +630,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
return beanDescriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a stateless update should also delete any missing details beans.
|
||||
*/
|
||||
public boolean isDeleteMissingChildren() {
|
||||
return deleteMissingChildren;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if deleteMissingChildren occurs on cascade save to OneToMany or ManyToMany.
|
||||
*/
|
||||
public void setDeleteMissingChildren(boolean deleteMissingChildren) {
|
||||
this.deleteMissingChildren = deleteMissingChildren;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the update after potential modifications in a BeanPersistController.
|
||||
*/
|
||||
@@ -751,14 +738,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
return parentBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the controller if there is one associated with this type of bean. This returns null if
|
||||
* there is no controller associated.
|
||||
*/
|
||||
public BeanPersistController getBeanController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the intercept if there is one.
|
||||
*/
|
||||
@@ -907,12 +886,8 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
|
||||
private void postUpdateNotify() {
|
||||
if (pendingPostUpdateNotify > 0) {
|
||||
// invoke the delayed postUpdate notification (combined with element collection update)
|
||||
if (pendingPostUpdateNotify) {
|
||||
controller.postUpdate(this);
|
||||
} else {
|
||||
// batched update with no element collection, send postUpdate notification once it executes
|
||||
pendingPostUpdateNotify = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -935,6 +910,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
*/
|
||||
@Override
|
||||
public void postExecute() {
|
||||
saveQueuedMany();
|
||||
postExecute = true;
|
||||
if (controller != null) {
|
||||
controllerPost();
|
||||
@@ -962,12 +938,11 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
if (isLogSummary()) {
|
||||
logSummary();
|
||||
}
|
||||
saveQueuedManyIntersection();
|
||||
}
|
||||
|
||||
private void saveQueuedManyIntersection() {
|
||||
if (saveManyIntersections != null) {
|
||||
saveManyIntersections.forEach(SaveManyBeans::saveIntersectionBatch);
|
||||
private void saveQueuedMany() {
|
||||
if (saveMany != null) {
|
||||
saveMany.forEach(SaveMany::saveBatch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -978,19 +953,14 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
if (controller != null && !dirty) {
|
||||
// fire preUpdate notification when only element collection updated
|
||||
controller.preUpdate(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine with the beans postUpdate event notification.
|
||||
*/
|
||||
public boolean postElementCollectionUpdate() {
|
||||
if (controller != null) {
|
||||
pendingPostUpdateNotify += 2;
|
||||
pendingPostUpdateNotify = true;
|
||||
}
|
||||
if (!dirty) {
|
||||
setNotifyCache();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isNotifyCache() {
|
||||
return notifyCache;
|
||||
}
|
||||
|
||||
@@ -1000,13 +970,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
controller.postInsert(this);
|
||||
break;
|
||||
case UPDATE:
|
||||
if (pendingPostUpdateNotify == -1) {
|
||||
// notify now - batched bean update with no element collection
|
||||
controller.postUpdate(this);
|
||||
} else {
|
||||
// delay notify to combine with element collection update
|
||||
pendingPostUpdateNotify++;
|
||||
}
|
||||
controller.postUpdate(this);
|
||||
break;
|
||||
case DELETE_SOFT:
|
||||
controller.postSoftDelete(this);
|
||||
@@ -1051,16 +1015,6 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the update DML/SQL must be dynamically generated.
|
||||
* <p>
|
||||
* This is the case for updates/deletes of partially populated beans.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isDynamicUpdateSql() {
|
||||
return beanDescriptor.isUpdateChangesOnly() || !intercept.isFullyLoadedBean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the property should be included in the update.
|
||||
*/
|
||||
@@ -1133,7 +1087,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
updatedManysOnly = true;
|
||||
setNotifyCache();
|
||||
addPostCommitListeners();
|
||||
saveQueuedManyIntersection();
|
||||
saveQueuedMany();
|
||||
}
|
||||
notifyCacheOnComplete();
|
||||
postUpdateNotify();
|
||||
@@ -1290,6 +1244,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
|
||||
private void executeInsert() {
|
||||
setGeneratedId();
|
||||
setTenantId();
|
||||
if (controller == null || controller.preInsert(this)) {
|
||||
beanManager.getBeanPersister().insert(this);
|
||||
@@ -1471,19 +1426,34 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the intersection table updates should be queued and batched.
|
||||
* Return true if the intersection table updates or element collection updates should be queued.
|
||||
*/
|
||||
public boolean isQueueManyIntersection() {
|
||||
public boolean isQueueSaveMany() {
|
||||
return !postExecute;
|
||||
}
|
||||
|
||||
/**
|
||||
* The intersection table updates to the batch executed later on postExecute.
|
||||
* The intersection table updates or element collection to the batch executed later on postExecute.
|
||||
*/
|
||||
public void addManyIntersection(SaveManyBeans saveManyIntersection) {
|
||||
if (this.saveManyIntersections == null) {
|
||||
this.saveManyIntersections = new ArrayList<>();
|
||||
public void addSaveMany(SaveMany saveManyRequest) {
|
||||
if (this.saveMany == null) {
|
||||
this.saveMany = new ArrayList<>();
|
||||
}
|
||||
this.saveManyIntersections.add(saveManyIntersection);
|
||||
this.saveMany.add(saveManyRequest);
|
||||
}
|
||||
|
||||
public boolean isForcedUpdate() {
|
||||
return Flags.isUpdateForce(flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set when this request is from cascading persist.
|
||||
*/
|
||||
public void setSaveRecurse() {
|
||||
saveRecurse = true;
|
||||
}
|
||||
|
||||
private void setGeneratedId() {
|
||||
beanDescriptor.setGeneratedId(entityBean, transaction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ public final class PersistRequestUpdateSql extends PersistRequest {
|
||||
private final boolean forceNoBatch;
|
||||
|
||||
private boolean batchThisRequest;
|
||||
private boolean flushQueue;
|
||||
|
||||
public PersistRequestUpdateSql(SpiEbeanServer server, SpiSqlUpdate sqlUpdate,
|
||||
SpiTransaction t, PersistExecute persistExecute, boolean forceNoBatch) {
|
||||
@@ -50,7 +51,7 @@ public final class PersistRequestUpdateSql extends PersistRequest {
|
||||
|
||||
@Override
|
||||
public void profile(long offset, int flushCount) {
|
||||
profileBase(EVT_UPDATESQL, offset, (short)0, flushCount);
|
||||
profileBase(EVT_UPDATESQL, offset, (short) 0, flushCount);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,9 +78,15 @@ public final class PersistRequestUpdateSql extends PersistRequest {
|
||||
if (control == null) {
|
||||
control = persistExecute.createBatchControl(transaction);
|
||||
}
|
||||
flushQueue = true;
|
||||
control.addToFlushQueue(this, early);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFlushQueue() {
|
||||
return flushQueue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeNow() {
|
||||
return persistExecute.executeSqlUpdate(this);
|
||||
|
||||
@@ -30,11 +30,6 @@ public interface Persister {
|
||||
*/
|
||||
void update(EntityBean entityBean, Transaction t);
|
||||
|
||||
/**
|
||||
* Update the bean specifying deleteMissingChildren.
|
||||
*/
|
||||
void update(EntityBean entityBean, Transaction t, boolean deleteMissingChildren);
|
||||
|
||||
/**
|
||||
* Force an Insert using the given bean.
|
||||
*/
|
||||
@@ -109,8 +104,18 @@ public interface Persister {
|
||||
void executeOrQueue(SpiSqlUpdate update, SpiTransaction t, boolean queue);
|
||||
|
||||
/**
|
||||
* Add the statement to JDBC batch for later execution via executeBatch.
|
||||
* Queue the SqlUpdate for early execution (with JDBC batch).
|
||||
*/
|
||||
void addToFlushQueue(SpiSqlUpdate update, SpiTransaction t);
|
||||
|
||||
/**
|
||||
* Queue the SqlUpdate for late execution (with JDBC batch).
|
||||
*/
|
||||
void addToFlushQueueLast(SpiSqlUpdate update, SpiTransaction t);
|
||||
|
||||
/**
|
||||
* Add the statement to JDBC batch for later execution via executeBatch.
|
||||
*/
|
||||
void addBatch(SpiSqlUpdate sqlUpdate, SpiTransaction transaction);
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,6 +30,7 @@ public class PlatformDdlBuilder {
|
||||
return new H2Ddl(platform);
|
||||
case DB2:
|
||||
return new DB2Ddl(platform);
|
||||
case MYSQL55:
|
||||
case MYSQL:
|
||||
return new MySqlDdl(platform);
|
||||
case HSQLDB:
|
||||
|
||||
@@ -171,4 +171,10 @@ public interface SpiOrmQueryRequest<T> extends BeanQueryRequest<T>, DocQueryRequ
|
||||
* Return true if delete by statement is allowed for this type given cascade rules etc.
|
||||
*/
|
||||
boolean isDeleteByStatement();
|
||||
|
||||
/**
|
||||
* Set when we want to use a single persistence context for all beans returned
|
||||
* in the query (so all beans are held in memory)
|
||||
*/
|
||||
void setIterateSingleContext();
|
||||
}
|
||||
|
||||
@@ -21,9 +21,7 @@ class AssocOneHelpRefInherit extends AssocOneHelp {
|
||||
@Override
|
||||
void loadIgnore(DbReadContext ctx) {
|
||||
property.targetIdBinder.loadIgnore(ctx);
|
||||
if (inherit.hasChildren()) {
|
||||
ctx.getDataReader().incrementPos(1);
|
||||
}
|
||||
ctx.getDataReader().incrementPos(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,14 +55,13 @@ class AssocOneHelpRefInherit extends AssocOneHelp {
|
||||
boolean disableLazyLoading = ctx.isDisableLazyLoading();
|
||||
Object ref = desc.contextRef(pc, ctx.isReadOnly(), disableLazyLoading, id);
|
||||
if (!disableLazyLoading) {
|
||||
ctx.register(property.name, ((EntityBean) ref)._ebean_getIntercept());
|
||||
ctx.registerBeanInherit(property, ((EntityBean) ref)._ebean_getIntercept());
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
@Override
|
||||
void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
|
||||
// add join to support the discriminator column
|
||||
String relativePrefix = ctx.getRelativePrefix(property.name);
|
||||
property.tableJoin.addJoin(joinType, relativePrefix, ctx);
|
||||
@@ -76,7 +73,7 @@ class AssocOneHelpRefInherit extends AssocOneHelp {
|
||||
@Override
|
||||
void appendSelect(DbSqlContext ctx, boolean subQuery) {
|
||||
|
||||
if (!subQuery && inherit.hasChildren()) {
|
||||
if (!subQuery) {
|
||||
// add discriminator column
|
||||
String relativePrefix = ctx.getRelativePrefix(property.getName());
|
||||
String tableAlias = ctx.getTableAlias(relativePrefix);
|
||||
|
||||
@@ -66,7 +66,6 @@ import io.ebeaninternal.server.el.ElPropertyChainBuilder;
|
||||
import io.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.persist.DeleteMode;
|
||||
import io.ebeaninternal.server.persist.DmlUtil;
|
||||
import io.ebeaninternal.server.query.CQueryPlan;
|
||||
import io.ebeaninternal.server.query.ExtraJoin;
|
||||
import io.ebeaninternal.server.query.STreeProperty;
|
||||
@@ -77,6 +76,7 @@ import io.ebeaninternal.server.query.STreeType;
|
||||
import io.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import io.ebeaninternal.server.querydefn.DefaultOrmQuery;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryProperties;
|
||||
import io.ebeaninternal.server.rawsql.SpiRawSql;
|
||||
import io.ebeaninternal.server.type.DataBind;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
@@ -140,6 +140,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
private final short profileBeanId;
|
||||
|
||||
private final boolean multiValueSupported;
|
||||
private boolean batchEscalateOnCascadeInsert;
|
||||
private boolean batchEscalateOnCascadeDelete;
|
||||
|
||||
public enum EntityType {
|
||||
ORM, EMBEDDED, VIEW, SQL, DOC
|
||||
@@ -333,6 +335,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* list of properties that are Lists/Sets/Maps (Derived).
|
||||
*/
|
||||
private final BeanProperty[] propertiesNonMany;
|
||||
private final BeanProperty[] propertiesAggregate;
|
||||
private final BeanPropertyAssocMany<?>[] propertiesMany;
|
||||
private final BeanPropertyAssocMany<?>[] propertiesManySave;
|
||||
private final BeanPropertyAssocMany<?>[] propertiesManyDelete;
|
||||
@@ -405,11 +408,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
|
||||
private final String baseTableAlias;
|
||||
|
||||
/**
|
||||
* If true then only changed properties get updated.
|
||||
*/
|
||||
private final boolean updateChangesOnly;
|
||||
|
||||
private final boolean cacheSharableBeans;
|
||||
|
||||
private final String docStoreQueueId;
|
||||
@@ -465,7 +463,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
this.selectLastInsertedId = deploy.getSelectLastInsertedId();
|
||||
this.selectLastInsertedIdDraft = deploy.getSelectLastInsertedIdDraft();
|
||||
this.concurrencyMode = deploy.getConcurrencyMode();
|
||||
this.updateChangesOnly = deploy.isUpdateChangesOnly();
|
||||
this.indexDefinitions = deploy.getIndexDefinitions();
|
||||
|
||||
this.readAuditing = deploy.isReadAuditing();
|
||||
@@ -515,6 +512,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
|
||||
this.propertiesMany = listHelper.getMany();
|
||||
this.propertiesNonMany = listHelper.getNonMany();
|
||||
this.propertiesAggregate = listHelper.getAggregates();
|
||||
this.propertiesManySave = listHelper.getManySave();
|
||||
this.propertiesManyDelete = listHelper.getManyDelete();
|
||||
this.propertiesManyToMany = listHelper.getManyToMany();
|
||||
@@ -756,6 +754,24 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasCircularImportedId() {
|
||||
for (BeanPropertyAssocOne<?> assocOne : propertiesOneImportedSave) {
|
||||
if (assocOne.hasCircularImportedId(this)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean hasCircularImportedIdTo(BeanDescriptor sourceDesc) {
|
||||
for (BeanPropertyAssocOne<?> assocOne : propertiesOneImportedSave) {
|
||||
if (assocOne.getTargetDescriptor() == sourceDesc) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void registerColumn(String dbColumn, String path) {
|
||||
String key = dbColumn.toLowerCase();
|
||||
// check for clash with imported OneToOne PK
|
||||
@@ -816,6 +832,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
void initialiseDocMapping() {
|
||||
batchEscalateOnCascadeInsert = supportBatchEscalateOnInsert();
|
||||
batchEscalateOnCascadeDelete = supportBatchEscalateOnDelete();
|
||||
for (BeanPropertyAssocMany<?> many : propertiesMany) {
|
||||
many.initialisePostTarget();
|
||||
}
|
||||
@@ -830,6 +848,30 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
cacheHelp.deriveNotifyFlags();
|
||||
}
|
||||
|
||||
private boolean supportBatchEscalateOnDelete() {
|
||||
if (softDelete) {
|
||||
return false;
|
||||
}
|
||||
for (BeanPropertyAssocMany<?> assocMany : propertiesManyDelete) {
|
||||
if (assocMany.isCascadeDeleteEscalate()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean supportBatchEscalateOnInsert() {
|
||||
return idType == IdType.IDENTITY || !hasCircularImportedId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return false if JDBC batch can't be implicitly escalated to.
|
||||
* This happens when we have circular import id situation (need to defer setting identity value).
|
||||
*/
|
||||
public boolean isBatchEscalateOnCascade(PersistRequest.Type type) {
|
||||
return type == PersistRequest.Type.INSERT ? batchEscalateOnCascadeInsert : batchEscalateOnCascadeDelete;
|
||||
}
|
||||
|
||||
void initInheritInfo() {
|
||||
if (inheritInfo != null) {
|
||||
// need to check every BeanDescriptor in the inheritance hierarchy
|
||||
@@ -1260,7 +1302,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
/**
|
||||
* Return the draft dirty boolean property or null if there is not one assigned to this bean type.
|
||||
*/
|
||||
public BeanProperty getDraftDirty() {
|
||||
BeanProperty getDraftDirty() {
|
||||
return draftDirty;
|
||||
}
|
||||
|
||||
@@ -1701,14 +1743,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return "update " + baseTable + " set " + prop.importedIdClause() + " where " + idBinder.getBindIdSql(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if updates should only include changed properties. Otherwise
|
||||
* all loaded properties are included in the update.
|
||||
*/
|
||||
public boolean isUpdateChangesOnly() {
|
||||
return updateChangesOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if save does not recurse to other beans. That is return true if
|
||||
* there are no assoc one or assoc many beans that cascade save.
|
||||
@@ -3073,6 +3107,18 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return idType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the generated Id value if appropriate.
|
||||
*/
|
||||
public void setGeneratedId(EntityBean entityBean, Transaction transaction) {
|
||||
if (idGenerator == null || idProperty == null || idProperty.isEmbedded()) {
|
||||
return;
|
||||
}
|
||||
if (isNullOrZero(idProperty.getValue(entityBean))) {
|
||||
convertSetId(nextId(transaction), entityBean);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the Id value is marked as a <code>@GeneratedValue</code>.
|
||||
*/
|
||||
@@ -3169,6 +3215,27 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return propertiesEmbedded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the query detail includes an aggregation property.
|
||||
*/
|
||||
public boolean includesAggregation(OrmQueryDetail detail) {
|
||||
return detail != null && propertiesAggregate.length > 0 && includesAggregation(detail.getChunk(null, false));
|
||||
}
|
||||
|
||||
private boolean includesAggregation(OrmQueryProperties rootProps) {
|
||||
if (rootProps != null) {
|
||||
final Set<String> included = rootProps.getIncluded();
|
||||
if (included != null) {
|
||||
for (BeanProperty property : propertiesAggregate) {
|
||||
if (included.contains(property.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all properties to be loaded (recurse to embedded beans).
|
||||
*/
|
||||
@@ -3494,6 +3561,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
throw new IllegalStateException("Unexpected - expect Element override");
|
||||
}
|
||||
|
||||
public boolean isJsonReadCollection() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void jsonWrite(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
|
||||
jsonHelp.jsonWrite(writeJson, bean, null);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,11 @@ abstract class BeanDescriptorElement<T> extends BeanDescriptor<T> {
|
||||
this.elementHelp = elementHelp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isJsonReadCollection() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return the first base scalar type (and we expect only 1).
|
||||
*/
|
||||
|
||||
@@ -35,6 +35,11 @@ class BeanDescriptorElementEmbedded<T> extends BeanDescriptorElement<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isJsonReadCollection() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialiseOther(BeanDescriptorInitContext initContext) {
|
||||
super.initialiseOther(initContext);
|
||||
|
||||
@@ -28,10 +28,14 @@ class BeanDescriptorElementEmbeddedMap<T> extends BeanDescriptorElementEmbedded<
|
||||
this.stringKey = String.class.equals(scalarTypeKey.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isJsonReadCollection() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void jsonWriteMapEntry(SpiJsonWriter ctx, Map.Entry<?, ?> entry) throws IOException {
|
||||
ctx.writeStartObject();
|
||||
if (stringKey) {
|
||||
Object key = entry.getKey();
|
||||
String keyName = (key == null) ? "null" : key.toString();
|
||||
@@ -43,7 +47,6 @@ class BeanDescriptorElementEmbeddedMap<T> extends BeanDescriptorElementEmbedded<
|
||||
ctx.writeFieldName("value");
|
||||
writeJsonElement(ctx, entry.getValue());
|
||||
}
|
||||
ctx.writeEndObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,30 +55,21 @@ class BeanDescriptorElementEmbeddedMap<T> extends BeanDescriptorElementEmbedded<
|
||||
JsonParser parser = readJson.getParser();
|
||||
ElementCollector add = elementHelp.createCollector();
|
||||
do {
|
||||
JsonToken token = parser.nextToken();
|
||||
if (token != JsonToken.START_OBJECT) {
|
||||
String fieldName = parser.nextFieldName();
|
||||
if (fieldName == null) {
|
||||
break;
|
||||
}
|
||||
if (stringKey) {
|
||||
String key = parser.nextFieldName();
|
||||
parser.nextToken();
|
||||
Object val = readJsonElement(readJson, null);
|
||||
add.addKeyValue(key, val);
|
||||
|
||||
add.addKeyValue(fieldName, val);
|
||||
} else {
|
||||
parser.nextFieldName();
|
||||
Object key = scalarTypeKey.jsonRead(parser);
|
||||
|
||||
parser.nextFieldName();
|
||||
Object val = readJsonElement(readJson, null);
|
||||
add.addKeyValue(key, val);
|
||||
}
|
||||
|
||||
token = parser.nextToken();
|
||||
if (token != JsonToken.END_OBJECT) {
|
||||
break;
|
||||
}
|
||||
|
||||
} while (true);
|
||||
|
||||
return add.collection();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
@@ -35,7 +34,6 @@ class BeanDescriptorElementScalarMap<T> extends BeanDescriptorElement<T> {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void jsonWriteMapEntry(SpiJsonWriter ctx, Map.Entry<?, ?> entry) throws IOException {
|
||||
ctx.writeStartObject();
|
||||
if (stringKey) {
|
||||
Object key = entry.getKey();
|
||||
String keyName = (key == null) ? "null" : key.toString();
|
||||
@@ -47,7 +45,6 @@ class BeanDescriptorElementScalarMap<T> extends BeanDescriptorElement<T> {
|
||||
ctx.writeFieldName("value");
|
||||
scalarTypeVal.jsonWrite(ctx.gen(), entry.getValue());
|
||||
}
|
||||
ctx.writeEndObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,30 +53,21 @@ class BeanDescriptorElementScalarMap<T> extends BeanDescriptorElement<T> {
|
||||
JsonParser parser = readJson.getParser();
|
||||
ElementCollector add = elementHelp.createCollector();
|
||||
do {
|
||||
JsonToken token = parser.nextToken();
|
||||
if (token != JsonToken.START_OBJECT) {
|
||||
String fieldName = parser.nextFieldName();
|
||||
if (fieldName == null) {
|
||||
break;
|
||||
}
|
||||
if (stringKey) {
|
||||
String key = parser.nextFieldName();
|
||||
parser.nextToken();
|
||||
Object val = scalarTypeVal.jsonRead(parser);
|
||||
add.addKeyValue(key, val);
|
||||
|
||||
add.addKeyValue(fieldName, val);
|
||||
} else {
|
||||
parser.nextFieldName();
|
||||
Object key = scalarTypeKey.jsonRead(parser);
|
||||
|
||||
parser.nextFieldName();
|
||||
Object val = scalarTypeVal.jsonRead(parser);
|
||||
add.addKeyValue(key, val);
|
||||
}
|
||||
|
||||
token = parser.nextToken();
|
||||
if (token != JsonToken.END_OBJECT) {
|
||||
break;
|
||||
}
|
||||
|
||||
} while (true);
|
||||
|
||||
return add.collection();
|
||||
|
||||
@@ -144,8 +144,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
private int entityBeanCount;
|
||||
|
||||
private final boolean updateChangesOnly;
|
||||
|
||||
private final BootupClasses bootupClasses;
|
||||
|
||||
private final String serverName;
|
||||
@@ -227,11 +225,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
this.deplyInherit = config.getDeployInherit();
|
||||
this.deployUtil = config.getDeployUtil();
|
||||
this.typeManager = deployUtil.getTypeManager();
|
||||
|
||||
this.beanManagerFactory = new BeanManagerFactory(config.getDatabasePlatform());
|
||||
|
||||
this.updateChangesOnly = serverConfig.isUpdateChangesOnly();
|
||||
|
||||
this.beanLifecycleAdapterFactory = new BeanLifecycleAdapterFactory(serverConfig);
|
||||
this.persistControllerManager = new PersistControllerManager(bootupClasses);
|
||||
this.postLoadManager = new PostLoadManager(bootupClasses);
|
||||
@@ -1310,9 +1304,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
private <T> DeployBeanInfo<T> createDeployBeanInfo(Class<T> beanClass) {
|
||||
|
||||
DeployBeanDescriptor<T> desc = new DeployBeanDescriptor<>(this, beanClass, serverConfig);
|
||||
|
||||
desc.setUpdateChangesOnly(updateChangesOnly);
|
||||
|
||||
beanLifecycleAdapterFactory.addLifecycleMethods(desc);
|
||||
|
||||
// set bean controller, finder and listener
|
||||
|
||||
@@ -163,11 +163,12 @@ public class BeanMapHelp<T> extends BaseCollectionHelp<T> {
|
||||
}
|
||||
|
||||
if (!map.isEmpty() || ctx.isIncludeEmpty()) {
|
||||
ctx.beginAssocMany(name);
|
||||
final boolean elementCollection = many.isElementCollection();
|
||||
ctx.beginAssocManyMap(name, elementCollection);
|
||||
for (Entry<?, ?> entry : map.entrySet()) {
|
||||
many.jsonWriteMapEntry(ctx, entry);
|
||||
}
|
||||
ctx.endAssocMany();
|
||||
ctx.endAssocManyMap(elementCollection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -835,6 +835,13 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
return scalarType.format(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value from String format into Object value.
|
||||
*/
|
||||
public Object parse(String value) {
|
||||
return scalarType.parse(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the value for this property from L2 cache entry and set it to the bean.
|
||||
* <p>
|
||||
|
||||
@@ -14,6 +14,7 @@ import io.ebean.text.PathProperties;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiSqlUpdate;
|
||||
import io.ebeaninternal.api.json.SpiJsonReader;
|
||||
import io.ebeaninternal.api.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.deploy.id.ImportedId;
|
||||
@@ -49,6 +50,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
private final TableJoin intersectionJoin;
|
||||
private final String intersectionPublishTable;
|
||||
private final String intersectionDraftTable;
|
||||
private final boolean orphanRemoval;
|
||||
|
||||
private IntersectionTable intersectionTable;
|
||||
|
||||
@@ -123,6 +125,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
public BeanPropertyAssocMany(BeanDescriptor<?> descriptor, DeployBeanPropertyAssocMany<T> deploy) {
|
||||
super(descriptor, deploy);
|
||||
this.unidirectional = deploy.isUnidirectional();
|
||||
this.orphanRemoval = deploy.isOrphanRemoval();
|
||||
this.o2mJoinTable = deploy.isO2mJoinTable();
|
||||
this.hasOrderColumn = deploy.hasOrderColumn();
|
||||
this.manyToMany = deploy.isManyToMany();
|
||||
@@ -226,6 +229,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
desc.registerTable(targetDescriptor.getBaseTable(), this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying collection of beans.
|
||||
*/
|
||||
@@ -315,7 +319,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
// do not add to the selectChain at the top level of the Many bean
|
||||
}
|
||||
|
||||
public SqlUpdate deleteByParentId(Object parentId, List<Object> parentIdist) {
|
||||
public SpiSqlUpdate deleteByParentId(Object parentId, List<Object> parentIdist) {
|
||||
if (parentId != null) {
|
||||
return sqlHelp.deleteByParentId(parentId);
|
||||
} else {
|
||||
@@ -345,7 +349,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
|
||||
/**
|
||||
* Add the loaded current bean to its associated parent.
|
||||
*
|
||||
* <p>
|
||||
* Helper method used by Elastic integration when loading with a persistence context.
|
||||
*/
|
||||
@Override
|
||||
@@ -509,6 +513,10 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
return hasOrderColumn;
|
||||
}
|
||||
|
||||
public boolean isOrphanRemoval() {
|
||||
return orphanRemoval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssocMany() {
|
||||
return true;
|
||||
@@ -948,10 +956,14 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
}
|
||||
|
||||
public boolean isIncludeCascadeDelete() {
|
||||
return cascadeInfo.isDelete() || o2mJoinTable || ModifyListenMode.REMOVALS == modifyListenMode;
|
||||
return cascadeInfo.isDelete() || hasJoinTable() || ModifyListenMode.REMOVALS == modifyListenMode;
|
||||
}
|
||||
|
||||
public String insertElementCollection() {
|
||||
boolean isCascadeDeleteEscalate() {
|
||||
return !elementCollection && cascadeInfo.isDelete();
|
||||
}
|
||||
|
||||
public SpiSqlUpdate insertElementCollection() {
|
||||
return sqlHelp.insertElementCollection();
|
||||
}
|
||||
|
||||
@@ -960,13 +972,24 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
}
|
||||
|
||||
void jsonWriteMapEntry(SpiJsonWriter ctx, Map.Entry<?, ?> entry) throws IOException {
|
||||
elementDescriptor.jsonWriteMapEntry(ctx, entry);
|
||||
if (elementDescriptor != null) {
|
||||
elementDescriptor.jsonWriteMapEntry(ctx, entry);
|
||||
} else {
|
||||
targetDescriptor.jsonWrite(ctx, (EntityBean)entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
void jsonWriteElementValue(SpiJsonWriter ctx, Object element) {
|
||||
elementDescriptor.jsonWriteElement(ctx, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only cache Many Ids if the target bean is also cached.
|
||||
*/
|
||||
public boolean isUseCache() {
|
||||
return targetDescriptor.isBeanCaching();
|
||||
}
|
||||
|
||||
/**
|
||||
* A Many (element collection) property in bean cache to held as JSON.
|
||||
*/
|
||||
@@ -1033,10 +1056,9 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
*/
|
||||
public Object jsonReadCollection(SpiJsonReader readJson, EntityBean parentBean) throws IOException {
|
||||
|
||||
if (elementDescriptor != null && manyType.isMap()) {
|
||||
if (elementDescriptor != null && elementDescriptor.isJsonReadCollection()) {
|
||||
return elementDescriptor.jsonReadCollection(readJson, parentBean);
|
||||
}
|
||||
|
||||
BeanCollection<?> collection = createEmpty(parentBean);
|
||||
BeanCollectionAdd add = getBeanCollectionAdd(collection);
|
||||
do {
|
||||
|
||||
@@ -40,22 +40,18 @@ class BeanPropertyAssocManyJsonHelp {
|
||||
if (!this.many.jsonDeserialize) {
|
||||
return;
|
||||
}
|
||||
|
||||
JsonParser parser = readJson.getParser();
|
||||
JsonToken event = parser.nextToken();
|
||||
if (JsonToken.VALUE_NULL == event) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (many.isTransient()) {
|
||||
jsonReadTransientUsingObjectMapper(readJson, parentBean);
|
||||
return;
|
||||
}
|
||||
|
||||
if (JsonToken.START_ARRAY != event) {
|
||||
throw new JsonParseException(parser, "Unexpected token " + event + " - expecting start_array");
|
||||
if (JsonToken.START_ARRAY != event && JsonToken.START_OBJECT != event) {
|
||||
throw new JsonParseException(parser, "Unexpected token " + event + " - expecting start array or object");
|
||||
}
|
||||
|
||||
many.setValue(parentBean, many.jsonReadCollection(readJson, parentBean));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiSqlUpdate;
|
||||
import io.ebeaninternal.dbmigration.model.visitor.BaseTablePropertyVisitor;
|
||||
import io.ebeaninternal.dbmigration.model.visitor.VisitAllUsing;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
@@ -59,8 +59,8 @@ class BeanPropertyAssocManySqlHelp<T> {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
String insertElementCollection() {
|
||||
return elementCollectionInsertSql;
|
||||
SpiSqlUpdate insertElementCollection() {
|
||||
return new DefaultSqlUpdate(elementCollectionInsertSql);
|
||||
}
|
||||
|
||||
private static class Cols extends BaseTablePropertyVisitor {
|
||||
@@ -165,13 +165,13 @@ class BeanPropertyAssocManySqlHelp<T> {
|
||||
return server.findIds(q, t);
|
||||
}
|
||||
|
||||
SqlUpdate deleteByParentId(Object parentId) {
|
||||
SpiSqlUpdate deleteByParentId(Object parentId) {
|
||||
DefaultSqlUpdate sqlDelete = new DefaultSqlUpdate(deleteByParentIdSql);
|
||||
many.bindParentId(sqlDelete, parentId);
|
||||
return sqlDelete;
|
||||
}
|
||||
|
||||
SqlUpdate deleteByParentIdList(List<Object> parentIds) {
|
||||
SpiSqlUpdate deleteByParentIdList(List<Object> parentIds) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(100);
|
||||
sb.append(deleteByParentIdInSql);
|
||||
|
||||
@@ -39,11 +39,8 @@ import java.util.Map;
|
||||
public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STreePropertyAssocOne {
|
||||
|
||||
private final boolean oneToOne;
|
||||
|
||||
private final boolean oneToOneExported;
|
||||
|
||||
private final boolean orphanRemoval;
|
||||
|
||||
private final boolean primaryKeyExport;
|
||||
private final boolean primaryKeyJoin;
|
||||
|
||||
@@ -89,7 +86,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
for (BeanProperty embeddedProp : embeddedProps) {
|
||||
embeddedPropsMap.put(embeddedProp.getName(), embeddedProp);
|
||||
}
|
||||
|
||||
} else {
|
||||
embeddedProps = null;
|
||||
embeddedPropsMap = null;
|
||||
@@ -811,4 +807,8 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasCircularImportedId(BeanDescriptor<?> sourceDesc) {
|
||||
return targetDescriptor.hasCircularImportedIdTo(sourceDesc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,11 @@ public interface DbReadContext {
|
||||
*/
|
||||
void register(String path, EntityBeanIntercept ebi);
|
||||
|
||||
/**
|
||||
* Register a reference with inheritance for lazy loading.
|
||||
*/
|
||||
void registerBeanInherit(BeanPropertyAssocOne<?> property, EntityBeanIntercept ebi);
|
||||
|
||||
/**
|
||||
* Register a collection for lazy loading.
|
||||
*/
|
||||
|
||||
@@ -10,7 +10,7 @@ public interface DbSqlContext {
|
||||
/**
|
||||
* Add a join to the sql query.
|
||||
*/
|
||||
void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2, String inheritance);
|
||||
void addJoin(String type, String table, TableJoinColumn[] cols, String a1, String a2);
|
||||
|
||||
/**
|
||||
* Push the current table alias onto the stack.
|
||||
|
||||
@@ -219,9 +219,6 @@ public class InheritInfo {
|
||||
* Return the associated InheritInfo for this DB row read.
|
||||
*/
|
||||
public InheritInfo readType(DbReadContext ctx) throws SQLException {
|
||||
if (!hasChildren()) {
|
||||
return this;
|
||||
}
|
||||
return readType(ctx.getDataReader().getString());
|
||||
}
|
||||
|
||||
|
||||
@@ -154,11 +154,8 @@ public final class TableJoin {
|
||||
|
||||
public SqlJoinType addJoin(SqlJoinType joinType, String a1, String a2, DbSqlContext ctx) {
|
||||
|
||||
String inheritance = inheritInfo != null ? inheritInfo.getWhere() : null;
|
||||
|
||||
String joinLiteral = joinType.getLiteral(type);
|
||||
ctx.addJoin(joinLiteral, table, columns(), a1, a2, inheritance);
|
||||
|
||||
ctx.addJoin(joinLiteral, table, columns(), a1, a2);
|
||||
return joinType.autoToOuter(type);
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -27,6 +27,8 @@ public class GeneratedPropertyFactory {
|
||||
|
||||
private final HashSet<String> numberTypes = new HashSet<>();
|
||||
|
||||
private final UuidGeneratedProperty generatedUuid = new UuidGeneratedProperty();
|
||||
|
||||
private final GeneratedWhoModified generatedWhoModified;
|
||||
|
||||
private final GeneratedWhoCreated generatedWhoCreated;
|
||||
@@ -123,6 +125,10 @@ public class GeneratedPropertyFactory {
|
||||
return idGeneratorMap.get(generatorName);
|
||||
}
|
||||
|
||||
public void setUuid(DeployBeanProperty prop) {
|
||||
prop.setGeneratedProperty(generatedUuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the custom IdGenerator to implement PlatformIdGenerator.
|
||||
*/
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package io.ebeaninternal.server.deploy.generatedproperty;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class UuidGeneratedProperty implements GeneratedProperty {
|
||||
|
||||
@Override
|
||||
public Object getInsertValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return UUID.randomUUID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getUpdateValue(BeanProperty prop, EntityBean bean, long now) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDDLNotNullable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeInAllUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -263,11 +263,9 @@ public final class IdBinderEmbedded implements IdBinder {
|
||||
public Object convertIdFromJson(Object value) {
|
||||
|
||||
Map<String, Object> map = (Map<String, Object>) value;
|
||||
|
||||
EntityBean idValue = idDesc.createEntityBean();
|
||||
for (BeanProperty prop : props) {
|
||||
Object val = map.get(prop.getName());
|
||||
prop.setValue(idValue, val);
|
||||
prop.setValue(idValue, map.get(prop.getName()));
|
||||
}
|
||||
return idValue;
|
||||
}
|
||||
@@ -465,7 +463,14 @@ public final class IdBinderEmbedded implements IdBinder {
|
||||
|
||||
@Override
|
||||
public Object convertId(Object idValue) {
|
||||
// can not cast/convert if it is embedded
|
||||
if (idValue instanceof String) {
|
||||
final EntityBean embId = idDesc.createEntityBean();
|
||||
final String[] rawVals = ((String)idValue).split("\\|");
|
||||
for (int i = 0; i < props.length; i++) {
|
||||
props[i].setValue(embId, props[i].parse(rawVals[i]));
|
||||
}
|
||||
return embId;
|
||||
}
|
||||
return idValue;
|
||||
}
|
||||
|
||||
|
||||
@@ -131,8 +131,6 @@ public class DeployBeanDescriptor<T> {
|
||||
*/
|
||||
private ConcurrencyMode concurrencyMode;
|
||||
|
||||
private boolean updateChangesOnly;
|
||||
|
||||
private List<IndexDefinition> indexDefinitions;
|
||||
|
||||
private String storageEngine;
|
||||
@@ -523,14 +521,6 @@ public class DeployBeanDescriptor<T> {
|
||||
this.concurrencyMode = concurrencyMode;
|
||||
}
|
||||
|
||||
public boolean isUpdateChangesOnly() {
|
||||
return updateChangesOnly;
|
||||
}
|
||||
|
||||
public void setUpdateChangesOnly(boolean updateChangesOnly) {
|
||||
this.updateChangesOnly = updateChangesOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a compound unique constraint.
|
||||
*/
|
||||
|
||||
@@ -45,6 +45,8 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
|
||||
|
||||
private PropertyForeignKey foreignKey;
|
||||
|
||||
boolean orphanRemoval;
|
||||
|
||||
/**
|
||||
* Construct the property.
|
||||
*/
|
||||
@@ -148,6 +150,14 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
|
||||
}
|
||||
}
|
||||
|
||||
public void setOrphanRemoval() {
|
||||
orphanRemoval = true;
|
||||
}
|
||||
|
||||
public boolean isOrphanRemoval() {
|
||||
return orphanRemoval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set DocStoreEmbedded deployment information.
|
||||
*/
|
||||
|
||||
@@ -21,8 +21,6 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
|
||||
|
||||
private String columnPrefix;
|
||||
|
||||
private boolean orphanRemoval;
|
||||
|
||||
/**
|
||||
* Create the property.
|
||||
*/
|
||||
@@ -145,14 +143,6 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public void setOrphanRemoval(boolean orphanRemoval) {
|
||||
this.orphanRemoval = orphanRemoval;
|
||||
}
|
||||
|
||||
public boolean isOrphanRemoval() {
|
||||
return orphanRemoval;
|
||||
}
|
||||
|
||||
public void setJoinType(boolean outerJoin) {
|
||||
tableJoin.setType(outerJoin ? SqlJoinType.OUTER : SqlJoinType.INNER);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import io.ebeaninternal.server.deploy.BeanPropertyIdClass;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyOrderColumn;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertySimpleCollection;
|
||||
import io.ebeaninternal.server.deploy.InheritInfo;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
|
||||
import io.ebeaninternal.server.properties.BeanPropertySetter;
|
||||
import io.ebeaninternal.server.type.ScalarTypeString;
|
||||
@@ -54,6 +53,8 @@ public class DeployBeanPropertyLists {
|
||||
|
||||
private final List<BeanProperty> nonManys = new ArrayList<>();
|
||||
|
||||
private final List<BeanProperty> aggs = new ArrayList<>();
|
||||
|
||||
private final List<BeanPropertyAssocOne<?>> ones = new ArrayList<>();
|
||||
|
||||
private final List<BeanPropertyAssocOne<?>> onesImported = new ArrayList<>();
|
||||
@@ -232,6 +233,9 @@ public class DeployBeanPropertyLists {
|
||||
|
||||
} else {
|
||||
nonManys.add(prop);
|
||||
if (prop.isAggregation()) {
|
||||
aggs.add(prop);
|
||||
}
|
||||
if (prop.isTenantId()) {
|
||||
tenant = prop;
|
||||
}
|
||||
@@ -331,6 +335,10 @@ public class DeployBeanPropertyLists {
|
||||
return nonManys.toArray(new BeanProperty[0]);
|
||||
}
|
||||
|
||||
public BeanProperty[] getAggregates() {
|
||||
return aggs.toArray(new BeanProperty[0]);
|
||||
}
|
||||
|
||||
public BeanPropertyAssocMany<?>[] getMany() {
|
||||
return manys.toArray(new BeanPropertyAssocMany[0]);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package io.ebeaninternal.server.deploy.parse;
|
||||
import io.ebean.annotation.DbForeignKey;
|
||||
import io.ebean.annotation.FetchPreference;
|
||||
import io.ebean.annotation.HistoryExclude;
|
||||
import io.ebean.annotation.PrivateOwned;
|
||||
import io.ebean.annotation.Where;
|
||||
import io.ebean.bean.BeanCollection.ModifyListenMode;
|
||||
import io.ebean.config.BeanNotRegisteredException;
|
||||
@@ -84,14 +83,10 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
if (oneToMany != null) {
|
||||
readToOne(oneToMany, prop);
|
||||
if (readOrphanRemoval(oneToMany)) {
|
||||
prop.setOrphanRemoval();
|
||||
prop.setModifyListenMode(ModifyListenMode.REMOVALS);
|
||||
prop.getCascadeInfo().setDelete(true);
|
||||
}
|
||||
PrivateOwned privateOwned = get(prop, PrivateOwned.class);
|
||||
if (privateOwned != null) {
|
||||
prop.setModifyListenMode(ModifyListenMode.REMOVALS);
|
||||
prop.getCascadeInfo().setDelete(privateOwned.cascadeRemove());
|
||||
}
|
||||
OrderColumn orderColumn = get(prop, OrderColumn.class);
|
||||
if (orderColumn != null) {
|
||||
// need to cascade as we set the order on cascade
|
||||
|
||||
@@ -225,7 +225,9 @@ public class AnnotationAssocOnes extends AnnotationParser {
|
||||
prop.setNullable(propAnn.optional());
|
||||
prop.setFetchType(propAnn.fetch());
|
||||
prop.setMappedBy(propAnn.mappedBy());
|
||||
prop.setOrphanRemoval(readOrphanRemoval(propAnn));
|
||||
if (readOrphanRemoval(propAnn)) {
|
||||
prop.setOrphanRemoval();
|
||||
}
|
||||
if (!"".equals(propAnn.mappedBy())) {
|
||||
prop.setOneToOneExported();
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import io.ebean.annotation.Index;
|
||||
import io.ebean.annotation.InvalidateQueryCache;
|
||||
import io.ebean.annotation.ReadAudit;
|
||||
import io.ebean.annotation.StorageEngine;
|
||||
import io.ebean.annotation.UpdateMode;
|
||||
import io.ebean.annotation.View;
|
||||
import io.ebean.config.TableName;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
|
||||
@@ -135,12 +134,12 @@ public class AnnotationClass extends AnnotationParser {
|
||||
}
|
||||
|
||||
for (Index index : findAnnotationsRecursive(cls, Index.class)) {
|
||||
descriptor.addIndex(new IndexDefinition(index.columnNames(), index.name(), index.unique()));
|
||||
descriptor.addIndex(new IndexDefinition(convertColumnNames(index.columnNames()), index.name(), index.unique()));
|
||||
}
|
||||
|
||||
UniqueConstraint uc = findAnnotationRecursive(cls, UniqueConstraint.class);
|
||||
if (uc != null) {
|
||||
descriptor.addIndex(new IndexDefinition(uc.columnNames()));
|
||||
descriptor.addIndex(new IndexDefinition(convertColumnNames(uc.columnNames())));
|
||||
}
|
||||
|
||||
View view = findAnnotationRecursive(cls, View.class);
|
||||
@@ -151,7 +150,7 @@ public class AnnotationClass extends AnnotationParser {
|
||||
if (table != null) {
|
||||
UniqueConstraint[] uniqueConstraints = table.uniqueConstraints();
|
||||
for (UniqueConstraint c : uniqueConstraints) {
|
||||
descriptor.addIndex(new IndexDefinition(c.columnNames()));
|
||||
descriptor.addIndex(new IndexDefinition(convertColumnNames(c.columnNames())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,11 +189,6 @@ public class AnnotationClass extends AnnotationParser {
|
||||
descriptor.setDbComment(comment.value());
|
||||
}
|
||||
|
||||
UpdateMode updateMode = findAnnotationRecursive(cls, UpdateMode.class);
|
||||
if (updateMode != null) {
|
||||
descriptor.setUpdateChangesOnly(updateMode.updateChangesOnly());
|
||||
}
|
||||
|
||||
if (!disableL2Cache) {
|
||||
Cache cache = findAnnotationRecursive(cls, Cache.class);
|
||||
if (cache != null) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import io.ebean.annotation.CreatedTimestamp;
|
||||
import io.ebean.annotation.DbArray;
|
||||
import io.ebean.annotation.DbComment;
|
||||
import io.ebean.annotation.DbDefault;
|
||||
import io.ebean.annotation.DbHstore;
|
||||
import io.ebean.annotation.DbMap;
|
||||
import io.ebean.annotation.DbJson;
|
||||
import io.ebean.annotation.DbJsonB;
|
||||
import io.ebean.annotation.DbMigration;
|
||||
@@ -202,12 +202,11 @@ public class AnnotationFields extends AnnotationParser {
|
||||
prop.setDbColumn(dbColumn);
|
||||
}
|
||||
|
||||
Id id = get(prop, Id.class);
|
||||
GeneratedValue gen = get(prop, GeneratedValue.class);
|
||||
if (gen != null) {
|
||||
readGenValue(gen, prop);
|
||||
readGenValue(gen, id, prop);
|
||||
}
|
||||
|
||||
Id id = get(prop, Id.class);
|
||||
if (id != null) {
|
||||
readIdScalar(prop);
|
||||
}
|
||||
@@ -246,9 +245,9 @@ public class AnnotationFields extends AnnotationParser {
|
||||
if (comment != null) {
|
||||
prop.setDbComment(comment.value());
|
||||
}
|
||||
DbHstore dbHstore = get(prop, DbHstore.class);
|
||||
if (dbHstore != null) {
|
||||
util.setDbHstore(prop, dbHstore);
|
||||
DbMap dbMap = get(prop, DbMap.class);
|
||||
if (dbMap != null) {
|
||||
util.setDbMap(prop, dbMap);
|
||||
}
|
||||
DbJson dbJson = get(prop, DbJson.class);
|
||||
if (dbJson != null) {
|
||||
@@ -519,8 +518,13 @@ public class AnnotationFields extends AnnotationParser {
|
||||
return util.createDataEncryptSupport(table, column);
|
||||
}
|
||||
|
||||
private void readGenValue(GeneratedValue gen, DeployBeanProperty prop) {
|
||||
|
||||
private void readGenValue(GeneratedValue gen, Id id, DeployBeanProperty prop) {
|
||||
if (id == null) {
|
||||
if (UUID.class.equals(prop.getPropertyType())) {
|
||||
generatedPropFactory.setUuid(prop);
|
||||
return;
|
||||
}
|
||||
}
|
||||
descriptor.setIdGeneratedValue();
|
||||
String genName = gen.generator();
|
||||
|
||||
|
||||
@@ -103,8 +103,7 @@ public abstract class AnnotationParser extends AnnotationBase {
|
||||
void readColumn(Column columnAnn, DeployBeanProperty prop) {
|
||||
|
||||
if (!isEmpty(columnAnn.name())) {
|
||||
String dbColumn = databasePlatform.convertQuotedIdentifiers(columnAnn.name());
|
||||
prop.setDbColumn(dbColumn);
|
||||
prop.setDbColumn(databasePlatform.convertQuotedIdentifiers(columnAnn.name()));
|
||||
}
|
||||
|
||||
prop.setDbInsertable(columnAnn.insertable());
|
||||
@@ -138,4 +137,11 @@ public abstract class AnnotationParser extends AnnotationBase {
|
||||
}
|
||||
return groups.length == 0 || groups.length == 1 && Default.class.isAssignableFrom(groups[0]);
|
||||
}
|
||||
|
||||
String[] convertColumnNames(String[] columnNames) {
|
||||
for (int i = 0; i < columnNames.length; i++) {
|
||||
columnNames[i] = databasePlatform.convertQuotedIdentifiers(columnNames[i]);
|
||||
}
|
||||
return columnNames;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package io.ebeaninternal.server.deploy.parse;
|
||||
|
||||
import io.ebean.Model;
|
||||
import io.ebean.annotation.DbArray;
|
||||
import io.ebean.annotation.DbHstore;
|
||||
import io.ebean.annotation.DbMap;
|
||||
import io.ebean.annotation.DbJson;
|
||||
import io.ebean.annotation.DbJsonB;
|
||||
import io.ebean.annotation.UnmappedJson;
|
||||
@@ -274,7 +274,7 @@ public class DeployCreateProperties {
|
||||
return (AnnotationUtil.findAnnotation(field, DbJson.class) != null)
|
||||
|| (AnnotationUtil.findAnnotation(field, DbJsonB.class) != null)
|
||||
|| (AnnotationUtil.findAnnotation(field, DbArray.class) != null)
|
||||
|| (AnnotationUtil.findAnnotation(field, DbHstore.class) != null)
|
||||
|| (AnnotationUtil.findAnnotation(field, DbMap.class) != null)
|
||||
|| (AnnotationUtil.findAnnotation(field, UnmappedJson.class) != null);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.deploy.parse;
|
||||
|
||||
import io.ebean.annotation.DbArray;
|
||||
import io.ebean.annotation.DbHstore;
|
||||
import io.ebean.annotation.DbMap;
|
||||
import io.ebean.annotation.DbJson;
|
||||
import io.ebean.annotation.DbJsonB;
|
||||
import io.ebean.annotation.DbJsonType;
|
||||
@@ -171,15 +171,15 @@ public class DeployUtil {
|
||||
/**
|
||||
* Map to Postgres HSTORE type (with fallback to JSON storage in VARCHAR).
|
||||
*/
|
||||
void setDbHstore(DeployBeanProperty prop, DbHstore dbHstore) {
|
||||
void setDbMap(DeployBeanProperty prop, DbMap dbMap) {
|
||||
|
||||
ScalarType<?> scalarType = typeManager.getHstoreScalarType();
|
||||
ScalarType<?> scalarType = typeManager.getDbMapScalarType();
|
||||
int dbType = scalarType.getJdbcType();
|
||||
prop.setDbType(dbType);
|
||||
prop.setScalarType(scalarType);
|
||||
if (dbType == Types.VARCHAR) {
|
||||
// this is actually the fallback of JSON storage into VARCHAR
|
||||
int dbLength = dbHstore.length();
|
||||
int dbLength = dbMap.length();
|
||||
int columnLength = (dbLength > 0) ? dbLength : DEFAULT_JSON_VARCHAR_LENGTH;
|
||||
prop.setDbLength(columnLength);
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ public class SimpleExpression extends AbstractValueExpression {
|
||||
return;
|
||||
}
|
||||
if (prop.isDbEncrypted()) {
|
||||
String dsql = prop.getBeanProperty().getDecryptSql();
|
||||
String dsql = prop.getBeanProperty().getDecryptProperty(propName);
|
||||
request.append(dsql).append(type.bind());
|
||||
return;
|
||||
}
|
||||
|
||||
+1
@@ -17,6 +17,7 @@ public class DbExpressionHandlerFactory {
|
||||
return new H2DbExpression();
|
||||
case POSTGRES:
|
||||
return new PostgresDbExpression();
|
||||
case MYSQL55:
|
||||
case MYSQL:
|
||||
return new MySqlDbExpression();
|
||||
case ORACLE:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user