Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion docs/manual/working/javaGuide/main/sql/JavaEbean.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,13 @@ Play Ebean creates the Ebean databases while the application starts, and Ebean's

Otherwise Ebean tries to create the database itself from its own configuration. That usually fails, e.g. with `Configuration error creating DataSource for the default Database`, and leaves Ebean unusable until the JVM restarts. A later error like `NoClassDefFoundError: Could not initialize class io.ebean.DbContext` only means that this initialization failed before, so look for the first error to find out why.

Note that this only makes sure that the databases exist, not that the evolutions have been applied. By default, in dev mode, Play only applies pending evolutions once you confirm them in the browser, and it can't show that page if the application fails to start, e.g. because a component queries a table that doesn't exist yet. Depending on `play.api.db.evolutions.ApplicationEvolutions` doesn't help with that either, as in dev mode it doesn't wait for pending evolutions (its `upToDate()` method tells if there are any). So don't use the database schema while components get created, but later, e.g. when they get used for the first time. Or let Play apply the evolutions automatically, which you can limit to dev mode in your `build.sbt`:
Note that this only makes sure that the databases exist, not that the evolutions have been applied. By default, in dev mode, Play only applies pending evolutions once you confirm them in the browser, and it can't show that page if the application fails to start, e.g. because a component queries a table that doesn't exist yet. So if a component uses the database schema while it gets created, e.g. to insert some initial data, make it depend on `play.api.db.evolutions.ApplicationEvolutions` instead. Play then checks the evolutions (and, if `autoApply` is enabled, applies them) before it creates the component, and as `ApplicationEvolutions` depends on `DynamicEvolutions`, the databases exist as well. In dev mode, Play starts the application even if evolutions still need to be applied, so only use the schema if `upToDate()` returns `true`:

@[startup-evolutions](code/javaguide/ebean/StartupDataLoader.java)

Once you apply the evolutions in the browser, Play reloads the application, which creates the component again, this time with `upToDate()` returning `true`. In test mode, Play applies the evolutions automatically, and in prod mode, it doesn't start the application while evolutions still need to be applied, unless they get applied automatically.

You can also let Play apply the evolutions automatically, limited to dev mode in your `build.sbt`:

```scala
PlayKeys.devSettings += "play.evolutions.db.default.autoApply" -> "true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,16 @@ public void traditional() {
assertThat(Task.find.byId(34L).isDone(), is(true));
}

@Test
public void startupDataLoader() {
// Evolutions get applied automatically in test mode
app.injector().instanceOf(StartupDataLoader.class);

List<Task> tasks = Task.find.all();
assertThat(tasks.size(), equalTo(1));
assertThat(tasks.get(0).getName(), equalTo("Initial task"));
}

private void createTask() {
Task task = new Task();
task.setId(34L);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
*/

// #startup-evolutions
// ###replace: package services;
package javaguide.ebean;

import javax.inject.Inject;
import javax.inject.Singleton;
// ###insert: import models.Task;
import play.api.db.evolutions.ApplicationEvolutions;

@Singleton
public class StartupDataLoader {

@Inject
public StartupDataLoader(ApplicationEvolutions evolutions) {
// Only use the schema once all evolutions have been applied. In dev mode, applying them in
// the browser reloads the application, which creates this component again.
if (evolutions.upToDate() && Task.find.query().findCount() == 0) {
Task task = new Task();
task.setName("Initial task");
task.save();
}
}
}
// #startup-evolutions