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
2 changes: 1 addition & 1 deletion .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
java: 25, 21, 17
# Symbolic lanes resolve to exact versions in project/Dependencies.scala.
scala: 2.13.x, 3.3.x, 3.9.x, 3.next
cmd: cd docs && sbt -Dscala.version=$MATRIX_SCALA validateCode
cmd: cd docs && sbt -Dscala.version=$MATRIX_SCALA "validateCode; test"

tests:
name: Tests
Expand Down
3 changes: 2 additions & 1 deletion docs/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ lazy val docs = project
)
.settings(PlayEbean.unscopedSettings)
.settings(
// The code samples are test sources, so enhance their models there
inConfig(Test)(
Seq(
PlayEbean.scopedSettings ++ Seq(
playEbeanModels := Seq("javaguide.ebean.*")
)
)
Expand Down
6 changes: 4 additions & 2 deletions docs/manual/working/javaGuide/main/sql/JavaEbean.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@ In this example, we have access to two Ebean servers - each using its own databa

Each `ebean.` config line (as above) can map *any* classes that Ebean may be interested in registering (eg. `@Entity`/`Model` classes, `@Embeddable`s, custom `ScalarType`s and `CompoundType`s, `BeanPersistController`s, `BeanPersistListener`s, `BeanFinder`s, `ServerConfigStartup`s, etc). These can be individually listed separated by commas, and/or you can use the wildcard `.*`. For example, `models.*` registers with Ebean all classes within the models package that Ebean can make use of.

To customise the underlying Ebean Server configuration, you can either add a [`conf/application.yaml`](https://ebean.io/docs/intro/configuration/) file, or create an instance of the `ServerConfigStartup` interface to programmatically manipulate the Ebean `ServerConfig` before the server is initialised.
To customise the underlying Ebean Server configuration, you can either add a [`conf/application.yaml`](https://ebean.io/docs/intro/configuration/) file, or implement the `ServerConfigStartup` interface to programmatically manipulate the Ebean `DatabaseBuilder` before the server is initialised.

As an example, the fairly common problem of reducing the sequence batch size in order to minimise sequence gaps, could be solved quite simply with a class like this:

@[content](code/javaguide/ebean/MyServerConfigStartup.java)

Make sure that such a class is covered by an `ebean.` config line, for example by putting it into the `models` package. Play Ebean registers the configured classes with Ebean explicitly, which disables Ebean's own classpath scanning, so Ebean silently ignores any `ServerConfigStartup` that is not covered.

Note that Ebean will also make use of a `conf/orm.xml` file (if present), to configure `<entity-mappings>`.

#### Using a read replica
Expand Down Expand Up @@ -108,7 +110,7 @@ By default Ebean will use transactions. However these transactions will be creat

@[transaction](code/javaguide/ebean/JavaEbeanTest.java)

So, if you want to do more than one action in the same transaction you can use TxRunnable and TxCallable:
So, if you want to do more than one action in the same transaction you can use `DB.execute` (or `DB.executeCall` to return a value):

@[txrunnable](code/javaguide/ebean/JavaEbeanTest.java)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
import static org.junit.Assert.*;
import static play.test.Helpers.*;

import io.ebean.Ebean;
import io.ebean.DB;
import io.ebean.Transaction;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -91,11 +92,11 @@ public void txRunnable() {

// ###insert: ...

Ebean.execute(
DB.execute(
() -> {
// code running in "REQUIRED" transactional scope
// ... as "REQUIRED" is the default TxType
System.out.println(Ebean.currentTransaction());
System.out.println(DB.currentTransaction());

Task task = Task.find.byId(34L);
task.setDone(true);
Expand Down Expand Up @@ -131,16 +132,17 @@ public void traditional() {
createTask();

// #traditional
Ebean.beginTransaction();
try {
// ###insert: import io.ebean.*;

// ###insert: ...

try (Transaction transaction = DB.beginTransaction()) {
Task task = Task.find.byId(34L);
task.setDone(true);

task.save();

Ebean.commitTransaction();
} finally {
Ebean.endTransaction();
transaction.commit();
}
// #traditional

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
// ###replace: package models;
package javaguide.ebean;

import io.ebean.config.ServerConfig;
import io.ebean.DatabaseBuilder;
import io.ebean.event.ServerConfigStartup;

public class MyServerConfigStartup implements ServerConfigStartup {
public void onStart(ServerConfig serverConfig) {
serverConfig.setDatabaseSequenceBatchSize(1);
@Override
public void onStart(DatabaseBuilder config) {
config.databaseSequenceBatchSize(1);
}
}
// #content
25 changes: 24 additions & 1 deletion sbt-play-ebean/src/main/scala/play/ebean/sbt/PlayEbean.scala
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,25 @@ object PlayEbean extends AutoPlugin {
// This is replacement of old Stamp `Exists` representation
private final val notPresent = "absent"

/**
* Turns the configured models into the packages the enhancer processes: packages (`models.*` or `models`) as they
* are, and single classes (`models.Task`) via their package. Entries without compiled classes (e.g. packages not
* existing yet, or provided by a dependency) are skipped, because the enhancer fails for them.
*/
private def packagesToEnhance(models: Seq[String], classes: File): Seq[String] = {
def isPackage(name: String) = new File(classes, name.replace('.', '/')).isDirectory
models
.map(_.trim)
.flatMap { model =>
// Like the enhancer, ignore a trailing ".*" or ".**"
val pkg = model.stripSuffix("*").stripSuffix("*").stripSuffix(".")
if (isPackage(pkg)) Some(pkg)
else if (pkg == model && model.contains('.')) Some(model.substring(0, model.lastIndexOf('.'))).filter(isPackage)
else None
}
.distinct
}

def ebeanEnhance: Def.Initialize[Task[CompileResult]] =
Def.task {

Expand All @@ -120,7 +139,11 @@ object PlayEbean extends AutoPlugin {
val transformer = new Transformer(classLoader, agentArgsString)
val fileTransform = new OfflineFileTransform(transformer, classLoader, classes.getAbsolutePath)

fileTransform.process(models.mkString(","))
val packages = packagesToEnhance(models, classes)
// An empty list would make the enhancer process all classes
if (packages.nonEmpty) {
fileTransform.process(packages.mkString(","))
}

} finally {
Thread.currentThread.setContextClassLoader(originalContextClassLoader)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
*/

package models;

import java.util.*;
import jakarta.persistence.*;

import io.ebean.*;
import play.data.format.*;
import play.data.validation.*;

@Entity
public class Task extends Model {

public static final Finder<Long, Task> find = new Finder<>(Task.class);

@Id
public Long id;

@Constraints.Required
public String name;

public boolean done;

@Formats.DateTime(pattern = "dd/MM/yyyy")
public Date dueDate = new Date();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>

lazy val root = project
.in(file("."))
.enablePlugins(PlayJava, PlayEbean)

scalaVersion := sys.props("scala.version")

resolvers ++= Seq(Resolver.sonatypeCentralSnapshots, Resolver.ApacheMavenSnapshotsRepo)

Test / sourceDirectory := baseDirectory.value / "tests"

Test / scalaSource := baseDirectory.value / "tests"

Test / javaSource := baseDirectory.value / "tests"

libraryDependencies += "com.h2database" % "h2" % "2.5.252"
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# A package without any compiled classes (listed first, so it must not stop the enhancement of the
# following entries) and a single model class instead of a package
ebean.default = [ "missing.*", "models.Task" ]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>

resolvers += Resolver.sonatypeCentralSnapshots

addSbtPlugin("org.playframework" % "sbt-play-ebean" % sys.props("project.version"))
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
> test
$ exists conf/evolutions/default/1.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
*/

package models;

import org.junit.*;

import static org.junit.Assert.*;

import play.test.*;
import play.Application;

import static play.test.Helpers.*;

public class TestTask extends WithApplication {

protected Application provideApplication() {
return fakeApplication(inMemoryDatabase());
}

@Test
public void saveAndFind() {
Task task = new Task();
task.id = 10L;
task.name = "Hello";
task.done = false;
task.save();

Task saved = Task.find.byId(10L);
assertEquals("Hello", saved.name);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
*/

package models;

import java.util.*;
import jakarta.persistence.*;

import io.ebean.*;
import play.data.format.*;
import play.data.validation.*;

@Entity
public class Task extends Model {

public static final Finder<Long, Task> find = new Finder<>(Task.class);

@Id
public Long id;

@Constraints.Required
public String name;

public boolean done;

@Formats.DateTime(pattern = "dd/MM/yyyy")
public Date dueDate = new Date();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
*/

package other;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;

/** An entity outside of the configured package, so it must not get enhanced. */
@Entity
public class Other {
@Id public Long id;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>

lazy val root = project
.in(file("."))
.enablePlugins(PlayJava, PlayEbean)

scalaVersion := sys.props("scala.version")

resolvers ++= Seq(Resolver.sonatypeCentralSnapshots, Resolver.ApacheMavenSnapshotsRepo)

Test / sourceDirectory := baseDirectory.value / "tests"

Test / scalaSource := baseDirectory.value / "tests"

Test / javaSource := baseDirectory.value / "tests"

libraryDependencies += "com.h2database" % "h2" % "2.5.252"

// A plain package name (without ".*"), which must not be taken for a class
Compile / playEbeanModels := Seq("models")
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>

resolvers += Resolver.sonatypeCentralSnapshots

addSbtPlugin("org.playframework" % "sbt-play-ebean" % sys.props("project.version"))
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
> test
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
*/

package models;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import io.ebean.bean.EntityBean;
import org.junit.Test;

public class EnhancementTest {

@Test
public void enhancesOnlyTheConfiguredPackage() {
assertTrue(EntityBean.class.isAssignableFrom(models.Task.class));
assertFalse(EntityBean.class.isAssignableFrom(other.Other.class));
}
}