欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Play 2.6 使用Ebean

程序员文章站 2022-05-17 20:05:06
...

Ebean的使用

https://playframework.com/documentation/2.6.x/JavaEbean

配置Ebean

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.*"]

这里定义了一个defaultEbean服务,使用default数据源。通过配置ebeanconfig.datasource.default可以重写default服务的名字。在使用独立的数据库进行开发和测试时会很有用。你可以根据需要尽可能多的创建Ebean服务,然后对类和服务进行映射

ebean.orders = ["models.Order", "models.OrderItem"]
ebean.customers =  ["models.Customer", "models.Address"]

每一行都可以映射任意多个Ebean会被使用到的类(eg. @Entity/Model classes, @Embeddables, custom ScalarTypes and CompoundTypes, BeanPersistControllers, BeanPersistListeners, BeanFinders, ServerConfigStartups, 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插件

默认情况下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.*")

使用Model超类

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<Long, Task> 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<Task> 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<Task> 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();
}