2010-02-26 34 views
12

Tengo una clase Persona que tiene una colección de alias String que representa los nombres adicionales a los que esa persona puede acceder. Por ejemplo, Clark Kent puede tener alias "Superman" y "Man of Steel". Dwight Howard también tiene un alias de "Superman".¿Es posible obtener el alias SQL de una tabla de unión para una Hibernate sqlRestriction?

@Entity 
class Person { 

    @CollectionOfElements(fetch=FetchType.EAGER) 
    Set<String> aliases = new TreeSet<String>(); 

Hibernate crea dos tablas en mi base de datos, Person y Person_aliases. Person_aliases es una tabla de unión con las columnas Person_id y element. Digamos Person_aliases tiene los siguientes datos

-------------------------------- 
| Person_id  | element  | 
-------------------------------- 
| Clark Kent | Superman  | 
| Clark Kent | Man of Steel | 
| Dwight Howard | Superman  | 
| Bruce Wayne | Batman  | 
-------------------------------- 

Quiero hacer una consulta Criterios de hibernación para todas las personas que van por el alias de "Superman".

Por razones demasiado largas para enumerarlas aquí, realmente me gustaría hacer de esto una consulta de Criteria, no una consulta HQL (a menos que sea posible agregar una restricción HQL en un objeto Criteria, en cuyo caso soy todo oídos) o una consulta SQL sin formato. Dado que de acuerdo con How do I query for objects with a value in a String collection using Hibernate Criteria? es imposible hacer referencia a los elementos de las colecciones de valores usando el API de Criteria, pensé que recurriría a agregar una SqlRestriction en mi objeto de criterios.

Criteria crit = session.createCriteria(Person.class); 
crit.add(Restrictions.sqlRestriction("XXXXX.element='superman'"); 

con la esperanza de que Hibernate crear una instrucción SQL como

select * 
from 
    Person this_ 
left outer join 
    Person_aliases aliases2_ 
     on this_.id=aliases2_.Person_id 
where 
    XXXXX.element='superman' 

Sin embargo, tengo que rellenar el XXXXX con el alias de tabla para la tabla Person_aliases en la consulta SQL, que en este caso sería 'aliases2_'. Noté que si necesitaba la referencia al alias de la tabla Person podría usar {alias}. Pero esto no funcionará porque Persona es la tabla principal para este Criterio, no Person_aliases.

¿Qué debo completar para el XXXXX? Si no hay un token de substición agradable como {alias}, ¿hay alguna forma de que pueda hibernar para decirme cuál será ese alias? Noté un método llamado generateAlias ​​() org.hibernate.util.StringHelper class. ¿Esto me ayudaría a predecir cuál sería el alias?

Realmente me gustaría evitar la codificación difícil 'aliases2_'.

¡Gracias por su tiempo!

Respuesta

4

Parece que la API de Criteria no permite consultar colecciones de elementos, consulte HHH-869 (que aún está abierto). Por lo tanto, pruebe la solución alternativa sugerida (no lo hice) o cambie a HQL. La siguiente consulta HQL funcionaría:

from Person p where :alias in elements(p.aliases) 
+1

Gracias por su respuesta: en realidad supe la desafortunada limitación de API de Criteria gracias a una pregunta previa sobre desbordamiento de pila, pero tenía esperanzas de que el uso de un fragmento de SQL nativo mediante sqlRestriction proporcionara un 'truco' que nos permitiera seguir utilizando Criteria. Pero no puedo encontrar una manera de obtener una referencia al nombre de la tabla de unión. Si no hay forma, recurriré a HQL como sugirió. Sin embargo, hay razones que no quiero, que tomarían más espacio del que tengo aquí para explicar (esta pregunta es una representación simplificada de nuestra consulta real) –

+0

@Jason Tal vez es posible, pero * I * no veo cómo sin codificando el nombre de la tabla. –

2

¿Le ayudó this link?Se aconseja a:

List persons = sess.createCriteria(Person.class) 
     .createCriteria("company") 
     .add(Restrictions.sqlRestriction("companyName || name like (?)", "%Fritz%", Hibernate.STRING)) 
     .list(); 
+0

Llamar al método createCriteria() para el objeto join en la clase principal resolvió mi problema. Agregué todos los campos de unión en los criterios secundarios de la clase de objeto de unión y mi problema fue resuelto. –

-4
public class Products { 
private Brands brand; 
... 
} 
public class Brands { 
private long id; 
... 
} 
... 

DetachedCriteria dc=DetachedCriteria.forClass(Products.class, "prod"); 

dc.add(Restrictions.ge("prod.brand.id", Long.parseLong("12345"))); 
8

como xmedeko alude a, cuando se quiere hacer:

crit.add(Restrictions.sqlRestriction(
    "{alias}.joinedEntity.property='something'")); 

que necesitan en lugar de hacer:

crit.createCriteria("joinedEntity").add(Restrictions.sqlRestriction(
    "{alias}.property='something'")); 

Esto ha resuelto problemas similares para mí sin ir a HQL

+0

¿Se supone que debo usar la cadena real '{alias}' o reemplazarla con el nombre del objeto como 'accountUser'? – NobleUplift

+1

use la cadena {alias} es una palabra clave hibernate y desafortunadamente es la única forma de usar alias en el mundo de los criterios. – pstanton

+0

Esto funciona bien, pero no puedo encontrar un documento relacionado con esto. ¿Conoces uno? –

1

La pregunta es en realidad bastante viejo, pero ya que me encontré con el mismo problema hoy y sin La respuesta satisfizo mis necesidades, se me ocurrió la siguiente solución, basada en el comment de Brett Meyer en HHH-6353, que estos problemas no se solucionarán.

Básicamente, extendí la clase SQLCriterion para poder manejar más que el alias de la tabla base. Por razones de conveniencia, escribí una pequeña clase de contenedor que vincula el alias dado por el usuario con la instancia de subcriterios correspondiente para poder reemplazar el alias dado por el usuario con el hibernate de alias creado para los subcriterios.

Este es el código de la clase MultipleAliasSQLCriterion

public class MultipleAliasSQLCriterion extends SQLCriterion 
{ 
    /** 
    * Convenience container class to pack the info necessary to replace the alias  generated at construction time 
    * with the alias generated by hibernate 
    */ 
    public static final class SubCriteriaAliasContainer 
    { 
     /** The alias assigned at construction time */ 
     private String alias; 

     /** The criteria constructed with the specified alias */ 
     private Criteria subCriteria; 

     /** 
     * @param aAlias 
     *   - the alias assigned by criteria construction time 
     * @param aSubCriteria 
     *   - the criteria 
     */ 
     public SubCriteriaAliasContainer(final String aAlias, final Criteria aSubCriteria) 
     { 
      this.alias = aAlias; 
      this.subCriteria = aSubCriteria; 
     } 

     /** 
     * @return String - the alias 
     */ 
     public String getAlias() 
     { 
      return this.alias; 
     } 

     /** 
     * @return Criteria - the criteria 
     */ 
     public Criteria getSubCriteria() 
     { 
      return this.subCriteria; 
     } 
    } 

    private final SubCriteriaAliasContainer[] subCriteriaAliases; 

    /** 
    * This method constructs a new native SQL restriction with support for multiple aliases 
    * 
    * @param sql 
    *   - the native SQL restriction 
    * @param aSubCriteriaAliases 
    *   - the aliases 
    */ 
    public MultipleAliasSQLCriterion(final String sql, final SubCriteriaAliasContainer... aSubCriteriaAliases) 
    { 
     super(sql, ArrayHelper.EMPTY_OBJECT_ARRAY, ArrayHelper.EMPTY_TYPE_ARRAY); 

     this.subCriteriaAliases = aSubCriteriaAliases; 
    } 

    @Override 
    public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException 
    { 
     // First replace the alias of the base table {alias} 
     String sql = super.toSqlString(criteria, criteriaQuery); 

     if (!ArrayUtils.isEmpty(this.subCriteriaAliases)) 
     { 
      for (final SubCriteriaAliasContainer subCriteriaAlias : this.subCriteriaAliases) 
      { 
       sql = StringHelper.replace(sql, subCriteriaAlias.getAlias(), criteriaQuery.getSQLAlias(subCriteriaAlias.getSubCriteria())); 
      } 
     } 

     return sql; 
    } 
} 

lo uso como esto

final String sqlRestriction = "..."; 
final String bankAccountAlias = "ba"; 
final Criteria bankAccountCriteria = customerCriteria.createCriteria("bankAccount", bankAccountAlias); 

SubCriteriaAliasContainer bankAccountSubAliasCon = new SubCriteriaAliasContainer(bankAccountAlias, bankAccountCriteria);   

customerCriteria.add(new MultipleAliasSQLCriterion(sqlRestriction, bankAccountSubAliasCon)); 

pero no hay necesidad de especificar el alias en la creación de criterios - también puede especificar que en la restricción de SQL y pasarlo a un contenedor.

final String sqlRestriction = "... VALUES(ba.status_date), (ba.account_number) ..."; 
final Criteria bankAccountCriteria = customerCriteria.createCriteria("bankAccount"); 

SubCriteriaAliasContainer bankAccountSubAliasCon = new SubCriteriaAliasContainer("ba", bankAccountCriteria);   

customerCriteria.add(new MultipleAliasSQLCriterion(sqlRestriction, bankAccountSubAliasCon)); 
+0

¡Gracias, esto fue útil! – Ben

+0

Me pregunto si no hay dos errores ortográficos en su ejemplo. ¿No se supone que los objetos bankAccountSubAliasCon se pasan al constructor MultipleAliasSQLCriterion? – Stephane

+0

Así es como utilicé su utilidad: 'String hibernatePersonAlias ​​=" p "; Criterios personCriteria = mainCriteria.createCriteria ("persona", hibernatePersonAlias); Cadena personSalarySqlCriterion = hibernatePersonAlias ​​+ ".salary> 1500"; MultipleAliasSQLCriterion.SubCriteriaAliasContainer personAliasContainer = new MultipleAliasSQLCriterion.SubCriteriaAliasContainer (hibernatePersonAlias, personCriteria); mainCriteria.add (nuevo MultipleAliasSQLCriterion (personSalarySqlCriterion, personAliasContainer)); ' – Stephane

3

intenta crear otros criterios como

Criteria crit = session.createCriteria(Person.class, "person"); 
Criteria subC = crit.createCriteria("Person_aliases", "Person_aliases"); 
subC.add(Restrictions.sqlRestriction("{alias}.element='superman'"); 
0

org.hibernate.criterion.CriteriaQuery tiene un método getColumnsUsingProjection que le da el nombre de columna alias.

Puede implementar su propio Criterion, usando org.hibernate.criterion.PropertyExpression como ejemplo.

Cuestiones relacionadas