2012-04-15 17 views
5

? Me gustaría crear una consulta utilizando el JPA CriteriaBuilder y me gustaría agregar una cláusula ORDER BY. Este es mi entidad:¿Es posible ordenar mediante una clave compuesta con JPA y CriteriaBuilder

@Entity 
@Table(name = "brands") 
public class Brand implements Serializable { 

    public enum OwnModeType { 
     OWNER, LICENCED 
    } 

    @EmbeddedId 
    private IdBrand id; 
    private String code; 
    //bunch of other properties 
} 

clase Embedded es:

@Embeddable 
public class IdBrand implements Serializable { 

    @ManyToOne 
    private Edition edition; 
    private String name; 
} 

Y la forma en que estoy construyendo mi consulta es la siguiente:

CriteriaBuilder cb = em.getCriteriaBuilder(); 
CriteriaQuery<Brand> q = cb.createQuery(Brand.class).distinct(true); 
Root<Brand> root = q.from(Brand.class); 
if (f != null) { 
    f.addCriteria(cb, q, root); 
    f.addOrder(cb, q, root, sortCol, ascending); 
} 
return em.createQuery(q).getResultList(); 

Y aquí se llaman las funciones:

public void addCriteria(CriteriaBuilder cb, CriteriaQuery<?> q, Root<Brand> r) { 
} 

public void addOrder(CriteriaBuilder cb, CriteriaQuery<?> q, Root<Brand> r, String sortCol, boolean ascending) { 
    if (ascending) { 
     q.orderBy(cb.asc(r.get(sortCol))); 
    } else { 
     q.orderBy(cb.desc(r.get(sortCol))); 
    } 
} 

Si trato de establecer sortCol a algo así como "id.name" me sale el siguiente error:

javax.ejb.EJBException: java.lang.IllegalArgumentException: Unable to resolve attribute [id.name] against path

alguna idea de cómo podría lograr eso? He intentado buscar en línea, pero no pude encontrar una pista sobre esto ... También sería genial si pudiera hacer una ORDER BY similares cuando tengo una relación @ManyToOne (por ejemplo, "id.edition.number")

Respuesta

5

El problema es su comprensión del uso de ruta de la APP. JPA Manual dice:

Path expression whose type is a persistable user class can be extended further by reusing the dot (.) operator. For example, c.capital.name is a nested path expression that continues from the Capital entity object to its name field. A path expression can be extended further only if its type is also a user defined persistable class. The dot (.) operator cannot be applied to collections, maps and values of simple types (number, boolean, string, date).

En su caso ha utilizado id.edition.number, donde IdBrand (id) no es la clase de usuario con persistencia.

Para la solución es expresión de ruta de construcción de esta manera: root.get("id").get("edition.number");


Para evitar estos problemas, se puede escribir un constructor de camino, que basa en javax.persistence.metamodel.Attribute.PersistentAttributeType decidiría la forma en ruta se construye. Para elementos básicos: use .get(), para elementos de colección use .join().

Para que entienda su error, necesita leer más información sobre las rutas JPA en general. Here is a good manual on JPA paths.

+0

Sí, eso es lo que dice en la otra respuesta a la que me he vinculado. Encuentro esto realmente molesto y no entiendo por qué no podría funcionar automágicamente. Que usted sepa, ¿tendría el mismo problema si utilizo IdClass en lugar de EmbeddedId? – Kjir

+0

Bueno, si miramos [Jerarquía de tipos de JPA] (http://www.objectdb.com/java/jpa/persistence/metamodel#Type_Interface_Hierarchy_), podemos ver que EntityType extiende IdentifiableType, por lo que quizás @Id (@IdClass) es tipo identificable, por lo que puede hacer eso. Deberías probarlo. Obtenga también PersistentType from attribute, luego puede definir qué puede hacer con él. – JMelnik

+0

Como digo, ** tal vez ** es posible, así que es mejor que revises PersistentType y lo proporciones aquí si puedes. – JMelnik

1

De hecho, me encontré la respuesta en otro tema similar: JPA - Criteria API and EmbeddedId

Así es como me fijo mi código:

public void addOrder(CriteriaBuilder cb, CriteriaQuery<?> q, Root<Brand> r, String sortCol, boolean ascending) { 
    Path<?> p = r; 
    String []sortCols = sortCol.split("\\."); // This is a regexp, hence the escape backslash 
    for(String sc: sortCols) { 
     p = p.get(sc); 
    } 
    if (ascending) { 
     q.orderBy(cb.asc(p)); 
    } else { 
     q.orderBy(cb.desc(p)); 
    } 
} 
Cuestiones relacionadas