From c1de7a99181b3aeb9c2a22db4450821ac892614c Mon Sep 17 00:00:00 2001 From: Matthias Kurz Date: Thu, 8 Oct 2026 13:35:11 +0200 Subject: [PATCH 1/3] Move the read replica setting to play.ebean.db. Rename play.ebean.readOnlyDatasources. to play.ebean.db..readOnlyDatasource, so that all settings of a single Ebean server live in one place, like Play's own play.evolutions.db. settings. The previous setting has not been released yet. --- docs/manual/working/javaGuide/main/sql/JavaEbean.md | 6 +++--- .../main/java/play/db/ebean/DefaultEbeanConfig.java | 4 ++-- .../main/java/play/db/ebean/EbeanParsedConfig.java | 12 +++++++++--- play-ebean/src/main/resources/reference.conf | 9 +++++---- .../java/play/db/ebean/EbeanParsedConfigTest.java | 2 +- .../play/db/ebean/EbeanReadOnlyDatasourceTest.java | 10 +++++----- 6 files changed, 25 insertions(+), 18 deletions(-) diff --git a/docs/manual/working/javaGuide/main/sql/JavaEbean.md b/docs/manual/working/javaGuide/main/sql/JavaEbean.md index 96da08e4..ee3eefb7 100644 --- a/docs/manual/working/javaGuide/main/sql/JavaEbean.md +++ b/docs/manual/working/javaGuide/main/sql/JavaEbean.md @@ -51,7 +51,7 @@ Note that Ebean will also make use of a `conf/orm.xml` file (if present), to con Ebean can run queries against a separate, read-only data source, typically a [read replica](https://ebean.io/docs/read-replicas/). This applies to queries executed outside of a transaction and to explicitly read-only transactions (e.g. `TxScope.required().setReadOnly(true)`). All writes and all queries inside of other transactions, including the ones of `@Transactional` actions, keep using the main data source. -To use a read replica, configure it as an additional Play database and map it to the Ebean server via `play.ebean.readOnlyDatasources`: +To use a read replica, configure it as an additional Play database and set it as the `readOnlyDatasource` of the Ebean server: ```properties db.default.url = "jdbc:postgresql://primary-host/app" @@ -59,10 +59,10 @@ db.replica.url = "jdbc:postgresql://replica-host/app" db.replica.hikaricp.readOnly = true ebean.default = ["models.*"] -play.ebean.readOnlyDatasources.default = "replica" +play.ebean.db.default.readOnlyDatasource = "replica" ``` -The key is the name of the Ebean server (here `default`) and the value is the name of the Play database used for its read-only queries. Play manages the connection pool of the read replica like that of any other database, so all the usual `db` settings apply. +Here `default` is the name of the Ebean server and `replica` the name of the Play database used for its read-only queries. Play manages the connection pool of the read replica like that of any other database, so all the usual `db` settings apply. Ebean supports one read-only data source per Ebean server. To spread the read-only queries across multiple read replicas, use a JDBC URL that lists all of them (if supported by your JDBC driver, e.g. the PostgreSQL driver's `loadBalanceHosts` and `targetServerType` parameters), or point the URL to a load balancer or the reader endpoint of your database service. diff --git a/play-ebean/src/main/java/play/db/ebean/DefaultEbeanConfig.java b/play-ebean/src/main/java/play/db/ebean/DefaultEbeanConfig.java index eef195eb..752485fc 100644 --- a/play-ebean/src/main/java/play/db/ebean/DefaultEbeanConfig.java +++ b/play-ebean/src/main/java/play/db/ebean/DefaultEbeanConfig.java @@ -94,7 +94,7 @@ public EbeanConfig parse() { for (String key : ebeanConfig.getReadOnlyDatasources().keySet()) { if (!ebeanConfig.getDatasourceModels().containsKey(key)) { throw new ConfigException.BadValue( - "play.ebean.readOnlyDatasources." + key, "There is no Ebean server '" + key + "'"); + "play.ebean.db." + key, "There is no Ebean server '" + key + "'"); } } @@ -150,7 +150,7 @@ private void setServerConfigReadOnlyDataSource( } if (dbApi.getDatabase(readOnlyDatasource) == null) { throw new ConfigException.BadValue( - "play.ebean.readOnlyDatasources." + key, + "play.ebean.db." + key + ".readOnlyDatasource", "There is no Play database '" + readOnlyDatasource + "'"); } // Not wrapped: Ebean's implicit read-only transactions work best with auto-commit diff --git a/play-ebean/src/main/java/play/db/ebean/EbeanParsedConfig.java b/play-ebean/src/main/java/play/db/ebean/EbeanParsedConfig.java index 69e6c615..1712a4f6 100644 --- a/play-ebean/src/main/java/play/db/ebean/EbeanParsedConfig.java +++ b/play-ebean/src/main/java/play/db/ebean/EbeanParsedConfig.java @@ -101,11 +101,17 @@ public static EbeanParsedConfig parseFromConfig(Config config) { }); } Map readOnlyDatasources = new HashMap<>(); - Config readOnlyDatasourcesConfig = playEbeanConfig.getConfig("readOnlyDatasources"); - readOnlyDatasourcesConfig + Config serversConfig = playEbeanConfig.getConfig("db"); + serversConfig .root() .keySet() - .forEach(key -> readOnlyDatasources.put(key, readOnlyDatasourcesConfig.getString(key))); + .forEach( + server -> { + Config serverConfig = serversConfig.getConfig(server); + if (serverConfig.hasPath("readOnlyDatasource")) { + readOnlyDatasources.put(server, serverConfig.getString("readOnlyDatasource")); + } + }); return new EbeanParsedConfig( defaultDatasource, datasourceModels, generateEvolutionsScripts, readOnlyDatasources); diff --git a/play-ebean/src/main/resources/reference.conf b/play-ebean/src/main/resources/reference.conf index 03a2e294..def7ae33 100644 --- a/play-ebean/src/main/resources/reference.conf +++ b/play-ebean/src/main/resources/reference.conf @@ -13,9 +13,10 @@ play { # The key to control the generation of the Evolutions scripts generateEvolutionsScripts = true - # Maps Ebean servers to the Play databases their read-only queries should use (e.g. a read - # replica). For example, `default = "replica"` makes the default Ebean server run queries - # outside of transactions against the `replica` database. - readOnlyDatasources {} + # Settings of single Ebean servers, keyed by the name of the Ebean server: + # - readOnlyDatasource: the Play database the Ebean server should use for its read-only queries + # (e.g. a read replica). For example, `db.default.readOnlyDatasource = "replica"` makes the + # default Ebean server run queries outside of transactions against the `replica` database. + db {} } } diff --git a/play-ebean/src/test/java/play/db/ebean/EbeanParsedConfigTest.java b/play-ebean/src/test/java/play/db/ebean/EbeanParsedConfigTest.java index f277b916..8223387b 100644 --- a/play-ebean/src/test/java/play/db/ebean/EbeanParsedConfigTest.java +++ b/play-ebean/src/test/java/play/db/ebean/EbeanParsedConfigTest.java @@ -64,7 +64,7 @@ public void disableGenerateEvolutionsScripts() { @Test public void readOnlyDatasources() { EbeanParsedConfig config = - parse(ImmutableMap.of("play.ebean.readOnlyDatasources.default", "replica")); + parse(ImmutableMap.of("play.ebean.db.default.readOnlyDatasource", "replica")); assertThat(config.getReadOnlyDatasources().size(), equalTo(1)); assertThat(config.getReadOnlyDatasources().get("default"), equalTo("replica")); } diff --git a/play-ebean/src/test/java/play/db/ebean/EbeanReadOnlyDatasourceTest.java b/play-ebean/src/test/java/play/db/ebean/EbeanReadOnlyDatasourceTest.java index 488e8972..9a528da2 100644 --- a/play-ebean/src/test/java/play/db/ebean/EbeanReadOnlyDatasourceTest.java +++ b/play-ebean/src/test/java/play/db/ebean/EbeanReadOnlyDatasourceTest.java @@ -41,7 +41,7 @@ private static String currentDatabase(Database database) { @Test public void queriesOutsideOfTransactionsUseReadOnlyDatasource() { Application app = - appBuilder(Map.of("play.ebean.readOnlyDatasources.default", "replica")).build(); + appBuilder(Map.of("play.ebean.db.default.readOnlyDatasource", "replica")).build(); Helpers.start(app); try { Database database = DB.getDefault(); @@ -71,18 +71,18 @@ public void queriesUseMainDatasourceByDefault() { @Test public void failsForUnknownPlayDatabase() { GuiceApplicationBuilder builder = - appBuilder(Map.of("play.ebean.readOnlyDatasources.default", "unknown")); + appBuilder(Map.of("play.ebean.db.default.readOnlyDatasource", "unknown")); Exception e = assertThrows(Exception.class, builder::build); - assertThat(e.getMessage(), containsString("play.ebean.readOnlyDatasources.default")); + assertThat(e.getMessage(), containsString("play.ebean.db.default.readOnlyDatasource")); assertThat(e.getMessage(), containsString("There is no Play database 'unknown'")); } @Test public void failsForUnknownEbeanServer() { GuiceApplicationBuilder builder = - appBuilder(Map.of("play.ebean.readOnlyDatasources.unknown", "replica")); + appBuilder(Map.of("play.ebean.db.unknown.readOnlyDatasource", "replica")); Exception e = assertThrows(Exception.class, builder::build); - assertThat(e.getMessage(), containsString("play.ebean.readOnlyDatasources.unknown")); + assertThat(e.getMessage(), containsString("play.ebean.db.unknown")); assertThat(e.getMessage(), containsString("There is no Ebean server 'unknown'")); } } From aeeff83af6ef914ee98a74e1c3a4ed2caa3f6d0d Mon Sep 17 00:00:00 2001 From: Matthias Kurz Date: Thu, 8 Oct 2026 13:35:39 +0200 Subject: [PATCH 2/3] Allow turning evolutions script generation on or off per Ebean server play.ebean.generateEvolutionsScripts turns the generation of the evolutions scripts on or off for all Ebean servers. The new play.ebean.db..generateEvolutionsScripts setting overrides it for a single Ebean server, in both directions, e.g. to not generate a script for a read-only database, or for a database whose evolutions come from the classpath, which a generated conf/evolutions file would shadow. This is independent of Play's play.evolutions.db..enabled setting, and nothing changes unless the new setting is used. Custom EbeanConfig implementations keep working, as the new per-server method defaults to the global setting. --- build.sbt | 7 ++ .../working/javaGuide/main/sql/JavaEbean.md | 15 ++++ .../play/db/ebean/DefaultEbeanConfig.java | 25 +++++- .../main/java/play/db/ebean/EbeanConfig.java | 8 ++ .../play/db/ebean/EbeanDynamicEvolutions.java | 6 +- .../java/play/db/ebean/EbeanParsedConfig.java | 38 +++++++- play-ebean/src/main/resources/reference.conf | 5 +- .../db/ebean/EbeanEvolutionsScriptsTest.java | 89 +++++++++++++++++++ .../play/db/ebean/EbeanParsedConfigTest.java | 20 +++++ .../db/ebean/models/first/FirstModel.java | 13 +++ .../db/ebean/models/second/SecondModel.java | 13 +++ 11 files changed, 230 insertions(+), 9 deletions(-) create mode 100644 play-ebean/src/test/java/play/db/ebean/EbeanEvolutionsScriptsTest.java create mode 100644 play-ebean/src/test/java/play/db/ebean/models/first/FirstModel.java create mode 100644 play-ebean/src/test/java/play/db/ebean/models/second/SecondModel.java diff --git a/build.sbt b/build.sbt index e1ab5027..da6ef310 100644 --- a/build.sbt +++ b/build.sbt @@ -65,6 +65,13 @@ lazy val core = project (Compile / classDirectory).value, "play/db/ebean/**" ), + // The models of the tests need to be enhanced as well + Test / compile := enhanceEbeanClasses( + (Test / dependencyClasspath).value, + (Test / compile).value, + (Test / classDirectory).value, + "play/db/ebean/models/**" + ), ) lazy val plugin = project diff --git a/docs/manual/working/javaGuide/main/sql/JavaEbean.md b/docs/manual/working/javaGuide/main/sql/JavaEbean.md index ee3eefb7..e60eddf2 100644 --- a/docs/manual/working/javaGuide/main/sql/JavaEbean.md +++ b/docs/manual/working/javaGuide/main/sql/JavaEbean.md @@ -66,6 +66,21 @@ Here `default` is the name of the Ebean server and `replica` the name of the Pla Ebean supports one read-only data source per Ebean server. To spread the read-only queries across multiple read replicas, use a JDBC URL that lists all of them (if supported by your JDBC driver, e.g. the PostgreSQL driver's `loadBalanceHosts` and `targetServerType` parameters), or point the URL to a load balancer or the reader endpoint of your database service. +#### Generating evolution scripts + +In dev and test mode, Play Ebean generates the evolution script `conf/evolutions//1.sql` of each Ebean server from its models, so that Play's [evolutions](https://www.playframework.com/documentation/latest/Evolutions) can create the database schema. Whenever the models change, Play Ebean updates the script, as long as it starts with the `-- Created by Ebean DDL` comment. To write the evolutions yourself, remove this comment (both lines), then Play Ebean doesn't touch the file anymore. + +You can turn off generating the scripts for all Ebean servers, and override that for single Ebean servers: + +```properties +# Don't generate the scripts of the Ebean servers... +play.ebean.generateEvolutionsScripts = false +# ...except the one of the "default" Ebean server +play.ebean.db.default.generateEvolutionsScripts = true +``` + +This is independent of whether Play applies the evolutions to a database (`play.evolutions.db..enabled`). For example, turn off generating the script for a read-only database, or for a database whose evolution scripts come from the classpath (e.g. from a dependency), because a `conf/evolutions//1.sql` file takes precedence over these. + > For more information about Ebean, see the [Ebean documentation](https://ebean-orm.github.io/docs). #### Configuring the sbt plugin diff --git a/play-ebean/src/main/java/play/db/ebean/DefaultEbeanConfig.java b/play-ebean/src/main/java/play/db/ebean/DefaultEbeanConfig.java index 752485fc..28a09397 100644 --- a/play-ebean/src/main/java/play/db/ebean/DefaultEbeanConfig.java +++ b/play-ebean/src/main/java/play/db/ebean/DefaultEbeanConfig.java @@ -31,13 +31,24 @@ public class DefaultEbeanConfig implements EbeanConfig { private final boolean generateEvolutionsScripts; + private final Map generateEvolutionsScriptsByServer; + public DefaultEbeanConfig( String defaultServer, Map serverConfigs, - boolean generateEvolutionsScripts) { + boolean generateEvolutionsScripts, + Map generateEvolutionsScriptsByServer) { this.defaultServer = defaultServer; this.serverConfigs = serverConfigs; this.generateEvolutionsScripts = generateEvolutionsScripts; + this.generateEvolutionsScriptsByServer = generateEvolutionsScriptsByServer; + } + + public DefaultEbeanConfig( + String defaultServer, + Map serverConfigs, + boolean generateEvolutionsScripts) { + this(defaultServer, serverConfigs, generateEvolutionsScripts, Collections.emptyMap()); } public DefaultEbeanConfig(String defaultServer, Map serverConfigs) { @@ -59,6 +70,11 @@ public boolean generateEvolutionsScripts() { return generateEvolutionsScripts; } + @Override + public boolean generateEvolutionsScripts(String server) { + return generateEvolutionsScriptsByServer.getOrDefault(server, generateEvolutionsScripts); + } + @Singleton public static class EbeanConfigParser implements Provider { @@ -91,7 +107,9 @@ public EbeanConfig parse() { EbeanParsedConfig ebeanConfig = EbeanParsedConfig.parseFromConfig(config); - for (String key : ebeanConfig.getReadOnlyDatasources().keySet()) { + Set configuredServers = new HashSet<>(ebeanConfig.getReadOnlyDatasources().keySet()); + configuredServers.addAll(ebeanConfig.getGenerateEvolutionsScriptsByServer().keySet()); + for (String key : configuredServers) { if (!ebeanConfig.getDatasourceModels().containsKey(key)) { throw new ConfigException.BadValue( "play.ebean.db." + key, "There is no Ebean server '" + key + "'"); @@ -132,7 +150,8 @@ public EbeanConfig parse() { return new DefaultEbeanConfig( ebeanConfig.getDefaultDatasource(), serverConfigs, - ebeanConfig.generateEvolutionsScripts()); + ebeanConfig.generateEvolutionsScripts(), + ebeanConfig.getGenerateEvolutionsScriptsByServer()); } private void setServerConfigDataSource(String key, DatabaseConfig serverConfig) { diff --git a/play-ebean/src/main/java/play/db/ebean/EbeanConfig.java b/play-ebean/src/main/java/play/db/ebean/EbeanConfig.java index f2a2b4f2..cde632a7 100644 --- a/play-ebean/src/main/java/play/db/ebean/EbeanConfig.java +++ b/play-ebean/src/main/java/play/db/ebean/EbeanConfig.java @@ -14,4 +14,12 @@ public interface EbeanConfig { Map serverConfigs(); boolean generateEvolutionsScripts(); + + /** + * @param server the name of the Ebean server + * @return whether to generate the evolutions script of the given Ebean server + */ + default boolean generateEvolutionsScripts(String server) { + return generateEvolutionsScripts(); + } } diff --git a/play-ebean/src/main/java/play/db/ebean/EbeanDynamicEvolutions.java b/play-ebean/src/main/java/play/db/ebean/EbeanDynamicEvolutions.java index 6dc66cb1..80a3b961 100644 --- a/play-ebean/src/main/java/play/db/ebean/EbeanDynamicEvolutions.java +++ b/play-ebean/src/main/java/play/db/ebean/EbeanDynamicEvolutions.java @@ -64,13 +64,13 @@ public void create() { if (environment.isProd()) { return; } - if (!config.generateEvolutionsScripts()) { - return; - } config .serverConfigs() .forEach( (key, serverConfig) -> { + if (!config.generateEvolutionsScripts(key)) { + return; + } String evolutionScript = generateEvolutionScript(databases.get(key)); if (evolutionScript == null) { return; diff --git a/play-ebean/src/main/java/play/db/ebean/EbeanParsedConfig.java b/play-ebean/src/main/java/play/db/ebean/EbeanParsedConfig.java index 1712a4f6..0caa661c 100644 --- a/play-ebean/src/main/java/play/db/ebean/EbeanParsedConfig.java +++ b/play-ebean/src/main/java/play/db/ebean/EbeanParsedConfig.java @@ -24,16 +24,20 @@ public class EbeanParsedConfig { private final boolean generateEvolutionsScripts; + private final Map generateEvolutionsScriptsByServer; + private final Map readOnlyDatasources; public EbeanParsedConfig( String defaultDatasource, Map> datasourceModels, boolean generateEvolutionsScripts, + Map generateEvolutionsScriptsByServer, Map readOnlyDatasources) { this.defaultDatasource = defaultDatasource; this.datasourceModels = datasourceModels; this.generateEvolutionsScripts = generateEvolutionsScripts; + this.generateEvolutionsScriptsByServer = generateEvolutionsScriptsByServer; this.readOnlyDatasources = readOnlyDatasources; } @@ -41,7 +45,12 @@ public EbeanParsedConfig( String defaultDatasource, Map> datasourceModels, boolean generateEvolutionsScripts) { - this(defaultDatasource, datasourceModels, generateEvolutionsScripts, Collections.emptyMap()); + this( + defaultDatasource, + datasourceModels, + generateEvolutionsScripts, + Collections.emptyMap(), + Collections.emptyMap()); } public EbeanParsedConfig(String defaultDatasource, Map> datasourceModels) { @@ -60,6 +69,22 @@ public boolean generateEvolutionsScripts() { return generateEvolutionsScripts; } + /** + * @param server the name of the Ebean server + * @return whether to generate the evolutions script of the given Ebean server + */ + public boolean generateEvolutionsScripts(String server) { + return generateEvolutionsScriptsByServer.getOrDefault(server, generateEvolutionsScripts); + } + + /** + * @return the explicit settings whether to generate the evolutions scripts, keyed by Ebean server + * name. + */ + public Map getGenerateEvolutionsScriptsByServer() { + return generateEvolutionsScriptsByServer; + } + /** * @return the names of the Play databases the Ebean servers use for read-only queries, keyed by * Ebean server name. @@ -100,6 +125,7 @@ public static EbeanParsedConfig parseFromConfig(Config config) { datasourceModels.put(key, models); }); } + Map generateEvolutionsScriptsByServer = new HashMap<>(); Map readOnlyDatasources = new HashMap<>(); Config serversConfig = playEbeanConfig.getConfig("db"); serversConfig @@ -108,12 +134,20 @@ public static EbeanParsedConfig parseFromConfig(Config config) { .forEach( server -> { Config serverConfig = serversConfig.getConfig(server); + if (serverConfig.hasPath("generateEvolutionsScripts")) { + generateEvolutionsScriptsByServer.put( + server, serverConfig.getBoolean("generateEvolutionsScripts")); + } if (serverConfig.hasPath("readOnlyDatasource")) { readOnlyDatasources.put(server, serverConfig.getString("readOnlyDatasource")); } }); return new EbeanParsedConfig( - defaultDatasource, datasourceModels, generateEvolutionsScripts, readOnlyDatasources); + defaultDatasource, + datasourceModels, + generateEvolutionsScripts, + generateEvolutionsScriptsByServer, + readOnlyDatasources); } } diff --git a/play-ebean/src/main/resources/reference.conf b/play-ebean/src/main/resources/reference.conf index def7ae33..21705648 100644 --- a/play-ebean/src/main/resources/reference.conf +++ b/play-ebean/src/main/resources/reference.conf @@ -10,13 +10,16 @@ play { # The name of the default ebean datasource defaultDatasource = "default" - # The key to control the generation of the Evolutions scripts + # Whether to generate the evolutions scripts of the Ebean servers (in dev and test mode). Can be + # overridden for single Ebean servers, see `db` below. generateEvolutionsScripts = true # Settings of single Ebean servers, keyed by the name of the Ebean server: # - readOnlyDatasource: the Play database the Ebean server should use for its read-only queries # (e.g. a read replica). For example, `db.default.readOnlyDatasource = "replica"` makes the # default Ebean server run queries outside of transactions against the `replica` database. + # - generateEvolutionsScripts: whether to generate the evolutions script of the Ebean server, + # overriding the global generateEvolutionsScripts setting above. db {} } } diff --git a/play-ebean/src/test/java/play/db/ebean/EbeanEvolutionsScriptsTest.java b/play-ebean/src/test/java/play/db/ebean/EbeanEvolutionsScriptsTest.java new file mode 100644 index 00000000..5ebd6279 --- /dev/null +++ b/play-ebean/src/test/java/play/db/ebean/EbeanEvolutionsScriptsTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) from 2022 The Play Framework Contributors , 2011-2021 Lightbend Inc. + */ + +package play.db.ebean; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.io.File; +import java.util.List; +import java.util.Map; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import play.inject.guice.GuiceApplicationBuilder; +import play.test.Helpers; + +public class EbeanEvolutionsScriptsTest { + + @Rule public TemporaryFolder rootPath = new TemporaryFolder(); + + private GuiceApplicationBuilder appBuilder(Map config) { + return new GuiceApplicationBuilder() + .in(rootPath.getRoot()) + .configure( + Map.of( + "db.first.driver", "org.h2.Driver", + "db.first.url", "jdbc:h2:mem:play-ebean-first", + "db.second.driver", "org.h2.Driver", + "db.second.url", "jdbc:h2:mem:play-ebean-second", + "ebean.first", List.of("play.db.ebean.models.first.*"), + "ebean.second", List.of("play.db.ebean.models.second.*"), + "play.ebean.defaultDatasource", "first", + // Generating the scripts doesn't depend on Play applying evolutions + "play.evolutions.enabled", false)) + .configure(config); + } + + /** Runs the application and returns for which Ebean servers it generated evolution scripts. */ + private List generatedScripts(Map config) { + Helpers.running(appBuilder(config).build(), () -> {}); + return List.of(scriptExists("first"), scriptExists("second")); + } + + private boolean scriptExists(String server) { + return new File(rootPath.getRoot(), "conf/evolutions/" + server + "/1.sql").isFile(); + } + + @Test + public void generatesScriptsByDefault() { + assertEquals(List.of(true, true), generatedScripts(Map.of())); + } + + @Test + public void doesNotGenerateScriptsWhenDisabled() { + assertEquals( + List.of(false, false), + generatedScripts(Map.of("play.ebean.generateEvolutionsScripts", false))); + } + + @Test + public void doesNotGenerateScriptOfServerWhenDisabledForServer() { + assertEquals( + List.of(true, false), + generatedScripts(Map.of("play.ebean.db.second.generateEvolutionsScripts", false))); + } + + @Test + public void generatesScriptOfServerWhenEnabledForServer() { + assertEquals( + List.of(false, true), + generatedScripts( + Map.of( + "play.ebean.generateEvolutionsScripts", false, + "play.ebean.db.second.generateEvolutionsScripts", true))); + } + + @Test + public void failsForUnknownEbeanServer() { + GuiceApplicationBuilder builder = + appBuilder(Map.of("play.ebean.db.unknown.generateEvolutionsScripts", false)); + Exception e = assertThrows(Exception.class, builder::build); + assertThat(e.getMessage(), containsString("play.ebean.db.unknown")); + assertThat(e.getMessage(), containsString("There is no Ebean server 'unknown'")); + } +} diff --git a/play-ebean/src/test/java/play/db/ebean/EbeanParsedConfigTest.java b/play-ebean/src/test/java/play/db/ebean/EbeanParsedConfigTest.java index 8223387b..1987e384 100644 --- a/play-ebean/src/test/java/play/db/ebean/EbeanParsedConfigTest.java +++ b/play-ebean/src/test/java/play/db/ebean/EbeanParsedConfigTest.java @@ -28,6 +28,7 @@ public void defaultConfig() { assertThat(config.getDatasourceModels().size(), equalTo(0)); assertThat(config.generateEvolutionsScripts(), equalTo(Boolean.TRUE)); assertThat(config.getReadOnlyDatasources().size(), equalTo(0)); + assertThat(config.getGenerateEvolutionsScriptsByServer().size(), equalTo(0)); } @Test @@ -61,6 +62,25 @@ public void disableGenerateEvolutionsScripts() { assertThat(config.generateEvolutionsScripts(), equalTo(Boolean.FALSE)); } + @Test + public void disableGenerateEvolutionsScriptsForServer() { + EbeanParsedConfig config = + parse(ImmutableMap.of("play.ebean.db.other.generateEvolutionsScripts", false)); + assertThat(config.generateEvolutionsScripts("default"), equalTo(Boolean.TRUE)); + assertThat(config.generateEvolutionsScripts("other"), equalTo(Boolean.FALSE)); + } + + @Test + public void enableGenerateEvolutionsScriptsForServer() { + EbeanParsedConfig config = + parse( + ImmutableMap.of( + "play.ebean.generateEvolutionsScripts", false, + "play.ebean.db.other.generateEvolutionsScripts", true)); + assertThat(config.generateEvolutionsScripts("default"), equalTo(Boolean.FALSE)); + assertThat(config.generateEvolutionsScripts("other"), equalTo(Boolean.TRUE)); + } + @Test public void readOnlyDatasources() { EbeanParsedConfig config = diff --git a/play-ebean/src/test/java/play/db/ebean/models/first/FirstModel.java b/play-ebean/src/test/java/play/db/ebean/models/first/FirstModel.java new file mode 100644 index 00000000..1293de31 --- /dev/null +++ b/play-ebean/src/test/java/play/db/ebean/models/first/FirstModel.java @@ -0,0 +1,13 @@ +/* + * Copyright (C) from 2022 The Play Framework Contributors , 2011-2021 Lightbend Inc. + */ + +package play.db.ebean.models.first; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +@Entity +public class FirstModel { + @Id public Long id; +} diff --git a/play-ebean/src/test/java/play/db/ebean/models/second/SecondModel.java b/play-ebean/src/test/java/play/db/ebean/models/second/SecondModel.java new file mode 100644 index 00000000..783a1a5a --- /dev/null +++ b/play-ebean/src/test/java/play/db/ebean/models/second/SecondModel.java @@ -0,0 +1,13 @@ +/* + * Copyright (C) from 2022 The Play Framework Contributors , 2011-2021 Lightbend Inc. + */ + +package play.db.ebean.models.second; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +@Entity +public class SecondModel { + @Id public Long id; +} From 6504285e12f874a1262e7f169001f74cea55517a Mon Sep 17 00:00:00 2001 From: Matthias Kurz Date: Thu, 8 Oct 2026 13:38:03 +0200 Subject: [PATCH 3/3] Keep tests without models from writing an evolutions script Without any configured models, Ebean searches the classpath for them, and finds the models of the new evolutions scripts test. So the tests using an empty models list wrote conf/evolutions/default/1.sql into the working directory. --- .../play/db/ebean/EbeanReadOnlyDatasourceTest.java | 5 ++++- .../test/java/play/db/ebean/EbeanShutdownTest.java | 14 +++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/play-ebean/src/test/java/play/db/ebean/EbeanReadOnlyDatasourceTest.java b/play-ebean/src/test/java/play/db/ebean/EbeanReadOnlyDatasourceTest.java index 9a528da2..3dbd3004 100644 --- a/play-ebean/src/test/java/play/db/ebean/EbeanReadOnlyDatasourceTest.java +++ b/play-ebean/src/test/java/play/db/ebean/EbeanReadOnlyDatasourceTest.java @@ -30,7 +30,10 @@ private static GuiceApplicationBuilder appBuilder(Map config) { "db.default.url", "jdbc:h2:mem:play-ebean-primary", "db.replica.driver", "org.h2.Driver", "db.replica.url", "jdbc:h2:mem:play-ebean-replica", - "ebean.default", List.of())) + "ebean.default", List.of(), + // Without models, Ebean searches the classpath and finds the test models, so don't + // write their evolutions script into the working directory + "play.ebean.generateEvolutionsScripts", false)) .configure(config); } diff --git a/play-ebean/src/test/java/play/db/ebean/EbeanShutdownTest.java b/play-ebean/src/test/java/play/db/ebean/EbeanShutdownTest.java index 14895a97..d9921d2a 100644 --- a/play-ebean/src/test/java/play/db/ebean/EbeanShutdownTest.java +++ b/play-ebean/src/test/java/play/db/ebean/EbeanShutdownTest.java @@ -26,9 +26,17 @@ public void ebeanShutdownHookDoesNotShutDownDatabasesBeforeApplicationStops() th new GuiceApplicationBuilder() .configure( Map.of( - "db.default.driver", "org.h2.Driver", - "db.default.url", "jdbc:h2:mem:play-ebean-shutdown", - "ebean.default", List.of())) + "db.default.driver", + "org.h2.Driver", + "db.default.url", + "jdbc:h2:mem:play-ebean-shutdown", + "ebean.default", + List.of(), + // Without models, Ebean searches the classpath and finds the test models, so + // don't + // write their evolutions script into the working directory + "play.ebean.generateEvolutionsScripts", + false)) .build(); Helpers.start(app); Database database = DB.getDefault();