web-dev-qa-db-fra.com

Renvoyer un objet personnalisé à partir d'une requête Spring Data Jpa

J'ai une requête personnalisée dans une classe de référentiel jpa:

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

La requête renvoie les 10 tuteurs/professeurs de stage les plus demandés; le résultat est composé des informations du professeur et d'une valeur entière (le nombre).

Professeur classe modèle:

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

Modèle ProfessorInternshipCount (créé pour incapsuler le résultat de la requête):

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

Maintenant, j'ai du mal à lier ce que la requête retourne avec le modèle que j'ai créé. Plus précisément, je reçois l'exception suivante:

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]
...

Est-ce que je fais quelque chose de mal? Y a-t-il une meilleure façon de faire ce que j'essaie de faire?

6
valent0ne

Vous pouvez facilement y parvenir en utilisant la projection. Voici les colonnes ci-dessous:

private String firstName;
private String lastName;
private Long id;

Créez une interface avec getter à partir de votre requête. Votre projection aimera ceci:

public interface ITestProjection {
    Long getId();
    Integer getCount();
    String getFirstName();
    String getLastName();
}

Votre requête aimera ceci:

@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);

J'espère que cela résoudra votre problème.

Pour plus de détails, visitez ce fil .

Merci :)

6
Md. Sajedul Karim

Par exemple, disons que nous avons:

  • tilisateur - objet entité avec de nombreux champs.

  • serBean - juste un objet où nos données seront converties.

Mauvaise pratique:

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

    }

mais il renverra des valeurs dans tableaux, donc ce sera plus beau si nous écrivons le code suivant, c'est-à-dire meilleure pratique:

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();

Donc, finalement, tout est converti en classe UserBean. Note que dans la méthode addScalar vous devez passer le nom de variable d'instance (dans votre question vous avez count variable)

1
grep

Vous pouvez le faire de l'une des manières suivantes.

  1. Utilisation de ModelMapper pour convertir l'entité en type de bean cible
  2. Utilisez Spring Projection pour y arriver

Parcourez l'article pour obtenir plus de détails.

http://javasampleapproach.com/spring-framework/spring-data/query-alter-domain-model-spring-jpa-projection-springboot-mysql-database

1
Gaurav Srivastav