简体   繁体   English

spring data jpa - 带有自定义查询的基于类的投影

[英]spring data jpa - class based projections with custom query

I need to have a spring data repository method for a custom query and would like to use class based projection.我需要一个用于自定义查询的 spring 数据存储库方法,并希望使用基于类的投影。

Looking at this https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections看着这个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 works well findDistinct 效果很好
  • findDistinctQuery and findDistinctNativeQuery throw findDistinctQuery 和 findDistinctNativeQuery 抛出

No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [com.x.PersonDTO]没有找到能够从类型 [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] 转换为类型 [com.x.PersonDTO] 的转换器

Is there any option to make it work with classes (not interfaces)?是否有任何选项可以使其与类(而不是接口)一起使用?

please write your method in person repository like below请亲自编写您的方法,如下所示

@Query(value = "select distinct new xxx.xxx.dto.PersonDTO(p.first_name, p.last_name) from person p")
List<PersonProjection> findDistinctNativeQuery();

"xxx.xxx.dto.PersonDTO" this should point to your fully qualified class name. “xxx.xxx.dto.PersonDTO”这应该指向您的完全限定类名。

for your reference please look into this https://www.bytestree.com/spring/spring-data-jpa-projections-5-ways-return-custom-object/供您参考,请查看此https://www.bytestree.com/spring/spring-data-jpa-projections-5-ways-return-custom-object/

I'm not sure there is a Spring Data solution for native query, but you can use JPA ConstructorResult :我不确定是否有适用于本机查询的 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();

}

Or you can return Object[] from findDistinctNativeQuery() and then manually create PersonDTO.或者您可以从 findDistinctNativeQuery() 返回 Object[],然后手动创建 PersonDTO。

With HQL you can use class based projections as well, the only condition PersonDTO must have the all argument constructor which parameter names must match properties of the root entity class:使用 HQL,您也可以使用基于类的投影,唯一的条件 PersonDTO 必须具有所有参数构造函数,其参数名称必须与根实体类的属性匹配:

public interface PersonRepository extends Repository<Person, Long> {

    ...

    List<PersonDTO> findDistinct();

}

If PersonDTO properties doesn't match the base entity properties, than HQL dynamic instantiation may be used.如果 PersonDTO 属性与基本实体属性不匹配,则可以使用HQL 动态实例化 Again, this doesn't work with native query:同样,这不适用于本机查询:

public interface PersonRepository extends Repository<Person, Long> {

    ...

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

}

This works too:这也有效:

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;
    }
}

This can be done with FluentJPA :这可以通过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();
}

Note , that you must annotate PersonDTO with @Data and not @Value .请注意,您必须使用@Data而不是@Value注释@Value More details about JPA Repositories integration .有关JPA 存储库集成的更多详细信息。

You need to add constructor with fields name same as root entity.您需要添加具有与根实体相同的字段名称的构造函数。 So your DTO would be,所以你的 DTO 将是,

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

You can simplify DTO by using Lombok's @Value annotation,您可以使用 Lombok 的@Value注释来简化 DTO,

 @Value
 class PersonDTO {
    String firstname, lastname;
 }

Check more details here 在此处查看更多详细信息

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM