如何在Spring Data JPA中跳过关联数据?

huangapple go评论66阅读模式
英文:

How do I skip relational data in Spring data JPA?

问题

在某些情况下,我只需要Type数据,而不需要Nature数据。如何避免获取Nature数据?

要避免获取Nature数据,您可以在查询Type时使用不同的方法,具体取决于您的需求。以下是两种可能的方法:

  1. 使用延迟加载(Lazy Loading):

    默认情况下,您的关联关系使用FetchType.EAGER,这意味着在获取Type时也会立即获取关联的Nature数据。要避免这种情况,您可以将关联关系更改为延迟加载(Lazy Loading)。

    修改Type实体类中的关联关系注解如下:

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "type_id", referencedColumnName = "id")
    private List<Nature> natures;
    

    这将使得在访问Type对象的natures属性时才会加载Nature数据,否则将不会加载。

  2. 创建另一个DTO(数据传输对象):

    如果您需要在某些情况下获取完整的Type数据(包括关联的Nature数据),而在其他情况下只获取Type数据而不包括Nature数据,可以创建一个不包括Nature数据的DTO。在这种情况下,您需要分别查询和映射Type数据。

    首先,创建一个不包括Nature数据的DTO类,例如:

    public class TypeDTO {
        private Long id;
        private String name;
    
        // Getter和Setter方法
    }
    

    然后,在服务层中,根据您的需求选择性地映射DTO或完整的Type实体类数据。在获取Type数据时,如果您不需要Nature数据,只需映射到DTO即可:

    public List<TypeDTO> getAllComplaintTypesWithoutNature() {
        List<Type> typeList = typeRepository.findAll();
        return typeList.stream()
            .map(type -> {
                TypeDTO typeDTO = new TypeDTO();
                typeDTO.setId(type.getId());
                typeDTO.setName(type.getName());
                return typeDTO;
            })
            .collect(Collectors.toList());
    }
    

    这将允许您在不同的情况下选择性地获取包含或不包含Nature数据的Type信息。

根据您的具体需求,您可以选择上述两种方法之一或两者结合使用。这取决于您的应用程序架构和查询模式。

英文:

I have two Entities Type and Nature given below.

Type:

@Entity
@Getter
@Setter
@Table(name = &quot;types&quot;)
public class Type {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = &quot;type_id&quot;, referencedColumnName = &quot;id&quot;)
    private List&lt;Nature&gt; natures;
}

Nature:

@Entity
@Getter
@Setter
@Table(name = &quot;natures&quot;)
public class Nature {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private String description;
}

For some cases, I need to get Type data with Nature data.
So my Controller, Service looks like below.

Controller:

@GetMapping(&quot;/v1/complaints/types&quot;)
public ResponseEntity&lt;List&lt;Type&gt;&gt; getAllType() {
    List&lt;Type&gt; typesList = complaintService.getAllComplaintTypes();
    return new ResponseEntity&lt;&gt;(typesList, HttpStatus.OK);
}

Service:

public List&lt;Type&gt; getAllComplaintTypes() {
    List&lt;Type&gt; typeList = typeRepository.findAll();
    return typeList;
}

That gives me data like this:

    {
        &quot;id&quot;: 2,
        &quot;name&quot;: &quot;demo type name&quot;,
        &quot;natures&quot;: [
            {
                &quot;id&quot;: 12,
                &quot;name&quot;: &quot;nature name 1&quot;,
                &quot;description&quot;: &quot;nature description 1&quot;
            },
            {
                &quot;id&quot;: 14,
                &quot;name&quot;: &quot;nature name 2&quot;,
                &quot;description&quot;: &quot;nature name 2&quot;
            }
        ]
    }

But in some other cases I need only Type data without Nature data.
How can I avoid the Nature data?

答案1

得分: 2

按默认情况,急切地获取数据是不良实践。切换到懒加载模式并实现两个服务方法,一个用于获取所有数据(使用JPA标准方式),另一个用于获取带有Nature属性的数据,JPA仓库中的实现如下:

@EntityGraph(type = EntityGraph.EntityGraphType.FETCH, attributePaths = "natures")
@Query(
    "SELECT type FROM Type type"
)
List<Type> findAllWithNature();

EntityGraph 注解会告诉Hibernate同时获取natures属性,然后自动将这些属性填充到实体中,无需额外操作。

然后在您的服务类中添加以下代码:

public List<Type> getAllComplaintTypesWithNatures() {
    List<Type> typeListWithNature = typeRepository.findAllWithNature();
    return typeListWithNature;
}

此外,您可能需要考虑使用除了 type 以外的关键字,因为type 在许多编程语言中是保留字,可能会引起混淆或问题。

英文:

It is by default bad practice to fetch eagerly. Switch to fetch type lazy and implement two service methods, one to fetch all (standard via JPA), and one with Nature, done like this in JPA repository:

    @EntityGraph(type = EntityGraph.EntityGraphType.FETCH, attributePaths = &quot;natures&quot;)
    @Query(&quot;&quot;&quot;
            SELECT type
            FROM Type type
            &quot;&quot;&quot;)
    List&lt;Type&gt; findAllWithNature();

The EntityGraph annotation will tell Hibernate to fetch the natures as well and then populates the entities with the natures. All this will be done automagically without any further ado.

And into your service goes this:

public List&lt;Type&gt; getAllComplaintTypesWithNatures() {
    List&lt;Type&gt; typeListWithNature = typeRepository.findAllWithNature();
    return typeListWithNature;
}

Also you may consider to use another keyword other than type, since this can lead to confusion or problems cause type is reserved in many languages.

答案2

得分: 2

正如 @Slevin 所说,对实体使用急加载类型是不好的实践,而是应该使用 @EntityGraph 在需要时急加载数据。

此外,为了仅获取所需的数据,我建议您使用投影(Projections)。投影可以基于类和接口。它们之间的区别是什么呢?基于类的投影不支持嵌套数据,而基于接口的投影会从数据库中获取所有字段,并在嵌套投影的情况下以编程方式映射数据。

例如,当您需要没有自然属性的类型时,您可以定义以下投影:

@Getter
@Setter
@AllArgsConstructor
public class TypeDto {

    private Long id;

    private String name;

}

然后,将以下方法添加到存储库中:

   List<TypeDto> findAllTypeDtosBy();

有关投影的更多详细信息,请参阅 博客文章

英文:

As @Slevin said, it's a bad practice to use eager fetch type on entities, instead use @EntityGraph to fetch data eagerly when needed.

Additionally, to fetch only needed data I suggest you use projections. Projection can be class-based and interface-based. What's the difference? Class-based projections don't support nested data, whereas interface-based projections fetch all fields from the database and map the data programmatically in case of nested projections.

For example, when you need types without natures, you can define the following projection:

@Getter
@Setter
@AllArgsConstructor
public class TypeDto {

    private Long id;

    private String name;

}

Then, add the method below to the repository:

   List&lt;TypeDto&gt; findAllTypeDtosBy();

For more details about projections refer to the blog post.

huangapple
  • 本文由 发表于 2023年7月10日 11:59:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/76650610.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定