HQL to get elements that possess all items in a set - java

Currently, I have an HQL query that returns all Members who possess ANY Award from a set of specified Awards:
from Member m left join m.awards as a where a.name in ("Trophy","Ribbon");
What I now need is HQL that will return all Members who possess ALL Awards specified in the set of Awards.
So, assuming this data:
Joe has Trophy, Medal
Sue has Trophy, Ribbon
Tom has Trophy, Ribbon, Medal
The query above would return Joe, Sue, and Tom because all three possess at least one of Trophy or Ribbon. But I need to return only Sue and Tom, because they are the only ones who possess all of the specified awards (Trophy and Ribbon).
Here's the class structure (simplified):
class Member {
private String name;
private Set<Award> awards;
}
class Award {
private String name;
}

select m from Member m left join m.awards as a where a.name in ("Trophy","Ribbon") group by m having count(a)=2

Just repeating myself...
The code to get the members that have EXACTLY the given collection of awards:
from Member m
where not exists (
from Award a where a.name in {"Trophy", "Ribbon"}
and a not in(
select * from Award a2 where a2.owner = m
)
) and not exists (
from Award a3 where a3.owner = m and a3 not in {"Trophy", "Ribbon"}
)

You can force distinct results by adding a DISTINCT_ROOT_ENTITY result transformer to the query call, IE:
getSession().createQuery(hql).setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
I'm having a similar problem, but what I need to do is (following your example) to select all the members, who posess ALL of the awards and no more. So in your example the only correct result would be Sue. Any ideas?

Related

Function wouldn't increment by 1

I have a project in JavaEE, my task is to affect a professor to a form( a student placement form), I have an attribute called 'nb_sup' which is the number of student supervised by a selected professor, this number is incremented by one each time a professor is affected to a student. The problem is that my function sets the 'nub_sup' to one no matter how many times I affect the same professor.
Here is my code
public void SetNbSupPrfessor(Professor prof)
{
Query query = em.createQuery("update Professor u set nb_sup=:nb_sup WHERE u.id =:id");
query.setParameter("nb_sup", (1+ prof.getNb_sup()));
query.setParameter("id",prof.getId() );
query.executeUpdate();
}
It's safe to say that the professor inherits from the entity 'User' ,the 'nb_sup' is of type int and the 'id' is auto-increment.
I edited my code so this one worked
Professor prof = em.createQuery("select p from Professor p where p.id = :id",Professor.class)
.setParameter("id",id)
.getSingleResult();
prof.setNb_sup(prof.getNb_sup()+1);
em.merge(prof);

Hibernate criteria, distinct association property

Say I have at least these two entities:
class Person {
String firstname, lastname;
Address address;
ManyOtherPropertiesAndEntities ...;
}
class Address {
String street;
Country country;
}
Now, I would like to query the Person table and ONLY Persons that live on different streets.
That is, ignore all Persons that live on same street, and return only one of these Person, any one.
How can I perform such a query?
Is that possibly using Criteria?
Criteria criteria = session.createCriteria(Person.class, "person");
Criteria addressCriteria = criteria.createCriteria("address")
criteria.setProjection(
Projections.distinct(
Projections.projectionList().add(Projections.property("address.street"))
)
);
This doesnt really work.
I've also tried to do:
projectionList.add( Projections.sqlProjection("DISTINCT ON ( address.street ), ... ", columns.toArray(new String[columns.size()]), types.toArray(new Type[types.size()])));
But also fruitless.
>>>>>>>>>>>>>>>>>>>>>>>EDIT<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
I was able to get this to run, and generate an SQL query that actually returns results in a pure sql mode, but seems to return zero in hibernate:
List<String> columns = Lists.lst();
List<Type> types = Lists.lst();
bondCriteria.setProjection(
Projections.sqlProjection ("DISTINCT ON ( tvmux2_.polarization ) * "
, columns.toArray (new String[columns.size()])
, types.toArray (new Type[types.size()])
)
// Projections.projectionList().add(Projections.distinct(Projections.property("polarization")))
);
ResultTransformer resultTransformer = new ResultTransformer() {
#Override
public List transformList( List collection ) {
return null;
}
#Override
public Object transformTuple( Object[] tuple, String[] aliases ) {
return null;
}
};
bondCriteria.setResultTransformer(resultTransformer);
* ITS 2017 and SO still hasn't included a proper editor to easily be able to format code so that indentation and copy and paste is not a complete hell. Feel free to scroll horizontally. *
THis generates teh following query basically
select DISTINCT ON ( tvmux2_.polarization ) * from TvChannelBond this_ inner join TvChannel tvchannel1_ on this_.channel=tvchannel1_.id inner join TvMux tvmux2_ on this_.mux=tvmux2_.id where this_.enabled=true order by tvmux2_.polarization asc limit 100
which does return results in a non hibernate mode.
However, since sqlProjection method requires the supplementation of 3 params, I am not sure what to add to the second and third params. Types can not be other than predefined Hibernate types just as DOUBLE, STRING and so on.
When debugging into the resultTransformer, it gets into transformTuple with zero length tuple[] and aliases[].
Might have to do with the sqlProjection zero length types and columns lists.
In SQL, you could do it like this:
SELECT p.*
FROM Address a
INNER JOIN Person p ON ...
GROUP BY a.Street
HAVING p.id = MIN(p.id)
This statement selects for every distinct Street from Address the Person with the minimum id value. Instead of MIN(p.id) you can of course use any other field and aggregate function which will match exactly one person per street; MAX(p.id) will work, MIN(p.lastname) won't if there can be more than one "Smith" in a street.
Can you transform the above SQL to your Criteria query?

Find entities that have two specific entries in a collection

This has turned out to be really difficult to google for, so I'll try my luck here.
I have two classes like this:
#Entity
public class Cat {
#Id
private Integer catId;
private String name;
private String color;
#OneToMany
private List<Kitten> kittens;
}
#Entity
public class Kitten {
#Id
private Integer kittenId;
private String name;
private String color;
#ManyToOne
private Cat parent;
}
And I'm looking to find all grey Cat that has a Kitten with a name starting with P and that are white and a separate kitten that has a name starting with Q and is black. So far I've got this:
Criteria criteria = session.createCriteria(Cat.class);
criteria.add(Restriction.eq("color", "grey"));
criteria.createAlias("kittens", "k");
criteria.add(Restriction.and(
Restriction.eq("k.color", "white"),
Restriction.ilike("k.name", "P%")
));
criteria.add(Restriction.and(
Restriction.eq("k.color", "black"),
Restriction.ilike("k.name", "Q%")
));
List<Cat> results = criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list();
Now this doesn't work and gives me no hits at all even though such a Cat exists. Removing the very last restriction set works just fine. I imagine I need a different approach than just adding another restriction for the second Kitten but I haven't managed to figure out what it is I want to do.
It's also important that I don't match a Cat with four kittens that have one of these properties each.
The reason you fail to fetch after adding the second restrictions is that you tell criteria to find a kitten with all of those restriction: black and white - which of course doesnt exist unless it is shrodinger's cat.
You can accomplish this by joining cat with kittens twice: create two aliases for kittens: k1, k2 and restrict one by the first kitten and the second by the second kitten.
New answer based on comments:
In SQL I think something like this would work. I don't know Hibernate, but you can use this as starting point to convert the concept into code.
Select
C.name, count(distinct K.color)
From Cat C, Kitten K
Where
C.color = 'grey'
and C.id = K.catId
and (
(K.color = 'white' and K.name like 'P%')
or
(K.color = 'black' and K.name like 'Q%')
)
group by C.name
having count(distinct K.color) = 2
This first joins the eligible Cats on the eligible Kittens. By itself that does not solve the problem (as evidenced by the Old Content below and the comment chain), but then if you add a grouping to count the number of distinct kitten colors that were found per cat, if it equals two then it means the cat has one of each kitten.
See this SQLFiddle which I used to test it out: http://sqlfiddle.com/#!9/ee93f5/1/0
Old content (retained so that comments make sense):
I don't know hibernate but I think the SQL should look like:
Select <your stuff>
From Cat C, Kitten K
Where
C.color = 'grey'
and C.id = K.catId (I guess Hibernate provides these keys automatically?)
and (
(K.color = 'white' and K.name like 'P%')
or
(K.color = 'black' and K.name like 'Q%')
)
Basically, the two kitten constraints must be part of an OR clause which is added to overall constraint as an AND clause.
In your example, if I'm understanding the Hibernate code correctly, all of the constraints are added as AND clauses, so the result set will be empty because any Kitten row your Cat joins on cannot be black and white at the same time.

hibernate criteria with exists clause

I cannot find a solution to a problem that seems to be easy. Say there are 2 entity classes:
class A {
Set<B> bs;
}
class B {
String text;
}
How to create a criteria query that returns all A's that contains at least one B entity which fulfills a given condition (like b.text = 'condition')?
I think this link can be useful:
http://mikedesjardins.net/2008/09/22/hibernate-criteria-subqueries-exists/
It contains the following example about how create n exists criteria:
"What you’re really trying to do is to obtain all Pizza Orders where an associated small pizza exists. In other words, the SQL query that you’re trying to emulate is
SELECT *
FROM PIZZA_ORDER
WHERE EXISTS (SELECT 1
FROM PIZZA
WHERE PIZZA.pizza_size_id = 1
AND PIZZA.pizza_order_id = PIZZA_ORDER.pizza_order_id)
The way that you do that is by using an “exists” Subquery, like this:
Criteria criteria = Criteria.forClass(PizzaOrder.class,"pizzaOrder");
DetachedCriteria sizeCriteria = DetachedCriteria.forClass(Pizza.class,"pizza");
sizeCriteria.add("pizza_size_id",1);
sizeCriteria.add(Property.forName("pizza.pizza_order_id").eqProperty("pizzaOrder.pizza_order_id"));
criteria.add(Subqueries.exists(sizeCriteria.setProjection(Projections.property("pizza.id"))));
List<pizzaOrder> ordersWithOneSmallPizza = criteria.list();
And voila, the result will contain two PizzaOrders!"

Hibernate recursive query

My desired query is to get a list of Course objects that belong to a Category. My objects are as follows:
public class Course{
String name;
List<Category> categories;
}
public class Category{
String name;
Category parent;
}
Since the categories reference each other, they can have an infinite depth:
A
A.A
A.A.A
A.A.B
A.B
A.B.A
B
B.A
B.B
C
How can I query for courses within the category "A.A", and return all Courses associated with A.A, A.A.A, and A.A.B?
If you are willing to use native SQL and your database supports recursive common table expressions (basically all major DBMS except MySQL) it's pretty easy:
WITH RECURSIVE course_tree (name) AS (
SELECT name
FROM course
WHERE name = 'A.A'
UNION ALL
SELECT name
FROM course
WHERE parent_id = course_tree.id
)
SELECT *
FROM course_tree
Because you do not know how deep is the tree, you can use some kind of pattern as follows
select distinct
c
from
Course c
left join fetch
c.categories c
where
c.name like 'A.A%'

Categories

Resources