原生 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
我们可以指定 firstRow
和 maxRows
,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,将列更明确地映射到属性。