Spring Data JPA - 使用自定义查询进行基于类的投影

9

我需要为自定义查询编写一个Spring Data存储库方法,并希望使用基于类的投影。

参考此文档:https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections

@Entity
public class Person {
  @Id
  private Long id;
  private String firstName;
  private String lastName;
  private int age;
}

@Value // lombok annotation to create constructor, equals and hash-code
public class PersonDTO {
  private String firstName;
  private String lastName;
}

public interface PersonRepository extends Repository<Person, Long> {

List<PersonProjection> findDistinct();

@Query("select distinct firstName, lastName from Person")
List<PersonProjection> findDistinctQuery();

@Query(value = "select distinct first_name, last_name from person", nativeQuery = true)
List<PersonProjection> findDistinctNativeQuery();

}
  • findDistinct工作正常
  • findDistinctQuery和findDistinctNativeQuery会抛出异常

找不到转换器,无法将类型[org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap]转换为类型[com.x.PersonDTO]

是否有任何选项可使其与类(而非接口)配合使用?

5个回答

11

1
这对我很有效,我甚至没有使用本地查询。 - Tharaka Devinda
感谢您提供的参考链接,伙计。 - JPA
这在本地查询中不起作用。 - birca123
是的,@birca说得对。 - swyrik

6

这个也可以:

public interface PersonRepository extends CrudRepository<Person, Long> {

    @Query(nativeQuery = true,
        value = "SELECT DISTINCT fist_name AS firstName, last_name AS lastName FROM person ")
    List<PersonNameOnly> findDistinctNames();

    interface PersonNameOnly {
        String getFirstName;
        String getLastName;
    }
}

5

我不确定是否有Spring Data的本地查询解决方案,但您可以使用JPA的ConstructorResult

@Entity
@NamedNativeQuery(
        name="Person.findDistinctNativeQuery",
        query="select distinct first_name as firstName, last_name as lastName from person",
        resultSetMapping="PersonMapping"
)
@SqlResultSetMapping(name="PersonMapping",
        classes={
                @ConstructorResult(targetClass=PersonDTO.class, columns={
                        @ColumnResult(name="firstName", type=String.class),
                        @ColumnResult(name="lastName", type=String.class)
                })
        })
public class Person {
    @Id
    private Long id;
    private String firstName;
    private String lastName;
    private int age;
}

@Value // lombok annotation to create constructor, equals and hash-code
public class PersonDTO {
    private String firstName;
    private String lastName;
}

public interface PersonRepository extends Repository<Person, Long> {

    List<PersonProjection> findDistinct();

    @Query("select distinct firstName, lastName from Person")
    List<PersonProjection> findDistinctQuery();

    @Query(name = "Person.findDistinctNativeQuery", nativeQuery = true)
    List<PersonDTO> findDistinctNativeQuery();

}

或者你可以从findDistinctNativeQuery()返回Object [],然后手动创建PersonDTO。

使用HQL时,您还可以使用基于类的投影,唯一的条件是PersonDTO必须具有所有参数构造函数,其参数名称必须与根实体类的属性名称匹配:

public interface PersonRepository extends Repository<Person, Long> {

    ...

    List<PersonDTO> findDistinct();

}

如果PersonDTO的属性与基本实体属性不匹配,可以使用HQL动态实例化。但是,请注意这种方法不能用于原生查询:

public interface PersonRepository extends Repository<Person, Long> {

    ...

    @Query("select new com.x.PersonDTO(firstName, lastName) from Person")
    List<PersonDTO> findDistinct();

}

1
这可以通过 FluentJPA 完成:
default List<PersonDTO> findDistinctQuery() {
    FluentQuery query = FluentJPA.SQL((Person p) -> {
        SELECT(DISTINCT(p.getFirstName(), p.getLastName()));
        FROM(p);
    });
    return query.createQuery(getEntityManager(), PersonDTO.class).getResultList();
}

请注意,您必须使用 @Data 注释 PersonDTO 而不是 @Value。有关 JPA存储库集成 的更多详细信息,请参阅。

0

您需要添加一个构造函数,其字段名称与根实体相同。因此,您的DTO将如下所示:

public class PersonDTO {
  private String firstName;
  private String lastName;
  // constructor , getters setters, equals(…) and hashCode() implementations 
}

您可以通过使用Lombok的@Value注释来简化DTO,

 @Value
 class PersonDTO {
    String firstname, lastname;
 }

查看更多细节在这里


@Code_Mode,这是findDistinct正常工作所必需的。但自定义查询仍然无法正常工作。 - Boris
因为这并没有回答问题。但好吧,我已经取消了负评。 - Boris
更新了描述,使其更清晰,DTO满足基于类的投影的所有要求。 - Boris
@Code_Mode,我已经取消了我的踩票,请您也这样做。 - Boris
@Boris 不是我的。 - Vikas
你可以尝试使用别名,例如:select distinct p.firstName, p.lastName from Person p。另外你也可以使用构造器,例如:select com.pkg.PersonDTO(p.firstName, p.lastName) from Person p。希望这能帮到你。 - Vikas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接