2010-06-08 16 views
11

tengo un modelo de datos que se ve algo como esto:consulta de Hibernate por varios elementos en una colección

public class Item { 
    private List<ItemAttribute> attributes; 
    // other stuff 
} 

public class ItemAttribute { 
    private String name; 
    private String value; 
} 

(esto obviamente simplifica la distancia de un montón de cosas extrañas)

Lo que yo quiero hacer es crear una consulta para pedir todos los artículos con uno o más atributos particulares, idealmente unidos con ANDs y OR arbitrarios. En este momento lo estoy manteniendo simple y estoy tratando de implementar el caso AND. En pseudo-SQL (o pseudo-HQL si lo haría), sería algo así como:

select all items 
where attributes contains(ItemAttribute(name="foo1", value="bar1")) 
AND attributes contains(ItemAttribute(name="foo2", value="bar2")) 

Los ejemplos en la documentación de Hibernate no parecía hacer frente a este caso en particular, pero parece bastante uno común. El caso disyunción también sería útil, sobre todo por lo que podría especificar una lista de valores posibles, es decir

where attributes contains(ItemAttribute(name="foo", value="bar1")) 
OR attributes contains(ItemAttribute(name="foo", value="bar2")) 
-- etc. 

He aquí un ejemplo que funciona bien para un solo atributo:

return getSession().createCriteria(Item.class) 
     .createAlias("itemAttributes", "ia") 
     .add(Restrictions.conjunction() 
      .add(Restrictions.eq("ia.name", "foo")) 
      .add(Restrictions.eq("ia.attributeValue", "bar"))) 
     .list(); 

aprendizaje de cómo hacer esto Haría un largo camino para ampliar mi comprensión del potencial de Hibernate. :)

+0

La respuesta aquí funciona: http://stackoverflow.com/questions/4834372/hibernate-criteria-on-collection-values ​​ – Taylor

Respuesta

0
SELECT item FROM Item item JOIN item.attributes attr 
    WHERE attr IN (:attrList) GROUP BY item 

y luego en el código Java:

List<ItemAttribute> attrList = new ArrayList<ItemAttribute>(); 
attrList.add(..); // add as many attributes as needed 
...// create a Query with the above string 
query.setParameter("attrList", attrList); 
+0

Parece que aborda el caso OR (es decir, el elemento tiene al menos un ItemAttribute que especificamos en el attrList), pero ¿qué pasa con el caso AND? ? ¿Sería algo así como DONDE attr1 en item.attributes Y attr2 en item.attributes etc. ? ¿Eso tiene sentido para Hibernate? – aarestad

+0

Además, parece que estoy obteniendo un seguimiento de la pila cuando intento hacer lo anterior. El error raíz: Causado por: java.lang.IllegalArgumentException: No se puede establecer el campo java.lang.Integer com.mycompany.domain.ImmutableDomainEntity.id to java.util.ArrayList – aarestad

+0

Está recibiendo un error porque está intentando aliar un conjunto (ItemAttributes) a una sola entidad (attr). Solo puede usar la cláusula IN con una sola entidad. Una forma mejor (de trabajo) sería invertir la consulta: "SELECCIONE un elemento desde ItemAttribute a WHERE a.name IN (: attrlist)" - no se requiere un JOIN explícito si ha correlacionado las relaciones correctamente. –

0

Por qué no sería el siguiente trabajo?

return getSession().createCriteria(Item.class) 
    .createAlias("itemAttributes", "ia") 
    .add(Restrictions.or() 
     .add(Restrictions.conjunction() 
      .add(Restrictions.eq("ia.name", "foo1")) 
      .add(Restrictions.eq("ia.attributeValue", "bar1"))) 
     .add(Restrictions.conjunction() 
      .add(Restrictions.eq("ia.name", "foo2")) 
      .add(Restrictions.eq("ia.attributeValue", "bar2")))) 
    .list(); 

Eso sería (name=foo1 && attributeValue=bar1) OR (name=foo2 && attributeValue=bar2)

+0

Esto podría funcionar para la disyunción, pero la versión de conjunción donde usaría Restrictions.and() no funcionó. – aarestad

+0

Sí, porque eso sería decir 'WHERE x = 1 AND x = 2' esencialmente, que no arrojará ningún resultado. No puedo pensar en una forma de hacer el caso de conjunción en SQL sin procesar. Puede que tenga que cargar el caso de disyunción y luego refinarlo utilizando la lógica de la aplicación. – Andy

0

no he probado, pero esta es la forma que debería tratar de resolver su problema si tendría que:

Map<String,String> map1 = new TreeMap<String,String>(); 
map1.put("ia.name","foo1"); 
map1.put("ia.value","bar1"); 
Map<String,String> map2 = new TreeMap<String,String>(); 
map2.put("ia.name","foo2"); 
map2.put("ia.value","bar2"); 
return getSession().createCriteria(Item.class) 
.createAlias("itemAttributes", "ia") 
.add(Restrictions.and() 
    .add(Restrictions.allEq(map1)) 
    .add(Restrictions.allEq(map2)) 
) 
.list(); 

Por favor, déjame saber si funcionó Creo que lo mismo debería funcionar con o() ...

1

¿Podría usar aliasing para hacer esto?

Criteria itemCriteria = session.createCriteria(Item.class); 
itemCriteria.createAlias("itemAttributes", "ia1") 
      .createAlias("itemAttributes", "ia2") 
      .add(Restrictions.eq("ia1.name", "foo1")) 
      .add(Restrictions.eq("ia1.attributeValue", "bar1"))) 
      .add(Restrictions.eq("ia2.name", "foo2")) 
      .add(Restrictions.eq("ia2.attributeValue", "bar2"))) 

No estoy seguro de cómo hibernate maneja la unión en la misma propiedad dos veces explícitamente así, ¿vale la pena intentarlo?

+1

Obtengo una org.hibernate.QueryException: ruta de asociación duplicada: itemAttributes – Taylor

0

Uso LEFT_OUTER_JOIN para evitar "donde x = 1 y x = 2" tipo de problema

CreateAlias ​​("ItemAttributes", "ia", JoinType.LEFT_OUTER_JOIN)

Cuestiones relacionadas