Hibernate中,left join、inner join以及left join fetch区别

本文深入探讨SQL的Inner Join、Left Outer Join等连接类型及其在Hibernate中的应用,特别是Fetch特性如何提高数据检索效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

³
——————————————————————————————————————————————————
Inner Join 的主要精神就是 exclusive , 叫它做排他性吧! 就是讲 Join 规则不相符的资料就会被排除掉, 譬如讲在 Product 中有一项产品的供货商代码 (SupplierId), 没有出现在 Suppliers 资料表中, 那么这笔记录便会被排除掉

 

Outer Join:

Select <要查询的字段> From <Left 资料表>

  <Left | Right> [Outer] Join <Right 资料表> On <Join 规则>

 

语法中的 Outer 是可以省略的, 例如你可以用 Left Join 或是 Right Join, 在本质上, Outer Join 是 inclusive, 叫它做包容性吧! 不同于 Inner Join 的排他性, 因此在 Left Outer Join 的查询结果会包含所有 Left 资料表的资料, 颠倒过来讲, Right Outer Join 的查询就会包含所有 Right 资料表的资料

Fetch:

在我们查询Parent对象的时候,默认只有Parent的内容,并不包含childs的信息,如果在Parent.hbm.xml里设置lazy="false"的话才同时取出关联的所有childs内容.
问题是我既想要hibernate默认的性能又想要临时的灵活性该怎么办?  这就是fetch的功能。我们可以把fetch与lazy="true"的关系类比为事务当中的编程式事务与声明式事务,不太准确,但是大概是这个意思。
总值,fetch就是在代码这一层给你一个主动抓取得机会.

Parent parent = (Parent)hibernateTemplate.execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
                Query q = session.createQuery(
"from Parent as parent "+
" left outer join fetch parent.childs " +
" where parent.id = :id"
                );
                q.setParameter("id",new Long(15));
return (Parent)q.uniqueResult();
            }
        });
        Assert.assertTrue(parent.getChilds().size() > 0);

你可以在lazy="true"的情况下把fetch去掉,就会报异常. 当然,如果lazy="false"就不需要fetch
——————————————————
from:
inner join(内连接)

left outer join(左外连接)

right outer join(右外连接)

full join (全连接,并不常用)

语句inner join, left outer join 以及 right outer join 可以简写。
from Cat as cat     join cat.mate as mate    left join cat.kittens as kitten

通过HQL的with关键字,你可以提供额外的join条件。
from Cat as cat     left join cat.kittens as kitten         with kitten.bodyWeight > 10.0

还有,一个"fetch"连接允许仅仅使用一个选择语句就将相关联的对象或一组值的集合随着他们的父对象的初始化而被初始化,这种方法在使用到集合的 情况下尤其有用,对于关联和集合来说,它有效的代替了映射文件中的外联接 与延迟声明(lazy declarations).
from Cat as cat     inner join fetch cat.mate    left join fetch cat.kittens

一个fetch连接通常不需要被指定别名, 因为相关联的对象不应当被用在 where 子句 (或其它任何子句)中。同时,相关联的对象 并不在查询的结果中直 接返回,但可以通过他们的父对象来访问到他们。
from Cat as cat     inner join fetch cat.mate    left join fetch cat.kittens child    left join fetch child.kittens

假若使用iterate()来调用查询,请注意fetch构造是不能使用的(scroll() 可以使用)。fetch也不应该与setMaxResults() 或setFirstResult()共用,这是因为 这些操作是基于结果集的,而在预先抓取集合类时可能包含重复的数据,也就是说无法预先知道精确的行数。fetch还不能与独立的 with条件一起使用 。通过在一次查询中fetch多个集合,可以制造出笛卡尔积,因此请多加注意。对bag映射来说,同时join fetch多个集合角色可能在某些情况下给出并非 预期的结果,也请小心。最后注意,使用full join fetch 与 right join fetch是没有意义的。

如果你使用属性级别的延迟获取(lazy fetching)(这是通过重新编写字节码实现的),可以使用 fetch all properties 来强制Hibernate立即取得那些原 本需要延迟加载的属性(在第一个查询中)。
from Document fetch all properties order by name
from Document doc fetch all properties where lower(doc.name) like ''%cats%''

 

_____________________________________________________________________________

 

参考如下:

http://www.javaeye.com/problems/34156

 

http://www.javaeye.com/problems/20785

 

http://www.javaeye.com/problems/16661

 

http://j-butterfly.javaeye.com/blog/293587

<think>我们正在讨论JPA(特别是Hibernate作为实现)中的查询构建。用户的问题是关于在CriteriaQuery中使用root.join("requestNo")并手动添加等于条件是否会导致重复的JOIN操作。 首先,我们需要理解`root.join`的作用。在JPA Criteria API中,`root.join`用于指定一个关联(join)路径。当我们在一个关联属性上使用`join`方法时,Hibernate会生成相应的SQL JOIN。 用户提到“手动添加等于条件”,这可能指的是在`ON`子句中添加额外的条件,或者是在`WHERE`子句中添加条件?在Criteria API中,我们通常通过`Join.on`方法来添加`ON`子句的条件,或者通过`where`方法来添加`WHERE`子句的条件。 关键点:重复JOIN是否会发生? 1. **重复JOIN的情况**:如果我们多次对同一个关联路径进行JOIN,那么就会导致重复的JOIN。例如: ```java Join<Object, Object> join1 = root.join("requestNo"); Join<Object, Object> join2 = root.join("requestNo"); // 这会导致第二个JOIN ``` 2. **使用同一个Join对象添加多个条件**:如果我们只进行一次JOIN,然后使用这个Join对象添加多个条件(无论是在ON子句还是WHERE子句),那么不会导致重复JOIN。 用户的具体操作是:调用`root.join("requestNo")`一次,然后使用这个Join对象添加一个等于条件。例如: ```java Join<Object, Object> requestNoJoin = root.join("requestNo"); requestNoJoin.on(cb.equal(requestNoJoin.get("someColumn"), value)); ``` 或者是在WHERE子句中: ```java Join<Object, Object> requestNoJoin = root.join("requestNo"); Predicate equalPredicate = cb.equal(requestNoJoin.get("someColumn"), value); query.where(equalPredicate); ``` 在这种情况下,我们只进行了一次JOIN,所以不会产生重复JOIN。 但是,如果我们在同一个查询中多次对同一个关联路径调用`root.join`,那么每次调用都会创建一个新的JOIN,从而导致重复JOIN。 另外,Hibernate可能会自动处理关联,例如当我们通过路径表达式访问关联实体的属性时,Hibernate会自动生成JOIN(隐式JOIN)。但是,在Criteria API中,我们显式地使用`join`方法,所以控制权在开发者手中。 **结论**:如果只调用一次`root.join("requestNo")`,然后使用这个Join对象添加条件,那么不会导致重复JOIN。重复JOIN只有在多次对同一个关联路径调用`join`方法时才会发生。 **建议**:为了避免重复JOIN,我们应该确保对于同一个关联路径只调用一次`join`方法,并将返回的Join对象重用。 **相关问题**: 1. 在JPA Criteria API中,如何避免重复JOIN? 2. Hibernate在处理JOIN时有哪些优化策略? 3. 在JPA中,显式JOIN和隐式JOIN有什么区别?</think>### JPA/Hibernate 中 `root.join("requestNo")` 与手动添加等于条件是否导致冗余 JOIN 的分析 在 JPA Criteria API 中使用 `root.join("requestNo")` 并手动添加等于条件时,**通常不会产生冗余 JOIN**。以下是详细分析: #### 1. **JOIN 操作的本质** - 当调用 `root.join("requestNo")` 时,Hibernate 会在生成的 SQL 中创建 **一个显式 JOIN 语句**(如 `LEFT JOIN request_no`)[^4]。 - 手动添加等于条件(如 `cb.equal(join.get("id"), value)`)会生成对应的 `ON` 或 `WHERE` 子句条件,**不会额外增加 JOIN 数量**。 #### 2. **冗余 JOIN 的产生场景** 冗余 JOIN 通常发生在以下情况: - **多次调用 `join()` 同一路径**: ```java Join<?, ?> join1 = root.join("requestNo"); // 第一次 JOIN Join<?, ?> join2 = root.join("requestNo"); // 冗余 JOIN ``` - **隐式 JOIN 与显式 JOIN 混用**: ```java root.join("requestNo"); // 显式 JOIN cb.equal(root.get("requestNo").get("id"), value); // 隐式 JOIN(可能触发额外 JOIN) ``` #### 3. **正确用法(无冗余)** ```java CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<Entity> query = cb.createQuery(Entity.class); Root<Entity> root = query.from(Entity.class); // 显式 JOIN 并添加条件(推荐) Join<Entity, RequestNo> requestJoin = root.join("requestNo", JoinType.LEFT); query.where(cb.equal(requestJoin.get("id"), 123)); // 不会产生额外 JOIN ``` → 生成的 SQL 类似: ```sql SELECT ... FROM entity LEFT JOIN request_no ON entity.request_id = request_no.id WHERE request_no.id = 123 ``` #### 4. **Hibernate 的优化机制** - Hibernate 会尝试合并同一路径的重复 JOIN(通过 `PathSource` 跟踪已注册的 JOIN)[^4]。 - 但需注意:**跨不同查询段的 JOIN 可能无法合并**(如在 `SELECT` 和 `WHERE` 中分别调用 `join()`)。 #### 5. **验证方法** - 启用 Hibernate SQL 日志(`spring.jpa.show-sql=true`)检查生成的 SQL。 - 使用 `EXPLAIN` 分析查询计划,确认 JOIN 数量。 #### 结论 ✅ **正确使用**时(单次显式 JOIN + 条件),**不会产生冗余 JOIN**。 ❌ 冗余 JOIN 主要源于 **同一路径的重复调用** 或 **隐式/显式 JOIN 混用**。 --- ### 相关问题 1. 在 JPA Criteria API 中,如何避免生成冗余的 SQL JOIN 语句? 2. 隐式 JOIN(如 `root.get("a.b")`)和显式 JOIN(`root.join("a")`)的性能差异是什么? 3. Hibernate 的 `JOIN FETCH` 和普通 `JOIN` 在查询优化上有何区别? [^1]: JPA 与 Hibernate 关系解析 [^2]: JPA 规范定义 [^3]: JPA 持久化机制 [^4]: Hibernate 实现 JPA 的适配层设计
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值