【问题标题】:JPA Returning Empty Set When Should Be Empty Optional<>JPA 在应该为空时返回空集 可选<>
【发布时间】:2022-01-20 15:45:50
【问题描述】:

我有以下两个实体:

@Entity
@Table(name = "organization")
@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class Organization {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    Long id;

    @Column(name = "name")
    String name;

    @OneToMany(mappedBy = "organization")
    Set<Team> teams;
    
}
@Entity
@Table(name = "team")
@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class Team {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    Long id;

    @Column(name = "name")
    String name;

    @Column(name = "description")
    String description;

    @ManyToOne
    @JoinColumn(name="organization_id", nullable=false)
    Organization organization;
    
}

以及这些实体的以下两个存储库

@Repository
public interface OrganizationRepo extends CrudRepository<Organization, Long> {
    
}
@Repository
public interface TeamRepo extends CrudRepository<Team, Long> {

    Optional<Set<Team>> findByOrganizationId(Long organizationId);

    Optional<Team> findByOrganizationIdAndTeamId(Long organizationId, Long teamId);
    
}

然后我尝试将 findByOrganizationId(..) 方法与一个不存在的 organizationId 一起使用,并希望它返回一个空的可选项,但我得到了一个包含 0 个项目的 HashSet?有没有一种方法可以使用 JPA 方法完成此功能?

    @Autowired
    TeamRepo repository;

    @ResponseBody
    @GetMapping(value = "/api/v1/organization/{organizationId}/team")
    ResponseEntity<Iterable<Team>> index(@PathVariable Long organizationId) {

        Optional<Set<Team>> result = repository.findByOrganizationId(organizationId);

        if(result.isPresent()) {

            return new ResponseEntity<>(result.get(), HttpStatus.OK);

        } else {

            return new ResponseEntity<>(HttpStatus.NOT_FOUND);

        }

    }

编辑: 由于OrganizationTeams 是OneToMany 关系,所以如果没有Organization,它应该返回一个空的Optional&lt;&gt;,但如果有一个具有0 个或多个团队的Organization,它应该是合理的返回一个 Optional&lt;&gt; 和一个 Set&lt;&gt; 零个或多个团队。

【问题讨论】:

    标签: java spring spring-boot hibernate spring-data-jpa


    【解决方案1】:

    当你使用集合作为结果时,你不需要将它包装在 Optional 中,你可以直接使用 isEmpty() 方法检查集合:

    @Repository
    public interface TeamRepo extends CrudRepository<Team, Long> {
    
        Set<Team> findByOrganizationId(Long organizationId);
    
        Optional<Team> findByOrganizationIdAndTeamId(Long organizationId, Long teamId);
        
    }
    
    
    
    
    @Autowired
    TeamRepo repository;
    
    @ResponseBody
    @GetMapping(value = "/api/v1/organization/{organizationId}/team")
    ResponseEntity<Iterable<Team>> index(@PathVariable Long organizationId) {
    
        Set<Team> result = repository.findByOrganizationId(organizationId);
    
        if(!result.isEmpty()) {
    
            return new ResponseEntity<>(result, HttpStatus.OK);
    
        } else {
    
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    
        }
    
    }
    

    Effective Java book 规定:容器类型,包括集合、映射、流、数组和可选项不应包装在可选项中。

    【讨论】:

    • 我正要说“但是为什么它用原始的findById(..) 方法来做,但后来我检查了一下,它没有。好点,但是我想要它代表OneToMany 关系,因此如果没有组织,则返回空可选,但如果有组织,则返回包含 0 个或更多项目的集合。
    • 根据我的理解,您在尝试查找该组织的团队之前尝试检查该组织是否存在该 ID。如果是这种情况,那么您需要注入 @Autowired OrganizationRepo organizationRepo;在您的控制器中,然后调用返回 Optional 的 organizationRepo.findById(organizationId),如果为空返回返回 new ResponseEntity(HttpStatus.NOT_FOUND);否则从 repository.findByOrganizationId(organizationId) 中返回结果
    • 对,我知道这是一种方法我只是想知道是否有一种更简洁的方法可以使用 JPA 存储库方法通过单个查询来实现。
    • 我认为没有更清洁的方法,你需要做这两个调用。
    • 好的,很酷。我将把这个问题留到周末,如果没有其他人回来提出任何想法,我会将其标记为答案。感谢您的快速回复!
    猜你喜欢
    • 2015-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-29
    • 2011-10-03
    • 2011-12-04
    • 1970-01-01
    相关资源
    最近更新 更多