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();