【问题标题】:One to many JPA query without join clause没有连接子句的一对多 JPA 查询
【发布时间】:2020-01-09 07:10:37
【问题描述】:

给定两个具有一对多关系的表(SQL Server 脚本):

CREATE TABLE address (
  id BIGINT IDENTITY (1, 1) NOT NULL,
  street VARCHAR(255),
  zip VARCHAR(32),
  city VARCHAR(255),  
  country VARCHAR(32),

  CONSTRAINT PK_address PRIMARY KEY NONCLUSTERED(id)
);
CREATE TABLE person (
  id BIGINT IDENTITY (1, 1) NOT NULL,
  address_id BIGINT NOT NULL,
  first_name VARCHAR(32),
  last_name VARCHAR(32),

  CONSTRAINT PK_person PRIMARY KEY NONCLUSTERED(id),
  CONSTRAINT FK_person_address FOREIGN KEY (address_id) REFERENCES address
); 

一个人只有一个地址,但同一个地址可以链接到多个不同的人。

还有一个带有一个显式查询的 Spring JPA 存储库:

@Repository
public interface PersonJpaRepository extends JpaRepository<Person, Long> {

    List<Person> findByAddressId(Long addressId);
}

使用 Hibernate 作为 JPA 提供程序,会为此 find 方法生成与此类似的 SQL:

select person0_.id, person0_.first_name, person0_.last_name
from person person0_ 
left outer join address address1_ on person0_.address_id=address1_.id 
where person0_.address_id=?

由于我对 address 表中的任何数据都不感兴趣,而且我知道 address_id 已经准备就绪,因此似乎不需要加入:

select person0_.id, person0_.first_name, person0_.last_name
from person person0_ 
where person0_.address_id=?

这个查询给出了相同的结果。

我是否可以让 JPA/Hibernate 在不通过 @Query 显式定义查询的情况下生成没有连接的 SQL?

【问题讨论】:

    标签: java sql-server spring hibernate jpa


    【解决方案1】:

    您无法更改 Hibernate 的行为,但您不必关心联接。

    数据库无论如何都会优化查询,因为没有表地址的保护。

    所以从数据库方面来看,两个查询是相同的。

    【讨论】:

    • 数据库优化是特定于 SQL-Server 还是其他数据库类型也这样做?
    • 每个数据库都会优化查询。因此,比较两个查询的执行计划而不是查询本身总是一个很好的建议。
    【解决方案2】:

    是的,你可以!我可以非常有帮助地消除连接,因为它不会引入第二个表名。例如,这可以使 ORDER 变得更加容易,因为没有必要为表命名。

    使它更具体。可以在生成的 SQL 中执行 HQL-SELCT 并限制 ManyToOne 关系,没有任何连接:

    而不是使用加入

    session.createQuery("FROM Person person WHERE person.adress.id = 42")
    

    我们可以使用adress_id列

    session.createQuery("FROM Person person WHERE person.adressId = 42")
    

    如果您指定一个额外的 adressId 字段,这将有效,该字段仅用作 Hibernate 的映射信息:

    @Entity
    @Access(AccessType.FIELD)
    public class Person{
        @Id
        String id;
    
        @JoinColumn(name = "adress_id") 
        @ManyToOne(fetch = FetchType.LAZY)
        @Nullable
        public Adress adress;
    
    
        @Column(name = "adress_id", insertable = false, updatable = false)
        private String adressId;
    
     // [... street, zip, city, country]
    }
    
    
    @Entity
    @Access(FIELD)
    public class Adress{
        @Id
        String id;
    
       // [... firstName, lastName]
    }
    

    AccessType.FIELD 不是必需的(但我们可以在示例中保留 getter/setter)。 FetchType.LAZY 和 @Nullable 也是可选的,但要明确何时使用它。我们能够加载具有特定Address 的Person 实体(我们知道地址ID)。但是我们不需要连接,因为 WHERE 子句不需要连接,初始获取也不需要连接(可以延迟获取地址)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-31
      • 1970-01-01
      • 2022-12-21
      • 2013-08-16
      • 2017-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多