web-dev-qa-db-fra.com

Créer une requête personnalisée avec Spring DATA JPA?

Je travaille sur un projet avec Spring Data JPA. J'ai une table dans la base de données comme my_query.

Je veux créer une méthode qui prend une chaîne en tant que paramètre, puis l'exécuter en tant que requête dans la base de données.

Méthode:

executeMyQuery(queryString)

Comme exemple, quand je passe 

queryString= "SELECT * FROM my_query"

alors il devrait exécuter cette requête au niveau DB.

La classe de référentiel est la suivante.

public interface MyQueryRepository extends JpaRepository<MyQuery, Long>{
    public MyQuery findById(long id);

    @Modifying(clearAutomatically = true)
    @Transactional
    @Query(value = "?1", nativeQuery = true)
    public void executeMyQuery(String query);

}

Cependant, cela n'a pas fonctionné comme je m'y attendais. Cela donne l'erreur suivante.

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''select * from my_query;'' at line 1

Existe-t-il un autre moyen pour atteindre cet objectif? Merci d'avance

10
B378

Malheureusement, votre approche ne fonctionnera pas. Si vous utilisez l'annotation @Query, vous fournissez une requête concrète correcte en JPA ou en notation native par méthode.

Les seuls éléments que vous pouvez paramétrer sont les valeurs utilisées dans la clause WHERE. Considérez cet échantillon de doc officiel :

public interface UserRepository extends JpaRepository<User, Long> {
  @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true)
  User findByEmailAddress(String emailAddress);
}
16
ilya

Merci @ilya. Existe-t-il une autre approche pour réaliser cette tâche en utilisant Spring Data JPA? Sans annotation @Query?

Je veux juste agir sur cette partie. oui, il existe un moyen de s'y prendre sans utiliser l'annotation @query. Pour ce faire, vous devez définir une requête dérivée à partir de votre interface qui implémente l'instance de référentiel JPA.

ensuite, à partir de votre instance de référentiel, vous serez exposé à toutes les méthodes permettant des opérations CRUD sur votre base de données, telles que

 interface UserRepository extends CrudRepository<User, Long> {

 long deleteByLastname(String lastname);

 List<User> removeByLastname(String lastname);
}

avec ces méthodes, Spring Data comprendra ce que vous essayez d’archiver et les implémentera en conséquence.

Notez également que les opérations CRUD de base sont fournies à partir de la définition de la classe de base et qu'il n'est pas nécessaire de les redéfinir. Par exemple, il s'agit de la classe JPARepository définie par spring, donc son extension vous donne toutes les méthodes.

 public interface CrudRepository<T, ID extends Serializable>
 extends Repository<T, ID> {

 <S extends T> S save(S entity);      

 Optional<T> findById(ID primaryKey); 

 Iterable<T> findAll();               

 long count();                        

 void delete(T entity);               

 boolean existsById(ID primaryKey);   


}

Pour des informations plus récentes, consultez la documentation sur https://docs.spring.io/spring-data/jpa/docs/current/reference/html/

1
Austine Gwa

Il n'y a pas de soutien particulier pour cela. Mais ce que vous pouvez faire est de créer une méthode personnalisée avec un paramètre String et dans votre implémentation, injectez la EntityManager et exécutez-la.

Liens éventuellement utiles:

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-implementations

Comment accéder à Entity Manager avec les données de démarrage et de ressort

Remarque: je reconsidérerais si ce que vous essayez de faire est une bonne idée car cela détruira les détails d'implémentation du référentiel dans le reste de l'application.

1
Jens Schauder

En utilisant EntityManager, vous pouvez y parvenir.

Supposons que votre classe d'entité est comme ci-dessous:

import javax.persistence.*;
import Java.math.BigDecimal;

@Entity
@Table(name = "USER_INFO_TEST")
public class UserInfoTest {
    private int id;
    private String name;
    private String rollNo;

    public UserInfoTest() {
    }

    public UserInfoTest(int id, String name) {
    this.id = id;
    this.name = name;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID", nullable = false, precision = 0)
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Basic
    @Column(name = "name", nullable = true)
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Basic
    @Column(name = "roll_no", nullable = true)
    public String getRollNo() {
        return rollNo;
    }

    public void setRollNo(String rollNo) {
        this.rollNo = rollNo;
    }
}

Et votre requête est "select id, nom des utilisateurs où roll_no = 1001".

Ici, la requête retournera un objet avec id et une colonne de nom. Votre classe de réponse est comme ci-dessous:

Votre classe de réponse est comme:

public class UserObject{
    int id;
    String name;
    String rollNo;

    public UserObject(Object[] columns) {
        this.id = (columns[0] != null)?((BigDecimal)columns[0]).intValue():0;
        this.name = (String) columns[1];
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getRollNo() {
        return rollNo;
    }

    public void setRollNo(String rollNo) {
        this.rollNo = rollNo;
    }
}

ici, le constructeur UserObject obtiendra un tableau d'objets et définira des données avec l'objet.

public UserObject(Object[] columns) {
            this.id = (columns[0] != null)?((BigDecimal)columns[0]).intValue():0;
            this.name = (String) columns[1];
        }

Votre fonction d'exécution de requête est comme ci-dessous:

public UserObject getUserByRoll(EntityManager entityManager,String rollNo) {

        String queryStr = "select id,name from users where roll_no = ?1";
        try {
            Query query = entityManager.createNativeQuery(queryStr);
            query.setParameter(1, rollNo);

            return new UserObject((Object[]) query.getSingleResult());
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }

Ici vous devez importer les paquets ci-dessous:

import javax.persistence.Query;
import javax.persistence.EntityManager;

Maintenant, votre classe principale, vous devez appeler cette fonction. Commencez par obtenir EntityManager et appelez cette fonction getUserByRoll(EntityManager entityManager,String rollNo). La procédure d'appel est donnée ci-dessous:

Voici les importations

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

obtenez EntityManager de cette façon:

@PersistenceContext
private EntityManager entityManager;

UserObject userObject = getUserByRoll(entityManager,"1001");

Vous avez maintenant des données dans cet objet utilisateur.

Remarque:

query.getSingleResult() renvoie un tableau d'objets. Vous devez gérer la position de la colonne et le type de données avec la position de la colonne de requête.

select id, nom des utilisateurs où roll_no = 1001 

query retourne un tableau et c'est [0] -> id et 1 -> nom.

Plus d'infos visitez ce fil de discussion .

0
Md. Sajedul Karim

Basé sur @jelies answer , j'utilise l'approche suivante

Vous pouvez créer une autre interface pour vos méthodes personnalisées (comme exemple MyQueryCustom), puis l'implémenter comme suit.

public class MyQueryRepositoryImpl implements MyQueryRepositoryCustom {
    @PersistenceContext
    private EntityManager entityManager;

    public int executeQuery(String query) {
        return entityManager.createNativeQuery(query).executeUpdate();
    }
}

Cela va exécuter une requête personnalisée.

0
B378