原生 SQL - findNative

使用 findNative 时,我们提供 SQL。SQL 可以包含带有 ? 的定位参数或带有 :foo 的命名参数

示例 - 定位参数
String sql = "select id, name from customer where name like ?";

Customer customer = DB.findNative(Customer.class, sql)
    .setParameter("Jo%")
    .findOne();
示例 - 命名参数
String sql = "select id, name from customer where name like :name order by name desc";

List<Customer> customers = DB.findNative(Customer.class, sql)
    .setParameter("name", "Jo%")
    .findList();

使用 findNative 时,列会自动映射到 Bean 属性。

在上述示例中,我们选择 部分 列,因此得到 部分填充的实体 Bean(这很好 - 我们应该只从数据库中提取我们需要的内容)。

如果需要所有列,我们可以使用 select *,但通常不建议这样做。

示例 - select *
String sql = "select * from customer where name like :name order by name desc";

List<Customer> customers = DB.findNative(Customer.class, sql)
    .setParameter("name", "Jo%")
    .findList();

列映射

列映射的工作方式是,当第一次运行查询时,会读取 JDBC 元数据以获取 resultSet 中的列,然后使用命名约定自动将这些列映射到 Bean 属性。

这只需要在首次执行查询时进行。查询如何映射到 Bean 会被缓存,因此我们只需要为每个查询读取一次 JDBC resultSet 元数据。

对于不符合命名约定的列,我们使用 SQL 列别名。

示例 - 列别名
// using sql column alias
String sql = "select id, fname as first_name, lname as last_name " +
             "from contact where lname like ?";

List<Contact> contacts = DB.findNative(Contact.class, sql)
  .setParameter(1, "B%")
  .findList();

未映射的列

有时我们希望在 resultSet 中指定无法映射到 Bean 属性的 order by 子句的列。在这种情况下,这些属性会被忽略。

示例 - 未映射的列用于 order by
String sql = "select id, name, " +
  "case when anniversary >= ? then 1 when anniversary < ? then 2 end as order_column_1 " +
  "from o_customer " +
  "order by order_column_1";

List<Customer> result = DB.findNative(Customer.class, sql)
  .setParameter(LocalDate.now())
  .setParameter(LocalDate.now())
  .findList();

firstRow / maxRows

我们可以指定 firstRowmaxRows,SQL 将使用 limit/offset 或等效子句进行适当修改。

示例 - 使用 maxRows
String sql = "select id, name from o_customer order by name, id";

List<Customer> result = DB.findNative(Customer.class, sql)
  .setMaxRows(50)
  .findList();

fetchQuery

使用 findNative 时,我们可以认为它实际上提供了“根”或“源”查询的 SQL,并且可以使用 fetchQuery 额外获取图的其他部分。

在下面的示例中,我们使用带有提供的 SQL 的 findNative 来获取客户,并额外使用 fetchQuery 来获取这些客户的相关订单和联系人。

示例 - 使用 fetchQuery
List<Customer> result = DB.findNative(Customer.class, sql)
  .fetchQuery("orders")
  .fetchQuery("contacts")
  .findList();

多张表

除了 Oracle(请参见下面的 Oracle 限制)之外,我们的 SQL 可以从多张表中选择,并且这些表可以自动映射到相关的实体 Bean。

例如,我们可以获取并填充 2 个相关的 Bean,如 Contact 和 Customer。返回的联系方式实体 Bean 将包括已填充客户 ID 和名称的客户 Bean。

示例 - 联系人及其关联的客户
// Contacts + Customer
String sql
  = "select con.id, con.first_name, con.last_name, cust.id, cust.name " +
   " from contact con " +
   " join customer cust on cust.id = con.customer_id " +
   " order by con.first_name desc";

List<Contact> contacts = DB.findNative(Contact.class, sql)
    .findList();
示例 - 订单行及其关联的产品
String sql = "select line.*, p.* " +
  " from order_line line " +
  " join product p on p.id = line.product_id " +
  " where line.order_id = ?";

List<OrderLine> lines = DB.findNative(OrderLine.class, sql)
  .setParameter(order.getId())
  .findList();

限制

使用 findNative 的限制(除了 Oracle)在于它只能在给定表的路径为唯一时映射多张表。

例如,假设我们有一个 Customer,它将 billingAddress 和 shippingAddress 都映射到同一个地址表。当 ebean 尝试映射地址表的列时,它不知道该列映射到哪个路径(billingAddress 或 shippingAddress)。

当我们遇到此限制时,我们需要使用 RawSql,我们可以在其中将列更明确地映射到 Bean 路径/属性。

Oracle 限制

Oracle JDBC 驱动程序存在一个限制,即元数据当前不包括列所关联的表。这意味着对于 Oracle,我们只能映射一张表,而不能映射多张表(并且必须使用 RawSql)。

RawSql

使用 findNative 优于RawSql,作为使用实体 Bean执行SQL查询的方式,因为它很好且简单,因为列会自动映射到我们的属性。

但是,当我们希望选择子句包含多张表中的列时,findNative 有一些限制(如上所述)。当我们遇到这些限制时,我们需要使用 RawSql,将列更明确地映射到属性。