web-dev-qa-db-fra.com

JPA 2.0, API de critères, sous-requêtes, dans des expressions

J'ai essayé d'écrire une instruction de requête avec une sous-requête et une expression IN plusieurs fois. Mais je n'ai jamais réussi.

Je reçois toujours l'exception, "Erreur de syntaxe près du mot clé 'IN'", l'instruction de requête était construite comme ceci,

SELECT t0.ID, t0.NAME
FROM EMPLOYEE t0
WHERE IN (SELECT ?
          FROM PROJECT t2, EMPLOYEE t1
          WHERE ((t2.NAME = ?) AND (t1.ID = t2.project)))

Je connais la Parole avant 'IN' perdre.

Avez-vous déjà écrit une telle requête? Toute suggestion?

40
Keating Wang

Vous trouverez ci-dessous le pseudo-code permettant d'utiliser une sous-requête à l'aide de l'API Criteria. 

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Object> criteriaQuery = criteriaBuilder.createQuery();
Root<EMPLOYEE> from = criteriaQuery.from(EMPLOYEE.class);
Path<Object> path = from.get("compare_field"); // field to map with sub-query
from.fetch("name");
from.fetch("id");
CriteriaQuery<Object> select = criteriaQuery.select(from);

Subquery<PROJECT> subquery = criteriaQuery.subquery(PROJECT.class);
Root fromProject = subquery.from(PROJECT.class);
subquery.select(fromProject.get("requiredColumnName")); // field to map with main-query
subquery.where(criteriaBuilder.and(criteriaBuilder.equal("name",name_value),criteriaBuilder.equal("id",id_value)));

select.where(criteriaBuilder.in(path).value(subquery));

TypedQuery<Object> typedQuery = entityManager.createQuery(select);
List<Object> resultList = typedQuery.getResultList();

En outre, il doit absolument être modifié car j'ai essayé de le mapper en fonction de votre requête. Voici un lien http://www.ibm.com/developerworks/Java/library/j-typesafejpa/ qui explique bien le concept.

60
Nayan Wadekar

Résurrection tardive.

Votre requête semble très similaire à celle de la page 259 du livre Pro JPA 2: Maîtriser l’API de persistance Java , qui se lit comme suit en JPQL: 

SELECT e 
FROM Employee e 
WHERE e IN (SELECT emp
              FROM Project p JOIN p.employees emp 
             WHERE p.name = :project)

En utilisant la base de données EclipseLink + H2, je n’ai pas réussi à obtenir le fonctionnement correct de JPQL ni des critères correspondants. Pour ce problème particulier, j'ai constaté que si vous référencez directement l'id au lieu de laisser le fournisseur de persistance le déterminer, tout se passe comme prévu:

SELECT e 
FROM Employee e 
WHERE e.id IN (SELECT emp.id
                 FROM Project p JOIN p.employees emp 
                WHERE p.name = :project)

Enfin, afin de répondre à votre question, voici une requête de critère équivalente fortement typée qui fonctionne:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Employee> c = cb.createQuery(Employee.class);
Root<Employee> emp = c.from(Employee.class);

Subquery<Integer> sq = c.subquery(Integer.class);
Root<Project> project = sq.from(Project.class);
Join<Project, Employee> sqEmp = project.join(Project_.employees);

sq.select(sqEmp.get(Employee_.id)).where(
        cb.equal(project.get(Project_.name), 
        cb.parameter(String.class, "project")));

c.select(emp).where(
        cb.in(emp.get(Employee_.id)).value(sq));

TypedQuery<Employee> q = em.createQuery(c);
q.setParameter("project", projectName); // projectName is a String
List<Employee> employees = q.getResultList();
49
Anthony Accioly
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Employee> criteriaQuery = criteriaBuilder.createQuery(Employee.class);
Root<Employee> empleoyeeRoot = criteriaQuery.from(Employee.class);

Subquery<Project> projectSubquery = criteriaQuery.subquery(Project.class);
Root<Project> projectRoot = projectSubquery.from(Project.class);
projectSubquery.select(projectRoot);

Expression<String> stringExpression = empleoyeeRoot.get(Employee_.ID);
Predicate predicateIn = stringExpression.in(projectSubquery);

criteriaQuery.select(criteriaBuilder.count(empleoyeeRoot)).where(predicateIn);
0
maniac787