JPA lookups with arrays - java

I have an unidirectional relationship. Here i have Employee and Andress entities. In Employee entity i have the following code:
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "HOME_ADDRESS")
private Address homeAddress;
I have an array of Adress objects and want to write a lookup that would return an array of Customer objects mapped to those adresses.
select e from Employee e where e.homeAddress.id IN '?'
I don't know what to do with the '?' part. Is the only option to loop over the address array, add id's to a string and pass it as a parameter to the query above, or is there a way to pass the array to the query and expect the same result?

No, you don't pass that as a String, but as a collection of IDs. And your query is invalid. It should be:
String jpql = "select e from Employee e where e.homeAddress.id IN :addresses";
Set<Long> addressIds = Arrays.stream(addresses)
.map(Address::getId)
.collect(Collectors.toSet());
return em.createQuery(jpql, Employee.class)
.setParameter("addresses", addressIds)
.getResultList();
This uses Java 8 to transform the array of addresses into a set of IDs, but you can of course use a goold old for loop.

2 Solutions:
HQL
String hql="select e from Employee e where e.homeAddress.id IN (:addresses)";
Query query = getSession().createQuery(hql);
query.setParameterList("addresses", your_list_address_collection);
Criteria
Criteria criteria = session.createCriteria(Employee.class);
criteria.add(Restrictions.in("addresses", your_list_address_collection));

Related

Order By in JPQL query on a #ElementCollection annotated map

In my #Entity annotated Course class, I have the following #ElementCollection annotated map:
#ElementCollection
private Map<Student, Double> courseStudAndAvgStudGrade;
In the example above, Student is another #Entity annotated class and the value is the average grade from a Course for each Student. I'm trying to write a query in JPQL that would retrieve all the entries of this map and sort by entry value in descending order.
So far, I have the following:
TypedQuery<Tuple> query =
em.createQuery("SELECT KEY(map), VALUE(map) "
+ "FROM Course c JOIN c.courseStudAndAvgStudGrade map WHERE c.id = :id", Tuple.class);
This retrieves the values correctly in a Tuple for the desired Course, however adding ORDER BY VALUE(map) DESC to the JPQL query results in a java.sql.SQLException: Subquery returns more than 1 row.
Is it possible to do ORDER BY on a map in JPQL?
JPA/Hibernate does not require the VALUE qualifier i.e. it's actually just there for completeness. Try the following query:
em.createQuery("SELECT KEY(map), map FROM Course c JOIN c.courseStudAndAvgStudGrade map WHERE c.id = :id ORDER BY map DESC", Tuple.class);

HQL: How to query certain fields when one of those fields is a one-to-many List?

I am having difficulty writing a HQL query to select ONLY the caseid, title, and caseStatus fields from my Cases entity. The cases returned have to be distinct based on caseid. I do not want the name and userid fields to be included. I also do not want to use Lazy fetching for caseid, title, and caseStatus fields. Note that the caseStatus field is a one-to-many List. Below are the entities. The getters/setters are omitted to save space.
#Entity
#Table(name = "Cases")
public class Cases {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "caseid", nullable = false)
private Integer caseid;
private Integer userid;
private String name;
private String title;
#OrderBy("caseStatusId DESC")
#OneToMany(mappedBy = "cases", fetch = FetchType.EAGER)
private List<CaseStatus> caseStatus;
}
#Entity
#Table(name = "CaseStatus")
public class CaseStatus {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "caseStatusId", nullable = false)
private Integer caseStatusId;
private String info;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "caseid")
private Cases cases;
}
My goal is to retrieve a distinct List<Cases> or List<Object[]> of the Cases entity containing only caseid, title, and a List<CaseStatus>. The List<CaseStatus> will contain CaseStatus objects with all of its fields populated.
public List<Object[]> getCases(String title) {
TypedQuery<Object[]> q = em.createQuery("select distinct c.caseid, c.title, cs "
+ "FROM Cases c join c.caseStatus cs "
+ "where c.title like :title", Object[].class);
q.setParameter("title", "%" + title + "%");
List<Object[]> results = q.getResultList();
return results;
}
The above method is close, but not correct because rather than returning a List<CaseStatus> in one of the indexes, it is only returning a single CaseStatus entity.
For example, if my DB contains a single Case with a List<CaseStatus> having a size of n for example, the results will be similar to the example below:
Example of results I'm getting now. Not correct:
List<Object[]> index 0:
Contains an Object[] where:
Object[0] = {some caseid}
Object[1] = {some title}
Object[2] = {1st CaseStatus}
List<Object[]> index 1:
Contains an Object[] where:
Object[0] = {same caseid as the one found in index 0 above}
Object[1] = {same title as the one found in index 0 above}
Object[2] = {2nd CaseStatus}
...
List<Object[]> index n-1:
Contains an Object[] where:
Object[0] = {same caseid as all the previous}
Object[1] = {same title as all the previous}
Object[2] = {nth CaseStatus}
Example of results I hope to achieve:
List<Object[]> index 0:
Contains an Object[] where:
Object[0] = {unique caseid}
Object[1] = {some title}
Object[2] = List<CaseStatus> with size of n
Updated the question. Instead of name, title, and List<CaseStatus>, the fields I want to retrieve are caseid, title, and List<CaseStatus>. caseid is the primary key of Cases.
I found various threads Select Collections with HQL - hibernate forum and Select collections with HQL - stackoverflow. It's pretty much the problem I ran into. Looks like no one found a solution in these threads.
Hibernates a bit confused about the query; in HQL do your join like this (apologies, I've not been able to test before posting due to wonky computer, but you should get the idea)
select distinct c from Cases c left join fetch c.caseStatus cs where....
the "fetch" makes it eager. Note that this will return an array of type Cases. You where clauses look about right.
In fact HQL is fully object-oriented and uses your classes structure in the Query, so by writing c.caseStatus HQL expects that your Cases class has a caseStatus property, which is wrong because it's a collection.
If you take a look at Hibernate HQL documentation you can see that:
Compared with SQL, however, HQL is fully object-oriented and understands notions like inheritance, polymorphism and association.
I think what you need to do here is to change your query so it matches your classes structures:
Query q = em.createQuery("select distinct c.name, c.title, cs.caseStatus FROM Cases c left join c.caseStatus where "
+ "c.name like :name and "
+ "c.title like :title");
Correct syntax should be
TypedQuery<Object[]> q = em.createQuery("select c.name, c.title, cs FROM Cases c "
+ "join c.caseStatus cs where "
+ "c.name = :name and "
+ "c.title = :title", Object[].class);
Return type will be List<Object[]>, where in first index of Object[] is c.name, second is c.title and third is associated caseStatus entity. It is possible to query for multiple instances (rows).
We need JOIN because relationship between CaseStatus and Case is mapped via collection.
SELECT cs
FROM Case c JOIN c.cases cs;
Why don't you just use
Query q = em.createQuery("select distinct c from Cases c where "
+ "c.name like :name and "
+ "c.title like :title");
Just try this. This may be a naive approach but should be able to solve the problem. You may be getting more fields than you required but the return type would be list of Cases.

using enum list as parameter in HQL query

I have an entity called Band with a attribute List<Genres> genres, Genres is a ENUM with the following values: ALTERNATIVE_ROCK("Alternative Rock"), CLASSIC_ROCK("Classic Rock"), HARD_ROCK("Hard Rock"), HEAVY_METAL("Heavy Metal"),PROGRESSIVE_ROCK("Progressive Rock");
I'm trying to create a method that returns a List<Band> using an List<Genres> as parameter using HQL, something like:
public List<Band> listBandsPerGenres(List<Genres> genres);
But i'm receiving some errors with HQL queries that i'd tried?
Above some hql queries that i've tried...
Query q = getSession().createQuery("SELECT b FROM Band b JOIN FETCH b.genres g WHERE g IN (?)");
q.setParameter(0, genres);
return q.list();
returns an error saying that an ArrayList cannot be cast to Enum...
or...
"SELECT b FROM Band b JOIN FETCH b.genres g WHERE g.value IN (?)"
returns an error like : dereference scalar collection element ENUM
property genres mapping, entity Band...
#Basic(optional=false)
#Enumerated(EnumType.STRING)
#ElementCollection(targetClass=Genres.class)
#CollectionTable(name="banda_generos", joinColumns=#JoinColumn(name="id_banda", nullable=false))
private List<Genres> genres;
This works for Hibernate 4
Query q = s
.createQuery("SELECT b FROM Q27715453$Band b JOIN b.genres g WHERE g IN (:genres)");
q.setParameterList("genres", Arrays.asList(Genres.ROCK, Genres.CLASSIC));
System.out.println(Arrays.toString(q.list().toArray()));
Check that the method Query#setParameterList is used instead of Query#setParameter, also it's used g insted of g.value.
I don't think you can do that. According to JPA spec (section 4.6.9. page 185) lists aren't supported as left-side operand with IN expression.

Possible to perform a NOT IN for one-to-many relationship with Hibernate criteria?

I have a question about Hibernate and if it's possible to achieve the query using the Criteria API with the current data model structure I'm using. I have a data model with the following structure:
ParentClass contains a List:
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, targetEntity = org.test.ChildClass.class)
#JoinColumn(name = "child_uuid")
private List<ChildClass> childList = new ArrayList<ChildClass>();
The child class has one field:
private String testField
The query I'm attempting to write can be expressed as follows:
Return a list of all ParentClass objects that have the ABSENCE OF an object ChildClass that has a "testField" value of "1". In other words, there must not be an object in that list that has the testField value of 1.
Currently I have this query:
Criteria criteria = getSession().createCriteria(org.test.ParentClass);
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
criteria.createAlias("childList", "c");
SimpleExpression expression = Restrictions.ne("c.testField", "1");
criteria.add(expression);
return (List<T>) criteria.list();
What happens is that I will get any ParentClass objects that contain at least one ChildClass that has a value of c.testField other than "1". This includes ParentClass objects that DO have a "testField" value of "1", which I'm trying to exclude entirely.
The table structure has ChildClass having a reference to ParentClass, but not vice versa. So it seems that each ChildClass object is joined with the ParentClass, and if they fulfill the above restriction, that ParentClass gets returned. This is the incorrect result as it means a ParentClass that has two ChildClass objects, one with value "1" and the other with value "2" will get returned.
Thanks for any help
You need the equivalent of the following HQL query:
select distinct p from Parent p
where not exists (select c.id from Parent p2 join p2.children c
where c.testField = '1' and p2.id = p.id)
You should be able to translate that to Criteria (if you really want to use criteria instead of using clearer HQL queries). You'll need to use Subqueries.notExists().
try with this one
Criteria criteria = getSession().createCriteria(org.test.ParentClass,"parent");
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
criteria.createAlias("parent.ChildClass", "pc");
SimpleExpression expression = Restrictions.ne("pc.testField", "1");
criteria.add(expression);
return (List<T>) criteria.list();

Using HibernateTemplate.find(...) with a String[]

I'm having troubles with HQL again :(
My desired SQL is this:
select employee.idemployee as id, employee.age as age,
employee.birthday as birthday, employee.firstName as firstName,
employee.gender as gender, employee.lastName as lastName from employee
employee
inner join employee_skillgroups skillgroup1 on
employee.idemployee=skillgroup1.idemployee
inner join employee_skillgroups skillgroup2 on
employee.idemployee=skillgroup.idemployee
where skillgroup1.idskillgroup = 'Sprachen'
and skillgroup.idskillgroup = 'SoftSkills'
But i just can't get HQL to generate me this ...
"Sprachen" and "SoftSkills" are two string coming out of a String[] I'm giving the method as a parameter. The method currently looks like this:
public List<Employee> findEmployeeWithTwoSkillGroups(final String[] skillGroups) {
return template.find("from Employee e join e.skillGroups as s where s in ?", Arrays.asList(skillGroups).toString
().substring(1, Arrays.asList(skillGroups).toString().length()-1));
}
I "cast" the array to a list, execute toString() on it (so i get "[Sprachen, SoftSkills]") and cut off the first and the last char (so i get "Sprachen, SoftSkills").
I guess the problem is that HQL generates "[...].idskillgroup in ('Sprachen, SoftSkills')", like it treats the two strings like ONE string ...
And i just can't get it to work like i want it to :/
Can someone please help me and give me a hint what to try/do next? :-)
Greetz
gilaras
Simmilar question:
HQL to get elements that possess all items in a set
The proper HQL statement should go more or less like this:
from Employee e join e.skillGroups as s where s in ( 'Foo', 'Bar', 'Baz' )
so you are missing the ' ' for each word.
EDIT: Now that I got what you want to achieve here :-)
I would recommend you doing this:
Query query = session.createQuery(
"from Employee e join e.skillGroups as s where s in (:skillGroups)");
// skillGroups must be a Collection type
query.setParameterList("skillGroups", skillGroups);
List list = query.list();
In case you need your result to be the AND of all elements of your String[] array, you can do the following:
Query query = session.createQuery(
"from Employee e join e.skillGroups as s where s in (:skillGroups) group by e having count(s) = :skillGroupsLength");
query.setParameterList("skillGroups", skillGroups);
query.setParameter("skillGroupsLength", skillGroups.length);
List list = query.list();
It's a prepared statement, not string concatenation. It handles ('Sprachen, SoftSkills') like a single string because it is a single string. You want to insert a collection in your query. I'm not sure how to do this in hibernateTemplate, but hibernate itself supports adding a collection. see the hibernate documentation.

Categories