https://playframework.com/documentation/2.6.x/JavaEbean
Play自带Ebean。首先在project/plugins.sbt中添加插件
addSbtPlugin("com.typesafe.sbt" % "sbt-play-ebean" % "4.0.1")
然后在build.sbt中启用
lazy val myProject = (project in file("."))
.enablePlugins(PlayJava, PlayEbean)
Play Ebean带有两个组件,一个运行时库与数据库进行交互,另一个sbt插件帮助增强编译java字节码。这两个组件都需要进行配置这样Ebean才能知道你的模型在哪。
可以通过添加一个含有Ebean模型的package或class列表来配置运行时环境。如果你的模型都在models包中,那么在application.conf中添加
ebean.default = ["models.*"]
这里定义了一个default
Ebean服务,使用default
数据源。通过配置ebeanconfig.datasource.default可以重写default服务的名字。在使用独立的数据库进行开发和测试时会很有用。你可以根据需要尽可能多的创建Ebean服务,然后对类和服务进行映射
ebean.orders = ["models.Order", "models.OrderItem"]
ebean.customers = ["models.Customer", "models.Address"]
每一行都可以映射任意多个Ebean会被使用到的类(eg. @Entity/Model
classes, @Embeddable
s, custom ScalarType
s and CompoundType
s, BeanPersistController
s, BeanPersistListener
s, BeanFinder
s, ServerConfigStartup
s, etc)。这些可以用逗号分割开,通配符.*也可以使用。
为了定制化Ebean服务的配置,可以另外添加一个conf/ebean.properties的文件,或者创建一个ServerConfigStartup在服务初始化前实例以编程的方式来配置EBeanServerConfig
。
一个例子, 一个非常场景的问题是为了最小sequence gap化要减少sequence batch的大小,可以通过下面的代码来解决。:
package models;
import io.ebean.config.ServerConfig;
import io.ebean.event.ServerConfigStartup;
public class MyServerConfigStartup implements ServerConfigStartup {
public void onStart(ServerConfig serverConfig) {
serverConfig.setDatabaseSequenceBatchSize(1);
}
}
Ebean也会使用conf/orm.xml文件(如果存在的话)来配置
Ebean文档地址
http://ebean-orm.github.io/docs/
默认情况下sbt插件会试着读取application.conf的配置来发现models的配置。在简单的工程中这会有效果,但是对于拥有多个子工程的工程来说,由于application.conf会在一个不同的工程(ebean模型所在的工程)中,这时就会出现问题。在这种情况下需要为每个含有model的子工程指定ebean model。使用playEbeanModels配置项:
playEbeanModels in Compile := Seq("models.*")
在配置时你可能想要启动debug,可以使用playEbeanDebugLevel来配置,-1是关闭,9是显示最多信息
playEbeanDebugLevel := 4
也可以通过playEbeanAgentArgs设置为ebean代理设置参数
playEbeanAgentArgs += ("detect" -> "false")
最后,如果逆向在测试中enhance models,可以对ebean测试进行配置
inConfig(Test)(PlayEbean.scopedSettings)
playEbeanModels in Test := Seq("models.*")
Ebean定义了一个非常方便的超类io.ebean.Model供你使用
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 {
@Id
@Constraints.Min(10)
public Long id;
@Constraints.Required
public String name;
public boolean done;
@Formats.DateTime(pattern="dd/MM/yyyy")
public Date dueDate = new Date();
public static final Finder find = new Finder<>(Task.class);
}
Play被设计为自动生成getter/setter来保证各中类库能够在运行时使用(ORM,DataBinder,JSON Binderd等)。如果Play检测到models中有任何手写的getter/setter方法,就不会产生getter/setter以避免冲突。
附加说明
(1)由于Ebean类型增强是在编译期之后,不要期待能够在编译期使用Ebean生成的getter/setter方法。如果你更喜欢在代码中直接使用,要么自己显示声明,要么保证你的model类在使用前已经编译了,eg,在另外一个子工程使用.
(2)Ebean对域访问的的增强(启动懒加载)仅对Java类有效。因此从Scala源文件(包括标准的Play模板)中直接访问域不会调用懒加载,这经常会导致域值为空(未发布)。为了保证获取正确的数据,有两种方法(a)手写getter/setter方法(b)在使用前保证实体已经完成发布。
如你所见我们添加了一个名为find的static域,为类型为Task的实体定了一个Finder,其中包含了一个Long的标志。这个域可以帮助我们进行简单的查询:
// Find all tasks
List tasks = Task.find.all();
// Find a task by ID
Task anyTask = Task.find.byId(34L);
// Delete a task by ID
Task.find.ref(34L).delete();
// More complex task query
List cocoTasks = Task.find.query().where()
.ilike("name", "%coco%")
.orderBy("dueDate asc")
.setFirstRow(0)
.setMaxRows(25)
.findPagedList()
.getList();
Ebean默认会启用事物,但是这些事务会在单一个sql语句前创建,然后提交或者回滚。
// Created implicit transaction
Task task = Task.find.byId(34L);
// Transaction committed or rolled back
task.done = true;
// Created implicit transaction
task.save();
// Transaction committed or rolled back
如果你想在事务中进行多项操作,你需要使用TxRunnable和TxCallable
import io.ebean.*;
...
Ebean.execute(() -> {
// code running in "REQUIRED" transactional scope
// ... as "REQUIRED" is the default TxType
System.out.println(Ebean.currentTransaction());
Task task = Task.find.byId(34L);
task.done = true;
task.save();
});
如果你的类是一个action,可以在action方法前添加注解@play.db.ebean.Transactional来将你的action方法域一个Action组合起来,这会自动管理一个事务
import play.db.ebean.Transactional;
...
@Transactional
public Result done(long id) {
Task task = Task.find.byId(34L);
task.done = true;
task.save();
return ok();
}
如果你想通过一个更传统的方法来开始、提交、回滚一个事务:
Ebean.beginTransaction();
try {
Task task = Task.find.byId(34L);
task.done = true;
task.save();
Ebean.commitTransaction();
} finally {
Ebean.endTransaction();
}