如何使用 Spring JPA 仅获取实体的选定属性?

How to fetch only selected attributes of an entity using Spring JPA?

我在我的项目中使用 Spring Boot (1.3.3.RELEASE) 和 Hibernate JPA。我的实体如下所示:

@Data
@NoArgsConstructor
@Entity
@Table(name = "rule")
public class RuleVO {

    @Id
    @GeneratedValue
    private Long id;

    @Column(name = "name", length = 128, nullable = false, unique = true)
    private String name;

    @Column(name = "tag", length = 256)
    private String tag;

    @OneToMany(mappedBy = "rule", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<RuleOutputArticleVO> outputArticles;

    @OneToMany(mappedBy = "rule", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<RuleInputArticleVO> inputArticles;
}

我的存储库如下所示:

@Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
}

在某些情况下,我只需要获取实体 RuleVO 的 idname 属性。我怎样才能做到这一点?我发现了一个通知,它应该可以使用 Criteria API 和 Projections 但是怎么做呢?提前谢谢了。 Vojtech

您可以使用@Query 注释(HQL) 来完成。

请参阅下面的 Spring 文档:

http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.at-query

(在 spring 文档中搜索 @Query)

更新:

正如有人向我指出的那样,我很懒惰,这可以很好地完成,因此我在网上寻找合适的答案后更新了我的答案。

下面是如何获取 ID 和 名称的示例:

@Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {

    @Query("SELECT r.id FROM RuleVo r where r.name = :name") 
    List<Long> findIdByName(@Param("name") String name);

    @Query("SELECT r.name FROM RuleVo r where r.id = :id") 
    String findNameById(@Param("id") Long id);
}

希望这次更新对您有所帮助


旧答案:

仅检索特定属性 name/id 是不可能的,因为这不是 spring 的设计方式或与此相关的任何 SQL 数据库,因为您总是 select行是一个实体。

你可以做的是查询实体中的变量,例如:

@Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {

    public RuleVo findOneByName(String name);
    public RuleVo findOneByNameOrId(String name, Long id);
    public List<RuleVo> findAllByName(String name);
    // etc, depending on what you want
}

您可以随意修改这些 w.r.t。您的需求。您可以通过自动装配存储库直接调用这些方法

有关更多选项和示例,请参阅 http://docs.spring.io/spring-data/jpa/docs/current/reference/html/ 第 5.3 节

是的,你可以用投影来实现。您有多种应用方式:

如果您可以升级到 Spring Data Hopper,它会提供易于使用的投影支持。在 reference documentation.

中查看如何使用它们

否则,首先创建一个包含您要加载的属性的 DTO,例如:

package org.example;

public class RuleProjection {

    private final Long id;

    private final String name;

    public RuleProjection(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }
}

当然,您也可以使用 Lombok 注释。

然后,您可以像这样在 JPQL 查询中使用:

select new org.example.RuleProjection(rule.id, rule.name) from RuleVO rule order by rule.name

如果您想避免在查询中使用 DTO class 名称,另一种选择是使用 QueryDSL 实现您自己的查询方法。使用 Spring Data JPA,您必须:

  • 使用新方法创建新界面。例如:

    public interface RuleRepositoryCustom {
       public List<RuleProjection> findAllWithProjection();
    }
    
  • 更改存储库以扩展新界面。例如:

    public interface RuleRepository extends JpaRepository<RuleVO, Long>, RuleRepositoryCustom {
    ...
    
  • 使用 Spring Data JPA QueryDSL 支持创建自定义存储库的实现。您必须使用其 Maven 插件预先生成 QueryDSL 的 Q 类。例如:

    public class RuleRepositoryImpl {
    
        public List<RuleProjection> findAllWithProjection() {
            QRuleVO rule = QRuleVO.ruleVO;
            JPQLQuery query = getQueryFrom(rule);     
            query.orderBy(rule.name.asc());
            return query.list(ConstructorExpression.create(RuleProjection.class, rule.id, rule.name));
        }
    }
    

您还可以定义自定义构造函数以使用 JPQL 获取特定列。

示例:

Replace {javaPackagePath} with complete java package path of the class use as a constructor in JPQL.

public class RuleVO {
   public RuleVO(Long id, String name) {
    this.id = id;
    this.name = name;
   }
}


@Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {

    @Query("SELECT new {javaPackagePath}.RuleVO(r.id, r.name) FROM RuleVo r where r.name = :name") 
    List<RuleVO> findIdByName(@Param("name") String name);
}
interface IdOnly{
    String getId();
}

@Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
    public List<IdOnly> findAllByName(String name);
}

我注意到这是一个非常古老的 post,但如果有人仍在寻找答案,请试试这个。它对我有用。