getCustomDeployParsers() {
+ return customDeployParsers;
+ }
+
/**
* Register all the BeanPersistListener instances.
*
@@ -2937,6 +2979,7 @@ protected void loadSettings(PropertiesWrapper p) {
jsonMutationDetection = p.getEnum(MutationDetection.class, "jsonMutationDetection", jsonMutationDetection);
skipDataSourceCheck = p.getBoolean("skipDataSourceCheck", skipDataSourceCheck);
+ skipStart = p.getBoolean("skipStart", skipStart);
runMigration = p.getBoolean("migration.run", runMigration);
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
ddlRun = p.getBoolean("ddl.run", ddlRun);
diff --git a/ebean-api/src/main/java/io/ebean/config/DeleteOnShutdownTempFileProvider.java b/ebean-api/src/main/java/io/ebean/config/DeleteOnShutdownTempFileProvider.java
new file mode 100644
index 0000000000..ad257c98ee
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/config/DeleteOnShutdownTempFileProvider.java
@@ -0,0 +1,68 @@
+package io.ebean.config;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * TempFileProvider implementation, which deletes all temp files on shutdown.
+ *
+ * @author Roland Praml, FOCONIS AG
+ *
+ */
+public class DeleteOnShutdownTempFileProvider implements TempFileProvider {
+
+ private static final Logger logger = LoggerFactory.getLogger(DeleteOnShutdownTempFileProvider.class);
+
+ List tempFiles = new ArrayList<>();
+ private final String prefix;
+ private final String suffix;
+ private final File directory;
+
+ /**
+ * Creates the TempFileProvider with default prefix "db-".
+ */
+ public DeleteOnShutdownTempFileProvider() {
+ this("db-", null, null);
+ }
+
+ /**
+ * Creates the TempFileProvider.
+ */
+ public DeleteOnShutdownTempFileProvider(String prefix, String suffix, File directory) {
+ this.prefix = prefix;
+ this.suffix = suffix;
+ this.directory = directory;
+ }
+
+ @Override
+ public File createTempFile() throws IOException {
+ File file = File.createTempFile(prefix, suffix, directory);
+ synchronized (tempFiles) {
+ tempFiles.add(file.getAbsolutePath());
+ }
+ return file;
+ }
+
+ /**
+ * Deletes all created files on shutdown.
+ */
+ @Override
+ public void shutdown() {
+ synchronized (tempFiles) {
+ for (String path : tempFiles) {
+ if (new File(path).delete()) {
+ logger.trace("deleted {}", path);
+ } else {
+ logger.warn("could not delete {}", path);
+ }
+ }
+ tempFiles.clear();
+ }
+ }
+
+}
diff --git a/ebean-api/src/main/java/io/ebean/config/TempFileProvider.java b/ebean-api/src/main/java/io/ebean/config/TempFileProvider.java
new file mode 100644
index 0000000000..4658b46c28
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/config/TempFileProvider.java
@@ -0,0 +1,23 @@
+package io.ebean.config;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Creates a temp file for the ScalarTypeFile datatype.
+ *
+ * @author Roland Praml, FOCONIS AG
+ *
+ */
+public interface TempFileProvider {
+
+ /**
+ * Creates a tempFile.
+ */
+ File createTempFile() throws IOException;
+
+ /**
+ * Shutdown the tempFileProvider.
+ */
+ void shutdown();
+}
diff --git a/ebean-api/src/main/java/io/ebean/config/WeakRefTempFileProvider.java b/ebean-api/src/main/java/io/ebean/config/WeakRefTempFileProvider.java
new file mode 100644
index 0000000000..aa3ae82905
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/config/WeakRefTempFileProvider.java
@@ -0,0 +1,145 @@
+package io.ebean.config;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * WeakRefTempFileProvider will delete the tempFile if all references to the returned File
+ * object are collected by the garbage collection.
+ *
+ * @author Roland Praml, FOCONIS AG
+ *
+ */
+public class WeakRefTempFileProvider implements TempFileProvider {
+
+ private static final Logger logger = LoggerFactory.getLogger(WeakRefTempFileProvider.class);
+
+ private final ReferenceQueue tempFiles = new ReferenceQueue<>();
+
+ private WeakFileReference root;
+
+ private final String prefix;
+ private final String suffix;
+ private final File directory;
+
+ /**
+ * We hold a linkedList of weak references. So we can remove stale files in O(1)
+ *
+ * @author Roland Praml, FOCONIS AG
+ */
+ private static class WeakFileReference extends WeakReference {
+
+ String path;
+ WeakFileReference prev;
+ WeakFileReference next;
+
+ WeakFileReference(File referent, ReferenceQueue super File> q) {
+ super(referent, q);
+ path = referent.getAbsolutePath();
+ }
+
+ boolean delete(boolean shutdown) {
+ File file = new File(path);
+ if (!file.exists()) {
+ logger.trace("already deleted {}", path);
+ return true;
+ } else if (file.delete()) {
+ logger.trace("deleted {}", path);
+ return true;
+ } else {
+ if (shutdown) {
+ logger.warn("could not delete {}", path);
+ } else {
+ logger.info("could not delete {} - will delete on shutdown", path);
+ }
+ return false;
+ }
+ }
+ }
+
+
+ /**
+ * Creates the TempFileProvider with default prefix "db-".
+ */
+ public WeakRefTempFileProvider() {
+ this("db-", null, null);
+ }
+
+ /**
+ * Creates the TempFileProvider.
+ */
+ public WeakRefTempFileProvider(String prefix, String suffix, File directory) {
+ this.prefix = prefix;
+ this.suffix = suffix;
+ this.directory = directory;
+ }
+
+ @Override
+ public File createTempFile() throws IOException {
+ File tempFile = File.createTempFile(prefix, suffix, directory);
+ logger.trace("createTempFile: {}", tempFile);
+ synchronized (this) {
+ add(new WeakFileReference(tempFile, tempFiles));
+ }
+ return tempFile;
+ }
+
+ /**
+ * Will delete stale files.
+ * This is public to use in tests.
+ */
+ public void deleteStaleTempFiles() {
+ synchronized (this) {
+ deleteStaleTempFilesInternal();
+ }
+ }
+
+ private void deleteStaleTempFilesInternal() {
+ WeakFileReference ref;
+ while ((ref = (WeakFileReference) tempFiles.poll()) != null) {
+ if (ref.delete(false)) {
+ remove(ref); // remove from linkedList only, if delete was successful.
+ }
+ }
+ }
+
+ private void add(WeakFileReference ref) {
+ deleteStaleTempFilesInternal();
+
+ if (root == null) {
+ root = ref;
+ } else {
+ ref.next = root;
+ root.prev = ref;
+ root = ref;
+ }
+ }
+
+ private void remove(WeakFileReference ref) {
+ if (ref.next != null) {
+ ref.next.prev = ref.prev;
+ }
+ if (ref.prev != null) {
+ ref.prev.next = ref.next;
+ } else {
+ root = ref.next;
+ }
+ }
+
+ /**
+ * Deletes all created files on shutdown.
+ */
+ @Override
+ public void shutdown() {
+ while (root != null) {
+ root.delete(true);
+ root = root.next;
+ }
+ }
+
+}
diff --git a/ebean-api/src/main/java/io/ebean/plugin/CustomDeployParser.java b/ebean-api/src/main/java/io/ebean/plugin/CustomDeployParser.java
new file mode 100644
index 0000000000..4c854f2f1e
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/plugin/CustomDeployParser.java
@@ -0,0 +1,15 @@
+package io.ebean.plugin;
+
+import io.ebean.config.dbplatform.DatabasePlatform;
+
+/**
+ * Fired after all beans are parsed. You may implement own parsers to handle custom annotations.
+ * (See test case for example)
+ *
+ * @author Roland Praml, FOCONIS AG
+ */
+@FunctionalInterface
+public interface CustomDeployParser {
+
+ void parse(DeployBeanDescriptorMeta descriptor, DatabasePlatform databasePlatform);
+}
diff --git a/ebean-api/src/main/java/io/ebean/plugin/DeployBeanDescriptorMeta.java b/ebean-api/src/main/java/io/ebean/plugin/DeployBeanDescriptorMeta.java
new file mode 100644
index 0000000000..4347a5057c
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/plugin/DeployBeanDescriptorMeta.java
@@ -0,0 +1,36 @@
+package io.ebean.plugin;
+
+import java.util.Collection;
+
+/**
+ * General deployment information. This is used in {@link CustomDeployParser}.
+ *
+ * @author Roland Praml, FOCONIS AG
+ */
+public interface DeployBeanDescriptorMeta {
+
+ /**
+ * Return a collection of all BeanProperty deployment information.
+ */
+ public Collection extends DeployBeanPropertyMeta> propertiesAll();
+
+ /**
+ * Get a BeanProperty by its name.
+ */
+ public DeployBeanPropertyMeta getBeanProperty(String secondaryBeanName);
+
+ /**
+ * Return the DeployBeanDescriptorMeta for the given bean class.
+ */
+ public DeployBeanDescriptorMeta getDeployBeanDescriptorMeta(Class> propertyType);
+
+ /**
+ * Returns the discriminator column, if any.
+ * @return
+ */
+ public String getDiscriminatorColumn();
+
+ public String getBaseTable();
+
+ DeployBeanPropertyMeta idProperty();
+}
diff --git a/ebean-api/src/main/java/io/ebean/plugin/DeployBeanPropertyAssocMeta.java b/ebean-api/src/main/java/io/ebean/plugin/DeployBeanPropertyAssocMeta.java
new file mode 100644
index 0000000000..d7d1d637a3
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/plugin/DeployBeanPropertyAssocMeta.java
@@ -0,0 +1,23 @@
+package io.ebean.plugin;
+
+public interface DeployBeanPropertyAssocMeta extends DeployBeanPropertyMeta {
+
+ /**
+ * Return the mappedBy deployment attribute.
+ *
+ * This is the name of the property in the 'detail' bean that maps back to
+ * this 'master' bean.
+ *
+ */
+ String getMappedBy();
+
+ /**
+ * Return the base table for this association.
+ *
+ * This has the table name which is used to determine the relationship for
+ * this association.
+ *
+ */
+ String getBaseTable();
+
+}
diff --git a/ebean-api/src/main/java/io/ebean/plugin/DeployBeanPropertyMeta.java b/ebean-api/src/main/java/io/ebean/plugin/DeployBeanPropertyMeta.java
new file mode 100644
index 0000000000..4ba2d3e2a1
--- /dev/null
+++ b/ebean-api/src/main/java/io/ebean/plugin/DeployBeanPropertyMeta.java
@@ -0,0 +1,37 @@
+package io.ebean.plugin;
+
+import java.lang.reflect.Field;
+
+public interface DeployBeanPropertyMeta {
+
+ /**
+ * Return the name of the property.
+ */
+ String getName();
+
+ /**
+ * The database column name this is mapped to.
+ */
+ String getDbColumn();
+
+ /**
+ * Return the bean Field associated with this property.
+ */
+ Field getField();
+
+ /**
+ * The property is based on a formula.
+ */
+ void setSqlFormula(String sqlSelect, String sqlJoin);
+
+ /**
+ * Return the bean type.
+ */
+ Class> getOwningType();
+
+ /**
+ * Return the property type.
+ */
+ Class> getPropertyType();
+
+}
diff --git a/ebean-api/src/main/java/io/ebean/plugin/Plugin.java b/ebean-api/src/main/java/io/ebean/plugin/Plugin.java
index 359adce38b..3314f9353c 100644
--- a/ebean-api/src/main/java/io/ebean/plugin/Plugin.java
+++ b/ebean-api/src/main/java/io/ebean/plugin/Plugin.java
@@ -14,6 +14,13 @@ public interface Plugin {
* Called just before the server starts indicating if it is coming up in online mode.
*/
void online(boolean online);
+
+ /**
+ * Called when server.start is invoked. This may be executed for each tenant.
+ */
+ default void start() {
+
+ };
/**
* Called when the server is shutting down.
diff --git a/ebean-api/src/main/java/module-info.java b/ebean-api/src/main/java/module-info.java
index 7c8453cea6..b7fda66b0a 100644
--- a/ebean-api/src/main/java/module-info.java
+++ b/ebean-api/src/main/java/module-info.java
@@ -41,5 +41,5 @@
exports io.ebean.text.json;
exports io.ebean.text.csv;
exports io.ebean.util;
-
+ exports io.ebean.annotation.ext;
}
diff --git a/ebean-api/src/test/java/io/ebean/TestWeakRefTempFileProvider.java b/ebean-api/src/test/java/io/ebean/TestWeakRefTempFileProvider.java
new file mode 100644
index 0000000000..dc69cc6876
--- /dev/null
+++ b/ebean-api/src/test/java/io/ebean/TestWeakRefTempFileProvider.java
@@ -0,0 +1,171 @@
+package io.ebean;
+
+
+import io.ebean.config.WeakRefTempFileProvider;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.nio.channels.FileLock;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Test for the WeakRefTempFileProvider. (Note: this test relies on an aggressive garbage collection.
+ * if GC implementation will change, the test may fail)
+ *
+ * @author Roland Praml, FOCONIS AG
+ */
+public class TestWeakRefTempFileProvider {
+
+ WeakRefTempFileProvider prov = new WeakRefTempFileProvider();
+
+ @AfterEach
+ public void shutdown() {
+ prov.shutdown();
+ }
+
+ /**
+ * Run the garbage collection and delete stale files.
+ */
+ private void gc() throws InterruptedException {
+ System.gc();
+ Thread.sleep(100);
+ prov.deleteStaleTempFiles();
+ }
+
+ @Test
+ public void testStaleEntries() throws Exception {
+ File tempFile = prov.createTempFile();
+ String fileName = tempFile.getAbsolutePath();
+
+ gc();
+
+ assertThat(new File(fileName)).exists();
+
+ tempFile = null; // give up reference
+
+ gc();
+
+ assertThat(new File(fileName)).doesNotExist();
+
+
+ }
+
+ @Test
+ public void testLinkedListForward() throws Exception {
+ File tempFile1 = prov.createTempFile();
+ String fileName1 = tempFile1.getAbsolutePath();
+ File tempFile2 = prov.createTempFile();
+ String fileName2 = tempFile2.getAbsolutePath();
+ File tempFile3 = prov.createTempFile();
+ String fileName3 = tempFile3.getAbsolutePath();
+
+ assertThat(new File(fileName1)).exists();
+ assertThat(new File(fileName2)).exists();
+ assertThat(new File(fileName3)).exists();
+
+ gc();
+
+ // give up first ref
+ tempFile1 = null;
+
+ gc();
+
+ assertThat(new File(fileName1)).doesNotExist();
+ assertThat(new File(fileName2)).exists();
+ assertThat(new File(fileName3)).exists();
+
+ // give up second ref
+ tempFile2 = null;
+
+ gc();
+
+ assertThat(new File(fileName1)).doesNotExist();
+ assertThat(new File(fileName2)).doesNotExist();
+ assertThat(new File(fileName3)).exists();
+
+ // give up third ref
+ tempFile3 = null;
+
+ gc();
+
+ assertThat(new File(fileName1)).doesNotExist();
+ assertThat(new File(fileName2)).doesNotExist();
+ assertThat(new File(fileName3)).doesNotExist();
+
+ }
+
+
+ @Test
+ public void testLinkedListReverse() throws Exception {
+ File tempFile1 = prov.createTempFile();
+ String fileName1 = tempFile1.getAbsolutePath();
+ File tempFile2 = prov.createTempFile();
+ String fileName2 = tempFile2.getAbsolutePath();
+ File tempFile3 = prov.createTempFile();
+ String fileName3 = tempFile3.getAbsolutePath();
+
+ assertThat(new File(fileName1)).exists();
+ assertThat(new File(fileName2)).exists();
+ assertThat(new File(fileName3)).exists();
+
+ gc();
+
+ // give up third ref
+ tempFile3 = null;
+
+ gc();
+
+ assertThat(new File(fileName1)).exists();
+ assertThat(new File(fileName2)).exists();
+ assertThat(new File(fileName3)).doesNotExist();
+
+ // give up second ref
+ tempFile2 = null;
+
+ gc();
+
+ assertThat(new File(fileName1)).exists();
+ assertThat(new File(fileName2)).doesNotExist();
+ assertThat(new File(fileName3)).doesNotExist();
+
+ // give up first ref
+ tempFile1 = null;
+
+ gc();
+
+ assertThat(new File(fileName1)).doesNotExist();
+ assertThat(new File(fileName2)).doesNotExist();
+ assertThat(new File(fileName3)).doesNotExist();
+
+ }
+
+ @Test
+ @Disabled("Runs on Windows only")
+ public void testFileLocked() throws Exception {
+ File tempFile = prov.createTempFile();
+ String fileName = tempFile.getAbsolutePath();
+
+ try (FileOutputStream os = new FileOutputStream(fileName)) {
+ FileLock lock = os.getChannel().lock();
+ try {
+ os.write(42);
+
+ tempFile = null;
+ gc();
+ } finally {
+ lock.release();
+ }
+
+ }
+
+ assertThat(new File(fileName)).exists();
+
+ prov.shutdown();
+
+ assertThat(new File(fileName)).doesNotExist();
+ }
+}
diff --git a/ebean-autotune/pom.xml b/ebean-autotune/pom.xml
index d87da836bd..85c66a71a1 100644
--- a/ebean-autotune/pom.xml
+++ b/ebean-autotune/pom.xml
@@ -4,7 +4,7 @@
ebean-parent
io.ebean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
@@ -13,8 +13,8 @@
- scm:git:git@github.com:ebean-orm/ebean.git
- HEAD
+ scm:git:git@github.com:FOCONIS/ebean.git
+ ebean-parent-13.6.4-FOC1
ebean autotune
@@ -26,7 +26,7 @@
io.ebean
ebean-core
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
provided
@@ -55,7 +55,7 @@
io.ebean
ebean-platform-h2
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
diff --git a/ebean-bom/pom.xml b/ebean-bom/pom.xml
index ebdcea3a70..a0281a6530 100644
--- a/ebean-bom/pom.xml
+++ b/ebean-bom/pom.xml
@@ -4,7 +4,7 @@
ebean-parent
io.ebean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
ebean bom
@@ -71,88 +71,94 @@
io.ebean
ebean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-api
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-core
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-core-type
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
+
+
+
+ io.ebean
+ ebean-dbmigration-runner
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-ddl-generator
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-externalmapping-api
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-externalmapping-xml
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-autotune
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-querybean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
querybean-generator
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
provided
io.ebean
kotlin-querybean-generator
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
provided
io.ebean
ebean-test
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
io.ebean
ebean-postgis
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-redis
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
diff --git a/ebean-core-type/pom.xml b/ebean-core-type/pom.xml
index f7da6a2568..5bbe71999b 100644
--- a/ebean-core-type/pom.xml
+++ b/ebean-core-type/pom.xml
@@ -4,7 +4,7 @@
ebean-parent
io.ebean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
ebean-core-type
@@ -16,7 +16,7 @@
io.ebean
ebean-api
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml
index 20a71700a1..5bd0d8faad 100644
--- a/ebean-core/pom.xml
+++ b/ebean-core/pom.xml
@@ -3,7 +3,7 @@
ebean-parent
io.ebean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
ebean-core
@@ -14,8 +14,8 @@
https://ebean.io/
- scm:git:git@github.com:ebean-orm/ebean.git
- HEAD
+ scm:git:git@github.com:FOCONIS/ebean.git
+ ebean-parent-13.6.4-FOC1
@@ -32,28 +32,22 @@
7.1
-
- io.ebean
- ebean-migration-auto
- ${ebean-migration-auto.version}
-
-
io.ebean
ebean-api
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-core-type
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-externalmapping-api
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
@@ -144,21 +138,21 @@
io.ebean
ebean-platform-h2
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
io.ebean
ebean-platform-postgres
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
io.ebean
ebean-platform-sqlserver
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiDdlGenerator.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiDdlGenerator.java
deleted file mode 100644
index b3c4858139..0000000000
--- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiDdlGenerator.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package io.ebeaninternal.api;
-
-/**
- * DDL generate and run for drop all/create all.
- */
-public interface SpiDdlGenerator {
-
- /**
- * Generate and run the DDL for drop-all and create-all scripts.
- *
- * Run based on on property settings for ebean.ddl.generate and ebean.ddl.run etc.
- */
- void execute(boolean online);
-
-}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/SpiDdlGeneratorProvider.java b/ebean-core/src/main/java/io/ebeaninternal/api/SpiDdlGeneratorProvider.java
deleted file mode 100644
index d162679573..0000000000
--- a/ebean-core/src/main/java/io/ebeaninternal/api/SpiDdlGeneratorProvider.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package io.ebeaninternal.api;
-
-/**
- * Provides the DDL Generator for create-all/drop-all.
- */
-public interface SpiDdlGeneratorProvider {
-
- /**
- * Provide the DDL generator.
- */
- SpiDdlGenerator generator(SpiEbeanServer server);
-
-}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java
index 70c0e0cf71..fc84373b02 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java
@@ -109,8 +109,12 @@ public SpiEbeanServer createServer(DatabaseConfig config) {
InternalConfiguration c = new InternalConfiguration(online, clusterManager, executor, config, bootupClasses);
DefaultServer server = new DefaultServer(c, c.cacheManager());
// generate and run DDL if required plus other plugins
+
if (!DbOffline.isGenerateMigration()) {
- startServer(online, server);
+ initServer(online, server);
+ if (online && config.getTenantMode().isDdlEnabled() && !config.skipStart()) {
+ server.start();
+ }
}
DbOffline.reset();
log.info("Started database[{}] platform[{}] in {}ms", config.getName(), config.getDatabasePlatform().getPlatform(), System.currentTimeMillis() - start);
@@ -146,7 +150,7 @@ private void checkMissingModulePathProvides() {
}
}
- private void startServer(boolean online, DefaultServer server) {
+ private void initServer(boolean online, DefaultServer server) {
server.executePlugins(online);
// initialise prior to registering with clusterManager
server.initialise();
@@ -155,8 +159,6 @@ private void startServer(boolean online, DefaultServer server) {
clusterManager.registerServer(server);
}
}
- // start any services after registering with clusterManager
- server.start();
}
/**
@@ -173,6 +175,7 @@ private BootupClasses bootupClasses(DatabaseConfig config) {
bootup.addPersistListeners(config.getPersistListeners());
bootup.addQueryAdapters(config.getQueryAdapters());
bootup.addServerConfigStartup(config.getServerConfigStartupListeners());
+ bootup.addCustomDeployParser(config.getCustomDeployParsers());
bootup.addChangeLogInstances(config);
bootup.runServerConfigStartup(config);
return bootup;
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java
index 13e4365fa7..3b96eb9644 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java
@@ -18,6 +18,7 @@
import io.ebean.MergeOptions;
import io.ebean.MergeOptionsBuilder;
import io.ebean.PagedList;
+import io.ebean.PersistVisitor;
import io.ebean.PersistenceContextScope;
import io.ebean.ProfileLocation;
import io.ebean.Query;
@@ -47,7 +48,14 @@
import io.ebean.bean.SingleBeanLoader;
import io.ebean.cache.ServerCacheManager;
import io.ebean.common.CopyOnFirstWriteList;
-import io.ebean.config.*;
+import io.ebean.config.CurrentTenantProvider;
+import io.ebean.config.DatabaseConfig;
+import io.ebean.config.EncryptKeyManager;
+import io.ebean.config.QueryPlanCapture;
+import io.ebean.config.QueryPlanListener;
+import io.ebean.config.SlowQueryEvent;
+import io.ebean.config.SlowQueryListener;
+import io.ebean.config.TempFileProvider;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.event.BeanPersistController;
import io.ebean.event.ShutdownManager;
@@ -58,7 +66,6 @@
import io.ebean.meta.MetricVisitor;
import io.ebean.meta.QueryPlanInit;
import io.ebean.meta.QueryPlanRequest;
-import io.ebean.migration.auto.AutoMigrationRunner;
import io.ebean.plugin.BeanType;
import io.ebean.plugin.Plugin;
import io.ebean.plugin.Property;
@@ -136,6 +143,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private final String serverName;
private final DatabasePlatform databasePlatform;
private final TransactionManager transactionManager;
+ private final TempFileProvider tempFileProvider;
private final QueryPlanManager queryPlanManager;
private final ExtraMetrics extraMetrics;
private final DataTimeZone dataTimeZone;
@@ -153,7 +161,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private final ReadAuditLogger readAuditLogger;
private final CQueryEngine cqueryEngine;
private final List serverPlugins;
- private final SpiDdlGenerator ddlGenerator;
private final ScriptRunner scriptRunner;
private final ExpressionFactory expressionFactory;
private final SpiBackgroundExecutor backgroundExecutor;
@@ -215,7 +222,7 @@ public DefaultServer(InternalConfiguration config, ServerCacheManager cache) {
this.queryPlanManager = config.initQueryPlanManager(transactionManager);
this.metaInfoManager = new DefaultMetaInfoManager(this);
this.serverPlugins = config.getPlugins();
- this.ddlGenerator = config.initDdlGenerator(this);
+ this.tempFileProvider = config.getConfig().getTempFileProvider();
this.scriptRunner = new DScriptRunner(this);
configureServerPlugins();
@@ -245,9 +252,6 @@ private void configureServerPlugins() {
* Execute all the plugins with an online flag indicating the DB is up or not.
*/
public void executePlugins(boolean online) {
- if (!config.isDocStoreOnly()) {
- ddlGenerator.execute(online);
- }
for (Plugin plugin : serverPlugins) {
plugin.online(online);
}
@@ -357,29 +361,15 @@ public void initialise() {
encryptKeyManager.initialise();
}
serverCacheManager.enabledRegions(config.getEnabledL2Regions());
+ startQueryPlanCapture();
}
- /**
- * Start any services after registering with the ClusterManager.
- */
+
+ @Override
public void start() {
- if (config.isRunMigration() && TenantMode.DB != config.getTenantMode()) {
- final AutoMigrationRunner migrationRunner = ServiceUtil.service(AutoMigrationRunner.class);
- if (migrationRunner == null) {
- throw new IllegalStateException("No AutoMigrationRunner found. Probably ebean-migration is not in the classpath?");
- }
- final String dbSchema = config.getDbSchema();
- if (dbSchema != null) {
- migrationRunner.setDefaultDbSchema(dbSchema);
- }
- migrationRunner.setName(config.getName());
- Platform platform = config.getDatabasePlatform().getPlatform();
- migrationRunner.setBasePlatform(platform.base().name().toLowerCase());
- migrationRunner.setPlatform(platform.name().toLowerCase());
- migrationRunner.loadProperties(config.getProperties());
- migrationRunner.run(config.getDataSource());
+ for (Plugin plugin : serverPlugins) {
+ plugin.start();
}
- startQueryPlanCapture();
}
private void startQueryPlanCapture() {
@@ -445,6 +435,8 @@ private void shutdownInternal(boolean shutdownDataSource, boolean deregisterDriv
backgroundExecutor.shutdown();
// shutdown DataSource (if its an Ebean one)
transactionManager.shutdown(shutdownDataSource, deregisterDriver);
+
+ tempFileProvider.shutdown();
dumpMetrics();
shutdown = true;
if (shutdownDataSource) {
@@ -655,7 +647,11 @@ public void clearQueryStatistics() {
*/
@Override
public T createEntityBean(Class type) {
- return descriptor(type).createBean();
+ final BeanDescriptor desc = descriptor(type);
+ if (desc == null) {
+ throw new IllegalArgumentException("No bean type " + type.getName() + " registered");
+ }
+ return desc.createBean();
}
/**
@@ -1638,6 +1634,11 @@ public void save(Object bean, @Nullable Transaction transaction) {
persister.save(checkEntityBean(bean), transaction);
}
+ @Override
+ public void visitSave(Object start, PersistVisitor visitor) {
+ new VisitHandler(descriptorManager).visit(start, visitor);
+ }
+
@Override
public void markAsDirty(Object bean) {
if (!(bean instanceof EntityBean)) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java
index e6944a9e96..d31c91e4b4 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java
@@ -8,6 +8,7 @@
import io.ebean.config.ExternalTransactionManager;
import io.ebean.config.ProfilingConfig;
import io.ebean.config.SlowQueryListener;
+import io.ebean.config.TempFileProvider;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbHistorySupport;
import io.ebean.event.changelog.ChangeLogListener;
@@ -75,6 +76,7 @@ public final class InternalConfiguration {
private final DatabasePlatform databasePlatform;
private final DeployInherit deployInherit;
private final TypeManager typeManager;
+ private final TempFileProvider tempFileProvider;
private final DtoBeanManager dtoBeanManager;
private final ClockService clockService;
private final DataTimeZone dataTimeZone;
@@ -115,6 +117,7 @@ public final class InternalConfiguration {
this.databasePlatform = config.getDatabasePlatform();
this.expressionFactory = initExpressionFactory(config);
this.typeManager = new DefaultTypeManager(config, bootupClasses);
+ this.tempFileProvider = config.getTempFileProvider();
this.multiValueBind = createMultiValueBind(databasePlatform.getPlatform());
this.deployInherit = new DeployInherit(bootupClasses);
this.deployCreateProperties = new DeployCreateProperties(typeManager);
@@ -511,6 +514,10 @@ SpiLogManager getLogManager() {
return logManager;
}
+ public TempFileProvider getTempFileProvider() {
+ return tempFileProvider;
+ }
+
private ServerCachePlugin initServerCachePlugin() {
if (config.isLocalOnlyL2Cache()) {
localL2Caching = true;
@@ -590,30 +597,10 @@ QueryPlanLogger queryPlanLogger(Platform platform) {
return new QueryPlanLoggerSqlServer();
case ORACLE:
return new QueryPlanLoggerOracle();
+ case DB2:
+ return new QueryPlanLoggerDb2();
default:
return new QueryPlanLoggerExplain();
}
}
-
- /**
- * Return the DDL generator.
- */
- public SpiDdlGenerator initDdlGenerator(SpiEbeanServer server) {
- final SpiDdlGeneratorProvider service = service(SpiDdlGeneratorProvider.class);
- return service == null ? new NoopDdl(server.config().isDdlRun()) : service.generator(server);
- }
-
- private static class NoopDdl implements SpiDdlGenerator {
- private final boolean ddlRun;
- NoopDdl(boolean ddlRun) {
- this.ddlRun = ddlRun;
- }
-
- @Override
- public void execute(boolean online) {
- if (online && ddlRun) {
- CoreLog.log.error("Configured to run DDL but ebean-ddl-generator is not in the classpath (or ebean-test in the test classpath?)");
- }
- }
- }
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/OrmQueryEngine.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/OrmQueryEngine.java
index d3ca32b6df..518b3e7cd7 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/core/OrmQueryEngine.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/OrmQueryEngine.java
@@ -34,7 +34,7 @@ public interface OrmQueryEngine {
*/
> A findSingleAttributeCollection(OrmQueryRequest> request, A collection);
- /**
+ /**
* Execute the findVersions query.
*/
List> findVersions(OrmQueryRequest request);
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/SpiOrmQueryRequest.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/SpiOrmQueryRequest.java
index c3f98854b6..1591cf4c67 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/core/SpiOrmQueryRequest.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/SpiOrmQueryRequest.java
@@ -121,7 +121,7 @@ public interface SpiOrmQueryRequest extends BeanQueryRequest, DocQueryRequ
Map findMap();
/**
- * Execute the findSingleAttributeList query.
+ * Execute the findSingleAttributeCollection query.
*/
> A findSingleAttributeCollection(A collection);
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/VisitHandler.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/VisitHandler.java
new file mode 100644
index 0000000000..0ab8d9c880
--- /dev/null
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/VisitHandler.java
@@ -0,0 +1,123 @@
+package io.ebeaninternal.server.core;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import io.ebean.PersistVisitor;
+import io.ebean.bean.EntityBean;
+import io.ebean.bean.EntityBeanIntercept;
+import io.ebeaninternal.server.deploy.BeanDescriptor;
+import io.ebeaninternal.server.deploy.BeanDescriptorManager;
+import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
+import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
+
+/**
+ * Handler to process persist graphs. It will allow you to visit a persist
+ * action (insert/update) and get information about all beans that will be
+ * affected by that action. This allows you to do validation and other things on
+ * a set of beans.
+ *
+ * @author Roland Praml, FOCONIS AG
+ *
+ */
+class VisitHandler {
+
+ private final Set
*/
+ @Override
public String getMappedBy() {
return mappedBy;
}
@@ -173,4 +175,9 @@ public void setFetchPreference(int fetchPreference) {
public void setTargetType(Class> targetType) {
this.targetType = (Class)targetType;
}
+
+ @Override
+ public String getBaseTable() {
+ return getBeanTable().getBaseTable();
+ }
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationClass.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationClass.java
index 956fa66c7b..356973c767 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationClass.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationClass.java
@@ -6,6 +6,8 @@
import io.ebean.annotation.DocStore;
import io.ebean.annotation.Draftable;
import io.ebean.annotation.DraftableElement;
+import io.ebean.annotation.ext.EntityImplements;
+import io.ebean.annotation.ext.EntityOverride;
import io.ebean.annotation.History;
import io.ebean.annotation.Identity;
import io.ebean.annotation.Index;
@@ -15,6 +17,7 @@
import io.ebean.annotation.Tablespace;
import io.ebean.annotation.View;
import io.ebean.config.TableName;
+import io.ebean.util.AnnotationUtil;
import io.ebeaninternal.api.CoreLog;
import io.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import io.ebeaninternal.server.deploy.IndexDefinition;
@@ -31,6 +34,7 @@
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
+import java.util.Set;
import static io.ebean.util.AnnotationUtil.typeGet;
@@ -86,7 +90,7 @@ public void parse() {
*/
private void setTableName() {
if (descriptor.isBaseTableType()) {
- Class> beanType = descriptor.getBeanType();
+ Class> beanType = descriptor.getBaseBeanType();
InheritInfo inheritInfo = descriptor.getInheritInfo();
if (inheritInfo != null) {
beanType = inheritInfo.getRoot().getType();
@@ -202,6 +206,18 @@ private void read(Class> cls) {
for (NamedQuery namedQuery : annotationClassNamedQuery(cls)) {
descriptor.addNamedQuery(namedQuery.name(), namedQuery.query());
}
+
+ Set entityImplements = AnnotationUtil.typeGetAll(cls, EntityImplements.class);
+ for (EntityImplements ann : entityImplements) {
+ for (Class> iface : ann.value()) {
+ descriptor.addInterface(iface);
+ }
+ }
+
+ EntityOverride entityOverride = AnnotationUtil.typeGet(cls, EntityOverride.class);
+ if (entityOverride != null) {
+ descriptor.setOverridePriority(entityOverride.priority());
+ }
}
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/DmlBeanPersister.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/DmlBeanPersister.java
index 043f210aa2..c781fe502d 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/DmlBeanPersister.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/DmlBeanPersister.java
@@ -4,6 +4,7 @@
import io.ebean.util.StringHelper;
import io.ebeaninternal.server.core.PersistRequestBean;
import io.ebeaninternal.server.persist.BeanPersister;
+import io.ebeaninternal.server.type.DataBindCapture;
import java.sql.SQLException;
@@ -70,7 +71,7 @@ private int execute(PersistRequestBean> request, PersistHandler handler) {
}
} catch (SQLException e) {
// log the error to the transaction log
- String msg = "Error[" + StringHelper.removeNewLines(e.getMessage()) + "]";
+ String msg = "Error[" + StringHelper.removeNewLines(e.getMessage()) + "] " + handler;
if (request.transaction().isLogSummary()) {
request.transaction().logSummary(msg);
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/DmlHandler.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/DmlHandler.java
index 7b82e7faa0..a594159872 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/DmlHandler.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dml/DmlHandler.java
@@ -259,4 +259,14 @@ PreparedStatement getPstmtBatch(SpiTransaction t, String sql, PersistRequestBean
return stmt;
}
+ @Override
+ public String toString() {
+ if (sql == null) {
+ return "not yet initialized";
+ } else if (bindLog == null || bindLog.length() == 0) {
+ return sql;
+ } else {
+ return Str.add(sql, " -- bind(", bindLog.toString(), ")");
+ }
+ }
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryEngine.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryEngine.java
index 416da3c311..12f56f2de6 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryEngine.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryEngine.java
@@ -118,6 +118,7 @@ private > A findAttributeCollection(OrmQueryRequest> r
collection = (A) new LinkedHashSet<>(collection);
}
}
+
}
return collection;
} catch (SQLException e) {
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/QueryPlanLoggerDb2.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/QueryPlanLoggerDb2.java
new file mode 100644
index 0000000000..4b892f987e
--- /dev/null
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/QueryPlanLoggerDb2.java
@@ -0,0 +1,55 @@
+
+package io.ebeaninternal.server.query;
+
+import io.ebeaninternal.api.CoreLog;
+import io.ebeaninternal.api.SpiDbQueryPlan;
+import io.ebeaninternal.api.SpiQueryPlan;
+import io.ebeaninternal.server.type.bindcapture.BindCapture;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Random;
+
+/**
+ * A QueryPlanLogger for DB2.
+ *
+ * To use query plan capturing, you have to install the explain tables with
+ * SYSPROC.SYSINSTALLOBJECTS( 'EXPLAIN', 'C' , '', CURRENT SCHEMA )
.
+ * To do this in a repeatable script, you may use this statement:
+ *
+ *
+ * BEGIN
+ * IF NOT EXISTS (SELECT * FROM SYSCAT.TABLES WHERE TABSCHEMA = CURRENT SCHEMA AND TABNAME = 'EXPLAIN_STREAM') THEN
+ * call SYSPROC.SYSINSTALLOBJECTS( 'EXPLAIN', 'C' , '', CURRENT SCHEMA );
+ * END IF;
+ * END
+ *
+ *
+ * @author Roland Praml, FOCONIS AG
+ */
+public final class QueryPlanLoggerDb2 extends QueryPlanLogger {
+
+ private Random rnd = new Random();
+ @Override
+ public SpiDbQueryPlan collectPlan(Connection conn, SpiQueryPlan plan, BindCapture bind) {
+ try (Statement stmt = conn.createStatement()) {
+ int queryNo = rnd.nextInt(Integer.MAX_VALUE);
+ try (PreparedStatement explainStmt = conn
+ .prepareStatement("EXPLAIN PLAN SET QUERYNO=" + queryNo + " FOR " + plan.getSql())) {
+ bind.prepare(explainStmt, conn);
+ explainStmt.execute();
+ }
+
+ try (ResultSet rset = stmt.executeQuery("select * from EXPLAIN_STATEMENT where QUERYNO=" + queryNo)) {
+ return readQueryPlan(plan, bind, rset);
+ }
+ } catch (SQLException e) {
+ CoreLog.log.warn("Could not log query plan", e);
+ return null;
+ }
+ }
+
+}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/text/json/WriteJson.java b/ebean-core/src/main/java/io/ebeaninternal/server/text/json/WriteJson.java
index 17c9e9372b..9a5ed94ad5 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/text/json/WriteJson.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/text/json/WriteJson.java
@@ -506,6 +506,9 @@ private boolean isIncludeProperty(BeanProperty prop) {
return true;
if (currentIncludeProps != null) {
// explicitly controlled by pathProperties
+ if (prop.isId() && currentIncludeProps.contains("${identifier}")) {
+ return true;
+ }
return currentIncludeProps.contains(prop.name());
} else {
// include only loaded properties
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java
index 4fb74db18e..89761e8093 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java
@@ -992,6 +992,9 @@ public final void postCommit() {
public final void preCommit() {
internalBatchFlush();
firePreCommit();
+ // we must flush the batch queue again, because the callback can
+ // modify current transaction
+ internalBatchFlush();
}
/**
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java
index 165d86c5a1..af652be120 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java
@@ -2,7 +2,11 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import io.ebean.annotation.*;
+import io.ebean.annotation.DbEnumType;
+import io.ebean.annotation.DbEnumValue;
+import io.ebean.annotation.EnumValue;
+import io.ebean.annotation.MutationDetection;
+import io.ebean.annotation.Platform;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.JsonConfig;
import io.ebean.config.PlatformConfig;
@@ -61,7 +65,7 @@ public final class DefaultTypeManager implements TypeManager {
private final DefaultTypeFactory extraTypeFactory;
private final ScalarType> hstoreType = new ScalarTypePostgresHstore();
- private final ScalarTypeFile fileType = new ScalarTypeFile();
+ private final ScalarTypeFile fileType;
private final ScalarType> charType = new ScalarTypeChar();
private final ScalarType> charArrayType = new ScalarTypeCharArray();
private final ScalarType> longVarcharType = new ScalarTypeLongVarchar();
@@ -143,6 +147,7 @@ public DefaultTypeManager(DatabaseConfig config, BootupClasses bootupClasses) {
this.arrayTypeSetFactory = arrayTypeSetFactory(config.getDatabasePlatform());
this.offlineMigrationGeneration = DbOffline.isGenerateMigration();
this.defaultEnumType = config.getDefaultEnumType();
+ this.fileType = new ScalarTypeFile(config.getTempFileProvider());
initialiseStandard(config);
initialiseJavaTimeTypes(config);
@@ -269,6 +274,7 @@ public ScalarType> getScalarType(Class> type) {
return found != ScalarTypeNotFound.INSTANCE ? found : null; // Do not return ScalarTypeNotFound, otherwise checks will fail
}
+
/**
* Checks the typeMap for inherited types.
*
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeCalendar.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeCalendar.java
index 9028523b4d..ccc315eb81 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeCalendar.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeCalendar.java
@@ -60,7 +60,7 @@ public Calendar convertFromInstant(Instant ts) {
@Override
protected String toJsonNanos(Calendar value) {
- return String.valueOf(value.getTime());
+ return String.valueOf(value.getTime().getTime());
}
@Override
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeFile.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeFile.java
index be712b6591..b10b206de3 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeFile.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeFile.java
@@ -2,6 +2,7 @@
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
+import io.ebean.config.TempFileProvider;
import io.ebean.core.type.DataBinder;
import io.ebean.core.type.DataReader;
import io.ebean.core.type.DocPropertyType;
@@ -17,26 +18,22 @@
*/
final class ScalarTypeFile extends ScalarTypeBase {
- private final String prefix;
- private final String suffix;
- private final File directory;
+ private final TempFileProvider tempFileProvider;
private final int bufferSize;
/**
- * Construct with reasonable defaults of Blob and 8096 buffer size.
+ * Construct with reasonable defaults of Blob and 8192 buffer size.
*/
- ScalarTypeFile() {
- this(Types.LONGVARBINARY, "db-", null, null, 8096);
+ ScalarTypeFile(TempFileProvider tempFileProvider) {
+ this(Types.LONGVARBINARY, tempFileProvider, 8192);
}
/**
* Create the ScalarTypeFile.
*/
- ScalarTypeFile(int jdbcType, String prefix, String suffix, File directory, int bufferSize) {
+ ScalarTypeFile(int jdbcType, TempFileProvider tempFileProvider, int bufferSize) {
super(File.class, false, jdbcType);
- this.prefix = prefix;
- this.suffix = suffix;
- this.directory = directory;
+ this.tempFileProvider = tempFileProvider;
this.bufferSize = bufferSize;
}
@@ -63,7 +60,7 @@ public File read(DataReader reader) throws SQLException {
}
try {
// stream from db into our temp file
- File tempFile = File.createTempFile(prefix, suffix, directory);
+ File tempFile = tempFileProvider.createTempFile();
OutputStream os = getOutputStream(tempFile);
pump(is, os);
return tempFile;
@@ -106,7 +103,7 @@ public void jsonWrite(JsonGenerator writer, File value) throws IOException {
@Override
public File jsonRead(JsonParser parser) throws IOException {
- File tempFile = File.createTempFile(prefix, suffix, directory);
+ File tempFile = tempFileProvider.createTempFile();
try (OutputStream os = getOutputStream(tempFile)) {
parser.readBinaryValue(os);
os.flush();
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJodaDateMidnight.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJodaDateMidnight.java
index 9a7990a6d7..9b5a194095 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJodaDateMidnight.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJodaDateMidnight.java
@@ -22,7 +22,7 @@ final class ScalarTypeJodaDateMidnight extends ScalarTypeBaseDate
+
+ 4.0.0
+
+ ebean-parent
+ io.ebean
+ 13.6.4-FOC2-SNAPSHOT
+
+
+ ebean dbmigration runner
+ DB Migration runner plugin
+ ebean-dbmigration-runner
+
+
+
+
+ io.ebean
+ ebean-migration-auto
+ ${ebean-migration-auto.version}
+
+
+
+ io.ebean
+ ebean-annotation
+ 8.0
+ provided
+
+
+
+ io.ebean
+ ebean-core
+ 13.6.4-FOC2-SNAPSHOT
+ provided
+
+
+
+ io.ebean
+ ebean-migration
+ ${ebean-migration.version}
+
+
+
+
diff --git a/ebean-dbmigration-runner/src/main/java/io/ebeaninternal/dbmigration/run/DbRunMigrationPlugin.java b/ebean-dbmigration-runner/src/main/java/io/ebeaninternal/dbmigration/run/DbRunMigrationPlugin.java
new file mode 100644
index 0000000000..8b994fd436
--- /dev/null
+++ b/ebean-dbmigration-runner/src/main/java/io/ebeaninternal/dbmigration/run/DbRunMigrationPlugin.java
@@ -0,0 +1,51 @@
+package io.ebeaninternal.dbmigration.run;
+
+import io.ebean.annotation.Platform;
+import io.ebean.config.DatabaseConfig;
+import io.ebean.migration.MigrationConfig;
+import io.ebean.migration.MigrationRunner;
+import io.ebean.plugin.Plugin;
+import io.ebean.plugin.SpiServer;
+
+public class DbRunMigrationPlugin implements Plugin {
+
+ private SpiServer server;
+ private MigrationConfig migrationConfig;
+
+ @Override
+ public void configure(SpiServer server) {
+ this.server = server;
+ DatabaseConfig config = server.config();
+ if (config.isRunMigration()) {
+ migrationConfig = new MigrationConfig();
+ final String dbSchema = config.getDbSchema();
+ if (dbSchema != null) {
+ migrationConfig.setSetCurrentSchema(false);
+ migrationConfig.setDbSchema(dbSchema);
+ }
+ migrationConfig.setName(config.getName());
+ Platform platform = config.getDatabasePlatform().getPlatform();
+ migrationConfig.setBasePlatform(platform.base().name().toLowerCase());
+ migrationConfig.setPlatform(platform.name().toLowerCase());
+ migrationConfig.load(config.getProperties());
+ migrationConfig.setRunPlaceholderMap(config.getDdlPlaceholderMap());
+ }
+ }
+
+ @Override
+ public void online(boolean online) {
+
+ }
+
+ @Override
+ public void shutdown() {
+
+ }
+
+ @Override
+ public void start() {
+ if (migrationConfig != null) {
+ new MigrationRunner(migrationConfig).run(server.dataSource());
+ }
+ }
+}
diff --git a/ebean-dbmigration-runner/src/main/java/module-info.java b/ebean-dbmigration-runner/src/main/java/module-info.java
new file mode 100644
index 0000000000..2841013a31
--- /dev/null
+++ b/ebean-dbmigration-runner/src/main/java/module-info.java
@@ -0,0 +1,10 @@
+module io.ebean.dbmigration.runner {
+
+ provides io.ebean.plugin.Plugin with io.ebeaninternal.dbmigration.run.DbRunMigrationPlugin;
+
+ /*requires transitive io.ebean.ddl.runner;
+ requires transitive io.ebean.core;*/
+ requires transitive io.ebean.annotation;
+ requires io.ebean.api;
+ requires io.ebean.migration;
+}
diff --git a/ebean-dbmigration-runner/src/main/resources/META-INF/services/io.ebean.plugin.Plugin b/ebean-dbmigration-runner/src/main/resources/META-INF/services/io.ebean.plugin.Plugin
new file mode 100644
index 0000000000..873033a797
--- /dev/null
+++ b/ebean-dbmigration-runner/src/main/resources/META-INF/services/io.ebean.plugin.Plugin
@@ -0,0 +1,2 @@
+
+io.ebeaninternal.dbmigration.run.DbRunMigrationPlugin
diff --git a/ebean-ddl-generator/pom.xml b/ebean-ddl-generator/pom.xml
index 5745e1c164..a93475bc36 100644
--- a/ebean-ddl-generator/pom.xml
+++ b/ebean-ddl-generator/pom.xml
@@ -4,7 +4,7 @@
ebean-parent
io.ebean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
ebean ddl generation
@@ -28,14 +28,14 @@
io.ebean
ebean-core-type
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
provided
io.ebean
ebean-core
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
provided
@@ -55,10 +55,17 @@
test
+
+ io.ebean
+ ebean-dbmigration-runner
+ 13.6.4-FOC2-SNAPSHOT
+ test
+
+
io.ebean
ebean-platform-all
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DbMigrationPlugin.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DbMigrationPlugin.java
new file mode 100644
index 0000000000..1839cf4917
--- /dev/null
+++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DbMigrationPlugin.java
@@ -0,0 +1,56 @@
+package io.ebeaninternal.dbmigration;
+
+import java.io.IOException;
+
+import io.ebean.plugin.Plugin;
+import io.ebean.plugin.SpiServer;
+
+/**
+ * Plugin to generate db-migration scripts automatically.
+ * @author Roland Praml, FOCONIS AG
+ */
+public class DbMigrationPlugin implements Plugin {
+
+ private DefaultDbMigration dbMigration;
+
+ private static String lastMigration;
+ private static String lastInit;
+
+ @Override
+ public void configure(SpiServer server) {
+ dbMigration = new DefaultDbMigration();
+ dbMigration.setServer(server);
+ }
+
+ @Override
+ public void online(boolean online) {
+ try {
+ lastInit = null;
+ lastMigration = null;
+ if (dbMigration.generate) {
+ String tmp = lastMigration = dbMigration.generateMigration();
+ if (tmp == null) {
+ return;
+ }
+ }
+ if (dbMigration.generateInit) {
+ lastInit = dbMigration.generateInitMigration();
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Error while generating migration", e);
+ }
+ }
+
+ @Override
+ public void shutdown() {
+ dbMigration = null;
+ }
+
+ public static String getLastInit() {
+ return lastInit;
+ }
+
+ public static String getLastMigration() {
+ return lastMigration;
+ }
+}
diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlGeneratorProvider.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlGeneratorProvider.java
deleted file mode 100644
index b551fa6a05..0000000000
--- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlGeneratorProvider.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package io.ebeaninternal.dbmigration;
-
-import io.ebeaninternal.api.SpiDdlGenerator;
-import io.ebeaninternal.api.SpiDdlGeneratorProvider;
-import io.ebeaninternal.api.SpiEbeanServer;
-
-public class DdlGeneratorProvider implements SpiDdlGeneratorProvider {
-
- @Override
- public SpiDdlGenerator generator(SpiEbeanServer server) {
- return new DdlGenerator(server);
- }
-}
diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlGenerator.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlPlugin.java
similarity index 83%
rename from ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlGenerator.java
rename to ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlPlugin.java
index 74ae5a64c8..1811ffa1f4 100644
--- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlGenerator.java
+++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlPlugin.java
@@ -5,9 +5,10 @@
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.ddlrunner.DdlRunner;
import io.ebean.ddlrunner.ScriptTransform;
+import io.ebean.plugin.Plugin;
+import io.ebean.plugin.SpiServer;
import io.ebean.util.IOUtils;
import io.ebean.util.JdbcClose;
-import io.ebeaninternal.api.SpiDdlGenerator;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.dbmigration.model.CurrentModel;
import io.ebeaninternal.extraddl.model.ExtraDdlXmlReader;
@@ -25,74 +26,54 @@
* Typically the "Create All" DDL is executed for running tests etc and has nothing to do
* with DB Migration (diff based) DDL.
*/
-public class DdlGenerator implements SpiDdlGenerator {
+public class DdlPlugin implements Plugin {
- private static final Logger log = LoggerFactory.getLogger(DdlGenerator.class);
+ private static final Logger log = LoggerFactory.getLogger(DdlPlugin.class);
private static final String[] BUILD_DIRS = {"target", "build"};
- private final SpiEbeanServer server;
+ private SpiEbeanServer server;
- private final boolean generateDdl;
- private final boolean runDdl;
- private final boolean extraDdl;
- private final boolean createOnly;
- private final boolean jaxbPresent;
- private final String dbSchema;
- private final ScriptTransform scriptTransform;
- private final Platform platform;
- private final String platformName;
- private final boolean useMigrationStoredProcedures;
+ private boolean generateDdl;
+ private boolean runDdl;
+ private boolean extraDdl;
+ private boolean createOnly;
+ private boolean jaxbPresent;
+ private String dbSchema;
+ private ScriptTransform scriptTransform;
+ private Platform platform;
+ private String platformName;
+ private boolean useMigrationStoredProcedures;
private CurrentModel currentModel;
private String dropAllContent;
private String createAllContent;
- private final File baseDir;
-
- public DdlGenerator(SpiEbeanServer server) {
- this.server = server;
+ private File baseDir;
+
+ @Override
+ public void configure(SpiServer server) {
+ this.server = (SpiEbeanServer) server;
final DatabaseConfig config = server.config();
- this.jaxbPresent = Detect.isJAXBPresent(config);
this.generateDdl = config.isDdlGenerate();
- this.extraDdl = config.isDdlExtra();
- this.createOnly = config.isDdlCreateOnly();
- this.dbSchema = config.getDbSchema();
- final DatabasePlatform databasePlatform = server.databasePlatform();
- this.platform = databasePlatform.getPlatform();
- this.platformName = platform.base().name();
- if (!config.getTenantMode().isDdlEnabled() && config.isDdlRun()) {
- log.warn("DDL can't be run on startup with TenantMode " + config.getTenantMode());
- this.runDdl = false;
- this.useMigrationStoredProcedures = false;
- } else {
- this.runDdl = config.isDdlRun();
+ this.runDdl = config.isDdlRun() && !config.isDocStoreOnly();
+ if (generateDdl || runDdl) {
+ this.jaxbPresent = Detect.isJAXBPresent(config);
+ this.extraDdl = config.isDdlExtra();
+ this.createOnly = config.isDdlCreateOnly();
+ this.dbSchema = config.getDbSchema();
+ final DatabasePlatform databasePlatform = server.databasePlatform();
+ this.platform = databasePlatform.getPlatform();
+ this.platformName = platform.base().name();
this.useMigrationStoredProcedures = config.getDatabasePlatform().isUseMigrationStoredProcedures();
- }
- this.scriptTransform = createScriptTransform(config);
- this.baseDir = initBaseDir();
- }
-
- private File initBaseDir() {
- for (String buildDir : BUILD_DIRS) {
- File dir = new File(buildDir);
- if (dir.exists() && dir.isDirectory()) {
- return dir;
- }
- }
- return new File(".");
- }
-
- @Override
- public void execute(boolean online) {
- generateDdl();
- if (online) {
- runDdl();
- }
+ this.scriptTransform = createScriptTransform(config);
+ this.baseDir = initBaseDir();
+ }
}
/**
* Generate the DDL drop and create scripts if the properties have been set.
*/
- protected void generateDdl() {
+ @Override
+ public void online(boolean online) {
if (generateDdl) {
if (!createOnly) {
writeDrop(getDropFileName());
@@ -100,11 +81,12 @@ protected void generateDdl() {
writeCreate(getCreateFileName());
}
}
-
+
/**
* Run the DDL drop and DDL create scripts if properties have been set.
*/
- protected void runDdl() {
+ @Override
+ public void start() {
if (runDdl) {
Connection connection = null;
try {
@@ -117,6 +99,20 @@ protected void runDdl() {
}
}
+ @Override
+ public void shutdown() {}
+
+
+ private File initBaseDir() {
+ for (String buildDir : BUILD_DIRS) {
+ File dir = new File(buildDir);
+ if (dir.exists() && dir.isDirectory()) {
+ return dir;
+ }
+ }
+ return new File(".");
+ }
+
private void runDdlWith(Connection connection) {
try {
if (dbSchema != null) {
@@ -201,7 +197,7 @@ protected void runCreateSql(Connection connection) throws IOException {
if (extraDdl && jaxbPresent) {
if (currentModel().isTablePartitioning()) {
String extraPartitioning = ExtraDdlXmlReader.buildPartitioning(platform);
- if (extraPartitioning != null && !extraPartitioning.isEmpty()) {
+ if (extraPartitioning != null && !extraPartitioning.isEmpty() && useMigrationStoredProcedures) {
runScript(connection, false, extraPartitioning, "builtin-partitioning-ddl");
}
}
diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java
index ed592e9d2e..eb36766905 100644
--- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java
+++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java
@@ -1,5 +1,6 @@
package io.ebeaninternal.dbmigration;
+import io.avaje.classpath.scanner.core.Location;
import io.ebean.DB;
import io.ebean.Database;
import io.ebean.annotation.Platform;
@@ -11,6 +12,7 @@
import io.ebean.config.dbplatform.DatabasePlatformProvider;
import io.ebean.dbmigration.DbMigration;
import io.ebean.util.IOUtils;
+import io.ebean.util.StringHelper;
import io.ebeaninternal.api.DbOffline;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions;
@@ -31,6 +33,8 @@
import java.util.List;
import java.util.Properties;
import java.util.ServiceLoader;
+import java.util.StringJoiner;
+
import static io.ebeaninternal.api.PlatformMatch.matchPlatform;
@@ -61,8 +65,8 @@ public class DefaultDbMigration implements DbMigration {
private static final String initialVersion = "1.0";
private static final String GENERATED_COMMENT = "THIS IS A GENERATED FILE - DO NOT MODIFY";
- private final List platformProviders = new ArrayList<>();
- protected final boolean online;
+ private List platformProviders = new ArrayList<>();
+ protected boolean online;
private boolean logToSystemOut = true;
protected SpiEbeanServer server;
protected String pathToResources = "src/main/resources";
@@ -77,8 +81,10 @@ public class DefaultDbMigration implements DbMigration {
protected List platforms = new ArrayList<>();
protected DatabaseConfig databaseConfig;
protected DbConstraintNaming constraintNaming;
+ @Deprecated
protected Boolean strictMode;
- protected Boolean includeGeneratedFileComment;
+ protected boolean includeGeneratedFileComment;
+ @Deprecated
protected String header;
protected String applyPrefix = "";
protected String version;
@@ -88,6 +94,9 @@ public class DefaultDbMigration implements DbMigration {
private int lockTimeoutSeconds;
protected boolean includeBuiltInPartitioning = true;
protected boolean includeIndex;
+ protected boolean generate = false;
+ protected boolean generateInit = false;
+ private boolean keepLastInit = true;
/**
* Create for offline migration generation.
@@ -123,12 +132,66 @@ public void setServerConfig(DatabaseConfig config) {
if (constraintNaming == null) {
this.constraintNaming = databaseConfig.getConstraintNaming();
}
+ if (databasePlatform == null) {
+ this.databasePlatform = databaseConfig.getDatabasePlatform();
+ }
Properties properties = config.getProperties();
if (properties != null) {
- PropertiesWrapper props = new PropertiesWrapper("ebean", config.getName(), properties, null);
+ PropertiesWrapper props = new PropertiesWrapper("ebean", config.getName(), properties, config.getClassLoadConfig());
migrationPath = props.get("migration.migrationPath", migrationPath);
migrationInitPath = props.get("migration.migrationInitPath", migrationInitPath);
pathToResources = props.get("migration.pathToResources", pathToResources);
+ addForeignKeySkipCheck = props.getBoolean("migration.addForeignKeySkipCheck", addForeignKeySkipCheck);
+ applyPrefix = props.get("migration.applyPrefix",applyPrefix);
+ databasePlatform = props.createInstance(DatabasePlatform.class, "migration.databasePlatform", databasePlatform);
+ generatePendingDrop = props.get("migration.generatePendingDrop", generatePendingDrop);
+ includeBuiltInPartitioning = props.getBoolean("migration.includeBuiltInPartitioning", includeBuiltInPartitioning);
+ includeGeneratedFileComment = props.getBoolean("migration.includeGeneratedFileComment", includeGeneratedFileComment);
+ includeIndex = props.getBoolean("migration.includeIndex", includeIndex);
+ lockTimeoutSeconds = props.getInt("migration.lockTimeoutSeconds", lockTimeoutSeconds);
+ logToSystemOut = props.getBoolean("migration.logToSystemOut", logToSystemOut);
+ modelPath = props.get("migration.modelPath", modelPath);
+ modelSuffix = props.get("migration.modelSuffix", modelSuffix);
+ name = props.get("migration.name", name);
+ online = props.getBoolean("migration.online", online);
+ vanillaPlatform = props.getBoolean("migration.vanillaPlatform", vanillaPlatform);
+ version = props.get("migration.version", version);
+ generate = props.getBoolean("migration.generate", generate);
+ generateInit = props.getBoolean("migration.generateInit", generateInit);
+ // header & strictMode must be configured at DatabaseConfig level
+ parsePlatforms(props, config);
+ }
+ }
+
+ protected void parsePlatforms(PropertiesWrapper props, DatabaseConfig config) {
+ String platforms = props.get("migration.platforms");
+ if (platforms == null || platforms.isEmpty()) {
+ return;
+ }
+ String[] tmp = StringHelper.splitNames(platforms);
+ for (String plat : tmp) {
+ DatabasePlatform dbPlatform;
+ String platformName = plat;
+ String platformPrefix = null;
+ int pos = plat.indexOf('=');
+ if (pos != -1) {
+ platformName = plat.substring(0, pos);
+ platformPrefix = plat.substring(pos + 1);
+ }
+
+ if (platformName.indexOf('.') == -1) {
+ // parse platform as enum value
+ Platform platform = Enum.valueOf(Platform.class, platformName.toUpperCase());
+ dbPlatform = platform(platform);
+ } else {
+ // parse platform as class
+ dbPlatform = (DatabasePlatform) config.getClassLoadConfig().newInstance(platformName);
+ }
+ if (platformPrefix == null) {
+ platformPrefix = dbPlatform.getPlatform().name().toLowerCase();
+ }
+
+ addDatabasePlatform(dbPlatform, platformPrefix);
}
}
@@ -319,7 +382,18 @@ private String generateMigrationFor(boolean initMigration) throws IOException {
}
String pendingVersion = generatePendingDrop();
- if (pendingVersion != null) {
+ if ("auto".equals(pendingVersion)) {
+ StringJoiner sj = new StringJoiner(",");
+ String diff = generateDiff(request);
+ if (diff != null) {
+ sj.add(diff);
+ request = createRequest(initMigration);
+ }
+ for (String pendingDrop : request.getPendingDrops()) {
+ sj.add(generatePendingDrop(request, pendingDrop));
+ }
+ return sj.length() == 0 ? null : sj.toString();
+ } else if (pendingVersion != null) {
return generatePendingDrop(request, pendingVersion);
} else {
return generateDiff(request);
@@ -376,6 +450,7 @@ private void configurePlatforms() {
private void generateExtraDdl(File migrationDir, DatabasePlatform dbPlatform, boolean tablePartitioning) throws IOException {
if (dbPlatform != null) {
if (tablePartitioning && includeBuiltInPartitioning) {
+
generateExtraDdlFor(migrationDir, dbPlatform, ExtraDdlXmlReader.readBuiltinTablePartitioning(), false);
}
// skip built-in migration stored procedures based on isUseMigrationStoredProcedures
@@ -384,6 +459,7 @@ private void generateExtraDdl(File migrationDir, DatabasePlatform dbPlatform, bo
}
}
+
private void generateExtraDdlFor(File migrationDir, DatabasePlatform dbPlatform, ExtraDdl extraDdl, boolean checkSkip) throws IOException {
if (extraDdl != null) {
List ddlScript = extraDdl.getDdlScript();
@@ -554,7 +630,7 @@ private String generateMigration(Request request, Migration dbMigration, String
return null;
} else {
if (!platforms.isEmpty()) {
- writeExtraPlatformDdl(fullVersion, request.currentModel, dbMigration, request.migrationDir);
+ writeExtraPlatformDdl(fullVersion, request.currentModel, dbMigration, request.migrationDir, request.initMigration && keepLastInit);
} else if (databasePlatform != null) {
// writer needs the current model to provide table/column details for
@@ -634,12 +710,17 @@ private String toUnderScore(String name) {
/**
* Write any extra platform ddl.
*/
- private void writeExtraPlatformDdl(String fullVersion, CurrentModel currentModel, Migration dbMigration, File writePath) throws IOException {
+ private void writeExtraPlatformDdl(String fullVersion, CurrentModel currentModel, Migration dbMigration, File writePath, boolean clear) throws IOException {
DdlOptions options = new DdlOptions(addForeignKeySkipCheck);
for (Pair pair : platforms) {
DdlWrite writer = new DdlWrite(new MConfiguration(), currentModel.read(), options);
PlatformDdlWriter platformWriter = createDdlWriter(pair.platform);
File subPath = platformWriter.subPath(writePath, pair.prefix);
+ if (clear) {
+ for (File existing : subPath.listFiles()) {
+ existing.delete();
+ }
+ }
platformWriter.processMigration(dbMigration, writer, subPath, fullVersion);
}
}
@@ -657,7 +738,7 @@ private boolean writeMigrationXml(Migration dbMigration, File resourcePath, Stri
if (file.exists()) {
return false;
}
- String comment = Boolean.TRUE.equals(includeGeneratedFileComment) ? GENERATED_COMMENT : null;
+ String comment = includeGeneratedFileComment ? GENERATED_COMMENT : null;
MigrationXmlWriter xmlWriter = new MigrationXmlWriter(comment);
xmlWriter.write(dbMigration, file);
return true;
@@ -675,6 +756,7 @@ private void setDefaults() {
databasePlatform = server.databasePlatform();
}
if (databaseConfig != null) {
+ // FIXME: Copy header and StrictMode to databaseConfig
if (strictMode != null) {
databaseConfig.setDdlStrictMode(strictMode);
}
@@ -749,15 +831,20 @@ public File migrationDirectory() {
* Return the file path to write the xml and sql to.
*/
File migrationDirectory(boolean initMigration) {
- // path to src/main/resources in typical maven project
- File resourceRootDir = new File(pathToResources);
- if (!resourceRootDir.exists()) {
- String msg = String.format("Error - path to resources %s does not exist. Absolute path is %s", pathToResources, resourceRootDir.getAbsolutePath());
- throw new UnknownResourcePathException(msg);
- }
- String resourcePath = migrationPath(initMigration);
+ Location resourcePath = migrationPath(initMigration);
// expect to be a path to something like - src/main/resources/dbmigration
- File path = new File(resourceRootDir, resourcePath);
+ File path;
+ if (resourcePath.isClassPath()) {
+ // path to src/main/resources in typical maven project
+ File resourceRootDir = new File(pathToResources);
+ if (!resourceRootDir.exists()) {
+ String msg = String.format("Error - path to resources %s does not exist. Absolute path is %s", pathToResources, resourceRootDir.getAbsolutePath());
+ throw new UnknownResourcePathException(msg);
+ }
+ path = new File(resourceRootDir, resourcePath.path());
+ } else {
+ path = new File(resourcePath.path());
+ }
if (!path.exists()) {
if (!path.mkdirs()) {
logInfo("Warning - Unable to ensure migration directory exists at %s", path.getAbsolutePath());
@@ -766,8 +853,9 @@ File migrationDirectory(boolean initMigration) {
return path;
}
- private String migrationPath(boolean initMigration) {
- return initMigration ? migrationInitPath : migrationPath;
+ private Location migrationPath(boolean initMigration) {
+ // remove classpath: or filesystem: prefix
+ return new Location(initMigration ? migrationInitPath : migrationPath);
}
/**
diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdl.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdl.java
index f7e8035490..17e6a3b26c 100644
--- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdl.java
+++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdl.java
@@ -141,7 +141,7 @@ private String translate(String ddl, String tableName, String columnName, String
private void handleStrictError(String tableName, String columnName) {
if (strictMode) {
- String message = "DB Migration of non-null column with no default value specified for: " + tableName + "." + columnName+" Use @DbDefault to specify a default value or specify dbMigration.setStrictMode(false)";
+ String message = "DB Migration of non-null column with no default value specified for: " + tableName + "." + columnName+" Use @DbDefault to specify a default value or disable strict mode for migration";
throw new IllegalArgumentException(message);
}
}
@@ -207,6 +207,7 @@ public void generate(DdlWrite writer, CreateTable createTable) {
writeInlineForeignKeys(apply, createTable);
}
apply.newLine().append(")");
+
addTableTableSpaces(apply, createTable);
addTableStorageEngine(apply, createTable);
addTableCommentInline(apply, createTable);
diff --git a/ebean-ddl-generator/src/main/java/module-info.java b/ebean-ddl-generator/src/main/java/module-info.java
index 1a7ef332e4..e22d066a08 100644
--- a/ebean-ddl-generator/src/main/java/module-info.java
+++ b/ebean-ddl-generator/src/main/java/module-info.java
@@ -1,9 +1,15 @@
module io.ebean.ddl.generator {
+
+ uses io.ebean.dbmigration.DbMigration;
+ uses io.ebean.plugin.Plugin;
+ uses io.ebean.config.dbplatform.DatabasePlatformProvider;
+
+
exports io.ebean.dbmigration;
+ provides io.ebean.plugin.Plugin with io.ebeaninternal.dbmigration.DbMigrationPlugin,io.ebeaninternal.dbmigration.DdlPlugin;
provides io.ebean.dbmigration.DbMigration with io.ebeaninternal.dbmigration.DefaultDbMigration;
- provides io.ebeaninternal.api.SpiDdlGeneratorProvider with io.ebeaninternal.dbmigration.DdlGeneratorProvider;
requires transitive io.ebean.ddl.runner;
requires transitive io.ebean.core;
@@ -11,8 +17,9 @@
requires io.ebean.core.type;
requires io.ebean.migration;
- uses io.ebean.dbmigration.DbMigration;
// support existing tests
exports io.ebeaninternal.extraddl.model to io.ebean.test;
+ opens io.ebeaninternal.extraddl.model to java.xml.bind;
+ opens io.ebeaninternal.dbmigration.migration to java.xml.bind;
}
diff --git a/ebean-ddl-generator/src/main/resources/META-INF/services/io.ebean.plugin.Plugin b/ebean-ddl-generator/src/main/resources/META-INF/services/io.ebean.plugin.Plugin
new file mode 100644
index 0000000000..8a340c377a
--- /dev/null
+++ b/ebean-ddl-generator/src/main/resources/META-INF/services/io.ebean.plugin.Plugin
@@ -0,0 +1,2 @@
+io.ebeaninternal.dbmigration.DdlPlugin
+io.ebeaninternal.dbmigration.DbMigrationPlugin
diff --git a/ebean-ddl-generator/src/main/resources/META-INF/services/io.ebeaninternal.api.SpiDdlGeneratorProvider b/ebean-ddl-generator/src/main/resources/META-INF/services/io.ebeaninternal.api.SpiDdlGeneratorProvider
deleted file mode 100644
index bfef4384b4..0000000000
--- a/ebean-ddl-generator/src/main/resources/META-INF/services/io.ebeaninternal.api.SpiDdlGeneratorProvider
+++ /dev/null
@@ -1 +0,0 @@
-io.ebeaninternal.dbmigration.DdlGeneratorProvider
diff --git a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/MChecksumTest.java b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/MChecksumTest.java
index 3bd677a7ef..035b905830 100644
--- a/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/MChecksumTest.java
+++ b/ebean-ddl-generator/src/test/java/io/ebeaninternal/dbmigration/MChecksumTest.java
@@ -10,10 +10,10 @@ public class MChecksumTest {
@Test
public void calculate() {
- File file = new File("src/test/resources/dbmigration/index/1.0__hello.sql");
+ File file = new File("src/test/resources/dbmigration/index-special-chars/1.0__hello.sql");
assertThat(file).exists();
- assertThat(MChecksum.calculate(file)).isEqualTo(907060870);
+ assertThat(MChecksum.calculate(file)).isEqualTo(1859426839);
}
@Test
diff --git a/ebean-ddl-generator/src/test/resources/application-test.properties b/ebean-ddl-generator/src/test/resources/application-test.properties
index dfcb052ab0..af973bb3a5 100644
--- a/ebean-ddl-generator/src/test/resources/application-test.properties
+++ b/ebean-ddl-generator/src/test/resources/application-test.properties
@@ -5,32 +5,12 @@ datasource.default=h2
datasource.h2.username=sa
datasource.h2.password=
-datasource.h2.url=jdbc:h2:mem:h2AutoTune
+datasource.h2.url=jdbc:h2:mem:h2AutoTune;NON_KEYWORDS=KEY,VALUE
+
+datasource.db2.username=migtest
+datasource.db2.password=migtest
+datasource.db2.url=jdbc:db2://localhost:50005/migtest
datasource.pg.username=sa
datasource.pg.password=
datasource.pg.url=jdbc:h2:mem:h2AutoTune
-
-# parameters for migration test
-datasource.migrationtest.username=SA
-datasource.migrationtest.password=SA
-datasource.migrationtest.url=jdbc:h2:mem:migration
-ebean.migrationtest.applyPrefix=V
-ebean.migrationtest.ddl.generate=false
-ebean.migrationtest.ddl.run=false
-ebean.migrationtest.ddl.header=-- Migrationscripts for ebean unittest
-ebean.migrationtest.migration.appName=migrationtest
-ebean.migrationtest.migration.migrationPath=dbmigration/migrationtest
-ebean.migrationtest.migration.strict=true
-
-# parameters for migration test
-datasource.migrationtest-history.username=SA
-datasource.migrationtest-history.password=SA
-datasource.migrationtest-history.url=jdbc:h2:mem:migration
-ebean.migrationtest-history.applyPrefix=V
-ebean.migrationtest-history.ddl.generate=false
-ebean.migrationtest-history.ddl.run=false
-ebean.migrationtest-history.ddl.header=-- Migrationscripts for ebean unittest DbMigrationDropHistoryTest
-ebean.migrationtest-history.migration.appName=migrationtest-history
-ebean.migrationtest-history.migration.migrationPath=dbmigration/migrationtest-history
-ebean.migrationtest-history.migration.strict=true
diff --git a/ebean-externalmapping-api/pom.xml b/ebean-externalmapping-api/pom.xml
index 8c60176730..265859aa09 100644
--- a/ebean-externalmapping-api/pom.xml
+++ b/ebean-externalmapping-api/pom.xml
@@ -4,7 +4,7 @@
ebean-parent
io.ebean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
ebean external mapping api
diff --git a/ebean-externalmapping-xml/pom.xml b/ebean-externalmapping-xml/pom.xml
index dff67f11b3..929bf882a5 100644
--- a/ebean-externalmapping-xml/pom.xml
+++ b/ebean-externalmapping-xml/pom.xml
@@ -4,12 +4,12 @@
ebean-parent
io.ebean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
- scm:git:git@github.com:ebean-orm/ebean.git
- HEAD
+ scm:git:git@github.com:FOCONIS/ebean.git
+ ebean-parent-13.6.4-FOC1
ebean external mapping xml
@@ -28,7 +28,7 @@
io.ebean
ebean-externalmapping-api
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
@@ -61,21 +61,21 @@
io.ebean
ebean-platform-h2
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
io.ebean
ebean-core
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
io.ebean
ebean-ddl-generator
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
diff --git a/ebean-kotlin/pom.xml b/ebean-kotlin/pom.xml
index 72fce9834a..ac25402bd4 100644
--- a/ebean-kotlin/pom.xml
+++ b/ebean-kotlin/pom.xml
@@ -1,12 +1,10 @@
-
+
4.0.0
ebean-parent
io.ebean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
ebean-kotlin
@@ -28,7 +26,7 @@
io.ebean
ebean-core
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
provided
@@ -50,7 +48,7 @@
io.ebean
ebean-test
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
diff --git a/ebean-postgis/pom.xml b/ebean-postgis/pom.xml
index 0117b699a5..2f73275211 100644
--- a/ebean-postgis/pom.xml
+++ b/ebean-postgis/pom.xml
@@ -4,7 +4,7 @@
ebean-parent
io.ebean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
ebean postgis
@@ -22,14 +22,14 @@
io.ebean
ebean-platform-postgres
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-core
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
provided
@@ -73,7 +73,7 @@
io.ebean
ebean-test
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
diff --git a/ebean-querybean/pom.xml b/ebean-querybean/pom.xml
index 9f531ac998..6def789a13 100644
--- a/ebean-querybean/pom.xml
+++ b/ebean-querybean/pom.xml
@@ -4,7 +4,7 @@
ebean-parent
io.ebean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
ebean querybean
@@ -17,7 +17,7 @@
io.ebean
ebean-core
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
provided
@@ -63,21 +63,21 @@
io.ebean
ebean-ddl-generator
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
io.ebean
querybean-generator
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
io.ebean
ebean-test
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
diff --git a/ebean-redis/pom.xml b/ebean-redis/pom.xml
index eb8a2f6c55..e17a465717 100644
--- a/ebean-redis/pom.xml
+++ b/ebean-redis/pom.xml
@@ -4,7 +4,7 @@
ebean-parent
io.ebean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
ebean-redis
@@ -22,35 +22,35 @@
io.ebean
ebean-api
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
provided
io.ebean
ebean-core
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
provided
io.ebean
ebean-querybean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
io.ebean
querybean-generator
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
io.ebean
ebean-test
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml
index bf0d252364..ecd26ae8bb 100644
--- a/ebean-test/pom.xml
+++ b/ebean-test/pom.xml
@@ -4,7 +4,7 @@
ebean-parent
io.ebean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
ebean test
@@ -29,20 +29,20 @@
io.ebean
ebean-platform-h2
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-core
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
provided
io.ebean
ebean-ddl-generator
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
@@ -106,14 +106,14 @@
io.ebean
ebean-platform-all
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
io.ebean
- ebean-migration
- 12.13.0
+ ebean-dbmigration-runner
+ 13.6.4-FOC2-SNAPSHOT
test
diff --git a/ebean-test/src/main/java/module-info.java b/ebean-test/src/main/java/module-info.java
index a23d1e65b8..5d86859fb6 100644
--- a/ebean-test/src/main/java/module-info.java
+++ b/ebean-test/src/main/java/module-info.java
@@ -1,5 +1,5 @@
-
-module io.ebean.test {
+// module must be open, so tests will pass
+open module io.ebean.test {
exports io.ebean.test;
exports io.ebean.test.config;
diff --git a/ebean-test/src/test/java/io/ebean/test/config/TestServerOffline.java b/ebean-test/src/test/java/io/ebean/test/config/TestServerOffline.java
new file mode 100644
index 0000000000..c778c870a1
--- /dev/null
+++ b/ebean-test/src/test/java/io/ebean/test/config/TestServerOffline.java
@@ -0,0 +1,154 @@
+package io.ebean.test.config;
+
+
+import io.ebean.Database;
+import io.ebean.DatabaseFactory;
+import io.ebean.annotation.Platform;
+import io.ebean.config.DatabaseConfig;
+import io.ebean.datasource.DataSourceAlert;
+import io.ebean.datasource.DataSourceInitialiseException;
+import io.ebean.xtest.ForPlatform;
+
+import io.ebean.xtest.base.PlatformCondition;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.tests.model.basic.EBasicVer;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Properties;
+
+import javax.persistence.PersistenceException;
+import javax.sql.DataSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+@ExtendWith(PlatformCondition.class)
+public class TestServerOffline {
+
+ @Test
+ @ForPlatform({Platform.H2})
+ public void testOffline_default() throws SQLException {
+
+ String url = "jdbc:h2:mem:testoffline1";
+ try (Connection bootup = DriverManager.getConnection(url, "sa", "secret")) {
+ Properties props = props(url);
+ DatabaseConfig config = config(props);
+
+ assertThatThrownBy(() -> DatabaseFactory.create(config))
+ .isInstanceOf(DataSourceInitialiseException.class);
+ }
+
+ }
+
+ private static class LazyDatasourceInitializer implements DataSourceAlert {
+
+ public Database server;
+
+ private boolean initialized;
+
+ @Override
+ public void dataSourceUp(DataSource dataSource) {
+ if (!initialized) {
+ initDatabase();
+ }
+ }
+
+ public synchronized void initDatabase() {
+ if (!initialized) {
+ server.start();
+ initialized = true;
+ }
+ }
+
+ @Override
+ public void dataSourceDown(DataSource dataSource, SQLException reason) {}
+
+ @Override
+ public void dataSourceWarning(DataSource dataSource, String msg) {}
+
+ }
+
+ @Test
+ @ForPlatform({Platform.H2})
+ public void testOffline_recovery() throws SQLException {
+
+ String url = "jdbc:h2:mem:testoffline3";
+ try (Connection bootup = DriverManager.getConnection(url, "sa", "secret")) {
+
+ Properties props = props(url);
+
+ // to bring up ebean without a database, we must disable various things
+ // that happen on startup
+ props.setProperty("datasource.h2_offline.failOnStart", "false");
+ props.setProperty("ebean.h2_offline.skipDataSourceCheck", "true");
+ props.setProperty("ebean.h2_offline.skipStart", "true");
+ DatabaseConfig config = config(props);
+
+ LazyDatasourceInitializer alert = new LazyDatasourceInitializer() ;
+ config.getDataSourceConfig().setAlert(alert);
+ config.getDataSourceConfig().setHeartbeatFreqSecs(1);
+
+ Database h2Offline = DatabaseFactory.create(config);
+ alert.server = h2Offline;
+ assertThat(h2Offline).isNotNull();
+ // DB is online now in offline mode
+
+ // Accessing the DB will throw a PE
+ assertThatThrownBy(() -> alert.initDatabase())
+ .isInstanceOf(PersistenceException.class)
+ .hasMessageContaining("Failed to obtain connection to run DDL");
+
+ assertThatThrownBy(() -> h2Offline.find(EBasicVer.class).findCount()).isInstanceOf(PersistenceException.class);
+
+ // so - reset the password so that the server can reconnect
+ try (Statement stmt = bootup.createStatement()) {
+ stmt.execute("alter user sa set password 'sa'");
+ }
+
+ assertThat(alert.initialized).isFalse();
+
+ // next access to ebean should bring DS online
+ h2Offline.find(EBasicVer.class).findCount();
+ assertThat(alert.initialized).isTrue();
+
+ // check if server is working (ie ddl was run)
+ EBasicVer bean = new EBasicVer("foo");
+ h2Offline.save(bean);
+ assertThat(h2Offline.find(EBasicVer.class).findCount()).isEqualTo(1);
+ h2Offline.delete(bean);
+ }
+ }
+
+ private Properties props(String url) {
+
+ Properties props = new Properties();
+
+ props.setProperty("datasource.h2_offline.username", "sa");
+ props.setProperty("datasource.h2_offline.password", "sa");
+ props.setProperty("datasource.h2_offline.url", url);
+ props.setProperty("datasource.h2_offline.driver", "org.h2.Driver");
+
+ props.setProperty("ebean.h2_offline.databasePlatformName", "h2");
+ props.setProperty("ebean.h2_offline.ddl.extra", "false");
+
+ props.setProperty("ebean.h2_offline.ddl.generate", "true");
+ props.setProperty("ebean.h2_offline.ddl.run", "true");
+
+ return props;
+ }
+
+ private DatabaseConfig config(Properties props) {
+ DatabaseConfig config = new DatabaseConfig();
+ config.setName("h2_offline");
+ config.loadFromProperties(props);
+ config.setDefaultServer(false);
+ config.setRegister(false);
+ config.getClasses().add(EBasicVer.class);
+ return config;
+ }
+
+}
diff --git a/ebean-test/src/test/java/io/ebean/xtest/base/EbeanServerFactory_MultiTenancy_Test.java b/ebean-test/src/test/java/io/ebean/xtest/base/EbeanServerFactory_MultiTenancy_Test.java
index 0dcc4afc28..2915657281 100644
--- a/ebean-test/src/test/java/io/ebean/xtest/base/EbeanServerFactory_MultiTenancy_Test.java
+++ b/ebean-test/src/test/java/io/ebean/xtest/base/EbeanServerFactory_MultiTenancy_Test.java
@@ -49,6 +49,39 @@ public void create_new_server_with_multi_tenancy_db() {
+ /**
+ * Tests using multi tenancy per database
+ */
+ @Test
+ public void create_new_server_with_multi_tenancy_db_with_master() {
+
+ String tenant = "customer";
+ CurrentTenantProvider tenantProvider = Mockito.mock(CurrentTenantProvider.class);
+ Mockito.doReturn(tenant).when(tenantProvider).currentId();
+
+ TenantDataSourceProvider dataSourceProvider = Mockito.mock(TenantDataSourceProvider.class);
+
+ DatabaseConfig config = new DatabaseConfig();
+
+ config.setName("h2");
+ config.loadFromProperties();
+ config.setRegister(false);
+ config.setDefaultServer(false);
+ config.setDdlGenerate(false);
+ config.setDdlRun(false);
+
+ config.setTenantMode(TenantMode.DB_WITH_MASTER);
+ config.setCurrentTenantProvider(tenantProvider);
+ config.setTenantDataSourceProvider(dataSourceProvider);
+
+ Mockito.doReturn(config.getDataSource()).when(dataSourceProvider).dataSource(tenant);
+
+ config.setDatabasePlatform(new PostgresPlatform());
+
+ final Database database = DatabaseFactory.create(config);
+ database.shutdown();
+ }
+
/**
* Tests using multi tenancy per schema
*/
diff --git a/ebean-test/src/test/java/io/ebean/xtest/base/ServerStartTest.java b/ebean-test/src/test/java/io/ebean/xtest/base/ServerStartTest.java
new file mode 100644
index 0000000000..b0dfe787b9
--- /dev/null
+++ b/ebean-test/src/test/java/io/ebean/xtest/base/ServerStartTest.java
@@ -0,0 +1,15 @@
+package io.ebean.xtest.base;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import io.ebean.DatabaseFactory;
+
+public class ServerStartTest {
+
+ @Test
+ @Disabled("run manually")
+ void testServerStartAndMigrateDb2() throws Exception {
+ DatabaseFactory.create("db2-migration").shutdown();
+ }
+}
diff --git a/ebean-test/src/test/java/io/ebean/xtest/dbmigration/DbMigrationDropHistoryTest.java b/ebean-test/src/test/java/io/ebean/xtest/dbmigration/DbMigrationDropHistoryTest.java
index 4f5471c62c..df9850d57f 100644
--- a/ebean-test/src/test/java/io/ebean/xtest/dbmigration/DbMigrationDropHistoryTest.java
+++ b/ebean-test/src/test/java/io/ebean/xtest/dbmigration/DbMigrationDropHistoryTest.java
@@ -79,13 +79,11 @@ public static void main(String[] args) throws IOException {
List pendingDrops = migration.getPendingDrops();
assertThat(pendingDrops).contains("1.1");
- //System.setProperty("ddl.migration.pendingDropsFor", "1.1");
migration.setGeneratePendingDrop("1.1");
assertThat(migration.generateMigration()).isEqualTo("1.2__dropsFor_1.1");
assertThatThrownBy(()->migration.generateMigration())
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("No 'pendingDrops'"); // subsequent call
- System.clearProperty("ddl.migration.pendingDropsFor");
server.shutdown();
logger.info("end");
diff --git a/ebean-test/src/test/java/io/ebean/xtest/dbmigration/DbMigrationGenerateTest.java b/ebean-test/src/test/java/io/ebean/xtest/dbmigration/DbMigrationGenerateTest.java
index df70742162..bf217cb20c 100644
--- a/ebean-test/src/test/java/io/ebean/xtest/dbmigration/DbMigrationGenerateTest.java
+++ b/ebean-test/src/test/java/io/ebean/xtest/dbmigration/DbMigrationGenerateTest.java
@@ -4,6 +4,8 @@
import io.ebean.DatabaseFactory;
import io.ebean.annotation.Platform;
import io.ebean.config.DatabaseConfig;
+import io.ebeaninternal.api.DbOffline;
+
import io.ebean.dbmigration.DbMigration;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
@@ -17,7 +19,6 @@
import java.util.Arrays;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
@@ -96,7 +97,7 @@ public static void run(String pathToResources) throws IOException {
config.getProperties().put("ebean.hana.generateUniqueDdl", "true"); // need to generate unique statements to prevent them from being filtered out as duplicates by the DdlRunner
config.setPackages(Arrays.asList("misc.migration.v1_0"));
- Database server = DatabaseFactory.create(config);
+ Database server = createServer(config);
migration.setServer(server);
// then we generate migration scripts for v1_0
@@ -107,43 +108,29 @@ public static void run(String pathToResources) throws IOException {
// and now for v1_1
config.setPackages(Arrays.asList("misc.migration.v1_1"));
server.shutdown();
- server = DatabaseFactory.create(config);
+ server = createServer(config);
migration.setServer(server);
- assertThat(migration.generateMigration()).isEqualTo("1.1");
- assertThat(migration.generateMigration()).isNull(); // subsequent call
-
-
-
- System.setProperty("ddl.migration.pendingDropsFor", "1.1");
- assertThat(migration.generateMigration()).isEqualTo("1.2__dropsFor_1.1");
-
- assertThatThrownBy(()->migration.generateMigration())
- .isInstanceOf(IllegalArgumentException.class)
- .hasMessageContaining("No 'pendingDrops'"); // subsequent call
-
- System.clearProperty("ddl.migration.pendingDropsFor");
+ assertThat(migration.generateMigration()).isEqualTo("1.1,1.2__dropsFor_1.1");
assertThat(migration.generateMigration()).isNull(); // subsequent call
// and now for v1_2 with
config.setPackages(Arrays.asList("misc.migration.v1_2"));
server.shutdown();
- server = DatabaseFactory.create(config);
+ server = createServer(config);
migration.setServer(server);
- assertThat(migration.generateMigration()).isEqualTo("1.3");
- assertThat(migration.generateMigration()).isNull(); // subsequent call
-
-
- System.setProperty("ddl.migration.pendingDropsFor", "1.3");
- assertThat(migration.generateMigration()).isEqualTo("1.4__dropsFor_1.3");
- assertThatThrownBy(migration::generateMigration)
- .isInstanceOf(IllegalArgumentException.class)
- .hasMessageContaining("No 'pendingDrops'"); // subsequent call
-
- System.clearProperty("ddl.migration.pendingDropsFor");
+ assertThat(migration.generateMigration()).isEqualTo("1.3,1.4__dropsFor_1.3");
assertThat(migration.generateMigration()).isNull(); // subsequent call
server.shutdown();
logger.info("end");
}
+ private static Database createServer(DatabaseConfig config) {
+ DbOffline.setGenerateMigration();
+ Database server = DatabaseFactory.create(config);
+ DbOffline.reset();
+ server.start();
+ return server;
+ }
+
}
diff --git a/ebean-test/src/test/java/io/ebean/xtest/dbmigration/DbMigrationTest.java b/ebean-test/src/test/java/io/ebean/xtest/dbmigration/DbMigrationTest.java
index 29da0d4190..a337fc1b3d 100644
--- a/ebean-test/src/test/java/io/ebean/xtest/dbmigration/DbMigrationTest.java
+++ b/ebean-test/src/test/java/io/ebean/xtest/dbmigration/DbMigrationTest.java
@@ -92,6 +92,10 @@ public void testRunMigration() throws IOException, SQLException {
if (isSqlServer() || isMariaDB() || isMySql() || isHana()) {
runScript("I__create_procs.sql");
}
+
+ if(isDb2()) {
+ runScript("I__create_tablespaces.sql");
+ }
if(isDb2()) {
runScript("I__create_tablespaces.sql");
diff --git a/ebean-test/src/test/java/io/ebean/xtest/internal/api/TDSpiServer.java b/ebean-test/src/test/java/io/ebean/xtest/internal/api/TDSpiServer.java
index 08f2bea9e8..cd6f2cc40e 100644
--- a/ebean-test/src/test/java/io/ebean/xtest/internal/api/TDSpiServer.java
+++ b/ebean-test/src/test/java/io/ebean/xtest/internal/api/TDSpiServer.java
@@ -283,6 +283,11 @@ public int saveAll(Object... beans) throws OptimisticLockException {
return 0;
}
+ @Override
+ public void visitSave(Object start, PersistVisitor visitor) {
+
+ }
+
@Override
public boolean delete(Object bean) throws OptimisticLockException {
return false;
@@ -632,4 +637,9 @@ public void loadBeanL2(EntityBeanIntercept ebi) {
public void loadBean(EntityBeanIntercept ebi) {
}
+
+ @Override
+ public void start() {
+
+ }
}
diff --git a/ebean-test/src/test/java/org/tests/basic/TPersistVisitor.java b/ebean-test/src/test/java/org/tests/basic/TPersistVisitor.java
new file mode 100644
index 0000000000..0d61a13f8a
--- /dev/null
+++ b/ebean-test/src/test/java/org/tests/basic/TPersistVisitor.java
@@ -0,0 +1,69 @@
+package org.tests.basic;
+
+import java.util.Collection;
+
+import io.ebean.DB;
+import io.ebean.PersistVisitor;
+import io.ebean.bean.EntityBean;
+import io.ebean.plugin.Property;
+/**
+ * Sample persist visitor that converts the visited beans in a XML-like structure
+ */
+public class TPersistVisitor implements PersistVisitor {
+
+ private final StringBuilder sb;
+ private final String indent;
+ private final String tag;
+ private boolean empty = true;
+
+ public TPersistVisitor() {
+ this(new StringBuilder(), "", "root");
+ }
+ private TPersistVisitor(StringBuilder sb, String indent, String tag) {
+ this.sb = sb;
+ this.indent = indent;
+ this.tag = tag;
+ this.sb.append(indent).append('<').append(tag);
+ }
+
+ TPersistVisitor newVisitor(String tag) {
+ if (empty) {
+ sb.append(">\n");
+ empty = false;
+ }
+ return new TPersistVisitor(sb, indent + " ", tag);
+ }
+
+ @Override
+ public void visitEnd() {
+ if (empty) {
+ sb.append("/>\n");
+ } else {
+ this.sb.append(indent).append("").append(tag).append(">\n");
+ }
+ }
+
+ TPersistVisitor attr(String attr, Object value) {
+ sb.append(' ').append(attr).append('=').append('\'').append(value).append('\'');
+ return this;
+ }
+
+ public TPersistVisitor visitBean(EntityBean bean) {
+ return newVisitor("bean").attr("type", bean.getClass().getSimpleName()).attr("newOrDirty", DB.beanState(bean).isNewOrDirty());
+ }
+
+ @Override
+ public PersistVisitor visitProperty(Property prop) {
+ return newVisitor("property").attr("name", prop.name());
+ }
+
+ @Override
+ public PersistVisitor visitCollection(Collection> collection) {
+ return newVisitor("collection").attr("size", collection.size());
+ }
+
+ @Override
+ public String toString() {
+ return sb.toString();
+ }
+ }
\ No newline at end of file
diff --git a/ebean-test/src/test/java/org/tests/basic/TestM2MCascadeOne.java b/ebean-test/src/test/java/org/tests/basic/TestM2MCascadeOne.java
index f3dc340dc0..ea35c50f40 100644
--- a/ebean-test/src/test/java/org/tests/basic/TestM2MCascadeOne.java
+++ b/ebean-test/src/test/java/org/tests/basic/TestM2MCascadeOne.java
@@ -3,12 +3,15 @@
import io.ebean.xtest.BaseTestCase;
import io.ebean.DB;
import io.ebean.Query;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
import org.junit.jupiter.api.Test;
import org.tests.model.basic.MRole;
import org.tests.model.basic.MUser;
public class TestM2MCascadeOne extends BaseTestCase {
-
+
@Test
public void test() {
@@ -30,8 +33,31 @@ public void test() {
u1.addRole(r0);
u1.addRole(r1);
+ TPersistVisitor tv = new TPersistVisitor();
+ DB.visitSave(u1, tv);
+ assertThat(tv.toString()).isEqualTo("\n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + "\n");
+
DB.save(u1);
-
+
+ u1 = DB.find(MUser.class, u.getUserid());
+
+ tv = new TPersistVisitor();
+ DB.visitSave(u1, tv);
+ // collection is unloaded
+ assertThat(tv.toString()).isEqualTo("\n"
+ + " \n"
+ + "\n");
+
+
}
@Test
diff --git a/ebean-test/src/test/java/org/tests/basic/TestManyOneInterface.java b/ebean-test/src/test/java/org/tests/basic/TestManyOneInterface.java
index c8a374302f..f4b35ae275 100644
--- a/ebean-test/src/test/java/org/tests/basic/TestManyOneInterface.java
+++ b/ebean-test/src/test/java/org/tests/basic/TestManyOneInterface.java
@@ -5,27 +5,51 @@
import org.junit.jupiter.api.Test;
import org.tests.model.basic.ResetBasicData;
import org.tests.model.interfaces.Address;
+import org.tests.model.interfaces.ExtPerson1and2;
import org.tests.model.interfaces.IAddress;
+import org.tests.model.interfaces.IExtPerson1;
+import org.tests.model.interfaces.IExtPerson2;
import org.tests.model.interfaces.IPerson;
import org.tests.model.interfaces.Person;
+import static org.assertj.core.api.Assertions.assertThat;
+
public class TestManyOneInterface extends BaseTestCase {
@Test
- public void test() {
-
+ public void testEntityOverrideEntityImplements() {
ResetBasicData.reset();
- IAddress a = new Address("hello");
+ IAddress a = DB.getDefault().createEntityBean(IAddress.class);
+ assertThat(a).isInstanceOf(Address.class);
- IPerson p = new Person();
+ IPerson p = DB.getDefault().createEntityBean(Person.class);
+ assertThat(p).isInstanceOf(ExtPerson1and2.class);
+ p = DB.getDefault().pluginApi().createEntityBean(IPerson.class);
+ assertThat(p).isInstanceOf(ExtPerson1and2.class);
p.setDefaultAddress(a);
- DB.save(a);
+ IAddress ea1 = DB.getDefault().createEntityBean(IAddress.class);
+ IAddress ea2 = DB.getDefault().createEntityBean(IAddress.class);
+
+ p.getExtraAddresses().add(ea1);
+ p.getExtraAddresses().add(ea2);
+
DB.save(p);
- //Assert.assertTrue();
+ IAddress a2 = DB.find(IAddress.class, a.getOid());
+ IPerson p2 = DB.find(IPerson.class, p.getOid());
+
+ assertThat(a2).isInstanceOf(Address.class);
+ assertThat(p2).isNotNull().isInstanceOf(ExtPerson1and2.class);
+ assertThat(p2.getDefaultAddress()).isInstanceOf(Address.class);
+
+ // some more checks
+ IExtPerson1 pe1 = DB.getDefault().pluginApi().createEntityBean(IExtPerson1.class);
+ IExtPerson2 pe2 = DB.getDefault().pluginApi().createEntityBean(IExtPerson2.class);
+ assertThat(pe1).isInstanceOf(ExtPerson1and2.class);
+ assertThat(pe2).isInstanceOf(ExtPerson1and2.class);
}
}
diff --git a/ebean-test/src/test/java/org/tests/cache/TestQueryCache.java b/ebean-test/src/test/java/org/tests/cache/TestQueryCache.java
index feff29a687..7024dddbf5 100644
--- a/ebean-test/src/test/java/org/tests/cache/TestQueryCache.java
+++ b/ebean-test/src/test/java/org/tests/cache/TestQueryCache.java
@@ -78,6 +78,7 @@ public void findSingleAttributeList() {
assertThat(colA_Second).isSameAs(colA_first);
+
List colA_NotDistinct = DB
.find(EColAB.class)
.setUseQueryCache(true)
@@ -91,12 +92,12 @@ public void findSingleAttributeList() {
// ensure that findCount & findSingleAttribute use different
// slots in cache. If not a "Cannot cast List to int" should happen.
int count = DB
- .find(EColAB.class)
- .setUseQueryCache(true)
- .select("columnA")
- .where()
- .eq("columnB", "SingleAttribute")
- .findCount();
+ .find(EColAB.class)
+ .setUseQueryCache(true)
+ .select("columnA")
+ .where()
+ .eq("columnB", "SingleAttribute")
+ .findCount();
assertThat(count).isEqualTo(2);
}
@@ -305,7 +306,7 @@ public void testReadOnlyFind() {
assertSame(list, list2B);
List list3 = DB.find(Customer.class).setUseQueryCache(true).setReadOnly(false).where()
- .ilike("name", "Rob").findList();
+ .ilike("name", "Rob").findList();
assertNotSame(list, list3);
BeanCollection bc3 = (BeanCollection) list3;
@@ -349,10 +350,10 @@ public void findIds() {
// and now, ensure that we hit the database
LoggedSql.start();
colA_second = DB.find(EColAB.class)
- .setUseQueryCache(CacheMode.PUT)
- .where()
- .eq("columnB", "someId")
- .findIds();
+ .setUseQueryCache(CacheMode.PUT)
+ .where()
+ .eq("columnB", "someId")
+ .findIds();
sql = LoggedSql.stop();
assertThat(sql).hasSize(1);
@@ -361,15 +362,15 @@ public void findIds() {
@Test
public void findCountDifferentQueriesBit() {
DB.getDefault().pluginApi().cacheManager().clearAll();
- differentFindCount(q->q.bitwiseAny("id",1), q->q.bitwiseAny("id",0));
- differentFindCount(q->q.bitwiseAll("id",1), q->q.bitwiseAll("id",0));
+ differentFindCount(q -> q.bitwiseAny("id", 1), q -> q.bitwiseAny("id", 0));
+ differentFindCount(q -> q.bitwiseAll("id", 1), q -> q.bitwiseAll("id", 0));
// differentFindCount(q->q.bitwiseNot("id",1), q->q.bitwiseNot("id",0)); NOT 1 == AND 1 = 0
- differentFindCount(q->q.bitwiseAnd("id",1, 0), q->q.bitwiseAnd("id",1, 1));
+ differentFindCount(q -> q.bitwiseAnd("id", 1, 0), q -> q.bitwiseAnd("id", 1, 1));
- differentFindCount(q->q.bitwiseAnd("id",2, 0), q->q.bitwiseAnd("id",4, 0));
- differentFindCount(q->q.bitwiseAnd("id",2, 1), q->q.bitwiseAnd("id",4, 1));
+ differentFindCount(q -> q.bitwiseAnd("id", 2, 0), q -> q.bitwiseAnd("id", 4, 0));
+ differentFindCount(q -> q.bitwiseAnd("id", 2, 1), q -> q.bitwiseAnd("id", 4, 1));
// Will produce hash collision
- differentFindCount(q->q.bitwiseAnd("id",10, 0), q->q.bitwiseAnd("id",0, 928210));
+ differentFindCount(q -> q.bitwiseAnd("id", 10, 0), q -> q.bitwiseAnd("id", 0, 928210));
}
diff --git a/ebean-test/src/test/java/org/tests/insert/TestInsertDataIntegrityException.java b/ebean-test/src/test/java/org/tests/insert/TestInsertDataIntegrityException.java
index 35f0bf271e..ce37db6dfd 100644
--- a/ebean-test/src/test/java/org/tests/insert/TestInsertDataIntegrityException.java
+++ b/ebean-test/src/test/java/org/tests/insert/TestInsertDataIntegrityException.java
@@ -5,11 +5,13 @@
import io.ebean.DataIntegrityException;
import io.ebean.xtest.IgnorePlatform;
import io.ebean.annotation.Platform;
+import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.tests.model.basic.Customer;
import org.tests.model.basic.Order;
import org.tests.model.basic.ResetBasicData;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class TestInsertDataIntegrityException extends BaseTestCase {
@@ -26,7 +28,10 @@ public void insert_invalidForeignKey() {
Order order = new Order();
order.setStatus(Order.Status.NEW);
order.setCustomer(invalidCustomer);
+ assertThatThrownBy(() -> DB.save(order))
+ .isInstanceOf(DataIntegrityException.class)
+ .hasMessageContaining("insert into o_order")
+ .hasMessageContaining(",900000");
- assertThrows(DataIntegrityException.class, () -> DB.save(order));
}
}
diff --git a/ebean-test/src/test/java/org/tests/json/TestDbJson_List.java b/ebean-test/src/test/java/org/tests/json/TestDbJson_List.java
index 27434601ef..0e6c7d60fb 100644
--- a/ebean-test/src/test/java/org/tests/json/TestDbJson_List.java
+++ b/ebean-test/src/test/java/org/tests/json/TestDbJson_List.java
@@ -2,19 +2,33 @@
import io.ebean.xtest.BaseTestCase;
import io.ebean.DB;
+import io.ebean.Database;
+import io.ebean.DatabaseFactory;
+import io.ebean.ValuePair;
import io.ebean.xtest.ForPlatform;
+import io.ebean.annotation.MutationDetection;
import io.ebean.annotation.Platform;
+import io.ebean.config.DatabaseConfig;
import io.ebean.test.LoggedSql;
import io.ebean.text.TextException;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.tests.model.json.EBasicJsonList;
import org.tests.model.json.PlainBean;
import javax.persistence.PersistenceException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
public class TestDbJson_List extends BaseTestCase {
@@ -231,4 +245,43 @@ public void testNullToEmpty() {
assertThat(bean.getTags()).isEmpty();
assertThat(bean.getBeanMap()).isEmpty();
}
+
+ @Test
+ @ForPlatform(Platform.H2)
+ @Disabled("breaks everything")
+ public void testDirtyValues() {
+ DatabaseConfig config = new DatabaseConfig();
+ config.loadFromProperties();
+ config.setDefaultServer(true);
+ config.setRegister(true);
+ config.setDdlRun(false);
+ config.setJsonMutationDetection(MutationDetection.SOURCE);
+ Database db = DatabaseFactory.create(config);
+ try {
+ assertThat(db).isNotNull();
+
+ EBasicJsonList bean = new EBasicJsonList();
+ bean.getTags().add("aa");
+ bean.getTags().add("bb");
+
+ db.save(bean);
+ bean = db.find(EBasicJsonList.class, bean.getId());
+
+ bean.getTags().add("cc");
+ final Map dirtyValues = db.beanState(bean).dirtyValues();
+ assertThat(dirtyValues).containsOnlyKeys("tags");
+
+ final ValuePair diff = dirtyValues.get("tags");
+ assertThat(diff.getOldValue()).isInstanceOf(List.class).asList()
+ .containsExactly("aa", "bb");
+ assertThat(diff.getNewValue()).isInstanceOf(List.class).asList()
+ .containsExactly("aa", "bb", "cc");
+ } finally {
+ if (db != null) {
+ db.shutdown();
+ }
+ }
+
+
+ }
}
diff --git a/ebean-test/src/test/java/org/tests/model/basic/MDateTime.java b/ebean-test/src/test/java/org/tests/model/basic/MDateTime.java
new file mode 100644
index 0000000000..010d378603
--- /dev/null
+++ b/ebean-test/src/test/java/org/tests/model/basic/MDateTime.java
@@ -0,0 +1,88 @@
+package org.tests.model.basic;
+
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.MonthDay;
+import java.time.OffsetDateTime;
+import java.time.Year;
+import java.time.YearMonth;
+import java.time.ZonedDateTime;
+import java.util.Calendar;
+
+import javax.annotation.Nullable;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+@Entity
+public class MDateTime {
+
+ @Id
+ private Integer id;
+
+ @Nullable
+ private LocalTime localTime;
+
+ @Nullable
+ private LocalDateTime localDateTime;
+
+ @Nullable
+ private LocalDate localDate;
+
+ @Nullable
+ private OffsetDateTime offsetDateTime;
+
+ @Nullable
+ private ZonedDateTime zonedDateTime;
+
+ @Nullable
+ private YearMonth propYearMonth;
+
+ @Nullable
+ private MonthDay propMonthDay;
+
+ @Nullable
+ private Year propYear;
+
+ @Nullable
+ private Instant propInstant;
+
+ @Nullable
+ private Calendar propCalendar;
+
+ @Nullable
+ private Timestamp propTimestamp;
+
+ @Nullable
+ private java.sql.Date sqlDate;
+
+ @Nullable
+ private java.sql.Time sqlTime;
+
+ @Nullable
+ private java.util.Date utilDate;
+
+ @Nullable
+ private org.joda.time.DateTime jodaDateTime;
+
+ @Nullable
+ private org.joda.time.LocalDateTime jodaLocalDateTime;
+
+ @Nullable
+ private org.joda.time.LocalDate jodaLocalDate;
+
+ @Nullable
+ private org.joda.time.LocalTime jodaLocalTime;
+
+ @Nullable
+ private org.joda.time.DateMidnight jodaDateMidnight;
+
+ public Integer getId() {
+ return id;
+ }
+ public void setId(Integer id) {
+ this.id = id;
+ }
+}
diff --git a/ebean-test/src/test/java/org/tests/model/interfaces/Address.java b/ebean-test/src/test/java/org/tests/model/interfaces/Address.java
index 052ef63f87..454616feac 100644
--- a/ebean-test/src/test/java/org/tests/model/interfaces/Address.java
+++ b/ebean-test/src/test/java/org/tests/model/interfaces/Address.java
@@ -1,10 +1,14 @@
package org.tests.model.interfaces;
+import io.ebean.annotation.ext.EntityImplements;
+
import javax.persistence.Entity;
import javax.persistence.Id;
+import javax.persistence.ManyToOne;
import javax.persistence.Version;
@Entity
+@EntityImplements(IAddress.class)
public class Address implements IAddress {
@Id
@@ -15,6 +19,9 @@ public class Address implements IAddress {
private String street;
+ @ManyToOne
+ private Person extraAddress;
+
public Address(String street) {
this.street = street;
}
diff --git a/ebean-test/src/test/java/org/tests/model/interfaces/ExtPerson1.java b/ebean-test/src/test/java/org/tests/model/interfaces/ExtPerson1.java
new file mode 100644
index 0000000000..dc9fac9e09
--- /dev/null
+++ b/ebean-test/src/test/java/org/tests/model/interfaces/ExtPerson1.java
@@ -0,0 +1,25 @@
+package org.tests.model.interfaces;
+
+import io.ebean.annotation.ext.EntityImplements;
+import io.ebean.annotation.ext.EntityOverride;
+
+import javax.persistence.Entity;
+
+@Entity()
+@EntityImplements(IExtPerson1.class)
+@EntityOverride(priority = 30)
+public class ExtPerson1 extends Person implements IExtPerson1 {
+
+ private int myField1;
+
+ @Override
+ public int getMyField1() {
+ return myField1;
+ }
+
+ @Override
+ public void setMyField1(int myField1) {
+ this.myField1 = myField1;
+ }
+
+}
diff --git a/ebean-test/src/test/java/org/tests/model/interfaces/ExtPerson1and2.java b/ebean-test/src/test/java/org/tests/model/interfaces/ExtPerson1and2.java
new file mode 100644
index 0000000000..a6d2661fd8
--- /dev/null
+++ b/ebean-test/src/test/java/org/tests/model/interfaces/ExtPerson1and2.java
@@ -0,0 +1,26 @@
+package org.tests.model.interfaces;
+
+import io.ebean.annotation.ext.EntityImplements;
+import io.ebean.annotation.ext.EntityOverride;
+
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+@Entity()
+@Table(name = "person")
+@EntityImplements(IExtPerson2.class)
+@EntityOverride(priority = -30)
+public class ExtPerson1and2 extends ExtPerson1 implements IExtPerson2 {
+
+ private int myField2;
+
+ @Override
+ public int getMyField2() {
+ return myField2;
+ }
+
+ @Override
+ public void setMyField2(int myField2) {
+ this.myField2 = myField2;
+ }
+}
diff --git a/ebean-test/src/test/java/org/tests/model/interfaces/ExtPerson2.java b/ebean-test/src/test/java/org/tests/model/interfaces/ExtPerson2.java
new file mode 100644
index 0000000000..670f73ad36
--- /dev/null
+++ b/ebean-test/src/test/java/org/tests/model/interfaces/ExtPerson2.java
@@ -0,0 +1,27 @@
+package org.tests.model.interfaces;
+
+import io.ebean.annotation.ext.EntityImplements;
+import io.ebean.annotation.ext.EntityOverride;
+
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+@Entity
+@EntityOverride(priority = 20)
+@EntityImplements(IExtPerson2.class)
+@Table(name = "person")
+public class ExtPerson2 extends Person implements IExtPerson2 {
+
+ private int myField2;
+
+ @Override
+ public int getMyField2() {
+ return myField2;
+ }
+
+ @Override
+ public void setMyField2(int myField2) {
+ this.myField2 = myField2;
+ }
+
+}
diff --git a/ebean-test/src/test/java/org/tests/model/interfaces/IAddress.java b/ebean-test/src/test/java/org/tests/model/interfaces/IAddress.java
index 78b6d20050..9097d23d76 100644
--- a/ebean-test/src/test/java/org/tests/model/interfaces/IAddress.java
+++ b/ebean-test/src/test/java/org/tests/model/interfaces/IAddress.java
@@ -1,6 +1,9 @@
package org.tests.model.interfaces;
public interface IAddress {
+
+ long getOid();
+
String getStreet();
void setStreet(String s);
diff --git a/ebean-test/src/test/java/org/tests/model/interfaces/IExtPerson1.java b/ebean-test/src/test/java/org/tests/model/interfaces/IExtPerson1.java
new file mode 100644
index 0000000000..b81a5266d6
--- /dev/null
+++ b/ebean-test/src/test/java/org/tests/model/interfaces/IExtPerson1.java
@@ -0,0 +1,7 @@
+package org.tests.model.interfaces;
+
+public interface IExtPerson1 extends IPerson {
+ int getMyField1();
+
+ void setMyField1(int myField1);
+}
diff --git a/ebean-test/src/test/java/org/tests/model/interfaces/IExtPerson2.java b/ebean-test/src/test/java/org/tests/model/interfaces/IExtPerson2.java
new file mode 100644
index 0000000000..49bf601339
--- /dev/null
+++ b/ebean-test/src/test/java/org/tests/model/interfaces/IExtPerson2.java
@@ -0,0 +1,7 @@
+package org.tests.model.interfaces;
+
+public interface IExtPerson2 extends IPerson {
+ int getMyField2();
+
+ void setMyField2(int myField2);
+}
diff --git a/ebean-test/src/test/java/org/tests/model/interfaces/IPerson.java b/ebean-test/src/test/java/org/tests/model/interfaces/IPerson.java
index 0d07ecc6b1..3c1366185d 100644
--- a/ebean-test/src/test/java/org/tests/model/interfaces/IPerson.java
+++ b/ebean-test/src/test/java/org/tests/model/interfaces/IPerson.java
@@ -1,7 +1,16 @@
package org.tests.model.interfaces;
+import java.util.List;
+
public interface IPerson {
+
+ long getOid();
+
IAddress getDefaultAddress();
void setDefaultAddress(IAddress address);
+
+ List getExtraAddresses();
+
+ List getAddressLinks();
}
diff --git a/ebean-test/src/test/java/org/tests/model/interfaces/Person.java b/ebean-test/src/test/java/org/tests/model/interfaces/Person.java
index 5edbb68c7c..a2e5e8dafd 100644
--- a/ebean-test/src/test/java/org/tests/model/interfaces/Person.java
+++ b/ebean-test/src/test/java/org/tests/model/interfaces/Person.java
@@ -1,11 +1,19 @@
package org.tests.model.interfaces;
+import io.ebean.annotation.ext.EntityImplements;
+
+import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
+import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
import javax.persistence.Version;
+import java.util.ArrayList;
+import java.util.List;
@Entity
+@EntityImplements(IPerson.class)
public class Person implements IPerson {
@Id
private long oid;
@@ -13,9 +21,15 @@ public class Person implements IPerson {
@Version
private int version;
- @ManyToOne(targetEntity = Address.class)
+ @ManyToOne(cascade = CascadeType.PERSIST)
private IAddress defaultAddress;
+ @OneToMany(cascade = CascadeType.PERSIST, orphanRemoval = true)
+ private List extraAddresses = new ArrayList<>();
+
+ @ManyToMany(cascade = CascadeType.PERSIST)
+ private List addressLinks = new ArrayList<>();
+
@Override
public IAddress getDefaultAddress() {
return defaultAddress;
@@ -42,4 +56,14 @@ public void setVersion(int version) {
this.version = version;
}
+ @Override
+ public List getExtraAddresses() {
+ return extraAddresses;
+ }
+
+ @Override
+ public List getAddressLinks() {
+ return addressLinks;
+ }
+
}
diff --git a/ebean-test/src/test/java/org/tests/model/interfaces/TestTargetEntity.java b/ebean-test/src/test/java/org/tests/model/interfaces/TestTargetEntity.java
index aa7030ef1d..f2ed05c573 100644
--- a/ebean-test/src/test/java/org/tests/model/interfaces/TestTargetEntity.java
+++ b/ebean-test/src/test/java/org/tests/model/interfaces/TestTargetEntity.java
@@ -30,7 +30,7 @@ public void test() {
private Person setup() {
Address address = new Address("street");
DB.save(address);
- Person person = new Person();
+ Person person = DB.getDefault().createEntityBean(Person.class);
person.setDefaultAddress(address);
DB.save(person);
return person;
diff --git a/ebean-test/src/test/java/org/tests/model/tevent/CustomFormulaAnnotationParser.java b/ebean-test/src/test/java/org/tests/model/tevent/CustomFormulaAnnotationParser.java
new file mode 100644
index 0000000000..3f392aed06
--- /dev/null
+++ b/ebean-test/src/test/java/org/tests/model/tevent/CustomFormulaAnnotationParser.java
@@ -0,0 +1,61 @@
+package org.tests.model.tevent;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import io.ebean.annotation.Formula;
+import io.ebean.config.dbplatform.DatabasePlatform;
+import io.ebean.plugin.CustomDeployParser;
+import io.ebean.plugin.DeployBeanDescriptorMeta;
+import io.ebean.plugin.DeployBeanPropertyMeta;
+import io.ebean.util.AnnotationUtil;
+import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
+
+/**
+ * Custom Annotation parser which parses @Count annotation
+ *
+ * @author Roland Praml, FOCONIS AG
+ */
+public class CustomFormulaAnnotationParser implements CustomDeployParser {
+
+ private int counter;
+
+
+ @Target(FIELD)
+ @Retention(RUNTIME)
+ @Formula(select="TODO", join = "TODO") // meta-formula
+ public @interface Count {
+ String value();
+ }
+
+
+
+ @Override
+ public void parse(final DeployBeanDescriptorMeta descriptor, final DatabasePlatform databasePlatform) {
+ for (DeployBeanPropertyMeta prop : descriptor.propertiesAll()) {
+ readField(descriptor, prop);
+ }
+ }
+
+ private void readField(DeployBeanDescriptorMeta descriptor, DeployBeanPropertyMeta prop) {
+ Count countAnnot = AnnotationUtil.get(prop.getField(), Count.class);
+ if (countAnnot != null) {
+ // @Count found, so build the (complex) count formula
+ DeployBeanPropertyAssocMany> countProp = (DeployBeanPropertyAssocMany>) descriptor.getBeanProperty(countAnnot.value());
+ counter++;
+ String tmpTable = "f"+counter;
+ String sqlSelect = "coalesce(" + tmpTable + ".child_count, 0)";
+ String parentId = countProp.getMappedBy() + "_id";
+ String tableName = countProp.getBeanTable().getBaseTable();
+ String sqlJoin = "left join (select " + parentId +", count(*) as child_count from " + tableName + " GROUP BY " + parentId + " )"
+ + " " + tmpTable + " on " + tmpTable + "." +parentId + " = ${ta}." + descriptor.idProperty().getDbColumn();
+ prop.setSqlFormula(sqlSelect, sqlJoin);
+// prop.setSqlFormula("f1.child_count",
+// "join (select parent_id, count(*) as child_count from child_entity GROUP BY parent_id) f1 on f1.parent_id = ${ta}.id");
+ }
+ }
+
+}
diff --git a/ebean-test/src/test/java/org/tests/model/tevent/TEventOne.java b/ebean-test/src/test/java/org/tests/model/tevent/TEventOne.java
index bcbad8ef2e..678ca9677c 100644
--- a/ebean-test/src/test/java/org/tests/model/tevent/TEventOne.java
+++ b/ebean-test/src/test/java/org/tests/model/tevent/TEventOne.java
@@ -42,6 +42,11 @@ public enum Status {
@OneToMany(mappedBy = "event", cascade = CascadeType.ALL)
List logs;
+ @CustomFormulaAnnotationParser.Count("logs")
+ //@Formula(select = "f1.child_count",
+ //join = "left join (select event_id, count(*) as child_count from tevent_many GROUP BY event_id ) as f1 on f1.event_id = ${ta}.id")
+ Long customFormula;
+
public TEventOne(String name, Status status) {
this.name = name;
this.status = status;
@@ -64,6 +69,10 @@ public Long getCount() {
return count;
}
+ public Long getCustomFormula() {
+ return customFormula;
+ }
+
public BigDecimal getTotalUnits() {
return totalUnits;
}
diff --git a/ebean-test/src/test/java/org/tests/query/aggregation/TestAggregationCount.java b/ebean-test/src/test/java/org/tests/query/aggregation/TestAggregationCount.java
index 8636d48d6f..9dde14d07f 100644
--- a/ebean-test/src/test/java/org/tests/query/aggregation/TestAggregationCount.java
+++ b/ebean-test/src/test/java/org/tests/query/aggregation/TestAggregationCount.java
@@ -49,7 +49,7 @@ public void testBaseSelect() {
List list = query.findList();
String sql = sqlOf(query, 5);
- assertThat(sql).contains("select t0.id, t0.name, t0.status, t0.version, t0.event_id from tevent_one t0");
+ assertThat(sql).contains("select t0.id, t0.name, t0.status, coalesce(f1.child_count, 0), t0.version, t0.event_id from tevent_one t0");
for (TEventOne eventOne : list) {
// lazy loading on Aggregation properties
diff --git a/ebean-test/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java b/ebean-test/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java
index df9d0f3b06..9d492127b3 100644
--- a/ebean-test/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java
+++ b/ebean-test/src/test/java/org/tests/query/other/TestQuerySingleAttribute.java
@@ -82,6 +82,112 @@ public void exampleUsage() {
}
}
+ @Test
+ public void oneToMany_normal() {
+
+ ResetBasicData.reset();
+
+ List statusList =
+ DB.find(Customer.class)
+ .fetch("orders", "status")
+ .findSingleAttributeList();
+
+ assertThat(statusList).hasSize(7);
+ }
+
+ @Test
+ public void oneToMany_notNull() {
+
+ ResetBasicData.reset();
+
+ List statusList =
+ DB.find(Customer.class)
+ .fetch("orders", "status")
+ .where()
+ .isNotNull("orders.status")
+ .findSingleAttributeList();
+
+ assertThat(statusList).hasSize(5);
+ }
+
+ @Test
+ public void oneToMany_orderBy() {
+
+ ResetBasicData.reset();
+
+ List statusList =
+ DB.find(Customer.class)
+ .fetch("orders", "status")
+ .orderBy("orders.status")
+ .findSingleAttributeList();
+
+ assertThat(statusList).hasSize(7);
+ assertThat(statusList.get(0)).isEqualTo(null);
+ assertThat(statusList.get(6)).isEqualTo(Order.Status.COMPLETE);
+ }
+
+ @Test
+ public void oneToMany_distinct() {
+
+ ResetBasicData.reset();
+
+ List statusList =
+ DB.find(Customer.class)
+ .fetch("orders", "status")
+ .setDistinct(true)
+ .findSingleAttributeList();
+
+ assertThat(statusList).hasSize(4);
+ }
+
+ @Test
+ public void oneToMany_distinct_orderBy() {
+
+ ResetBasicData.reset();
+
+ List statusList =
+ DB.find(Customer.class)
+ .fetch("orders", "status")
+ .setDistinct(true)
+ .orderBy("orders.status")
+ .findSingleAttributeList();
+
+ assertThat(statusList).hasSize(4);
+ assertThat(statusList.get(0)).isEqualTo(null);
+ assertThat(statusList.get(3)).isEqualTo(Order.Status.COMPLETE);
+ }
+
+ @Test
+ public void oneToMany_maxRows() {
+
+ ResetBasicData.reset();
+
+ List statusList =
+ DB.find(Customer.class)
+ .fetch("orders", "status")
+ .setMaxRows(2)
+ .findSingleAttributeList();
+
+ assertThat(statusList).hasSize(2);
+ }
+
+ @Test
+ public void oneToMany_maxRows_orderBy() {
+
+ ResetBasicData.reset();
+
+ List statusList =
+ DB.find(Customer.class)
+ .fetch("orders", "status")
+ .setMaxRows(2)
+ .orderBy("orders.status")
+ .findSingleAttributeList();
+
+ assertThat(statusList).hasSize(2);
+ assertThat(statusList.get(0)).isEqualTo(null);
+ assertThat(statusList.get(1)).isEqualTo(Order.Status.NEW);
+ }
+
@Test
public void exampleUsage_otherType() {
diff --git a/ebean-test/src/test/java/org/tests/rawsql/TestRawSqlNamedParams.java b/ebean-test/src/test/java/org/tests/rawsql/TestRawSqlNamedParams.java
index 17cf86356e..9f37c010ff 100644
--- a/ebean-test/src/test/java/org/tests/rawsql/TestRawSqlNamedParams.java
+++ b/ebean-test/src/test/java/org/tests/rawsql/TestRawSqlNamedParams.java
@@ -44,9 +44,12 @@ public void testMySqlColonEquals() throws SQLException {
Transaction transaction = DB.beginTransaction();
+ System.out.println(transaction.connection().getMetaData().getDriverName());
try {
- if ("MariaDB connector/J".equals(transaction.connection().getMetaData().getDriverName())) {
- return; // MariaDb only supports callable statements in the form "? = call function x(?)"
+ if ("MariaDB Connector/J".equals(transaction.connection().getMetaData().getDriverName())) {
+ // CHECKME: Should we report/fail if we are running mysql-tests with mariadb driver
+ // BTW: This happens with java 8 - the drivers in drivermanager are in different order
+ return; // MariaDB Connector/J only supports callable statements in the form "? = call function x(?)"
}
CallableSql callableSql = DB.createCallableSql("set @total = 0");
DB.getDefault().execute(callableSql);
diff --git a/ebean-test/src/test/resources/ebean.properties b/ebean-test/src/test/resources/ebean.properties
index 91c0d0c290..0a64c98084 100644
--- a/ebean-test/src/test/resources/ebean.properties
+++ b/ebean-test/src/test/resources/ebean.properties
@@ -196,6 +196,7 @@ datasource.hana.username=EBEAN_TEST
datasource.hana.password=Eb3an_test
datasource.hana.url=jdbc:sap://hxehost:39013/?databaseName=HXE
#datasource.hana.driver=com.sap.db.jdbc.Driver
+#
# parameters for migration test
datasource.migrationtest.username=SA
@@ -207,6 +208,21 @@ ebean.migrationtest.ddl.run=false
ebean.migrationtest.ddl.header=-- Migrationscripts for ebean unittest
ebean.migrationtest.migration.appName=migrationtest
ebean.migrationtest.migration.migrationPath=migrationtest/dbmigration
+ebean.migrationtest.migration.migrationInitPath=migrationtest/dbinit
+ebean.migrationtest.migration.strict=true
+ebean.migrationtest.migration.generate=true
+ebean.migrationtest.migration.run=false
+ebean.migrationtest.migration.includeIndex=true
+ebean.migrationtest.migration.generateInit=true
+ebean.migrationtest.migration.generatePendingDrop=auto
+ebean.migrationtest.migration.platforms=db2luw,h2,hsqldb,mysql,mysql55,mariadb,postgres,oracle,sqlite,sqlserver17,hana,yugabyte
+#migration.migrationtest.db2luw.prefix=db2
+#migration.migrationtest.sqlserver17.prefix=sqlserver
+dbmigration.platform.mariadb.useMigrationStoredProcedures=true
+dbmigration.platform.mysql.useMigrationStoredProcedures=true
+
+
+
ebean.migrationtest.migration.strict=true
# enable stored procedures f
dbmigration.platform.mariadb.useMigrationStoredProcedures=true
@@ -222,4 +238,17 @@ ebean.migrationtest-history.ddl.run=false
ebean.migrationtest-history.ddl.header=-- Migrationscripts for ebean unittest DbMigrationDropHistoryTest
ebean.migrationtest-history.migration.appName=migrationtest-history
ebean.migrationtest-history.migration.migrationPath=migrationtest-history/dbmigration
+ebean.migrationtest-history.migration.migrationInitPath=migrationtest-history/dbinit
ebean.migrationtest-history.migration.strict=true
+
+# ServerStartTest - can we run the migrations and do we find the correct ones!
+datasource.db2-migration.username=unit
+datasource.db2-migration.password=test
+datasource.db2-migration.url=jdbc:db2://127.0.0.1:50000/unit
+ebean.db2-migration.ddl.generate=false
+ebean.db2-migration.ddl.run=false
+ebean.db2-migration.migration.run=true
+ebean.db2-migration.databasePlatformName=db2luw
+# workaround for https://github.com/ebean-orm/ebean-migration/issues/102
+ebean.db2-migration.migration.migrationPath=migrationtest/dbmigration/db2
+ebean.db2-migration.migration.migrationInitPath=migrationtest/dbinit/db2
\ No newline at end of file
diff --git a/ebean-test/src/test/resources/extra-ddl.xml b/ebean-test/src/test/resources/extra-ddl.xml
index 130de7fac6..44d49d1589 100644
--- a/ebean-test/src/test/resources/extra-ddl.xml
+++ b/ebean-test/src/test/resources/extra-ddl.xml
@@ -64,6 +64,15 @@
create index ix_ebasic_jmjb_gin2 on ebasic_json_map_json_b using gin(content jsonb_path_ops);
+
+delimiter $$
+BEGIN
+IF NOT EXISTS (SELECT * FROM SYSCAT.TABLES WHERE TABSCHEMA = CURRENT SCHEMA AND TABNAME = 'EXPLAIN_STREAM') THEN
+call SYSPROC.SYSINSTALLOBJECTS( 'EXPLAIN', 'C' , '', CURRENT SCHEMA );
+END IF;
+END;$$
+
+
delimiter $$
BEGIN
@@ -101,4 +110,5 @@ END IF;
END
$$
+
diff --git a/ebean-test/src/test/resources/migrationtest/dbmigration/db2fori/R__db2_explain_tables.sql b/ebean-test/src/test/resources/migrationtest/dbmigration/db2fori/R__db2_explain_tables.sql
new file mode 100644
index 0000000000..b2539a5ea9
--- /dev/null
+++ b/ebean-test/src/test/resources/migrationtest/dbmigration/db2fori/R__db2_explain_tables.sql
@@ -0,0 +1,8 @@
+
+delimiter $$
+BEGIN
+IF NOT EXISTS (SELECT * FROM SYSCAT.TABLES WHERE TABSCHEMA = CURRENT SCHEMA AND TABNAME = 'EXPLAIN_STREAM') THEN
+call SYSPROC.SYSINSTALLOBJECTS( 'EXPLAIN', 'C' , '', CURRENT SCHEMA );
+END IF;
+END;$$
+
\ No newline at end of file
diff --git a/ebean-test/src/test/resources/migrationtest/dbmigration/db2fori/idx_db2.migrations b/ebean-test/src/test/resources/migrationtest/dbmigration/db2fori/idx_db2.migrations
index ad7785b1b8..7aa514bb3c 100644
--- a/ebean-test/src/test/resources/migrationtest/dbmigration/db2fori/idx_db2.migrations
+++ b/ebean-test/src/test/resources/migrationtest/dbmigration/db2fori/idx_db2.migrations
@@ -3,5 +3,6 @@
-1187336846, 1.2__dropsFor_1.1.sql
2066110925, 1.3.sql
-1713819679, 1.4__dropsFor_1.3.sql
+-133543359, R__db2_explain_tables.sql
561281075, R__order_views.sql
diff --git a/ebean-test/src/test/resources/migrationtest/dbmigration/db2luw/R__db2_explain_tables.sql b/ebean-test/src/test/resources/migrationtest/dbmigration/db2luw/R__db2_explain_tables.sql
new file mode 100644
index 0000000000..b2539a5ea9
--- /dev/null
+++ b/ebean-test/src/test/resources/migrationtest/dbmigration/db2luw/R__db2_explain_tables.sql
@@ -0,0 +1,8 @@
+
+delimiter $$
+BEGIN
+IF NOT EXISTS (SELECT * FROM SYSCAT.TABLES WHERE TABSCHEMA = CURRENT SCHEMA AND TABNAME = 'EXPLAIN_STREAM') THEN
+call SYSPROC.SYSINSTALLOBJECTS( 'EXPLAIN', 'C' , '', CURRENT SCHEMA );
+END IF;
+END;$$
+
\ No newline at end of file
diff --git a/ebean-test/src/test/resources/migrationtest/dbmigration/db2luw/idx_db2.migrations b/ebean-test/src/test/resources/migrationtest/dbmigration/db2luw/idx_db2.migrations
index 1de746cf38..946e5f5866 100644
--- a/ebean-test/src/test/resources/migrationtest/dbmigration/db2luw/idx_db2.migrations
+++ b/ebean-test/src/test/resources/migrationtest/dbmigration/db2luw/idx_db2.migrations
@@ -4,5 +4,6 @@
-1187336846, 1.2__dropsFor_1.1.sql
-1088179698, 1.3.sql
-1713819679, 1.4__dropsFor_1.3.sql
+-133543359, R__db2_explain_tables.sql
561281075, R__order_views.sql
diff --git a/ebean-test/src/test/resources/migrationtest/dbmigration/db2zos/R__db2_explain_tables.sql b/ebean-test/src/test/resources/migrationtest/dbmigration/db2zos/R__db2_explain_tables.sql
new file mode 100644
index 0000000000..b2539a5ea9
--- /dev/null
+++ b/ebean-test/src/test/resources/migrationtest/dbmigration/db2zos/R__db2_explain_tables.sql
@@ -0,0 +1,8 @@
+
+delimiter $$
+BEGIN
+IF NOT EXISTS (SELECT * FROM SYSCAT.TABLES WHERE TABSCHEMA = CURRENT SCHEMA AND TABNAME = 'EXPLAIN_STREAM') THEN
+call SYSPROC.SYSINSTALLOBJECTS( 'EXPLAIN', 'C' , '', CURRENT SCHEMA );
+END IF;
+END;$$
+
\ No newline at end of file
diff --git a/ebean-test/src/test/resources/migrationtest/dbmigration/db2zos/idx_db2.migrations b/ebean-test/src/test/resources/migrationtest/dbmigration/db2zos/idx_db2.migrations
index 5eba36e5d0..e89edc651f 100644
--- a/ebean-test/src/test/resources/migrationtest/dbmigration/db2zos/idx_db2.migrations
+++ b/ebean-test/src/test/resources/migrationtest/dbmigration/db2zos/idx_db2.migrations
@@ -3,5 +3,6 @@
-1187336846, 1.2__dropsFor_1.1.sql
-1088179698, 1.3.sql
-1713819679, 1.4__dropsFor_1.3.sql
+-133543359, R__db2_explain_tables.sql
561281075, R__order_views.sql
diff --git a/ebean-test/testconfig/ebean-oracle.properties b/ebean-test/testconfig/ebean-oracle.properties
index 32b3af2491..24764943b2 100644
--- a/ebean-test/testconfig/ebean-oracle.properties
+++ b/ebean-test/testconfig/ebean-oracle.properties
@@ -1,3 +1,4 @@
ebean.test.platform=oracle
ebean.test.dbName=test_eb
+ebean.test.dbPassword=test
datasource.default=oracle
diff --git a/ebean-test/testplatforms.sh b/ebean-test/testplatforms.sh
new file mode 100644
index 0000000000..1e19717c0e
--- /dev/null
+++ b/ebean-test/testplatforms.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+# A small script, to run a certain test on all platforms
+# invoke with ./testplatforms.sh -Dtest=DbMigrationTest
+# Hint: in case of DbMigrationTest, you may disable ddl.run temporary
+
+# default H2 platform
+set -e
+mvn test "$@"
+
+mvn surefire:test -Dprops.file=testconfig/ebean-mysql.properties "$@"
+
+mvn surefire:test -Dprops.file=testconfig/ebean-mariadb.properties "$@"
+mvn surefire:test -Dprops.file=testconfig/ebean-mariadb-10.3.properties "$@"
+
+mvn surefire:test -Dprops.file=testconfig/ebean-sqlserver17.properties "$@"
+mvn surefire:test -Dprops.file=testconfig/ebean-sqlserver19.properties "$@"
+
+mvn surefire:test -Dprops.file=testconfig/ebean-postgres.properties "$@"
+
+#mvn surefire:test -Dprops.file=testconfig/ebean-oracle.properties "$@"
+
+#mvn surefire:test -Dprops.file=testconfig/ebean-sqlite.properties "$@"
+
+mvn surefire:test -Dprops.file=testconfig/ebean-hana.properties "$@"
+
+mvn surefire:test -Dprops.file=testconfig/ebean-db2.properties "$@"
+
+## Test ignored
+## mvn surefire:test -Dprops.file=testconfig/ebean-yugabyte.properties "$@"
+
+## Scripts are not correct
+## mvn surefire:test -Dprops.file=testconfig/ebean-cockroach.properties "$@"
+
+## Transactions are not supported
+## mvn surefire:test -Dprops.file=testconfig/ebean-clickhouse.properties "$@"
+
+## I cannot start nuodb
+## mvn surefire:test -Dprops.file=testconfig/ebean-nuodb.properties.properties "$@"
+
+
diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml
index c36475b2e7..686377f170 100644
--- a/kotlin-querybean-generator/pom.xml
+++ b/kotlin-querybean-generator/pom.xml
@@ -4,7 +4,7 @@
ebean-parent
io.ebean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
kotlin querybean generator
@@ -29,7 +29,7 @@
io.ebean
ebean-querybean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
@@ -43,7 +43,7 @@
io.ebean
ebean-core
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
@@ -64,14 +64,14 @@
io.ebean
ebean-platform-h2
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
io.ebean
ebean-ddl-generator
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
diff --git a/platforms/all/pom.xml b/platforms/all/pom.xml
index 96858a7b21..f1d0c70f25 100644
--- a/platforms/all/pom.xml
+++ b/platforms/all/pom.xml
@@ -4,7 +4,7 @@
platforms
io.ebean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
ebean-platform-all
@@ -14,68 +14,72 @@
io.ebean
ebean-platform-h2
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-platform-clickhouse
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-platform-db2
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-platform-hana
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-platform-hsqldb
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-platform-mysql
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-platform-mariadb
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-platform-nuodb
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-platform-oracle
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-platform-postgres
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-platform-sqlanywhere
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-platform-sqlite
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
io.ebean
ebean-platform-sqlserver
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
+
+
+ ebean-parent-13.6.4-FOC1
+
diff --git a/platforms/clickhouse/pom.xml b/platforms/clickhouse/pom.xml
index b0e35711ec..8b0ca2c2e3 100644
--- a/platforms/clickhouse/pom.xml
+++ b/platforms/clickhouse/pom.xml
@@ -4,7 +4,7 @@
platforms
io.ebean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
ebean-platform-clickhouse
@@ -14,8 +14,12 @@
io.ebean
ebean-api
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
+
+
+ ebean-parent-13.6.4-FOC1
+
diff --git a/platforms/db2/pom.xml b/platforms/db2/pom.xml
index 3be2c4055d..da5501654b 100644
--- a/platforms/db2/pom.xml
+++ b/platforms/db2/pom.xml
@@ -4,7 +4,7 @@
platforms
io.ebean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
ebean-platform-db2
@@ -14,8 +14,12 @@
io.ebean
ebean-api
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
+
+
+ ebean-parent-13.6.4-FOC1
+
diff --git a/platforms/h2/pom.xml b/platforms/h2/pom.xml
index fe9b57fd2c..31d9f7e18e 100644
--- a/platforms/h2/pom.xml
+++ b/platforms/h2/pom.xml
@@ -4,7 +4,7 @@
platforms
io.ebean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
ebean-platform-h2
@@ -14,7 +14,7 @@
io.ebean
ebean-api
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
+
+
+ foconis-release
+ FOCONIS Release Repository
+ https://mvnrepo.foconis.de/repository/release/
+
+
+ foconis-snapshot
+ FOCONIS Snapshot Repository
+ https://mvnrepo.foconis.de/repository/snapshot/
+
+
diff --git a/querybean-generator/pom.xml b/querybean-generator/pom.xml
index e3e4e05e80..502092acc2 100644
--- a/querybean-generator/pom.xml
+++ b/querybean-generator/pom.xml
@@ -4,7 +4,7 @@
ebean-parent
io.ebean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
querybean generator
diff --git a/release.md b/release.md
new file mode 100644
index 0000000000..423bdb9ba6
--- /dev/null
+++ b/release.md
@@ -0,0 +1,18 @@
+## Release command
+
+We @foconis use this command to release.
+
+ mvn versions:set -DgenerateBackupPoms=false -DnewVersion=13.6.0-FOC2-SNAPSHOT
+ mvn release:prepare release:perform -Darguments="-Dgpg.skip -DskipTests" -PfoconisRelease
+
+ # RELEASE klappt nun, sollte es failen, ist wie folgt vorzugehen:
+ # um bei einen Fehler zu release ist dann ins target/checkout Verzeichnis zu gehen und
+ mvn clean source:jar install org.apache.maven.plugins:maven-deploy-plugin:deploy -DskipTests
+ # auszuführen. Aber auch das failed bei Kotlin.
+
+ # nach dem Release müssen die Versionen in ebean-kotlin/pom.xml, tests/test-java16/pom.xml und tests/test-kotlin/pom.xml manuell angepasst werden
+
+generate Java classes from .xsd:
+
+ export JAVA_TOOL_OPTIONS="-Duser.language=en -Duser.country=US -Dfile.encoding=UTF-8"
+ /c/Program\ Files/Java/jdk1.8.0_201/bin/xjc.exe src/main/resources/ebean-dbmigration-1.0.xsd -d src/main/java -p io.ebeaninternal.dbmigration.migration
diff --git a/tests/pom.xml b/tests/pom.xml
index ac7678298b..1942c9065a 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -5,7 +5,7 @@
org.avaje
java8-oss
3.3
-
+
io.ebean
@@ -41,4 +41,9 @@
+
+
+ ebean-parent-13.3.1-FOC1
+ scm:git:git@github.com:FOCONIS/ebean.git
+
diff --git a/tests/test-java16/pom.xml b/tests/test-java16/pom.xml
index 737e1c5274..69b806c045 100644
--- a/tests/test-java16/pom.xml
+++ b/tests/test-java16/pom.xml
@@ -1,6 +1,5 @@
-
+
4.0.0
io.ebean
@@ -15,7 +14,7 @@
io.ebean
ebean
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
@@ -27,7 +26,7 @@
io.ebean
ebean-test
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
@@ -53,7 +52,7 @@
io.ebean
querybean-generator
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
diff --git a/tests/test-kotlin/pom.xml b/tests/test-kotlin/pom.xml
index 8c8dbbe5d2..18cb56296b 100644
--- a/tests/test-kotlin/pom.xml
+++ b/tests/test-kotlin/pom.xml
@@ -1,13 +1,11 @@
-
+
4.0.0
org.avaje
java11-oss
3.7
-
+
test-kotlin
@@ -35,14 +33,14 @@
io.ebean
ebean-test
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
io.ebean
ebean-core
- 13.6.5-SNAPSHOT
+ 13.6.4-FOC2-SNAPSHOT
test
@@ -71,6 +69,11 @@
src/test/kotlin
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ false
+
org.jetbrains.kotlin
kotlin-maven-plugin
@@ -107,5 +110,4 @@
-