diff --git a/ebean-api/src/main/java/io/ebean/DatabaseBuilder.java b/ebean-api/src/main/java/io/ebean/DatabaseBuilder.java index 5203d1236f..3aa4fbde83 100644 --- a/ebean-api/src/main/java/io/ebean/DatabaseBuilder.java +++ b/ebean-api/src/main/java/io/ebean/DatabaseBuilder.java @@ -61,6 +61,10 @@ public interface DatabaseBuilder { /** * Build and return the Database instance. + *
+ * When {@link #setRegister(boolean)} is set to true, and a database with the same + * name is already registered, this may return the existing registered database + * rather than creating a new one. */ Database build(); diff --git a/ebean-api/src/main/java/io/ebean/DatabaseFactory.java b/ebean-api/src/main/java/io/ebean/DatabaseFactory.java index cd0d9cd62f..fddd00559c 100644 --- a/ebean-api/src/main/java/io/ebean/DatabaseFactory.java +++ b/ebean-api/src/main/java/io/ebean/DatabaseFactory.java @@ -7,6 +7,8 @@ import java.util.concurrent.locks.ReentrantLock; +import static java.lang.System.Logger.Level.WARNING; + /** * Creates Database instances. *
@@ -61,7 +63,11 @@ public static Database create(String name) { /** * Create using the DatabaseConfig object to configure the database. - * + *
+ * When the configuration has {@link DatabaseBuilder.Settings#isRegister()} set to true, + * and a database with the same name is already registered, this returns the existing + * registered database rather than creating a new one. + *
*{@code
*
* DatabaseConfig config = new DatabaseConfig();
@@ -76,18 +82,28 @@ public static Database create(DatabaseBuilder builder) {
lock.lock();
try {
var config = builder.settings();
- if (config.getName() == null) {
+ var name = config.getName();
+ if (name == null) {
throw new PersistenceException("The name is null (it is required)");
}
+ if (config.isRegister()) {
+ // We're explicitly creating a database to be registered, so avoid
+ // triggering DbContext static initialisation to auto-create a default one.
+ DbPrimary.setSkip(true);
+ Database existing = DbContext.getInstance().getRegistered(name);
+ if (existing != null) {
+ EbeanVersion.log.log(WARNING, "Using existing database with name:{0}", name);
+ return existing;
+ }
+ }
Database server = createInternal(config);
if (config.isRegister()) {
if (config.isDefaultServer()) {
- if (defaultServerName != null && !defaultServerName.equals(config.getName())) {
- throw new IllegalStateException("Registering [" + config.getName() + "] as the default server but [" + defaultServerName + "] is already registered as the default");
+ if (defaultServerName != null && !defaultServerName.equals(name)) {
+ throw new IllegalStateException("Registering [" + name + "] as the default server but [" + defaultServerName + "] is already registered as the default");
}
- defaultServerName = config.getName();
+ defaultServerName = name;
}
- DbPrimary.setSkip(true);
DbContext.getInstance().register(server, config.isDefaultServer());
}
return server;
diff --git a/ebean-api/src/main/java/io/ebean/DbContext.java b/ebean-api/src/main/java/io/ebean/DbContext.java
index a7694182ff..fb81f6c4ff 100644
--- a/ebean-api/src/main/java/io/ebean/DbContext.java
+++ b/ebean-api/src/main/java/io/ebean/DbContext.java
@@ -4,6 +4,8 @@
import io.ebean.datasource.DataSourceConfigurationException;
import jakarta.persistence.PersistenceException;
+import org.jspecify.annotations.Nullable;
+
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
@@ -75,6 +77,11 @@ Database getDefault() {
return defaultDatabase;
}
+ @Nullable
+ Database getRegistered(String name) {
+ return concMap.get(name);
+ }
+
/**
* Return the database by name.
*/
diff --git a/ebean-test/src/test/java/io/ebean/xtest/base/EbeanServerFactory_ServerConfigStart_Test.java b/ebean-test/src/test/java/io/ebean/xtest/base/EbeanServerFactory_ServerConfigStart_Test.java
index 46e532b06a..1e28e1d9d7 100644
--- a/ebean-test/src/test/java/io/ebean/xtest/base/EbeanServerFactory_ServerConfigStart_Test.java
+++ b/ebean-test/src/test/java/io/ebean/xtest/base/EbeanServerFactory_ServerConfigStart_Test.java
@@ -12,6 +12,7 @@
import java.util.HashSet;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
import static org.assertj.core.api.Assertions.assertThat;
@@ -81,6 +82,59 @@ public void test() throws InterruptedException {
restartedServer.shutdown(true, false);
}
+ @Test
+ public void create_registeredDatabase_twice_returnsExistingInstance() {
+
+ DatabaseBuilder config = new DatabaseConfig();
+ config.setName("h2");
+ config.loadFromProperties();
+ config.setName("dup-" + System.nanoTime());
+ config.setDdlGenerate(false);
+ config.setDdlRun(false);
+ config.setDdlExtra(false);
+ config.setDefaultServer(false);
+ config.setRegister(true);
+ config.addClass(UTDetail.class);
+
+ AtomicInteger startupCount = new AtomicInteger();
+ config.addServerConfigStartup(serverConfig -> startupCount.incrementAndGet());
+
+ Database db = DatabaseFactory.create(config);
+ Database existing = DatabaseFactory.create(config);
+
+ assertThat(existing).isSameAs(db);
+ assertThat(startupCount.get()).isEqualTo(1);
+
+ db.shutdown(true, false);
+ }
+
+ @Test
+ public void create_unregisteredDatabase_twice_returnsDifferentInstances() {
+
+ DatabaseBuilder config = new DatabaseConfig();
+ config.setName("h2");
+ config.loadFromProperties();
+ config.setName("dup-unregistered-" + System.nanoTime());
+ config.setDdlGenerate(false);
+ config.setDdlRun(false);
+ config.setDdlExtra(false);
+ config.setDefaultServer(false);
+ config.setRegister(false);
+ config.addClass(UTDetail.class);
+
+ AtomicInteger startupCount = new AtomicInteger();
+ config.addServerConfigStartup(serverConfig -> startupCount.incrementAndGet());
+
+ Database db = DatabaseFactory.create(config);
+ Database other = DatabaseFactory.create(config);
+
+ assertThat(other).isNotSameAs(db);
+ assertThat(startupCount.get()).isEqualTo(2);
+
+ db.shutdown(true, false);
+ other.shutdown(true, false);
+ }
+
public static class OnStartup implements ServerConfigStartup {
DatabaseBuilder calledWithConfig;