diff --git a/build.sbt b/build.sbt index 5931ac06..eb26b1b3 100644 --- a/build.sbt +++ b/build.sbt @@ -1,4 +1,5 @@ -import Dependencies.ScalaVersions.{scala212, scala213} +import Dependencies.ScalaVersions.scala212 +import Dependencies.ScalaVersions.scala213 import Dependencies.Versions import sbt.Append.appendSeq import xsbti.compile.CompileAnalysis @@ -20,7 +21,7 @@ Global / onLoad := (Global / onLoad).value.andThen { s => lazy val mimaSettings = Seq( mimaPreviousArtifacts := Set( organization.value %% name.value % "6.0.0" //previousStableVersion.value - //.getOrElse(throw new Error("Unable to determine previous version")) + //.getOrElse(throw new Error("Unable to determine previous version")) ), ) @@ -48,7 +49,13 @@ lazy val core = project (classDirectory in Compile).value, "play/db/ebean/**" ), - jacocoReportSettings := JacocoReportSettings("Jacoco Coverage Report", None, JacocoThresholds(), Seq(JacocoReportFormats.XML), "utf-8") + jacocoReportSettings := JacocoReportSettings( + "Jacoco Coverage Report", + None, + JacocoThresholds(), + Seq(JacocoReportFormats.XML), + "utf-8" + ) ) lazy val plugin = project @@ -72,27 +79,47 @@ lazy val plugin = project ) def sbtPluginDep(moduleId: ModuleID, sbtVersion: String, scalaVersion: String) = { - Defaults.sbtPluginExtra(moduleId, CrossVersion.binarySbtVersion(sbtVersion), CrossVersion.binaryScalaVersion(scalaVersion)) + Defaults.sbtPluginExtra( + moduleId, + CrossVersion.binarySbtVersion(sbtVersion), + CrossVersion.binaryScalaVersion(scalaVersion) + ) } // Ebean enhancement -def enhanceEbeanClasses(classpath: Classpath, analysis: CompileAnalysis, classDirectory: File, pkg: String): CompileAnalysis = { +def enhanceEbeanClasses( + classpath: Classpath, + analysis: CompileAnalysis, + classDirectory: File, + pkg: String +): CompileAnalysis = { // Ebean (really hacky sorry) val cp = classpath.map(_.data.toURI.toURL).toArray :+ classDirectory.toURI.toURL val cl = new java.net.URLClassLoader(cp) - val t = cl.loadClass("io.ebean.enhance.Transformer").getConstructor(classOf[ClassLoader], classOf[String]).newInstance(cl, "debug=0").asInstanceOf[AnyRef] - val ft = cl.loadClass("io.ebean.enhance.ant.OfflineFileTransform").getConstructor( - t.getClass, classOf[ClassLoader], classOf[String] - ).newInstance(t, ClassLoader.getSystemClassLoader, classDirectory.getAbsolutePath).asInstanceOf[AnyRef] + val t = cl + .loadClass("io.ebean.enhance.Transformer") + .getConstructor(classOf[ClassLoader], classOf[String]) + .newInstance(cl, "debug=0") + .asInstanceOf[AnyRef] + val ft = cl + .loadClass("io.ebean.enhance.ant.OfflineFileTransform") + .getConstructor( + t.getClass, + classOf[ClassLoader], + classOf[String] + ) + .newInstance(t, ClassLoader.getSystemClassLoader, classDirectory.getAbsolutePath) + .asInstanceOf[AnyRef] ft.getClass.getDeclaredMethod("process", classOf[String]).invoke(ft, pkg) analysis } // Version file -def generateVersionFile = Def.task { - val version = (Keys.version in core).value - val file = (resourceManaged in Compile).value / "play-ebean.version.properties" - val content = s"play-ebean.version=$version" - IO.write(file, content) - Seq(file) -} +def generateVersionFile = + Def.task { + val version = (Keys.version in core).value + val file = (resourceManaged in Compile).value / "play-ebean.version.properties" + val content = s"play-ebean.version=$version" + IO.write(file, content) + Seq(file) + } diff --git a/docs/build.sbt b/docs/build.sbt index 9e536960..3782d1c6 100644 --- a/docs/build.sbt +++ b/docs/build.sbt @@ -8,7 +8,7 @@ lazy val docs = project resolvers ++= DefaultOptions.resolvers(snapshot = true), libraryDependencies += component("play-java-forms"), libraryDependencies += component("play-test") % Test, - libraryDependencies += "com.h2database" % "h2" % "1.4.196" % Test, + libraryDependencies += "com.h2database" % "h2" % "1.4.196" % Test, PlayDocsKeys.javaManualSourceDirectories := (baseDirectory.value / "manual" / "working" / "javaGuide" ** "code").get, // No resource directories shuts the ebean agent up about java sources in the classes directory unmanagedResourceDirectories in Test := Nil, @@ -16,9 +16,24 @@ lazy val docs = project scalaVersion := "2.12.6" ) .settings(PlayEbean.unscopedSettings: _*) - .settings(inConfig(Test)(Seq( - playEbeanModels := Seq("javaguide.ebean.*") - )): _*) + .settings( + inConfig(Test)( + Seq( + ebeanQueryGenerate := false, + ebeanQueryDestDirectory := "app", + ebeanQueryResourceDirectory := "conf", + ebeanQueryModelsPackage := "models", + ebeanQueryModelsQueryModificationPackage := Set("models/query"), + ebeanQueryGenerateFinder := true, + ebeanQueryGenerateFinderField := true, + ebeanQueryGeneratePublicWhereField := true, + ebeanQueryGenerateAopStyle := true, + ebeanQueryArgs := "", + ebeanQueryProcessPackages := None, + playEbeanModels := Seq("javaguide.ebean.*") + ) + ): _* + ) .dependsOn(playEbean) lazy val playEbean = ProjectRef(Path.fileProperty("user.dir").getParentFile, "core") diff --git a/docs/manual/working/javaGuide/main/sql/JavaEbean.md b/docs/manual/working/javaGuide/main/sql/JavaEbean.md index 05dfd013..08ece5b0 100644 --- a/docs/manual/working/javaGuide/main/sql/JavaEbean.md +++ b/docs/manual/working/javaGuide/main/sql/JavaEbean.md @@ -1,9 +1,11 @@ + # Using the Ebean ORM ## Configuring Ebean -Play comes with the [Ebean](https://ebean-orm.github.io/) ORM. To enable it, add the Play Ebean plugin to your SBT plugins in `project/plugins.sbt`: +Play comes with the [Ebean](https://ebean-orm.github.io/) ORM. To enable it, add the Play Ebean plugin to your SBT +plugins in `project/plugins.sbt`: @[add-sbt-plugin](code/ebean.sbt) @@ -15,17 +17,24 @@ And then modify your `build.sbt` to enable the Play Ebean plugin: ### Configuring models -Play Ebean comes with two components, a runtime library that actually talks to the database, and an sbt plugin that enhances the compiled Java bytecode of your models for use with Ebean. Both of these components need to be configured so that Ebean knows where your models are. +Play Ebean comes with two components, a runtime library that actually talks to the database, and an sbt plugin that +enhances the compiled Java bytecode of your models for use with Ebean. Both of these components need to be configured so +that Ebean knows where your models are. #### Configuring the runtime library -The runtime library can be configured by putting the list of packages and/or classes that your Ebean models live in your application configuration file. For example, if all your models are in the `models` package, add the following to `conf/application.conf`: +The runtime library can be configured by putting the list of packages and/or classes that your Ebean models live in your +application configuration file. For example, if all your models are in the `models` package, add the following +to `conf/application.conf`: ```properties ebean.default = ["models.*"] ``` -This defines a `default` Ebean server, using the `default` data source, which must be properly configured. You can also override the name of the default Ebean server by configuring `ebeanconfig.datasource.default` property. This might be useful if you want to use separate databases for testing and development. You can actually create as many Ebean servers you need, and explicitly define the mapped class for each server: +This defines a `default` Ebean server, using the `default` data source, which must be properly configured. You can also +override the name of the default Ebean server by configuring `ebeanconfig.datasource.default` property. This might be +useful if you want to use separate databases for testing and development. You can actually create as many Ebean servers +you need, and explicitly define the mapped class for each server: ```properties ebean.orders = ["models.Order", "models.OrderItem"] @@ -34,11 +43,18 @@ ebean.customers = ["models.Customer", "models.Address"] In this example, we have access to two Ebean servers - each using its own database. -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. +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/ebean.properties` 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/ebean.properties` file, or create an +instance of the `ServerConfigStartup` interface to programmatically manipulate the Ebean `ServerConfig` 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: +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) @@ -48,11 +64,16 @@ Note that Ebean will also make use of a `conf/orm.xml` file (if present), to con #### Configuring the sbt plugin -By default, the sbt plugin will attempt to load your `application.conf` file to discover what your models configuration is. This will work in a simple project setup, however, for projects that have multiple sub projects, where the `application.conf` file lives in a different project to where the ebean model classes live, this may not work. In this case you will need to manually specify the ebean models for each sub project that contains ebean models, using the `playEbeanModels` configuration item: +By default, the sbt plugin will attempt to load your `application.conf` file to discover what your models configuration +is. This will work in a simple project setup, however, for projects that have multiple sub projects, where +the `application.conf` file lives in a different project to where the ebean model classes live, this may not work. In +this case you will need to manually specify the ebean models for each sub project that contains ebean models, using +the `playEbeanModels` configuration item: @[play-ebean-models](code/ebean.sbt) -In addition to configuring the models, you may wish to enable debug of the configuration. This can be done using `playEbeanDebugLevel`, with -1 being off, and 9 showing the most amount of debug: +In addition to configuring the models, you may wish to enable debug of the configuration. This can be done +using `playEbeanDebugLevel`, with -1 being off, and 9 showing the most amount of debug: @[play-ebean-debug](code/ebean.sbt) @@ -64,9 +85,15 @@ Finally, if you want to also enhance models in your tests, you can do this by co @[play-ebean-test](code/ebean.sbt) +## Generating Typesafe query beans. + +The plugin can also be configured to generate type safe query beans. By default, this is disabled. To enable it, set the +property `ebeanQueryGenerate := true`. + ## Using Model superclass -Ebean defines a convenient superclass for your Ebean model classes, `io.ebean.Model`. Here is a typical Ebean class, mapped in Play: +Ebean defines a convenient superclass for your Ebean model classes, `io.ebean.Model`. Here is a typical Ebean class, +mapped in Play: @[content](code/javaguide/ebean/Task.java) @@ -78,13 +105,15 @@ Ebean defines a convenient superclass for your Ebean model classes, `io.ebean.Mo > (2) Enhancement of direct Ebean field access (enabling lazy loading) is only applied to Java classes, not to Scala. Thus, direct field access from Scala source files (including standard Play templates) does not invoke lazy loading, often resulting in empty (unpopulated) entity fields. To ensure the fields get populated, either (a) manually create getter/setters and call them instead, or (b) ensure the entity is fully populated *before* accessing the fields. -As you can see, we've added a `find` static field, defining a `Finder` for an entity of type `Task` with a `Long` identifier. This helper field is then used to simplify querying our model: +As you can see, we've added a `find` static field, defining a `Finder` for an entity of type `Task` with a `Long` +identifier. This helper field is then used to simplify querying our model: @[operations](code/javaguide/ebean/JavaEbeanTest.java) ## Transactional actions -By default Ebean will use transactions. However these transactions will be created before and commited or rollbacked after every single query, update, create or delete, as you can see here: +By default Ebean will use transactions. However these transactions will be created before and commited or rollbacked +after every single query, update, create or delete, as you can see here: @[transaction](code/javaguide/ebean/JavaEbeanTest.java) @@ -92,7 +121,8 @@ So, if you want to do more than one action in the same transaction you can use T @[txrunnable](code/javaguide/ebean/JavaEbeanTest.java) -If your class is an action, you can annotate your action method with `@play.db.ebean.Transactional` to compose your action method with an `Action` that will automatically manage a transaction: +If your class is an action, you can annotate your action method with `@play.db.ebean.Transactional` to compose your +action method with an `Action` that will automatically manage a transaction: @[annotation](code/javaguide/ebean/JavaEbeanTest.java) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 45f49ef9..e81baa6a 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -4,39 +4,46 @@ import sbt._ object Dependencies { object ScalaVersions { - val scala212 = "2.12.10" - val scala213 = "2.13.2" + val scala212 = "2.12.13" + val scala213 = "2.13.5" } object Versions { - val play: String = "2.8.0" - val ebean = "12.8.1" - val ebeanAgent = "12.8.1" - val typesafeConfig = "1.3.4" + val play: String = "2.8.8" + val ebean = "12.8.3" + val ebeanAgent = "12.8.3" + val ebeanQueryBeanAgent = "10.1.3" + val ebeanQueryBeanGen = "12.8.3" + val typesafeConfig = "1.4.1" } val ebean = libraryDependencies ++= Seq( - "io.ebean" % "ebean" % Versions.ebean, - "io.ebean" % "ebean-ddl-generator" % Versions.ebean, - "io.ebean" % "ebean-agent" % Versions.ebeanAgent, - "com.typesafe.play" %% "play-java-jdbc" % Versions.play, + "io.ebean" % "ebean" % Versions.ebean, + "io.ebean" % "ebean-ddl-generator" % Versions.ebean, + "io.ebean" % "ebean-agent" % Versions.ebeanAgent, + "io.ebean" % "querybean-agent" % Versions.ebeanQueryBeanAgent, + "io.ebean" % "querybean-generator" % Versions.ebeanQueryBeanGen % Test, + "com.typesafe.play" %% "play-java-jdbc" % Versions.play, "com.typesafe.play" %% "play-jdbc-evolutions" % Versions.play, - "com.typesafe.play" %% "play-guice" % Versions.play % Test, - "com.typesafe.play" %% "filters-helpers" % Versions.play % Test, - "com.typesafe.play" %% "play-test" % Versions.play % Test, - ("org.reflections" % "reflections" % "0.9.12") + "com.typesafe.play" %% "play-guice" % Versions.play % Test, + "com.typesafe.play" %% "filters-helpers" % Versions.play % Test, + "com.typesafe.play" %% "play-test" % Versions.play % Test, + ("org.reflections" % "reflections" % "0.9.12") .exclude("com.google.code.findbugs", "annotations") .classifier("") ) val plugin = libraryDependencies ++= Seq( - "io.ebean" % "ebean" % Versions.ebean, - "io.ebean" % "ebean-agent" % Versions.ebeanAgent, - "com.typesafe" % "config" % Versions.typesafeConfig, - "com.typesafe.play" %% "play-java-jdbc" % Versions.play, + "io.ebean" % "ebean" % Versions.ebean, + "io.ebean" % "ebean-agent" % Versions.ebeanAgent, + "io.ebean" % "querybean-agent" % Versions.ebeanQueryBeanAgent, + "io.ebean" % "querybean-generator" % Versions.ebeanQueryBeanGen % Test, + "com.typesafe" % "config" % Versions.typesafeConfig, + "com.typesafe.play" %% "play-java-jdbc" % Versions.play, "com.typesafe.play" %% "play-jdbc-evolutions" % Versions.play, - "com.typesafe.play" %% "play-guice" % Versions.play % Test, - "com.typesafe.play" %% "filters-helpers" % Versions.play % Test, - "com.typesafe.play" %% "play-test" % Versions.play % Test + "com.typesafe.play" %% "play-guice" % Versions.play % Test, + "com.typesafe.play" %% "filters-helpers" % Versions.play % Test, + "com.typesafe.play" %% "play-test" % Versions.play % Test, + "org.clapper" %% "classutil" % "1.5.0" ) } diff --git a/sbt-play-ebean/src/main/scala-sbt-1.0/play/ebean/sbt/PlayEbean.scala b/sbt-play-ebean/src/main/scala-sbt-1.0/play/ebean/sbt/PlayEbean.scala index 3b8a3934..a88ece4d 100644 --- a/sbt-play-ebean/src/main/scala-sbt-1.0/play/ebean/sbt/PlayEbean.scala +++ b/sbt-play-ebean/src/main/scala-sbt-1.0/play/ebean/sbt/PlayEbean.scala @@ -1,8 +1,8 @@ package play.ebean.sbt -import java.net.URLClassLoader import io.ebean.enhance.Transformer import io.ebean.enhance.ant.OfflineFileTransform +import org.clapper.classutil.ClassFinder import sbt.Keys._ import sbt.internal.inc.Hash import sbt.internal.inc.LastModified @@ -14,10 +14,12 @@ import sbt.Task import sbt.inConfig import sbt.settingKey import sbt.taskKey +import sbt._ import xsbti.compile.CompileResult import xsbti.compile.analysis.Stamp -import sbt._ +import java.net.URLClassLoader +import scala.sys.process._ import scala.util.control.NonFatal object PlayEbean extends AutoPlugin { @@ -30,13 +32,31 @@ object PlayEbean extends AutoPlugin { "The debug level to use for the ebean agent. The higher, the more debug is output, with 9 being the most. -1 turns debugging off." ) val playEbeanAgentArgs = taskKey[Map[String, String]]("The arguments to pass to the agent.") + + val ebeanQueryGenerate = settingKey[Boolean]("Generate Query Beans from model classes. Default false.") + val ebeanQueryEnhance = settingKey[Boolean]("Enhance Query Beans from model classes. Defaults to false") + val ebeanQueryDestDirectory = settingKey[String]("Target directory for generated classes. Defaults to app ") + val ebeanQueryResourceDirectory = settingKey[String]("Resource directory to read configuration. Defaults to conf") + val ebeanQueryModelsPackage = settingKey[String]("Directory of models to scan to build query beans") + val ebeanQueryModelsQueryModificationPackage = settingKey[Set[String]]( + "Directories of matching query objects to rewrite field access to use getters. Defaults to [model/query]" + ) + val ebeanQueryGenerateFinder = settingKey[Boolean]("Generate finder objects") + val ebeanQueryGenerateFinderField = settingKey[Boolean]("Modify models to add finder field") + val ebeanQueryGeneratePublicWhereField = settingKey[Boolean]("Public finder field") + val ebeanQueryGenerateAopStyle = settingKey[Boolean]("Use AOP style generation. Default true") + val ebeanQueryArgs = settingKey[String]("Args for generation, useful for logging / debugging generation ") + val ebeanQueryProcessPackages = settingKey[Option[String]]( + "Change to alter the initial package for scanning for model classes. By default views all" + ) } import autoImport._ override def trigger = noTrigger - override def projectSettings = inConfig(Compile)(scopedSettings) ++ unscopedSettings + override def projectSettings: Seq[Def.Setting[_]] = + inConfig(Compile)(scopedSettings) ++ unscopedSettings ++ queryBeanSettings def scopedSettings = Seq( @@ -56,6 +76,22 @@ object PlayEbean extends AutoPlugin { ) ) + def queryBeanSettings = + Seq( + ebeanQueryGenerate := false, + ebeanQueryEnhance := false, + ebeanQueryDestDirectory := "app", + ebeanQueryResourceDirectory := "conf", + ebeanQueryModelsPackage := "models", + ebeanQueryModelsQueryModificationPackage := Set("models/query"), + ebeanQueryGenerateFinder := true, + ebeanQueryGenerateFinderField := true, + ebeanQueryGeneratePublicWhereField := true, + ebeanQueryGenerateAopStyle := true, + ebeanQueryArgs := "", + ebeanQueryProcessPackages := None + ) + // This is replacement of old Stamp `Exists` representation private final val notPresent = "absent" @@ -80,12 +116,38 @@ object PlayEbean extends AutoPlugin { Thread.currentThread.setContextClassLoader(classLoader) + if (ebeanQueryGenerate.value) { + val classpath = ((Compile / products).value ++ (Compile / dependencyClasspath).value.files).mkString(":") + val targetDir = (Compile / ebeanQueryResourceDirectory).value + val processor = "io.ebean.querybean.generator.Processor" + val finder = ClassFinder(List(ebeanQueryModelsPackage.value).map(new File(_))) + val classesToProcess = finder.getClasses().map(_.name).mkString(" ") + + val cmd = + s"javac -cp $classpath -proc:only -processor $processor -XprintRounds -d $targetDir $classesToProcess" + + val log = streams.value.log + val result = cmd ! log + + if (result != 0) { + log.error("Failed to process query bean annotations.") + sys.error(s"Failed running command: $cmd") + } + log.info("Done process query bean annotations.") + } + val transformer = new Transformer(classLoader, agentArgsString) val fileTransform = new OfflineFileTransform(transformer, classLoader, classes.getAbsolutePath) try { fileTransform.process(playEbeanModels.value.mkString(",")) + + if (ebeanQueryEnhance.value) { + val queryTransformer = new Transformer(classLoader, agentArgsString) + val fileQueryTransform = new OfflineFileTransform(queryTransformer, classLoader, classes.getAbsolutePath) + fileQueryTransform.process(ebeanQueryProcessPackages.value.orNull) + } } catch { case NonFatal(_) => } diff --git a/sbt-play-ebean/src/sbt-test/sbt-ebean/query-bean/app/models/Task.java b/sbt-play-ebean/src/sbt-test/sbt-ebean/query-bean/app/models/Task.java new file mode 100644 index 00000000..4d8b55f9 --- /dev/null +++ b/sbt-play-ebean/src/sbt-test/sbt-ebean/query-bean/app/models/Task.java @@ -0,0 +1,25 @@ +package models; + +import java.util.*; +import javax.persistence.*; + +import io.ebean.*; +import play.data.format.*; +import play.data.validation.*; + +@Entity +public class Task extends Model { + + public static final Finder 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(); +} diff --git a/sbt-play-ebean/src/sbt-test/sbt-ebean/query-bean/build.sbt b/sbt-play-ebean/src/sbt-test/sbt-ebean/query-bean/build.sbt new file mode 100644 index 00000000..abb67c15 --- /dev/null +++ b/sbt-play-ebean/src/sbt-test/sbt-ebean/query-bean/build.sbt @@ -0,0 +1,19 @@ +lazy val root = project + .in(file(".")) + .enablePlugins(PlayJava, PlayEbean) + +Compile / packageBin := (Compile / packageBin).dependsOn(Compile / playEbeanQueryBeanProcessAnnotation).value + +scalaVersion := "2.13.5" + +sourceDirectory in Test := baseDirectory.value / "tests" + +scalaSource in Test := baseDirectory.value / "tests" + +javaSource in Test := baseDirectory.value / "tests" + +resolvers ++= DefaultOptions.resolvers(snapshot = true) + +libraryDependencies += "com.h2database" % "h2" % "1.4.196" + +resolvers += "scalaz-releases".at("https://dl.bintray.com/scalaz/releases") // specs2 depends on scalaz-stream diff --git a/sbt-play-ebean/src/sbt-test/sbt-ebean/query-bean/conf/application.conf b/sbt-play-ebean/src/sbt-test/sbt-ebean/query-bean/conf/application.conf new file mode 100644 index 00000000..746fdea8 --- /dev/null +++ b/sbt-play-ebean/src/sbt-test/sbt-ebean/query-bean/conf/application.conf @@ -0,0 +1 @@ +ebean.default = [ "models.*" ] diff --git a/sbt-play-ebean/src/sbt-test/sbt-ebean/query-bean/project/plugins.sbt b/sbt-play-ebean/src/sbt-test/sbt-ebean/query-bean/project/plugins.sbt new file mode 100644 index 00000000..623f392c --- /dev/null +++ b/sbt-play-ebean/src/sbt-test/sbt-ebean/query-bean/project/plugins.sbt @@ -0,0 +1,2 @@ +resolvers ++= DefaultOptions.resolvers(snapshot = true) +addSbtPlugin("com.typesafe.sbt" % "sbt-play-ebean" % sys.props("project.version")) diff --git a/sbt-play-ebean/src/sbt-test/sbt-ebean/query-bean/test b/sbt-play-ebean/src/sbt-test/sbt-ebean/query-bean/test new file mode 100644 index 00000000..0f99eb3c --- /dev/null +++ b/sbt-play-ebean/src/sbt-test/sbt-ebean/query-bean/test @@ -0,0 +1,2 @@ +> test +$ exists conf/evolutions/default/1.sql \ No newline at end of file diff --git a/sbt-play-ebean/src/sbt-test/sbt-ebean/query-bean/tests/models/TestTask.java b/sbt-play-ebean/src/sbt-test/sbt-ebean/query-bean/tests/models/TestTask.java new file mode 100644 index 00000000..ea7343fa --- /dev/null +++ b/sbt-play-ebean/src/sbt-test/sbt-ebean/query-bean/tests/models/TestTask.java @@ -0,0 +1,27 @@ +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); + } + +}