I have a table/entity called Recipe with a child collection of type Tag.
I want to be able to find a Recipe searching by two or more tags. Something like:
SELECT re FROM Recipe re JOIN re.tags t WHERE t in :tagsIds
but I only want those hits where the Tag collection contains all tagIds.
Is it possible in HQL/SQL? (Maybe using Criteria?)
Thanks in advance.
I am assuming you have two different entities Recipe and Tag, it can be done as below.
Criteria criteria = getSession().createCriteria("Recipe.class");
criteria.createAlias("tags", "tag");
criteria.add(Restrictions.in("tag.id", Arrays.asList(1,2,3)));
return (List<Recipe>) criteria.list();
I believe you may be missing parentheses in the HQL. It should be:
... WHERE t in (:tagsIds)
Ok, so this did it. Thanks for the replies.
String hql = "select r from Recipe r " +
"join r.tags t " +
"where t.id in (:tags) " +
"group by r " +
"having count(t)=:tag_count";
Query query = session.createQuery(hql);
query.setParameterList("tags", tagIds);
query.setInteger("tag_count", tagIds.size());
List<Recipe> recipes = query.list();
Related
I am using following code to execute native SQL query with hibernate:
Query zonesQuery = session.createNativeQuery(
"Select * \n" +
"FROM dbo.Structure AS S\n" +
"JOIN dbo.StructureLocationType AS SLT ON SLT.StructureId = S.Id\n" +
"WHERE SLT.LocationTypeId = :lc").addEntity(StructureEntity.class);
zonesQuery.setParameter("lc", locationTypeID);
List<StructureEntity> zones = zonesQuery.list();
So it works and it gets me list of StructureEntity
now, because my sql query "join" from StructureLocationType table, is there possibility to get whole StructureLocationType row as well, still using single query?
Thank you.
It can be achieved with the following (notice curly braces in SQL and entities aliases):
Query query = session
.createNativeQuery(
"SELECT {S.*}, {SLT.*} " +
"FROM dbo.Structure AS S " +
"JOIN dbo.StructureLocationType AS SLT ON SLT.StructureId = S.Id " +
"WHERE SLT.LocationTypeId = :lc")
.unwrap(SQLQuery.class)
.addEntity("S", StructureEntity.class)
.addEntity("SLT", StructureLocationTypeEntity.class)
.setParameter("lc", locationTypeID);
List<Pair<StructureEntity, StructureLocationTypeEntity>> result = ((List<Object[]>) query.list())
.stream()
.map(p -> Pair.of((StructureEntity) p[0], (StructureLocationTypeEntity) p[1]))
.collect(Collectors.toList());
You can't get multiple Objects from one query.
But you could either select which columns you want and then iterate the returned Object array:
The query:
SELECT s.id, s.someColumn, slt.id, slt.structureId
FROM dbo.Structure AS s
JOIN dbo.StructureLocationType AS slt on slt.structureId = s.id
WHERE slt.locationTypeId = :lc
Then iterate over the Object array:
List<Object[]> result = query.getResultList();
Or you could create a view on your database and map it to a java entity using the Table annotation like it was a normal table:
#Entity
#Table(name = "STRUCTURE_LOCATION_TYPE_VIEW")
public class StructureAndLocationType {
// ...
}
I thought there is a way to map from a query to an Object without creating a DB view but couldn't find it right now.
I am new to new to hibernate and Java, so I apologize if these are ridiculously simply questions.
I don't understand why the following query gives me the error "ORA-00979: not a GROUP BY expression".
Query q = session.createQuery (
"select new tdata.Summary(tbForm.rYear, SUM(tbForm.pWaste)) "
+ "from TrForm tbForm "
+ "left join fetch tbForm.indKey "
+ "where tbForm.indKey.grpCode='999' "
+ "group by tbForm.rYear " );
The possible reasons for this error do not seem to apply to my query because I am including group by function SUM, and I do not have any expressions in the select that are not in the group by.
I also tried to write a criteria query to do what I need, and I could not get that to work either. In the criteria version, I am confused as to how to add restrictions to fetched data and how to get the data into the Summary object.
Criteria criteria = session.createCriteria(TrForm.class)
.setFetchMode("DimInd",FetchMode.JOIN)
.add(Restrictions.eq("indKey.indCode", "999"))
.setProjection(Projections.projectionList()
.add(Projections.groupProperty("rYear"))
.add(Projections.sum("pWaste")));
summaryList = (List<Summary>) criteria.list();
Any help would be greatly appreciated.
Thanks!
Update:
Thank you so much! I removed the "left join fetch tbForm.indKey" and the HQL version worked!
Generalized query is
select R_YEAR, sum(P_WASTE)
from TR_FORM tbForm
left join DIM_IND ind
on tbForm.IND_KEY = ind.IND_KEY
where ind.GRP_CODE='999'
group by tbForm.R_YEAR
tdata is the folder in which I have the java code for the data models.
I need to report data from 3 different tables in form of a report.
The various search combinations a user can search with are..
INS_USER_ID and DateRange
CUST_ACCT_ID
CUST_ACCT_ID and DateRange
Query..
select DISTINCT W.CUST_ACCT_ID, W.INS_USER_ID, WS.STTI_DATE, WS.STTI_AMT, WC.CMMT from T_WRK W
INNER JOIN T_WRK_STTI WS ON W.WRK_STTI_ID = WS.WRK_STTI_ID
INNER JOIN T_WRK_CMMT WC ON W.WRK_ID = WC.WRK_ID
WHERE W.CUST_ACCT_ID = 90610194 AND WS.STTI_DATE BETWEEN '01-JAN-12' AND '31-DEC-12'
select DISTINCT W.CUST_ACCT_ID, W.INS_USER_ID, WS.STTI_DATE, WS.STTI_AMT, WC.CMMT from T_WRKCS W
INNER JOIN T_WRKCS_STTI WS ON W.WRKCS_STTI_ID = WS.WRKCS_STTI_ID
INNER JOIN T_WRKCS_CMMT WC ON W.WRKCS_ID = WC.WRKCS_ID
WHERE W.INS_USER_ID = 231 AND WS.STTI_DATE BETWEEN '01-JAN-12' AND '31-DEC-12'
All are existing tables are already mapped using Hibernate/JPA.
I have read enough on the various approaches in google, can someone tell me which one of the following hibernate approaches is best suited for my scenario.
NamedQuery
NativeQuery
Criteria API
I am thinking NamedQuery, but have not seen a NamedQuery spanning across multiple tables, an example would be great. Thank you.
Example
#Entity
#NamedQuery(name="findSalaryForNameAndDepartment",
query="SELECT e.salary " +
"FROM Student e" +
"WHERE e.department.name = :deptName AND " +
" e.name = :empName")
You can as well put there an join to the department table and change the query a little bit
For date use parameters
em.createNamedQuery("xxx").setParameter("srtartDate", ...).setParameter("endDate",..)
I have two tables - one containing Address and another containing Photographs. The only common field between them is the PersonID. These were mapped to two POJO Classes Address and Photo. I was able to fetch details in these tables by creating criteria and adding restrictions on the fields . How should we write a join on the two tables. Is it possible to get the result as two objects -Address and Photo.
I want to do a left join so that i can get records of persons without photos as well.
I have read that this is possible only using hql but Can this be done using criterias as well?
You can easily write HQL query that will return result as two objects using Theta Join (as Adrian noted). Here is an example:
String queryText = "select address, photo from Address address, Photo photo "
+ " where address.personID=photo.personId";
List<Object[]> rows = session.createQuery(queryText).list();
for (Object[] row: rows) {
System.out.println(" ------- ");
System.out.println("Address object: " + row[0]);
System.out.println("Photo object: " + row[1]);
}
As you can see query returns list of Object[] arrays that represents each fetched row. First element of this array will contain one obejct and second element - another.
EDIT:
In case of left join I think you need to use native SQL query (not HQL query). Here how you can do this:
String queryText = "select address.*, photo.* from ADDRESS address
left join PHOTO photo on (address.person_id=photo.person_id)";
List<Object[]> rows = sess.createSQLQuery(queryText)
.addEntity("address", Address.class)
.addEntity("photo", Photo.class)
.list();
This should work for your case.
Basically, you have two options:
Since Hibernate 5.1, you can use ad-hoc joins for unrelated entities.
Tuple postViewCount = entityManager.createQuery(
"select p as post, count(pv) as page_views " +
"from Post p " +
"left join PageView pv on p.slug = pv.slug " +
"where p.title = :title " +
"group by p", Tuple.class)
.setParameter("title", "High-Performance Java Persistence")
.getSingleResult();
Prior to Hibernate 5.1, you could only use theta-style joins. However, a theta-style join is equivalent to an equijoin, hence you can only emulate INNER JOINs not OUTER JOINs.
List<Tuple> postViewCount = entityManager.createQuery(
"select p as post, count(pv) as page_views " +
"from Post p, PageView pv " +
"where p.title = :title and " +
" ( p.slug = pv.slug ) " +
"group by p", Tuple.class)
.setParameter("title", "Presentations")
.getResultList();
Finally after 12 years the Hibernate team has implemented such a feature
From Hibernate docs:
The FROM clause can also contain explicit relationship joins using the join keyword. These joins can be either inner or left outer style joins.
List<Person> persons = entityManager.createQuery(
"select distinct pr " +
"from Person pr " +
"join pr.phones ph " +
"where ph.type = :phoneType", Person.class )
.setParameter( "phoneType", PhoneType.MOBILE )
.getResultList();
List<Person> persons = entityManager.createQuery(
"select distinct pr " +
"from Person pr " +
"left join pr.phones ph " +
"where ph is null " +
" or ph.type = :phoneType", Person.class )
.setParameter( "phoneType", PhoneType.LAND_LINE )
.getResultList();
Or you can use WITH and ON keywords. A remark on those
The important distinction is that in the generated SQL the conditions
of the WITH/ON clause are made part of the ON clause in the generated
SQL, as opposed to the other queries in this section where the
HQL/JPQL conditions are made part of the WHERE clause in the generated
SQL.
Example
List<Object[]> personsAndPhones = session.createQuery(
"select pr.name, ph.number " +
"from Person pr " +
"left join pr.phones ph with ph.type = :phoneType " )
.setParameter( "phoneType", PhoneType.LAND_LINE )
.list();
I am currently eager to try the new feature.
Joining two unrelated entities are possible in Hibernate 5.1.
Eg :
select objA from ObjectA objA
JOIN ObjectB objB on objB.variable = objA.variable
where objA.id = 1
It's best to have a class containing those classes you want to join to have them all together.
But if you are joining these tables just for some occasional purposes, you can use criteria and manually load data from each table and put them together. (and yes, you can have these tables' data separately if for Address and Photo there are two separate classes and tables)
What you are looking for is
HQL
Join on fields that you haven't modeled as relationships
Left Join
(During the time the question was first asked and this answer was given) Hibernate supports Theta Join which allows you to do 1 & 2. However, only inner join is available for theta join style.
Personally I would recommend you to model proper relationships, so you just need 1 & 3 which is well-supported in HQL.
(Another answer actually provided an update on new Hibernate feature that provides such feature, that you may simply refer to)
I try to do the function searchBook with java and jpa. I have 2 classes which are Media and Book. Book extends Media. And I keep the data in the different table. I try to select the data from the query below:
TypedQuery<Media> query = em.createQuery(
"SELECT m.title, b.isbn, b.authors"
+ " FROM Book b, Media m" + " WHERE b.isbn = :isbn"
+ " OR lower(m.title) LIKE :title"
+ " OR b.authors LIKE :authors", Media.class);
query.setParameter("isbn", book.getisbn());
query.setParameter("title", "%" + book.getTitle().toLowerCase()
+ "%");
query.setParameter("authors", "%" + book.getAuthors() + "%");
bookList = query.getResultList();
But I got the error:
java.lang.IllegalArgumentException: Cannot create TypedQuery for query
with more than one return
This is the first time I use JPA. I can't find the the mistake.
As a workaround, to get entity composed by other entity attributes, you can create it within query, providing constructor for it.
Query :
TypedQuery<Media> query = em.createQuery("SELECT NEW package_name.Media(m.title, b.isbn, b.authors)"
+ " FROM Book b, Media m"
+ " WHERE b.isbn = :isbn"
+ " OR lower(m.title) LIKE :title"
+ " OR b.authors LIKE :authors", Media.class);
Entity :
public Media(String title, int isbn, String author){
//-- Setting appropriate values
}
I have provided sample, change the datatypes of the constructor accordingly.
Without goind into details about how Media and Book should be modeled, I will at least explain why you get this exception.
You're doing:
em.createQuery(someJPQL, Media.class);
This means: create a query using someJPQL, and this query will return instances of the Media entity.
But your JPQL is:
SELECT m.title, b.isbn, b.authors ...
So the query does not return entities of type Media. It returns three fields, from two different entities. There is no way your JPA engine could magically create instances of Media from these 3 columns. A query would return instances of Media if it looked like this:
select m from Media m ...
If you still want to use TypedQuery you can change the result type to Object[].
List<Object[]> results = entityManager
.createQuery("SELECT m.title, b.isbn, b.authors ...", Object[].class)
.getResultList();
Each Object[] in the List represents a row of data. It contains the selected values for that row in the order in which they were selected in the query. Element 0 is the title, element 1 is the ISBN, and element 2 is the authors. You'll likely need to cast those values if you want to use them in a meaningful way. Since the field values come from two different tables, you could store them in some kind of container object.
List<MediaContainer> mediaList = new ArrayList<>();
for (Object[] row : results) {
MediaContainer container = new MediaContainer();
container.setTitle((String) row[0]);
container.setIsbn((int) row[1]);
container.setAuthors((String) row[2]);
mediaList.add(container);
}
#WebUser instead of doing
List<EntityIDKey> companies =
getEntityManager().createQuery(sql, EntityIDKey.class).getResultList();
Try this :
List<EntityIDKey> companies =
(List<EntityIDKey>)getEntityManager().createQuery(sql).getResultList();
works for me.
if your are using Hibernate version < 4, you can meet this bug.
I go same problem with v3.5. Finally i had to use simple Query and cast each parameter manually
see other comments here : https://groups.google.com/forum/#!topic/axonframework/eUd1d4rotMY
I... remove
Media.class
of
createQuery
because you return more Entities in this source "SELECT m.title, b.isbn, b.authors"
Ex.:
TypedQuery<Media> query = em.createQuery(
"SELECT m.title, b.isbn, b.authors"
+ " FROM Book b, Media m" + " WHERE b.isbn = :isbn"
+ " OR lower(m.title) LIKE :title"
+ " OR b.authors LIKE :authors");
You can resolve the issue by doing this kind of query:
em.createQuery( "SELECT m"
+ " FROM Book b, Media m" + " WHERE b.isbn = :isbn"
+ " OR lower(m.title) LIKE :title"
+ " OR b.authors LIKE :authors", Media.class);
but this works only if you need fields only from one of the requested tables