Hibernate: Query By Example equivalent of association Criteria Query - java

I'd like to search my data source for all object instances based on the values of an object related by association. The data model can be simplified to: object of type A holds a list of objects of type B. The goal is to find all instances of A where A contains a B such that B has a property value of X.
I can already successfully achieve this using Criteria queries as follows:
List<A> results = session.createCriteria(A.class)
.createCriteria("listOfBs")
.add(Restrictions.eq("propertyInB", x))
.list();
This is a simplification, and multiple properties of B will apply - the search functionality is necessary for a user populated filter.
I would like to replace this approach with query by example - where I'd simply create an object graph with the desirable parameters. My attempt in following the Hibernate docs failed, and is described in this question.
I thought that it might be helpful to demonstrate what I'm trying to achieve in a manner that works, and then seek equivalents - that's why I'm re-asking the question.
In short, my question is: How would you implement the above Criteria Query as a Query by Example in Hibernate? I'm using Hibernate 3.6.6.
Thanks!

Suppose you want to do something like :
Select a.* , b*
from a join b on a.id = b.id
where a.property1 = "wwww"
and a.property2="xxxx"
and b.property1="yyyy"
and b.property2="zzzz"
To implement the above query using Query by Example(QBE):
/***Initialize an instance of Class A with the properties that you want to match***/
A instanceA = new A();
instanceA.setProperty1("wwww");
instanceA.setProperty2("xxxx");
Example exampleA = Example.create(instanceA);
/***Do the same for the Class B**/
B instanceB = new B();
instanceB.setProperty1("yyyy");
instanceB.setProperty2("zzzz");
Example exampleB = Example.create(instanceB);
/**Create and execute the QBE***/
List<A> results = session.createCriteria(A.class)
.add(exampleA)
.createCriteria("b",CriteriaSpecification.LEFT_JOIN) // b is the property of Class A
.add(exampleB)
.list();
The result is already fetch-joined , which means the collection instance B in the A is already fully initialized.

Related

Hibernate native SQL query - how to get distinct root entities with eagerly initialized one-to-many association

I have two entities Dept and Emp (real case changed and minimized). There is 1:n association between them, i.e. properties Dept.empList and Emp.dept exist with respective annotations. I want to get List<Dept> whose elements are distinct and have collection empList eagerly initialized, using native SQL query.
session.createSQLQuery("select {d.*}, {e.*} from dept d join emp e on d.id = e.dept_id")
.addEntity("d", Dept.class)
.addJoin("e", "d.empList")
//.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
.list();
This query returns List<Object[2]> with instances of Dept, Emp (in that order) in the array and field Dept.empList properly initialized. That's ok.
To get distinct Depts, I thought setting transformer DISTINCT_ROOT_ENTITY (uncommenting the line) would be enough. Unfortunately, the DistinctRootEntityResultTransformer is based on RootEntityResultTransformer which treats the last element in tuple as root entity (it's hardwired). Since the order of entities is determined by sequence of addEntity, addJoin calls, the transformer mistakenly treats Emp as root entity and returns list with all Emps of all Depts.
Is there any clean way how to make Hibernate recognized the Dept as root entity, even though it is not last in entity list?
Note 1: I tried to switch order to .addJoin("e", "d.empList").addEntity("d", Dept.class). Does not work since d.empList requires d defined. Fails on HibernateSystemException : Could not determine fetch owner somewhere in Hibernate internals (org.hibernate.loader.Loader).
Note 2: I tried to define order as .addEntity("e", Emp.class).addJoin("d", "e.dept"). This seemingly works but the association is actually filled only from the "many" side. Hence the collection Dept.empList is some uninitialized proxy until requested, which invokes explicit SQL query and thus does not utilize the join in my query.
Note 3: The custom transformer looking for hard-wired index works:
.setResultTransformer(new BasicTransformerAdapter() {
public Object transformTuple(Object[] tuple, String[] aliases) {
return tuple[0];
}
public List transformList(List list) {
return DistinctResultTransformer.INSTANCE.transformList( list );
}
})
though I'm hesitant to accept such easy task could have such complicated solution.
Hibernate version: 3.6.10 (I know - legacy project :-) though I looked into source code of latest version and seems the key points don't differ).
Finally found https://stackoverflow.com/a/17210746/653539 - make duplicate call of .addEntity to force root entity on the end of list:
.addEntity("d", Dept.class)
.addJoin("e", "d.empList")
.addEntity("d", Dept.class)
It's still workaround but cleaner than mine and - based on 36 upvotes - it seems as idiomatic solution.

Restrict JPA polymorphic query with Criteria API [duplicate]

I'm relativley new to relational databases and I have some problems concerning the creation of queries. First I want to explain the situation shortly. I have several entity classes. All of them extend AbstractEntity or EntityProperty. So entities can have properties and properties have owning entities, so there is a bidirectional relation.
Now let's say ConcreteEntity extends AbstractEntity and I want to create queries like this: Get all entities of type ConcreteEntity which has at least on property with a name contained in the given list propertyNames. Until now I have the following working criteria query:
CriteriaQuery<AbstractEntity> cq = cb.createQuery(AbstractEntity.class);
Root<EntityProperty> property = cq.from(EntityProperty.class);
Join<EntityProperty, AbstractEntity> entity = property.join(EntityProperty_.owningEntities);
cq.where(property.get(EntityProperty_.name).in((Object[]) propertyNames));
cq.select(entity);
But now I want only those entities of type ConcreteEntity. How could I achieve this?
In JPQL I wrote "SELECT entity FROM EntityProperty property JOIN property.owningEntities entity" and here I also have no idea how to write it in the way that only a specific type is returned...
Thanks for answers in advance!
EDIT: moved the second question to criteria query: indistinct result lists and removed distinct in the code (that didn't work)
I know this is an old question but just in case someone stumbles upon the same problem, here is how it can be solved.
You can easily filter by entity type like this:
Predicate p = cb.equal(entity.type(), cb.literal(ConcreteEntity.class));
where entity can be a Path (Root and Join included), cb is a CriteriaBuilder object. So in your case it would be something like this:
CriteriaQuery<AbstractEntity> cq = cb.createQuery(AbstractEntity.class);
Root<EntityProperty> property = cq.from(EntityProperty.class);
Join<EntityProperty, AbstractEntity> entity = property.join(EntityProperty_.owningEntities);
cq.where(cb.and(
property.get(EntityProperty_.name).in((Object[]) propertyNames),
cb.equal(entity.type(), cb.literal.ConcreteEntity.class)
));
cq.select(entity);
The only way I found until now was to create an enumeration with a value for each class The resulting criteria query is
CriteriaQuery<AbstractEntity> cq = cb.createQuery(AbstractEntity.class);
Root<EntityProperty> property = cq.from(EntityProperty.class);
SetJoin<EntityProperty, AbstractEntity> entity =
property.join(EntityProperty_.owningEntities);
cq.where(property.get(EntityProperty_.name).in((Object[]) propertyNames),
entity.get(AbstractEntity_.entityType).in(suitableSubTypes));
cq.select(entity);
List<AbstractEntity> resultList = em.createQuery(cq).getResultList();
As you can see, every entity now has the attribute entityType. I also have to create the collection suitableSubTypes every time. Another problem is that the returned type is List<AbstractEntity>. What I wanted was a method signature like
public static <T extends AbstractEntity> List<T>
getEntities(Class<T> entityClass, String... propertyNames)
but for now I have
public static List<AbstractEntity>
getEntities(Collection<AbstractEntityType> suitableSubTypes,
String... propertyNames)
So I still hope there exists a better solution...

Can I query in hibernate via DTO/Entity not by single fields

I have a question regarding querying in hibernate. If there is a way to search without specifying an where cause explicitly?
So what I mean is: let's say I have a search form with 10 columns that are bound to my dto fields. So the user can fill some of them and left the rest as nulls. And now I would like to search only by fields that are specified (filled) and left the nulls behind (they doesn't matter).
So the query would be like this:
select e
from entity e
where e.entity = e.searchedCriteriaEntityGivenInDTO
Or a better example via jpg: I'd like to have all sample entities without specifying "where number, where name, where firstanme", but over my dto by "where dtoFields". As mentioned the nulls should be ignored.
Thanks a lot in advance.
[EDIT]: Thanks to Dragon I have a great solution how to do it, but I have one more question: What about I have 2 row's, I can search? My query should look like:
select e
from example e,
where (e.entity = example) OR (e.entity = example2);
I tried to put the
session.createCriteria(MyEntity.class).add(Example.create(myEntityExample))
into an OR-Predicate but it seems it does not work.
Any suggestions?
No, you can't do it with DTOs, but you can use a prototype (example) entity instance for it:
MyEntity myEntityExample = new MyEntity();
myEntityExample.setNumber(12);
myEntityExample.setName("AA");
myEntityExample.setFirstName("BB");
List<MyEntity> results = session.createCriteria(MyEntity.class)
.add(Example.create(myEntityExample))
.list();

criteria query: indistinct result lists

I have 2 little problems. Let's say I have an entity class called MyEntity. This class has two kinds of properties. Therefore I have two different class Property1 and Property2 which are also entity classes. There are bidirectional relations betweens MyEntity and the property classes, especially Property1 has an attribute List<MyEntity> owningEntities and Property2 has the attribute MyEntity owningEntity. Now I have the following query:
SELECT e FROM Property1 p JOIN p.owningEntities e WHERE p.name IN :propertyNames
This query returns all entities that has a property of the first type with at least one of the given names. But in general the returned list is indistinct. The problem is that the SELECT DISTINCT downs't work here, at least with criteria API - I didn't try it with jpql. The code for the criteria query looks as follows:
CriteriaQuery<MyEntity> cq = cb.createQuery(MyEntity.class);
Root<Property1> property = cq.from(Property1.class);
SetJoin<Property1, MyEntity> entity =
property.join(Property1_.owningEntities);
cq.where(property.get(Property1_.name).in((Object[]) propertyNames));
cq.select(entity);
// cq.select(entity).distinct(true); --> runtime error
List<MyEntity> resultList = em.createQuery(cq).getResultList();
Is there any way to get a distict result list? I can simply convert the list into a set, but it seems ineffective to extract the same results multiple times.
The next problem is, of course I want all entities that has a property of the any type with at least one of the given names. Until now I have two queries and afterwards I merge the results. But again I extract many results multiple times. So, is there any way to combine the following two queries in an effective way?
SELECT e FROM Property1 p JOIN p.owningEntities e WHERE p.name IN :propertyNames
SELECT e FROM Property2 p JOIN p.owningEntity e WHERE p.name IN :propertyNames
Thanks in advance!

how do I group by a property of a related entity in a criteria?

I'm writing a criteria that should group the results by a property of a related entity. I've tried using an alias, tried using the property path itself, but so far I get nothing.
say my classes are (rough sketch):
class A{
#ManyToOne(...)
B b;
}
class B{
#OneToOne(...)
C c;
}
class C{
String s;
}
and I want a criteria that returns the amount of A's and B's for each unique string s in C.
my initial attempt was:
session.createCriteria(A.class)
.setProjection(Projections.projectionList()
.add(Projections.groupProperty("b.c.s"), "string")
.add(Projections.countDistinct("b"), "b's")
.add(Projections.rowCount(), "a's"))
This didn't help much as b.c.s is not a property of A.
then I tried
session.createCriteria(A.class)
.createAlias("b.c", "al")
.setProjection(Projections.projectionList()
.add(Projections.groupProperty("al.s"), "string")
.add(Projections.countDistinct("b"), "b's")
.add(Projections.rowCount(), "a's"))
this actually was translated to SQL, but did not get far, as it did not include any joins in the query.
seems I am doing something wrong here.
Is it possible to get an efficient query of this kind using the criteria API?
maybe something like this:
sess.createCriteria(A.class)
.createCriteria("b")
.createCriteria("c")
.setProjection(Projections.projectionList()
.add(Projections.groupProperty("s"), "string")
.list()
.size()
i think you must look in double createCriteria()

Categories

Resources