From 9dabd3cbb4ffdf5c0fe9cf632ec6babbf2331b3c Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Mon, 4 May 2026 22:19:52 +1200 Subject: [PATCH] With Database register(true) check for existing registered Database by name And return the existing Database instance if present. In theory this should not be needed. There are some test setups that hit this situation. --- .../main/java/io/ebean/DatabaseBuilder.java | 4 ++ .../main/java/io/ebean/DatabaseFactory.java | 28 +++++++--- .../src/main/java/io/ebean/DbContext.java | 7 +++ ...nServerFactory_ServerConfigStart_Test.java | 54 +++++++++++++++++++ 4 files changed, 87 insertions(+), 6 deletions(-) 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;