Where句

クエリBeanのプロパティは、`where`句に式を追加するためのタイプセーフな方法を提供します。クエリパス、式、バインド値の型のコンパイル時検証が可能です。

// using query bean
Customer rob =
  new QCustomer()
    .name.equalTo("Rob") // type safe expression with IDE auto-completion
    .findOne();

同じクエリは、クエリBeanを使用せずに以下のように記述できます。

// using standard query
Customer rob = DB.find(Customer.class)
    .where().eq("name", "Rob")
    .findOne();

上記の両方のクエリは、同じSQLを生成します。

select ...
from customer t0
where t0.name = ?

上記では、`name`プロパティはString/varchar型であるため、_like、startsWith、contains_などの有効な文字列式のみが許可され、式のバインド値は文字列である必要があります。

開発者は、クエリBeanを使用してクエリを作成する際に、IDEの支援/自動補完を受けることができます。利用可能なプロパティと式は、IDEによって型に基づいて提案されます。

さらに、クエリBeanを使用する場合、プロパティの名前が変更された場合(例:「name」が「fullName」になった場合)またはプロパティの型が変更された場合、クエリはコンパイルされなくなります。

パス

Bean(OneToOne、OneToMany、ManyToOne、ManyToMany)に関連付けられたプロパティを使用すると、これらのパスを「ナビゲート」して有効な式を追加できます。

例:Customerのパス "billingAddress.city"
// using query beans
List<Customer> customers =
  new QCustomer()
    .billingAddress.city.equalTo("Auckland")
    .findList();
// using standard query
List<Customer> customers = DB.find(Customer.class)
  .where()
     .eq("billingAddress.city", "Auckland")
  .findList();

Ebeanは、クエリ内の式をサポートするために適切なSQL JOINを自動的に追加します。

select ...
from customer t0
join address t1 on t1.id = t0.billing_address_id
where t1.city = ?

これらのプロパティパスは、任意の深さにすることができます。以下の例では、パスはOrder BeanからCustomer Bean、そしてAddress Beanへと_customer.billingAddress_を介して移動します。

例:Orderのパス "customer.billingAddress.city"
List<Order> orders =
  new QOrder()
    .customer.billingAddress.city.equalTo("Auckland")
    .findList();

AND

デフォルトでは、複数の式は`AND`を介して追加されます。

List<Customer> customers =
  new QCustomer()
    .status.equalTo(Status.NEW)
    .whenCreated.greaterThan(lastWeek)
    .findList();
select ...
from customer t0
where t0.status = ? and t0.when_created > ?
// using standard query
List<Customer> customers = DB.find(Customer.class)
  .where()
    .eq("status", Status.NEW)
    .gt("whenCreated", lastWeek)
    .findList();
例:Orderの複数のパス
List<Order> orders =
  new QOrder()
    .orderDate.greaterThan(lastWeek)
    .customer.status.equalTo(Status.NEW)
    .customer.billingAddress.city.equalTo("Auckland")
    .findList();

上記のクエリでは、_customer_と_customer.billingAddress_の追加パスがあります。Ebeanは、式をサポートするために必要な結合を決定します。

select ...
from orders t0
join customer t1 on t1.id = t0.order_id
join address t2 on t2.id = t1.billing_address_id
where t0.order_date >= ? and t1.status = ? and t2.city = ?

 

結合の種類に関する注意事項

使用される結合の種類(`left join`または`join`)は、リレーションシップのカーディナリティとオプション性に基づいています。たとえば、OrderのCustomer外部キーがNULL許容である場合、それはオプションのリレーションシップであり、_left join_が使用されます。

任意のパスについて、「上位レベル」の結合が_left join_の場合、そのパスに対するすべての「子結合」も_left join_である必要があります(つまり、「left joinカスケード」)。たとえば、上記の例では、OrderからCustomerへの結合がleft joinの場合、CustomerからAddressへの結合もleft joinである必要があります。

_ToMany_パス上の式に使用する結合の種類は、式が_OR_に含まれているかどうかによって異なります。式が_OR_内に含まれている場合、_left join_が使用されます。

OR

_OR_を介して複数の式を追加する場合、`or()`と`endOr()`を使用し、それらの間のすべての式は_OR_によって結合されます。

例:(name is null OR name = 'Rob')

Customer customer
  = new QCustomer()
     .or()
       .name.isNull()
       .name.equalTo("Rob")
     .endOr()
     .findOne()
select ...
from customer t0
where (t0.name is null or t0.name = ? )
Customer customer = DB.find(Customer.class)
   .where()
     .or()
       .isNull("name")
       .eq("name", "Rob")
     .endOr()
     .findOne()
生の式を使用したOR

_or()`と_endOr()`を使用した流暢なスタイルではなく、代わりに`raw()`式を使用できます。例えば

Customer customer
  = new QCustomer()
     .raw("(name is null or name = ?)", "Rob")
     .findOne()

生の式

where句に生の式を追加したい場合があります。 `raw()`を使用して、任意のSQL、関数、ストアドプロシージャをクエリ_where句_に含めます。

例:単純な生の式
List<Order> orders =
  new QOrder()
  .raw("orderDate > shipDate ")
  .findList()
例:SQL関数の使用
List<Order> orders =
  new QOrder()
  .raw("add_days(orderDate, 10) < ?", someDate)
  .findList();
例:SQLサブクエリ
List<Order> orders =
  new QOrder()
   .status.equalTo(Status.NEW)
   .raw("t0.customer_id in (select customer_id from customer_group where group_id = any(?::uuid[]))", groupIds)
   .findList()

 

SQL結合へのプロパティパス

`raw()`でプロパティパスを使用する場合、Ebeanは式をサポートするために適切な結合を自動的に追加します。例えば

List<Order> orders =
    new QOrder()
      .raw("(customer.name = ? or customer.billingAddress.city = ?)", "Rob", "Auckland")
      .findList();

...は、CustomerテーブルとAddressテーブルに結合が追加された、次のSQLを生成します。

select t0.id, t0.status, t0.order_date, t0.ship_date, ...
from orders t0
left join customer t1 on t1.id = t0.customer_id            -- supports customer.name
left join address t2 on t2.id = t1.billing_address_id      -- supports customer.billingAddress.city
where (t1.name = ? or t2.city = ?); --bind(Rob,Auckland)

 

式の組み合わせ

`raw()`式を他の式と組み合わせることができます。例えば

List<Order> orders =
  new QOrder()
    .status.eq(Order.Status.NEW)       // before raw()
    .raw("(customer.name = ? or customer.billingAddress.city = ?)", "Rob", "Auckland")
    .orderDate.greaterThan(lastWeek)   // after raw()
    .findList();
select ...
where t0.status = ?  and (t1.name = ? or t2.city = ?) and t0.order_date > ?
 --bind(NEW,Rob,Auckland,2019-01-17)

 

生の式への賛歌

開発者の生活がシンプルであれば、生の式は必要ありません。多くの実際のプロジェクトでは、優れたORMクエリで90%まで達成できるクエリがいくつかありますが、SQLで表現するのが最適な述語や、データベース固有の関数や機能を使用する述語が必要です。

_生の式_は、where句に任意のSQL式を挿入できる優れた小さな機能です。代わりにfindNativeなどを使用してクエリのすべてのSQLを提供することもできますが、それは大きなジャンプであり、生の式は柔軟性を提供します。

_お気に入りの機能_を選んで良いかわかりませんが、もしそうなら生の式に投票します。