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;