【问题标题】:JPA 2 Relationship JOIN Named QueryJPA 2 关系 JOIN 命名查询
【发布时间】:2021-07-18 02:05:34
【问题描述】:

我正在使用带有 JPA 2.0 的 SpringBoot 2.4,并且我有一个如下模型:

@Entity
@Data
public class Nation {
   @Id 
   @GeneratedValue (strategy = GenerationType.IDENTITY)
   private Integer id;

   @OneToMany(mappedBy = "nation")
   private List<Country> country;
}

还有:

@Entity
@Data
public class Country {
   @Id 
   @GeneratedValue (strategy = GenerationType.IDENTITY)
   private Integer id;

   @ManyToOne
   private Nation nation;
}

现在我想找到所有Nation 过滤的IDcountry ID。纯粹的SQL 就是这样的:

select * from nation n, country c where n.id = [nation_id] AND c.id = [country_id];

因此我想用 JPA 来做这种方式:

@Query("select n from Nation n JOIN n.country c where n.id = ?1 AND c.id = ?2)
public List<Nation> find(Integer nationID, Integer countryID);

但它不起作用;它按国家过滤,但不按国家/地区过滤。

如果我打印 Hibernate 通过添加生成 SQL:

spring.jpa.show.sql=true

我可以看到查询与我上面在纯 SQL 中发布的完全一样。当我调用nation.getCountry() 时出现问题,它会生成另一个查询,该查询会加载连接到给定国家 ID 的所有国家/地区。

有没有办法解决这个问题?

【问题讨论】:

    标签: sql jpa-2.0 named-query


    【解决方案1】:

    我相信你可以在这种情况下使用 JPA DTO projections ...

    所以,在你的 Nation 类中创建一个这样的构造函数:

     /**
      * Copy Constructor. It creates a "detached" copy of the
      * given nation with only a copy of the provided country.
      */
     public Nation(Nation n, Country c) {
         super();
         this.id = n.id;
         // copy other nation values ...
    
         this.country.add( new Country(c) );
     }
    

    修改您的查询以调用此类构造函数...类似这样(假设 Nation 在 java 包 my.domain 中声明):

    @Query("select new my.domain.Nation(n, c) from Nation n JOIN n.country c where n.id = ?1 AND c.id = ?2)
    

    免责声明:

    • 我已经使用 JPA 和休眠完成了这项工作。到目前为止,我还没有使用 spring 进行测试,但我想这并不重要,因为您的 JPA 提供程序可能也处于休眠状态。
    • 我只使用目标实体的部分(或属性)(如提供的链接中所述)来完成此操作...我从未传递完整的实体(正如我在查询中建议的那样)。让我解释一下,在我应用这个的情况下,我有一个像 Nation(String name, int population) 这样的构造函数,在查询中我做了类似的事情:SELECT new my.domain.Nation(n.name, c.population) ... 尝试传递完整的实体,看看它是否有效......如果它失败了,回退到创建一个仅接收您的业务案例所需的属性的构造函数。

    【讨论】:

    • 非常感谢 Calitos Way。我会尝试你的解决方案,我对TransactionLazyEager 属性仍然有点困惑。 @OneToMany 应该默认为Eager 吗?如果我使用嵌入式 SpringBoot tomcat 启动我的应用程序,我必须用 @Transactional 注释我的方法,以检索与我的 Nation 实体关联的 Country 列表。它们不应该默认加载吗?
    • 通常情况下,让你的关系尽可能地懒惰是一个好习惯(我的意思是避免使用 EAGER)。当您将关系注释为 EAGER 时,您指示 JPA 提供程序(休眠)在加载声明它的实体之后立即加载该关系。如果您不打算使用这种关系是您的业务案例,那可能是性能损失。但是,在某些情况下使用 EAGER 是合理的(例如,如果您确定实体总是需要这种关系,无论如何)。
    • 关于您的问题……答案是“视情况而定”。默认情况下,Spring 实现了一个名为:Open Session in View 的特性……这个特性允许你在 @Transactional 上下文之外加载你的关系……有些人 think 如果开发人员不这样做,这个特性很容易出现性能问题意识到它......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-11
    • 2017-04-10
    相关资源
    最近更新 更多