ネイティブ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を使用すると、列は自動的にビーンのプロパティにマッピングされます。

上記の例では、いくつかの列を選択しており、その結果、**部分的にデータが設定されたエンティティビーン**を取得しています(これは良いことです。必要なものだけをDBからフェッチする必要があります)。

すべての列を取得したい場合は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();

列のマッピング

列のマッピングの仕組みは、クエリが最初に実行されるときに、resultSet内の列のJDBCメタデータが読み取られ、これらの列が命名規則を使用してビーンのプロパティに自動的にマッピングされることです。

これは、クエリの初回実行に対してのみ行う必要があります。クエリがビーンにどのようにマッピングされるかはキャッシュされるため、クエリごとにJDBC resultSetメタデータを1回だけ読み取る必要があります。

命名規則に準拠していない列には、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();

マッピングされていない列

order by句で、ビーンのプロパティにマッピングできないresultSetの列を指定したい場合があります。この場合、これらのプロパティは無視されます。

例 - 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を指定すると、limit/offsetまたは同等の句を使用してSQLが適切に変更されます。

例 - 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は複数のテーブルから選択でき、これらは関連するエンティティビーンに自動的にマッピングできます。

たとえば、ContactとCustomerという2つの関連ビーンをフェッチして設定できます。返される連絡先エンティティビーンには、顧客IDと名前が設定された顧客ビーンが含まれます。

例 - 連絡先とその関連する顧客
// 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があるとします。ebeanが住所テーブルの列をマッピングしようとすると、列がどのパス(請求先住所または配送先住所)にマッピングされているかがわかりません。

この制限に遭遇した場合は、列をビーンのパス/プロパティに明示的にマッピングできるRawSqlを使用する必要があります。

Oracleの制限事項

Oracle JDBCドライバには、メタデータに現在、列が関連するテーブルが含まれていないという制限があります。つまり、Oracleでは、単一のテーブルのみをマッピングでき、複数のテーブルをマッピングできません(代わりにRawSqlを使用する必要があります)。

RawSql

列が自動的にプロパティにマッピングされるため、エンティティビーンSQLクエリを実行する方法として、RawSqlよりもfindNativeを使用することをお勧めします。

ただし、select句に複数のテーブルからの列を含めたい場合、findNativeにはいくつかの制限があります(上記の制限を参照)。これらの制限に遭遇した場合は、列をプロパティに明示的にマッピングするRawSqlを使用する必要があります。