从Spring Data Jpa查询返回自定义对象

7

我在JPA Repository类中有一个自定义查询:

package it.univaq.we.internshipTutor.repository;
import ...

public interface ProfessorRepository extends JpaRepository<Professor, Long> {

    List<Professor> findAll();

    ...

    @Query(value =  "SELECT professor.id, professor.department_id, " +
                    "professor.first_name, professor.last_name, " +
                    "professor.email, COUNT(professor_id) as count " +
                    "FROM professor LEFT JOIN student_internship ON professor.id = professor_id " +
                    "GROUP BY professor_id ORDER BY count DESC LIMIT ?1", nativeQuery = true)
    List<ProfessorInternshipCount> mostRequestedProfessors(int limit);
}

该查询返回前10名最受欢迎的实习导师/教授;结果由教授信息和一个整数值(计数)组成。

教授模型类:

package it.univaq.we.internshipTutor.model;

import ...

@Entity
@Table(name = "professor")
public class Professor {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Long id;

    @Transient
    private UUID uuid;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "department_id", nullable = false)
    @NotNull(message = "this field is mandatory")
    private Department department;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "professor")
    private List<StudentInternship> studentInternships;

    @Column(name = "first_name", nullable = false, length = 255)
    @NotEmpty
    private String firstName;

    @Column(name = "last_name", nullable = false, length = 255)
    @NotEmpty
    private String lastName;

    @Column(name = "email", nullable = false, length = 255)
    @Email
    @NotEmpty
    private String email;

    ...getters and setters...
}

ProfesssorInternshipCount模型(用于封装查询结果):

package it.univaq.we.internshipTutor.model;

public class ProfessorInternshipCount {
    private Professor professor;
    private Integer count;

    public ProfessorInternshipCount(Professor professor, int count) {
        this.professor = professor;
        this.count = count;
    }

    ...getters and setters...
}

现在,我在将查询结果与已创建的模型绑定时遇到了困难。 更准确地说,我遇到了以下异常:

org.springframework.core.convert.ConverterNotFoundException: 
    No converter found capable of converting from type 
    [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] 
    to type 
    [it.univaq.we.internshipTutor.model.ProfessorInternshipCount]
...

我做错了什么吗?有没有更好的方法来完成我正在尝试做的事情?


查看这个答案,它有完整的解决方案 https://dev59.com/E2Uo5IYBdhLWcg3wkAHB#50365522 - Md. Sajedul Karim
3个回答

9
您可以通过使用投影轻松实现此目标。 这里有以下列:
private String firstName;
private String lastName;
private Long id;

创建一个带有getter的接口来处理你的查询。你的投影将会像这样:
public interface ITestProjection {
    Long getId();
    Integer getCount();
    String getFirstName();
    String getLastName();
}

您的查询将会是这样的:
@Query(value = "SELECT professor.id, professor.department_id, " +
                    "professor.first_name, professor.last_name, " +
                    "professor.email, COUNT(professor_id) as count " +
                    "FROM professor LEFT JOIN student_internship ON professor.id = professor_id " +
                    "GROUP BY professor_id ORDER BY count DESC LIMIT =?1", nativeQuery = true)
    ArrayList<ITestProjection> findDataWithCount(Integer limit);

希望这能解决您的问题。欲了解更多详情,请访问此帖子。谢谢 :)

3
当名称为first_name时,getFirstName()对我无效,但是当它是firstName时有效。为什么? - Satish Patro

2
例如,假设我们有以下内容:
  • 用户 - 实体对象,具有多个字段。

  • UserBean - 仅是我们的数据将被转换的对象。

不好的做法:

 @Repository
    public class ReportingRepository {

        @PersistenceContext
        private EntityManager em;

        public List<UserBean> findQuery() {
            Query query = em.createNativeQuery("select  ...  from Table  INNER JOIN ...");
            List<UserBean> items = (List<UserBean>) query.getResultList();
            return items;
        }

    }

但它将返回数组中的值,因此如果我们编写以下代码,即最佳实践:,它将更加美观:

query.unwrap(SQLQuery.class)
           .addScalar("instance name", StringType.INSTANCE)
           .addScalar("second instance name", IntegerType.INSTANCE)
           .setResultTransformer(Transformers.aliasToBean(UserBean.class));

   List<UserBean> items = query.getResultList();

所以最终一切都被转换成了UserBean类。注意,在addScalar方法中应传递实例变量名称(在您的问题中,您有count变量)。


ResultTransformer自带一个不遵循函数式接口语法的传统定义。因此,在这个例子中我们不能使用lambda表达式。Hibernate 6.0旨在解决这个问题,这就是为什么Hibernate ORM 5.2 ResultTransformer被弃用的原因。你知道ResultTransformer的替代方案是什么吗? - Md. Sajedul Karim
目前Hibernate的稳定版本仍然是5.3。对于Hibernate 6.0,Hibernate团队承诺会有@FunctionalInterface,但是目前还没有变化。我们在等待 :) - grep
可以确认,在我的配置中,我收到了几个"已弃用"的警告。 - valent0ne
我使用Spring Boot 1.X,并且我没有使用Hibernate方法的过时版本。在较新版本的Hibernate中,该方法已被弃用,但仍然没有替代方法。目前,Hibernate 5.3仍然是稳定版本。我们将等待Hibernate的新更新,他们承诺实现@FunctionalInterface。因此,在没有任何替代方法之前,请继续使用过时的方法。 - grep

1

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