本文参考孙卫琴的经典Hibernate书籍《精通JPA与Hibernate:Java对象持久化技术详解》,清华大学出版社出版。
1. 立即左外连接
以下程序覆盖映射代码中指定的检索策略,显式指定对与Customer关联的Order对象采用立即左外连接检索策略:
//JPQL检索方式
List<Customer> result=entityManager
.createQuery("from Customer c left join fetch c.orders o "
+"where c.name like 'T%' ",Customer.class)
.getResultList();
//QBC检索方式
CriteriaQuery<Customer> criteriaQuery =
criteriaBuilder.createQuery(Customer.class);
Root<Customer> root = criteriaQuery.from(Customer.class );
root.fetch("orders",JoinType.LEFT);
criteriaQuery.select(root);
Predicate predicate = criteriaBuilder.like(
root.get("name"),"T%");
criteriaQuery.where(predicate);
List<Customer> result=entityManager
.createQuery(criteriaQuery)
.getResultList();
以上代码生成的SQL查询语句为:
select c.ID C_ID ,c.NAME, c.AGE,
o.ID O_ID, o.ORDER_NUMBER, o.CUSTOMER_ID
from CUSTOMERS c left outer join ORDERS o on c.ID=o.CUSTOMER_ID
where (c.NAME like 'T%');
2. 左外连接
以下代码分别通过JPQL和QBC进行左外连接查询:
//JPQL检索方式
List<Object[]> result=entityManager
.createQuery("from Customer c left join c.orders "
+" where c.name like 'T%'",Object[].class)
.getResultList();
//QBC检索方式
//“Object[]”设定查询结果中的元素的类型
CriteriaQuery<Object[]> criteriaQuery =
criteriaBuilder.createQuery(Object[].class);
Root<Customer> root = criteriaQuery.from(Customer.class );
Join<Customer,Order> orderJoin=root.join("orders",JoinType.LEFT);
//由于查询结果中的每个元素都包含了Customer对象和Order对象,
//此时需要调用CriteriaQuery的multiselect()方法
criteriaQuery.multiselect(root,orderJoin);
Predicate predicate = criteriaBuilder.like(root.get("name"),"T%");
criteriaQuery.where(predicate);
List<Object[]> result=entityManager
.createQuery(criteriaQuery)
.getResultList();
在JPQL查询语句中,left join 关键字表示左外连接查询。在QBC中,root.join("orders",JoinType.LEFT)表示左外连接查询。
使用左外连接查询时,将根据Customer类中的注解映射代码来决定orders集合的检索策略。不过,即使对orders集合设置了延迟检索策略,在运行以上Query的getResultList()方法时,Hibernate执行的SQL查询语句仍然与立即左外连接生成的查询语句相同:
select c.ID C_ID ,c.NAME, c.AGE,
o.ID O_ID, o.ORDER_NUMBER, o.CUSTOMER_ID
from CUSTOMERS c
left outer join ORDERS o on c.ID=o.CUSTOMER_ID
where (c.NAME like 'T%' );
3. 立即内连接
以下程序覆盖Customer类中通过注解映射代码为orders集合属性指定的延迟检索策略,显式指定对与Customer的orders集合属性采用立即内连接检索:
//JPQL检索方式
List<Customer> result=entityManager
.createQuery("from Customer c inner join fetch c.orders o "
+"where c.name like 'T%' ",Customer.class )
.getResultList();
//QBC检索方式
CriteriaQuery<Customer> criteriaQuery =
criteriaBuilder.createQuery(Customer.class);
Root<Customer> root = criteriaQuery.from(Customer.class );
root.fetch("orders",JoinType.INNER);
criteriaQuery.select(root);
Predicate predicate = criteriaBuilder.like(
root.get("name"),"T%");
criteriaQuery.where(predicate);
List<Customer> result=entityManager
.createQuery(criteriaQuery)
.getResultList();
以上代码生成的SQL查询语句为:
select c.ID C_ID ,c.NAME, c.AGE,
o.ID O_ID, o.ORDER_NUMBER, o.CUSTOMER_ID
from CUSTOMERS c inner join ORDERS o on c.ID=o.CUSTOMER_ID
where (c.NAME like 'T%');
4. 内连接
以下代码演示通过JPQL和QBC进行内连接查询。对于JPQL,inner join关键字表示内连接:
//QBC检索方式
List<Object[]> result=entityManager
.createQuery("from Customer c inner join c.orders o "
+"where c.name like 'T%' ",Object[].class)
.getResultList();
//QBC检索方式
CriteriaQuery<Object[]> criteriaQuery =
criteriaBuilder.createQuery(Object[].class);
Root<Customer> root = criteriaQuery.from(Customer.class );
Join<Customer,Order> orderJoin=root.join("orders",JoinType.INNER);
//由于查询结果中的每个元素都包含了Customer对象和Order对象,
//此时需要调用CriteriaQuery的multiselect()方法
criteriaQuery.multiselect(root,orderJoin);
Predicate predicate = criteriaBuilder. like(root.get("name"),"T%");
criteriaQuery.where(predicate);
List<Object[]> result=entityManager
.createQuery(criteriaQuery)
.getResultList();
可以省略inner关键字,单独的join关键字也表示内连接:
from Customer c join c.orders o where c.name like 'T%'
运行Query的getResultList()方法时,Hibernate执行的SQL查询语句为:
select c.ID C_ID, c.NAME, c.AGE,
o.ID O_ID,o.ORDER_NUMBER, o.CUSTOMER_ID
from CUSTOMERS c inner join ORDERS o on c.ID=o.CUSTOMER_ID
where (c.NAME like 'T%' );
5. 立即右外连接
以下代码演示立即右外连接检索方式:
//JPQL检索方式
List<Customer> result=entityManager
.createQuery("from Customer c right join fetch c.orders o "
+"where c.name like 'T%' ",Customer.class)
.getResultList();
//QBC检索方式
CriteriaQuery<Customer> criteriaQuery =
criteriaBuilder.createQuery(Customer.class);
Root<Customer> root = criteriaQuery.from(Customer.class );
root.fetch("orders",JoinType.RIGHT);
criteriaQuery.select(root);
Predicate predicate = criteriaBuilder.like(
root.get("name"),"T%");
criteriaQuery.where(predicate);
List<Customer> result=entityManager
.createQuery(criteriaQuery)
.getResultList();
6. 右外连接
在JPQL查询语句中,right join关键字表示右外连接,例如:
List<Object[]> result=
entityManager.createQuery("from Customer c right join c.orders o "
+"where c.name like 'T%' ",Object[].class)
.getResultList();
for (Iterator<Object[]> pairs = result.iterator(); pairs.hasNext();){
Object[] pair=pairs.next();
Customer customer=(Customer)pair[0];
Order order=(Order)pair[1];
//如果orders集合使用延迟检索策略,
//以下代码会初始化Customer对象的orders集合
customer.getOrders().iterator();
}
假定在Customer类的映射代码对orders集合设置了延迟检索策略。运行Query的getResultList()方法时,Hibernate执行的SQL查询语句为:
select c.ID C_ID, c.NAME, c.AGE,
o.ID O_ID,o.ORDER_NUMBER, o.CUSTOMER_ID
from CUSTOMERS c right outer join ORDERS o on c.ID=o.CUSTOMER_ID
where (c.NAME like 'T%' );
7. 交叉连接
JPQL支持交叉连接查询,例如:
from Customer,Order
以上JPQL查询语句对应的SQL语句为:
select c.ID , c.NAME, c.AGE,o.ID,o.ORDER_NUMBER,o.CUSTOMER_ID
from CUSTOMERS c cross join ORDERS o;
这个查询语句执行交叉连接查询,将返回CUSTOMERS表与ORDERS表的交叉组合,如果CUSTOMERS表有五条记录,ORDERS表有七条记录,那么返回的查询结果共包含35条(5×7)记录。
显然,以上交叉连接查询是没有实用意义的。但是对于不存在关联关系的两个类,既不能使用内连接查询,也不能使用外连接查询,此时可以使用交叉连接查询。
以下是标准的JPQL内连接查询语句,这种查询语句要求Customer类必须有用于存放Order对象的orders集合属性,并且在Customer类的映射代码中设定了Customer类与Order类的关联关系:
from Customer c inner join c.orders
如果Customer类中没有orders集合属性,但Order类中有一个表示Customer的OID的customer_id属性,那么可以采用交叉连接查询:
//JPQL检索方式
List<Object[]> result=entityManager
.createQuery("from Customer c ,Order o where c.id =o.customer_id",
Object[].class)
.getResultList();
//QBC检索方式
//“Object[]”设定查询结果中的元素的类型
CriteriaQuery<Object[]> criteriaQuery =
criteriaBuilder.createQuery(Object[].class);
Root<Customer> root1 = criteriaQuery.from(Customer.class );
Root<Order> root2 = criteriaQuery.from(Order.class );
//由于查询结果中的每个元素都包含了Customer对象和Order对象,
//此时需要调用CriteriaQuery的multiselect()方法
criteriaQuery.multiselect(root1,root2);
Predicate predicate = criteriaBuilder.equal(
root1.get("id"),root2.get("customer_id"));
criteriaQuery.where(predicate);
List<Object[]> result=entityManager
.createQuery(criteriaQuery)
.getResultList();
JPQL交叉连接查询语句不包含join关键字,并且用where子句设定连接条件:
from Customer c ,Order o where c.id =o.customer_id
以上JPQL交叉连接查询语句对应的SQL语句为:
select c.ID , c.NAME, c.AGE,o.ID,o.ORDER_NUMBER,o.CUSTOMER_ID
from CUSTOMERS c cross join ORDERS o
where c.ID=o.CUSTOMER_ID;
8. 隐式连接
以下JPQL查询语句通过o.customer.name的形式访问与Order关联的Customer对象的name属性:
//JPQL检索方式
List<Order> result=entityManager
.createQuery("from Order o where o.customer.name like 'T%' ",
Order.class)
.getResultList();
//QBC检索方式
CriteriaQuery<Order> criteriaQuery =
criteriaBuilder.createQuery(Order.class);
Root<Order> root = criteriaQuery.from(Order.class );
criteriaQuery.select(root);
Predicate predicate = criteriaBuilder.like(
root.get("customer").get("name"),"T%");
criteriaQuery.where(predicate);
以上程序代码中的JPQL语句尽管没有使用join关键字,其实隐式指明使用交叉连接查询,它实际对应的SQL语句为:
select o.ID, o.ORDER_NUMBER, o.PRICE,o.CUSTOMER_ID
from ORDERS o cross join CUSTOMERS c
where o.CUSTOMER_ID=c.ID and (c.NAME like 'T%')