DtoQuery

DtoQuery 是我们使用 SQL 并将其映射到普通 Bean 的地方。这些普通 Bean 只是具有公共构造函数和公共 getter/setter 的普通 Bean。

public static class CustomerDto {

  Integer id;
  String name;

  ... // getters & setters
}
// using positioned parameters

List<CustomerDto> beans =
  database.findDto(CustomerDto.class, "select id, name from customer where name = ?")
  .setParameter(1, "Rob")
  .findList();
// using named parameters

List<CustomerDto> beans =
  database.findDto(CustomerDto.class, "select id, name from customer where name = :name")
  .setParameter("name", "Rob")
  .findList();

映射

将 SQL resultSet 映射到 DTO Bean 时,将使用以下内容。

  • 首先,我们寻找具有与 resultSet 中列数相同的参数数的构造函数。如果我们有这样的构造函数,我们将使用它进行映射(假设类型正确)。
  • 其次,查看 resultSet 中的列是否比我们最大的构造函数多。如果是这样,我们使用最大的构造函数读取第一列,然后使用 setter 方法映射剩余的列。
  • 最后,我们使用默认构造函数和 setter 方法。

请注意,我们使用 MethodHandles 而不是反射来执行映射。这意味着它期望构造函数和 Setter 方法具有公共访问权限。

firstRow / maxRows

我们可以将 firstRows/maxRows 应用到 SQL 查询。

String sql = "select id, name from customer where name like ?";

List<CustomerDto> robs = server().findDto(CustomerDto.class, sql)
  .setParameter(1, "Rob%")
  .setMaxRows(10)
  .findList();

将添加适当的 SQL 以应用数据库平台的 firstRows/maxRows。

select id, name from o_customer where name like ?
limit 10

RelaxedMode

默认情况下,在执行映射时,如果我们无法将列映射到属性(setter 方法),则会引发异常。

相反,我们可以通过 query.setRelaxedMode() 在查询中设置 relaxed 模式,这意味着它将有效地忽略/跳过任何无法映射的列。

当可能存在大量现有查询并且只希望将某些列映射到 Bean 属性时,这很有用。

Postgres ANY

使用 Postgres 时,我们可以使用 DtoQuery 中的 Postgres ANY,方法是使用定位参数或使用 setArrayParameter()

使用 Postgres ANY 的好处是,无论参数数量如何,我们最终都会使用一个 SQL 查询 - 一个 JDBC PreparedStatement,并且数据库有一个要解析的 SQL 查询。这导致数据库执行较少的硬解析,更好地使用 PreparedStatement 缓存,减少要缓存的查询计划,减少内存消耗。

示例:定位参数

在使用索引定位参数和绑定将作为 JDBC ARRAY 绑定的集合时。

List<Integer> ids = List.of(1, 2);
String sql = "select id, name from customer where id = any(?)"

List<CustomerDto> list = DB.findDto(CustomerDto.class, sql)
  .setParameter(ids) // bind as JDBC ARRAY
  .findList();
示例:对命名参数使用 setArrayParameter()

在默认情况下使用命名参数时,Ebean 期望使用 IN 子句,并且集合会“展开”。例如,一个包含 3 个值的集合会展开成 3 个单独的 JDBC 绑定值 ?,?,?,而不是 JDBC 绑定一个 ARRAY。要将集合作为 Postgres ARRAY 绑定到 ANY 子句,我们需要使用 setArrayParameter()

List<Integer> ids = List.of(1, 2);
String sql = "select id, name from o_customer where id = any(:idList)"

List<CustomerDto> list2 = DB.findDto(CustomerDto.class, sql)
  .setArrayParameter("idList", ids) // bind as JDBC ARRAY
  .findList();

findEach

在处理一个我们不希望将所有 bean 保存在列表中而是希望一次处理一个 bean 的大型查询时,我们使用 findEach

String sql = "select id, name from o_customer where id > :id order by id desc";

database.findDto(CustomerDto.class, sql)
  .setParameter("id", 0)
  .findEach(customer -> {

    log.debug("got " + customer.getId() + " " + customer.getName());
    ...

  });

findStream

findStream 类似于 findEach,我们一次处理一个结果。我们应该使用 try with resources 块来确保关闭底层资源。

String sql = "select id, name from o_customer where id > ? order by id desc";

try (Stream<CustomerDto> customers =
  database.findDto(CustomerDto.class, sql)
    .setParameter(0)
    .findStream()) {

    // use the stream of customers ...
    ...
}

findIterate

findIterate 类似于 findStream 和 findEach,我们一次处理一个结果。我们应该使用 try with resources 块来确保关闭底层资源。

String sql = "select id, name from o_customer where id > :id order by id desc";

try (QueryIterator<CustomerDto> customers =
  database.findDto(CustomerDto.class, sql)
    .setParameter(0)
    .findIterate()) {

    // iterate through the customers
    ...
}

单列

如果我们希望选择一列,我们希望为此使用 SqlQuery.mapToScalar()

String sql = "select mysequence.nextval";

Long nextVal = DB.sqlQuery(sql)
  .mapToScalar(Long.class)
  .findOne();