findNative 优于 RawSql

请注意,findNative 优于 RawSql。findNative 使用列到属性的自动映射,使用起来更简单、更方便。

我们仅在遇到 findNative限制 时才真正需要使用 RawSql。

RawSql - 未解析

使用 unparsed RawSql,我们可以明确指定要使用的 SQL,并明确将列映射到属性。SQL 可以包含带 ? 的定位参数或带 :foo 的命名参数。

RawSql 实例是不可变的,可以安全地用于多线程,并且可以声明为static final

使用 unparsed RawSql,我们不能为 WHERE 和 HAVING 子句指定其他表达式,也不能设置 ORDER BY 和 LIMIT OFFSET 子句。我们只能使用 parsed RawSql 来执行这些操作。

示例 - 使用定位参数
static final RawSql rawSql =
  RawSqlBuilder
    .unparsed("select r.id, r.name from o_customer r where r.id >= ? and r.name like ?")
    .columnMapping("r.id", "id")
    .columnMapping("r.name", "name")
    .create();

...

List<Customer> list = DB.find(Customer.class)
  .setRawSql(rawSql)
  .setParameter(42)
  .setParameter("R%")
  .findList();
示例 - 使用命名参数
static final RawSql rawSql =
  RawSqlBuilder
    .unparsed("select r.id, r.name from o_customer r where r.id >= :a and r.name like :b")
    .columnMapping("r.id", "id")
    .columnMapping("r.name", "name")
    .create();

...

List<Customer> list = DB.find(Customer.class)
  .setRawSql(rawSql)
  .setParameter("a", 42)
  .setParameter("b", "R%")
  .findList();

RawSql - 已解析

使用 parsed RawSql,我们可以动态地为 WHERE 和 HAVING 子句添加表达式,并设置 ORDER BY 和 LIMIT OFFSET 子句。

RawSql 实例是不可变的,可以安全地用于多线程,并且可以声明为static final

// Use raw SQL with an aggregate function

String sql
= " select order_id, o.status, c.id, c.name, sum(d.order_qty*d.unit_price) as totalAmount"
+ " from orders o"
+ " join customer c on c.id = o.customer_id "
+ " join order_detail d on d.order_id = o.id "
+ " group by order_id, o.status ";

static final RawSql rawSql = RawSqlBuilder
  // let ebean parse the SQL so that it can add
  // expressions to the WHERE and HAVING clauses
  .parse(sql)
    // map resultSet columns to bean properties
    .columnMapping("order_id", "order.id")
    .columnMapping("o.status", "order.status")
    .columnMapping("c.id", "order.customer.id")
    .columnMapping("c.name", "order.customer.name")
    .create();

...

List<OrderAggregate> list = DB.find(OrderAggregate.class);
  query.setRawSql(rawSql)
  // add expressions to the WHERE and HAVING clauses
  .where().gt("order.id", 42)
  .having().gt("totalAmount", 20)
  .findList();

fetch

使用 RawSql 时,我们可以使用 fetchQuery 来获取对象图的其他部分。也就是说,我们对“根”或“原始”查询使用 RawSql,并且可以使用 fetchQuery 来获取图的其他相关部分。

// fetch additional parts of the object graph
// after the Raw SQL query is executed.

String sql
= " select order_id, sum(d.order_qty*d.unit_price) as totalAmount "
+ " from order_detail d"
+ " group by order_id ";

static final RawSql rawSql = RawSqlBuilder
  .parse(sql)
  .columnMapping("order_id", "order.id")
  .create();

...

Query<OrderAggregate> query = DB.find(OrderAggregate.class);
  query.setRawSql(rawSql)
    // get ebean to fetch parts of the order and customer
    // after the raw SQL query is executed
    .fetchQuery("order", "status,orderDate",new FetchConfig().query())
    .fetchQuery("order.customer", "name")
    .where().gt("order.id", 0)
    .having().gt("totalAmount", 20)
    .order().desc("totalAmount")
    .setMaxRows(10);

这是上面示例中使用的 OrderAggregate Bean。

package com.avaje.tests.model.basic;

import javax.persistence.Entity;
import javax.persistence.OneToOne;

import com.avaje.ebean.annotation.Sql;

/**
* An example of an Aggregate object.
*
* Note the @Sql indicates to Ebean that this bean is not based on a table but
* instead uses RawSql.
*
*/
@Entity
@Sql
public class OrderAggregate {

  @OneToOne
  Order order;

  Double totalAmount;

  Double totalItems;

  public String toString() {
    return order.getId() + " totalAmount:" + totalAmount + " totalItems:" + totalItems;
  }

  public Order getOrder() {
    return order;
  }

  public void setOrder(Order order) {
    this.order = order;
  }

  public Double getTotalAmount() {
    return totalAmount;
  }

  public void setTotalAmount(Double totalAmount) {
    this.totalAmount = totalAmount;
  }

  public Double getTotalItems() {
    return totalItems;
  }

  public void setTotalItems(Double totalItems) {
    this.totalItems = totalItems;
  }
}

tableAliasMapping()

tableAliasMapping() 会根据查询结果中的路径自动将列映射到关联的对象。它使用查询别名来执行此操作。这是一个便捷方法,因此您不必单独映射每列。

因此,测试用例如下所示

static final String rs =
  "select o.id, o.status, c.id, c.name, "+
  "       d.id, d.order_qty, p.id, p.name " +
  "from orders o join customer c on c.id = o.customer_id " +
  "join order_detail d on d.order_id = o.id " +
  "join product p on p.id = d.product_id " +
  "where o.id <= :maxOrderId and p.id = :productId "+
  "order by o.id, d.id asc";

static final RawSql rawSql = RawSqlBuilder.parse(rs)
    .tableAliasMapping("c", "customer")
    .tableAliasMapping("d", "details")
    .tableAliasMapping("p", "details.product")
    .create();

...

List<Order> ordersFromRaw = DB.find(Order.class)
    .setRawSql(rawSql)
    .setParameter("maxOrderId", 2)
    .setParameter("productId", 1)
    .findList();

而不是像下面这样使用 columnMapping

static final RawSql rawSql = RawSqlBuilder.parse(rs)
    .columnMapping("t0.id", "id")
    .columnMapping("t0.status", "status")
    .columnMapping("t1.id", "customer.id")
    .columnMapping("t1.name", "customer.name")
    .columnMapping("t2.id", "details.id")
    .columnMapping("t2.order_qty", "details.orderQty")
    .columnMapping("t3.id", "details.product.id")
    .columnMapping("t3.name", "details.product.name")
    .create();

命名查询

我们可以像以下示例一样以编程方式使用原始 SQL,或者将原始 SQL 和列映射放入 ebean-orm.xml 文件中,并将其作为“命名查询”引用 - 请参阅 database.createNamedQuery()。