Hibernate - HQL对象检索详解

简介: Hibernate - HQL对象检索详解

Hibernate 提供了以下几种检索对象的方式:

  • 导航对象图检索方式: 根据已经加载的对象导航到其他对象
  • OID 检索方式: 按照对象的 OID 来检索对象
  • HQL 检索方式: 使用面向对象的 HQL 查询语言
  • QBC 检索方式: 使用 QBC(Query By Criteria) API 来检索对象。这种 API 封装了基于字符串形式的查询语句, 提供了更加面向对象的查询接口。
  • 本地 SQL 检索方式: 使用本地数据库的 SQL 查询语句

【1】HQL检索

HQL(Hibernate Query Language) 是面向对象的查询语言, 它和 SQL 查询语言有些相似。在 Hibernate 提供的各种检索方式中, HQL 是使用最广的一种检索方式。


① 它有如下功能:

  • 在查询语句中设定各种查询条件
  • 支持投影查询, 即仅检索出对象的部分属性
  • 支持分页查询
  • 支持连接查询
  • 支持分组查询, 允许使用 HAVING 和 GROUP BY 关键字
  • 提供内置聚集函数, 如 sum(), min() 和 max()
  • 支持子查询
  • 支持动态绑定参数
  • 能够调用 用户定义的 SQL 函数或标准的 SQL 函数


② HQL 检索方式包括以下步骤:

  • 通过 Session 的 createQuery() 方法创建一个 Query 对象, 它包括一个 HQL 查询语句. HQL 查询语句中可以包含命名参数
  • 动态绑定参数
  • 调用 Query 相关方法执行查询语句。

Qurey 接口支持方法链编程风格, 它的 setXxx() 方法返回自身实例, 而不是 void 类型。


③ HQL vs SQL:

HQL 查询语句是面向对象的, Hibernate 负责解析 HQL 查询语句, 然后根据对象-关系映射文件中的映射信息, 把 HQL 查询语句翻译成相应的 SQL 语句。 HQL 查询语句中的主体是域模型中的类及类的属性。

SQL 查询语句是与关系数据库绑定在一起的。SQL 查询语句中的主体是数据库表及表的字段。


④ 绑定参数

Hibernate 的参数绑定机制依赖于 JDBC API 中的 PreparedStatement 的预定义 SQL 语句功能。

HQL 的参数绑定由两种形式:

  • 按参数名字绑定: 在 HQL 查询语句中定义命名参数, 命名参数以“:”开头
  • 按参数位置绑定: 在 HQL 查询语句中用 “?”来定义参数位置

相关方法:

  • setEntity(): 把参数与一个持久化类绑定
  • setParameter(): 绑定任意类型的参数。 该方法的第三个参数显式指定 Hibernate 映射类型

HQL 采用 ORDER BY 关键字对查询结果排序。

这里以Department:Employee=1:N双向一对多为例。

Department类如下:

public class Department {
  private Integer id;
  private String name;
  private Set<Employee> emps = new HashSet<>();
  public Integer getId() {
    return id;
  }
  public void setId(Integer id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public Set<Employee> getEmps() {
    return emps;
  }
  public void setEmps(Set<Employee> emps) {
    this.emps = emps;
  }
  @Override
  public String toString() {
    return "Department [id=" + id + "]";
  }
}

Department.hbm.xml如下:

<hibernate-mapping package="com.jane.model">
    <class name="Department" table="GG_DEPARTMENT">
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        <set name="emps" table="GG_EMPLOYEE" inverse="true" lazy="true">
            <key>
                <column name="DEPT_ID" />
            </key>
            <one-to-many class="Employee" />
        </set>
    </class>
</hibernate-mapping>

Employee类如下:

public class Employee {
  private Integer id;
  private String name;
  private float salary;
  private String email;
  private Department dept;
  public Integer getId() {
    return id;
  }
  public void setId(Integer id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public float getSalary() {
    return salary;
  }
  public void setSalary(float salary) {
    this.salary = salary;
  }
  public String getEmail() {
    return email;
  }
  public void setEmail(String email) {
    this.email = email;
  }
  public Department getDept() {
    return dept;
  }
  public void setDept(Department dept) {
    this.dept = dept;
  }
  @Override
  public String toString() {
    return "Employee [id=" + id + "]";
  }
  public Employee(String email, float salary, Department dept) {
    super();
    this.salary = salary;
    this.email = email;
    this.dept = dept;
  }
  public Employee() {
    // TODO Auto-generated constructor stub
  }
}

Employee.hbm.xml如下:

<hibernate-mapping package="com.jane.model">
    <class name="Employee" table="GG_EMPLOYEE">
      <!--  
      <cache usage="read-write"/>
      -->
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        <property name="salary" type="float">
            <column name="SALARY" />
        </property>
        <property name="email" type="java.lang.String">
            <column name="EMAIL" />
        </property>
        <many-to-one name="dept" class="Department">
            <column name="DEPT_ID" />
        </many-to-one>
    </class>
</hibernate-mapping>

测试HQL查询代码如下:

  @Test
  public void testHQL(){
    //1. 创建 Query 对象
    //基于位置的参数. 
    String hql = "FROM Employee e WHERE e.salary > ? AND e.email LIKE ? AND e.dept = ? "
        + "ORDER BY e.salary";
    Query query = session.createQuery(hql);
    //2. 绑定参数--位置索引绑定方式
    //Query 对象调用 setXxx 方法支持方法链的编程风格.
    Department dept = new Department();
    dept.setId(1); 
    query.setFloat(0, 3000)
         .setParameter(1, "%A%",StringType.INSTANCE)
         .setEntity(2, dept);
    //3. 执行查询
    List<Employee> emps = query.list();
    System.out.println(emps.size());  
  }

测试结果如下:

Hibernate: 
    select
        employee0_.ID as ID1_1_,
        employee0_.NAME as NAME2_1_,
        employee0_.SALARY as SALARY3_1_,
        employee0_.EMAIL as EMAIL4_1_,
        employee0_.DEPT_ID as DEPT_ID5_1_ 
    from
        GG_EMPLOYEE employee0_ 
    where
        employee0_.SALARY>? 
        and (
            employee0_.EMAIL like ?
        ) 
        and employee0_.DEPT_ID=? 
    order by
        employee0_.SALARY
1

使用命名参数进行参数绑定:

  @Test
  public void testHQLNamedParameter(){
    //1. 创建 Query 对象
    //基于命名参数. 
    String hql = "FROM Employee e WHERE e.salary > :sal AND e.email LIKE :email";
    Query query = session.createQuery(hql);
    //2. 绑定参数
    query.setFloat("sal", 7000)
         .setString("email", "%A%");
    //3. 执行查询
    List<Employee> emps = query.list();
    System.out.println(emps.size());  
  }

⑤ 分页查询

setFirstResult(int firstResult): 设定从哪一个对象开始检索, 参数 firstResult 表示这个对象在查询结果中的索引位置, 索引位置的起始值为 0。默认情况下, Query 从查询结果中的第一个对象开始检索。

setMaxResults(int maxResults): 设定一次最多检索出的对象的数目。在默认情况下, Query 和 Criteria 接口检索出查询结果中所有的对象。

测试代码如下:

  @Test
  public void testPageQuery(){
    String hql = "FROM Employee";
    Query query = session.createQuery(hql);
    int pageNo = 1;
    int pageSize = 5;
    List<Employee> emps = query.setFirstResult((pageNo - 1) * pageSize)
                     .setMaxResults(pageSize)
                     .list();
    System.out.println(emps);

测试结果如下:

Hibernate: 
    select
        employee0_.ID as ID1_1_,
        employee0_.NAME as NAME2_1_,
        employee0_.SALARY as SALARY3_1_,
        employee0_.EMAIL as EMAIL4_1_,
        employee0_.DEPT_ID as DEPT_ID5_1_ 
    from
        GG_EMPLOYEE employee0_ limit ?
[Employee [id=1], Employee [id=2], Employee [id=3], Employee [id=4], Employee [id=5]]

⑥ 在映射文件中定义命名查询语句

Hibernate 允许在映射文件中定义字符串形式的查询语句。

<query> 元素用于定义一个 HQL 查询语句, 它和 <class>元素并列。

在Employee.hbm.xml中添加query节点如下:

 <query name="salaryEmps">
  <![CDATA[FROM Employee e WHERE e.salary > :minSal AND e.salary < :maxSal]]>
 </query>

在程序中通过 Session 的 getNamedQuery() 方法获取查询语句对应的 Query 对象:

  @Test
  public void testNamedQuery(){
    Query query = session.getNamedQuery("salaryEmps");
    List<Employee> emps = query.setFloat("minSal", 5000)
                           .setFloat("maxSal", 10000)
                           .list();
    System.out.println(emps.size()); 
  }

测试结果如下:

Hibernate: 
    select
        employee0_.ID as ID1_1_,
        employee0_.NAME as NAME2_1_,
        employee0_.SALARY as SALARY3_1_,
        employee0_.EMAIL as EMAIL4_1_,
        employee0_.DEPT_ID as DEPT_ID5_1_ 
    from
        GG_EMPLOYEE employee0_ 
    where
        employee0_.SALARY>? 
        and employee0_.SALARY<?
7

⑦ 投影查询

投影查询,查询结果仅包含实体的部分属性, 通过 SELECT 关键字实现。

Query 的 list() 方法返回的集合中包含的是数组类型的元素, 每个对象数组代表查询结果的一条记录。

测试代码一如下:

  @Test
  public void testFieldQuery(){
    String hql = "SELECT e.email, e.salary, e.dept FROM Employee e WHERE e.dept = :dept";
    Query query = session.createQuery(hql);
    Department dept = new Department();
    dept.setId(1);
    List<Object[]> result = query.setEntity("dept", dept).list();
    for(Object [] objs: result){
      System.out.println(Arrays.asList(objs));
    }
  }

测试一结果如下:

Hibernate: 
    select
        employee0_.EMAIL as col_0_0_,
        employee0_.SALARY as col_1_0_,
        employee0_.DEPT_ID as col_2_0_,
        department1_.ID as ID1_0_,
        department1_.NAME as NAME2_0_ 
    from
        GG_EMPLOYEE employee0_ 
    inner join
        GG_DEPARTMENT department1_ 
            on employee0_.DEPT_ID=department1_.ID 
    where
        employee0_.DEPT_ID=?
[AA@QQ.COM, 5000.0, Department [id=1]]
[BB@QQ.COM, 6000.0, Department [id=1]]

可以在持久化类中定义一个对象的构造器来包装投影查询返回的记录, 使程序代码能完全运用面向对象的语义来访问查询结果集。

测试代码二如下:

  @Test
  public void testFieldQuery2(){
    //实体类Employee需要有该构造器
    String hql = "SELECT new Employee(e.email, e.salary, e.dept) "
        + "FROM Employee e "
        + "WHERE e.dept = :dept";
    Query query = session.createQuery(hql);
    Department dept = new Department();
    dept.setId(1);
    List<Employee> result = query.setEntity("dept", dept).list();
    for(Employee emp: result){
      System.out.println(emp.getId() + ", " + emp.getEmail() 
          + ", " + emp.getSalary() + ", " + emp.getDept());
    }
  }

测试二结果如下:

Hibernate: 
    select
        employee0_.EMAIL as col_0_0_,
        employee0_.SALARY as col_1_0_,
        employee0_.DEPT_ID as col_2_0_ 
    from
        GG_EMPLOYEE employee0_ 
    inner join //内连接
        GG_DEPARTMENT department1_ 
            on employee0_.DEPT_ID=department1_.ID 
    where
        employee0_.DEPT_ID=?
Hibernate: 
    select
        department0_.ID as ID1_0_0_,
        department0_.NAME as NAME2_0_0_ 
    from
        GG_DEPARTMENT department0_ 
    where
        department0_.ID=?
null, AA@QQ.COM, 5000.0, Department [id=1]
null, BB@QQ.COM, 6000.0, Department [id=1]

另外,可以通过 DISTINCT 关键字来保证查询结果不会返回重复元素。


⑧ 报表查询

报表查询用于对数据分组和统计, 与 SQL 一样, HQL 利用 GROUP BY 关键字对数据分组, 用 HAVING 关键字对分组数据设定约束条件,过滤分组数据。

在 HQL 查询语句中可以调用以下聚集函数:

count()
min()
max()
sum()
avg()

测试代码如下:

@Test
  public void testGroupBy(){
    String hql = "SELECT min(e.salary), max(e.salary) "
        + "FROM Employee e "
        + "GROUP BY e.dept "
        + "HAVING min(salary) > :minSal";
    Query query = session.createQuery(hql)
                     .setFloat("minSal", 5000);
    List<Object []> result = query.list();
    for(Object [] objs: result){
      System.out.println(Arrays.asList(objs));
    }
  }

测试结果如下:

Hibernate: 
    select
        min(employee0_.SALARY) as col_0_0_,
        max(employee0_.SALARY) as col_1_0_ 
    from
        GG_EMPLOYEE employee0_ 
    group by
        employee0_.DEPT_ID 
    having
        min(employee0_.SALARY)>?
[7000.0, 8000.0]
[9000.0, 10000.0]
[7000.0, 8000.0]

⑨ 左外连接和迫切左外连接

左外连接:

  • LEFT JOIN 关键字表示左外连接查询
  • list() 方法返回的集合中存放的是对象数组类型(list<Object[]>)
  • 根据配置文件来决定 Employee 集合的检索策略
  • 如果希望 list() 方法返回的集合中仅包含 Department 对象, 可以在HQL 查询语句中使用 SELECT 关键字

使用select关键字测试代码如下:

  @Test
  public void testLeftJoinFetch(){
    String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN  d.emps";
    Query query = session.createQuery(hql);
    List<Department> depts = query.list();
    depts = new ArrayList<>(new LinkedHashSet(depts));
    System.out.println(depts.size()); 
    for(Department dept: depts){
      System.out.println(dept.getName() + "-" + dept.getEmps().size());
    }
  }

测试结果如下:

Hibernate: 
    select
        distinct department0_.ID as ID1_0_,
        department0_.NAME as NAME2_0_ 
    from
        GG_DEPARTMENT department0_ 
    left outer join
        GG_EMPLOYEE emps1_ 
            on department0_.ID=emps1_.DEPT_ID
14
Hibernate: 
    select
        emps0_.DEPT_ID as DEPT_ID5_1_0_,
        emps0_.ID as ID1_1_0_,
        emps0_.ID as ID1_1_1_,
        emps0_.NAME as NAME2_1_1_,
        emps0_.SALARY as SALARY3_1_1_,
        emps0_.EMAIL as EMAIL4_1_1_,
        emps0_.DEPT_ID as DEPT_ID5_1_1_ 
    from
        GG_EMPLOYEE emps0_ 
    where
        emps0_.DEPT_ID=?
AA-2 //使用的时候再发送查询语句,SQL语句数目比较多
Hibernate: 
    select
        emps0_.DEPT_ID as DEPT_ID5_1_0_,
        emps0_.ID as ID1_1_0_,
        emps0_.ID as ID1_1_1_,
        emps0_.NAME as NAME2_1_1_,
        emps0_.SALARY as SALARY3_1_1_,
        emps0_.EMAIL as EMAIL4_1_1_,
        emps0_.DEPT_ID as DEPT_ID5_1_1_ 
    from
        GG_EMPLOYEE emps0_ 
    where
        emps0_.DEPT_ID=?
BB-2
//...

不适用select关键字测试如下:

  @Test
  public void testLeftJoin(){
    String hql = " FROM Department d LEFT JOIN  d.emps";
    Query query = session.createQuery(hql);
    List<Object []> result = query.list(); 
    result = new ArrayList<>(new LinkedHashSet<>(result));
    System.out.println(result); 
    for(Object [] objs: result){
      System.out.println(Arrays.asList(objs)+objs[1].toString());
    }
  }

测试结果如下:

Hibernate: 
    select
        department0_.ID as ID1_0_0_,
        emps1_.ID as ID1_1_1_,
        department0_.NAME as NAME2_0_0_,
        emps1_.NAME as NAME2_1_1_,
        emps1_.SALARY as SALARY3_1_1_,
        emps1_.EMAIL as EMAIL4_1_1_,
        emps1_.DEPT_ID as DEPT_ID5_1_1_ 
    from
        GG_DEPARTMENT department0_ 
    left outer join
        GG_EMPLOYEE emps1_ 
            on department0_.ID=emps1_.DEPT_ID
[[Ljava.lang.Object;@1763992e, [Ljava.lang.Object;@5c92166b, [Ljava.lang.Object;@659925f4, [Ljava.lang.Object;@4cd1c1dc, [Ljava.lang.Object;@47f08b81, [Ljava.lang.Object;@b9dfc5a, [Ljava.lang.Object;@2787de58, [Ljava.lang.Object;@659a2455, [Ljava.lang.Object;@267517e4, [Ljava.lang.Object;@426e505c, [Ljava.lang.Object;@5b022357, [Ljava.lang.Object;@6f8e0cee, [Ljava.lang.Object;@614aeccc, [Ljava.lang.Object;@5116ac09, [Ljava.lang.Object;@1bc425e7, [Ljava.lang.Object;@4b2a30d, [Ljava.lang.Object;@322803db, [Ljava.lang.Object;@56ba8773, [Ljava.lang.Object;@6ceb7b5e]
[Department [id=1], Employee [id=1]]Employee [id=1]
[Department [id=1], Employee [id=2]]Employee [id=2]
[Department [id=2], Employee [id=3]]Employee [id=3]
[Department [id=2], Employee [id=4]]Employee [id=4]
[Department [id=3], Employee [id=5]]Employee [id=5]
[Department [id=3], Employee [id=6]]Employee [id=6]
[Department [id=4], Employee [id=7]]Employee [id=7]
[Department [id=4], Employee [id=8]]Employee [id=8]
[Department [id=5], Employee [id=9]]Employee [id=9]
[Department [id=5], Employee [id=10]]Employee [id=10]

会将Department和Employee一并查询出来,但是返回的是list<Object[]>类型。


迫切左外连接:

  • LEFT JOIN FETCH 关键字表示迫切左外连接检索策略
  • list() 方法返回的集合中存放实体对象的引用(list<Departmet>), 每个 Department 对象关联的 Employee 集合都被初始化, 存放所有关联的 Employee 的实体对象.
  • 查询结果中可能会包含重复元素, 可以通过一个 HashSet 来过滤重复元素

使用select关键字测试代码如下:

  @Test
  public void testLeftJoinFetch(){
    String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN FETCH d.emps";
    Query query = session.createQuery(hql);
    List<Department> depts = query.list();
    depts = new ArrayList<>(new LinkedHashSet(depts));
    System.out.println(depts.size()); 
    for(Department dept: depts){
      System.out.println(dept.getName() + "-" + dept.getEmps().size());
    }
  }

测试结果如下:

Hibernate: 
    select
        distinct department0_.ID as ID1_0_0_,
        emps1_.ID as ID1_1_1_,
        department0_.NAME as NAME2_0_0_,
        emps1_.NAME as NAME2_1_1_,
        emps1_.SALARY as SALARY3_1_1_,
        emps1_.EMAIL as EMAIL4_1_1_,
        emps1_.DEPT_ID as DEPT_ID5_1_1_,
        emps1_.DEPT_ID as DEPT_ID5_1_0__,
        emps1_.ID as ID1_1_0__ 
    from
        GG_DEPARTMENT department0_ 
    left outer join //使用左外连接一并查出相关联的实体对象
        GG_EMPLOYEE emps1_ 
            on department0_.ID=emps1_.DEPT_ID
14
AA-2
BB-2
CC-2
DD-2
EE-2
FF-0
GG-0
HH-0
II-0
JJ-0
KK-0
LL-0
MM-0
NN-0

不使用select关键字测试如下:

  @Test
  public void testLeftJoin(){
    String hql = " FROM Department d LEFT JOIN fetch d.emps";
    Query query = session.createQuery(hql);
    List<Department> depts = query.list();
    System.out.println(depts.size());
    for(Department dept: depts){
      System.out.println(dept.getName() + ", " + dept.getEmps().size()); 
    }

测试结果如下:

Hibernate: 
    select
        department0_.ID as ID1_0_0_,
        emps1_.ID as ID1_1_1_,
        department0_.NAME as NAME2_0_0_,
        emps1_.NAME as NAME2_1_1_,
        emps1_.SALARY as SALARY3_1_1_,
        emps1_.EMAIL as EMAIL4_1_1_,
        emps1_.DEPT_ID as DEPT_ID5_1_1_,
        emps1_.DEPT_ID as DEPT_ID5_1_0__,
        emps1_.ID as ID1_1_0__ 
    from
        GG_DEPARTMENT department0_ 
    left outer join
        GG_EMPLOYEE emps1_ 
            on department0_.ID=emps1_.DEPT_ID
19
AA, 2
AA, 2
BB, 2
BB, 2
CC, 2
CC, 2
DD, 2
DD, 2
EE, 2
EE, 2
FF, 0
GG, 0
HH, 0
II, 0
JJ, 0
KK, 0
LL, 0
MM, 0
NN, 0

会将Department和Employee一并查询出来,但是返回的是list<Department>类型。


⑩ HQL (迫切)内连接

内连接:

  • INNER JOIN 关键字表示内连接, 也可以省略 INNER 关键字
  • list() 方法的集合中存放的每个元素对应查询结果的一条记录, 每个元素都是对象数组类型
  • 如果希望 list() 方法的返回的集合仅包含 Department 对象, 可以在 HQL 查询语句中使用 SELECT 关键字

测试代码如下:

  @Test
  public void testInnerJoin(){
    String hql = "FROM Department d INNER JOIN  d.emps";
//    String hql = "FROM Department d INNER JOIN FETCH d.emps";
    Query query = session.createQuery(hql);
    List<Object []> result = query.list(); 
    result = new ArrayList<>(new LinkedHashSet<>(result));
    System.out.println(result); 
    for(Object [] objs: result){
      System.out.println(Arrays.asList(objs));
    }
  }

测试结果如下:

Hibernate: 
    select
        department0_.ID as ID1_0_0_,
        emps1_.ID as ID1_1_1_,
        department0_.NAME as NAME2_0_0_,
        emps1_.NAME as NAME2_1_1_,
        emps1_.SALARY as SALARY3_1_1_,
        emps1_.EMAIL as EMAIL4_1_1_,
        emps1_.DEPT_ID as DEPT_ID5_1_1_ 
    from
        GG_DEPARTMENT department0_ 
    inner join
        GG_EMPLOYEE emps1_ 
            on department0_.ID=emps1_.DEPT_ID
[[Ljava.lang.Object;@57fae983, [Ljava.lang.Object;@4a29f290, [Ljava.lang.Object;@4bee18dc, [Ljava.lang.Object;@44f3fe83, [Ljava.lang.Object;@44c5a16f, [Ljava.lang.Object;@417d6615, [Ljava.lang.Object;@7a6ebe1e, [Ljava.lang.Object;@21325036, [Ljava.lang.Object;@489543a6, [Ljava.lang.Object;@6272c96f]
[Department [id=1], Employee [id=1]]
[Department [id=1], Employee [id=2]]
[Department [id=2], Employee [id=3]]
[Department [id=2], Employee [id=4]]
[Department [id=3], Employee [id=5]]
[Department [id=3], Employee [id=6]]
[Department [id=4], Employee [id=7]]
[Department [id=4], Employee [id=8]]
[Department [id=5], Employee [id=9]]
[Department [id=5], Employee [id=10]]

迫切内连接:

  • INNER JOIN FETCH 关键字表示迫切内连接, 也可以省略 INNER 关键字
  • list() 方法返回的集合中存放 Department 对象的引用, 每个 Department 对象的 Employee 集合都被初始化, 存放所有关联的 Employee 对象

测试代码如下:

  @Test
  public void testInnerJoin(){
    String hql = "FROM Department d INNER JOIN FETCH d.emps";
    Query query = session.createQuery(hql);
    List<Department> depts = query.list();
    System.out.println(depts.size());
    for(Department dept: depts){
      System.out.println(dept.getName() + ", " + dept.getEmps().size()); 
    }
  }

测试结果如下:

Hibernate: 
    select
        department0_.ID as ID1_0_0_,
        emps1_.ID as ID1_1_1_,
        department0_.NAME as NAME2_0_0_,
        emps1_.NAME as NAME2_1_1_,
        emps1_.SALARY as SALARY3_1_1_,
        emps1_.EMAIL as EMAIL4_1_1_,
        emps1_.DEPT_ID as DEPT_ID5_1_1_,
        emps1_.DEPT_ID as DEPT_ID5_1_0__,
        emps1_.ID as ID1_1_0__ 
    from
        GG_DEPARTMENT department0_ 
    inner join
        GG_EMPLOYEE emps1_ 
            on department0_.ID=emps1_.DEPT_ID
10
AA, 2
AA, 2
BB, 2
BB, 2
CC, 2
CC, 2
DD, 2
DD, 2
EE, 2
EE, 2

对比可以发现,inner join和inner join fetch发送的SQL查询一样,差别在返回的接收对象类型。前者为list<Object[]>,后者为list<Department>


另外,这里均使用的为“From”语句,并未使用select关键字。如果使用了select关键字,查出来的结果为list<Department>,但是SQL语句也会有变化。

测试一代码如下:

  @Test
  public void testInnerJoin(){
    String hql = "select d FROM Department d INNER JOIN  d.emps";
    Query query = session.createQuery(hql);
    List<Department> depts = query.list();
    System.out.println(depts.size());
    for(Department dept: depts){
      System.out.println(dept.getName() + ", " + dept.getEmps().size()); 
    }
  }

测试结果如下:

Hibernate: 
    select
        department0_.ID as ID1_0_,
        department0_.NAME as NAME2_0_ 
    from
        GG_DEPARTMENT department0_ 
    inner join
        GG_EMPLOYEE emps1_ 
            on department0_.ID=emps1_.DEPT_ID
10
Hibernate:  // 使用的时候再次发送SQL语句进行查询
    select
        emps0_.DEPT_ID as DEPT_ID5_1_0_,
        emps0_.ID as ID1_1_0_,
        emps0_.ID as ID1_1_1_,
        emps0_.NAME as NAME2_1_1_,
        emps0_.SALARY as SALARY3_1_1_,
        emps0_.EMAIL as EMAIL4_1_1_,
        emps0_.DEPT_ID as DEPT_ID5_1_1_ 
    from
        GG_EMPLOYEE emps0_ 
    where
        emps0_.DEPT_ID=?
AA, 2
AA, 2
//...

而inner join fetch则无论是否使用select关键字,都会将关联的实体一并查询出来。

测试二代码如下:

  @Test
  public void testInnerJoin(){
    String hql = "select d FROM Department d INNER JOIN fetch  d.emps";
    Query query = session.createQuery(hql);
    List<Department> depts = query.list();
    System.out.println(depts.size());
    for(Department dept: depts){
      System.out.println(dept.getName() + ", " + dept.getEmps().size()); 
    }
  }

测试二结果如下:

Hibernate: 
    select
        department0_.ID as ID1_0_0_,
        emps1_.ID as ID1_1_1_,
        department0_.NAME as NAME2_0_0_,
        emps1_.NAME as NAME2_1_1_,
        emps1_.SALARY as SALARY3_1_1_,
        emps1_.EMAIL as EMAIL4_1_1_,
        emps1_.DEPT_ID as DEPT_ID5_1_1_,
        emps1_.DEPT_ID as DEPT_ID5_1_0__,
        emps1_.ID as ID1_1_0__ 
    from
        GG_DEPARTMENT department0_ 
    inner join
        GG_EMPLOYEE emps1_ 
            on department0_.ID=emps1_.DEPT_ID
10
AA, 2
AA, 2
BB, 2
BB, 2
CC, 2
CC, 2
DD, 2
DD, 2
EE, 2
EE, 2

(11)关联级别运行时的检索策略

如果在 HQL 中没有显式指定检索策略, 将使用映射文件配置的检索策略。

HQL 会忽略映射文件中设置的迫切左外连接检索策略, 如果希望 HQL 采用迫切左外连接策略, 就必须在 HQL 查询语句中显式的指定它。

若在 HQL 代码中显式指定了检索策略, 就会覆盖映射文件中配置的检索策略。


目录
相关文章
|
10月前
|
SQL 缓存 Java
Hibernate对象状态之间的神奇转换
Hibernate对象状态之间的神奇转换
|
3天前
|
SQL Java 数据库连接
技术经验分享:Hibernate之HQL
技术经验分享:Hibernate之HQL
|
5天前
|
缓存 Java 数据库连接
解析Hibernate与JPA:Java对象的数据库化之旅
【6月更文挑战第25天】在Java企业开发中,Hibernate和JPA提供优雅的数据持久化方案。Hibernate是JPA规范的强大ORM实现,简化对象与数据库映射。配置环境后,通过@Entity注解定义实体类映射表,如`User`类映射&quot;users&quot;表。利用JPA的EntityManager执行查询和更新,如JPQL查询及对象更新。事务管理和性能优化是关键,确保数据完整性和应用性能。本文揭示了Hibernate与JPA的最佳实践,助开发者从容应对数据持久化。
|
5天前
|
存储 Java 数据库连接
深入探索Hibernate与JPA:Java对象与数据库表的桥梁
【6月更文挑战第25天】Java ORM如Hibernate和JPA简化了数据库交互。在电商系统案例中,JPA注解如`@Entity`、`@Table`、`@Id`定义了对象-表映射。Hibernate利用这些定义实现持久化,如`Session.save()`保存用户对象至数据库,降低了复杂性并提升了开发效率。
|
2月前
|
SQL Java 关系型数据库
Hibernate - 对象关系映射文件(*.hbm.xml)详解
Hibernate - 对象关系映射文件(*.hbm.xml)详解
149 1
|
2月前
|
SQL Java 数据库连接
Hibernate - QBC和本地SQL对象检索详解
Hibernate - QBC和本地SQL对象检索详解
42 0
|
2月前
|
SQL 缓存 Java
Hibernate - 检索策略入门与详解
Hibernate - 检索策略入门与详解
36 0
|
2月前
|
存储 缓存 Java
Hibernate - Session方法与持久化对象详解
Hibernate - Session方法与持久化对象详解
90 0
|
12月前
|
SQL Java 数据库连接
【java】——Hibernate持久化对象的三个状态
当我正在上课的时候这时老师出去去厕所了(session关闭),这个时候我的心飞向了外面的远方,这时我就处于离线状态。
|
2月前
|
SQL 缓存 Java
框架分析(9)-Hibernate
框架分析(9)-Hibernate