I need to create subquery using the Criteria API
the current Criteria query which i have created is
Criteria propCriteria = session.createCriteria(PropertyDetail.class);
propCriteria.createAlias("property","prop");
propCriteria.createAlias("country", "country");
propCriteria.add(Restrictions.eq("prop.propertyId",promotionFormBean.getPropertyId()));
The SQL produced by Hibernate is
select * from PROPERTY_DETAILS this_, COUNTRY_MASTER country2_,
PROPERTY_MASTER prop1_, USER_MASTER user6_, ROLE_MASTER role7_,
USER_MASTER user8_ where this_.country_id=country2_.country_Id and
this_.property_id=prop1_.PROPERTY_ID and
prop1_.archive_user_id=user6_.id(+) and
user6_.role_id=role7_.ROLE_ID(+) and
prop1_.create_user_id=user8_.id(+) and prop1_.PROPERTY_ID=?.
What i need to do is to produce the below SQL using criteria
Required:
select * from PROPERTY_DETAILS this_, COUNTRY_MASTER country2_,
PROPERTY_MASTER prop1_, USER_MASTER user6_, ROLE_MASTER role7_,
USER_MASTER user8_ where (this_.country_id=country2_.country_Id or
country2_.controller_order in (select zone_id from
property_zone_mapping where property_id=?)) and
this_.property_id=prop1_.PROPERTY_ID and
prop1_.archive_user_id=user6_.id(+) and
user6_.role_id=role7_.ROLE_ID(+) and
prop1_.create_user_id=user8_.id(+) and prop1_.PROPERTY_ID=?
The bold part of Required section.Property_zone_mapping is one another table which not associated with PropertyDetail.class.
I want somehow to use this table using subquery .
Thanks in advance.
I find criteria queries so much harder to read than HQL. Have you considered a NamedQuery?
#NamedQueries({
#NamedQuery(
name="PropertyDetail.findByPropertyId",
query="from PropertyDetail join property p join country c where p.propertyId = :propertyId"
)
})
public class PropertyDetail {
...
}
public class PropertyDetailDao {
public List<PropertyDetail> findByPropertyId(long propertyId) {
Query query = getSession().getNamedQuery("PropertyDetail.findByPropertyId");
query.setParameter("propertyId", propertyId);
return query.list();
}
}
Related
I'm relatively new to Spring JPA CriteriaQuery. Im trying to convert my old native query in my program to criteria query and haven't been successful on join query for multiple table with conditions. I need help converting native SQL query into Criteria Query for these query below :
select * from student s inner join (
select distinct on (student_id) * from class where status = 'Active' order by
student_id,date_register desc) c on s.id = c.user_id
inner join teacher t on t.subject_id = c.subject_id
where t.status = 'Active' and s.status='Active' order by s.name desc
Update :
Below code is as far as I can go cause I dont really know much. Am i in the right direction? I'm opting for Expression because i dont know how to use Join.
CriteriaQuery<Student> query = cb.createQuery(Student.class);
Root<Student> sRoot= query.from(Student.class);
query.select(sRoot);
Subquery<Integer> subquery = query.subquery(Integer.class);
Root<Class> cRoot= subquery.from(CLass.class);
Expression<Integer> max = cb.max(cRoot.get("dateRegister"));
subquery.select(max);
subquery.groupBy(cRoot.get("student"));
query.where(
cb.and(
cb.in(cRoot.get("dateRegister")).value(subquery)
)
);
Thanks in advance!
I create a query using criteria (java) on PostgreSQL DB ,
This is a snippet from my code:
protected Criteria createEntityCriteria() {
return createEntityCriteria(getSession());
}
protected Criteria createEntityCriteria(Session session) {
return session.createCriteria(getEntityClass(), "main").setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
}
Criteria crit = createEntityCriteria();
crit.setFirstResult((pagingInfo.getPageNumber() - 1) * pagingInfo.getPageSize());
crit.setMaxResults(pagingInfo.getPageSize());
now when i view the log from hibernate(criteria query) i see 2 queries
the first one:
the first query which i have NOT asked at all is:
/* criteria query */ select
count(*) as y0_
from some_Table this
left outer join moreTable
on ......
the second one is which i did asked for:
Hibernate:
/* criteria query */ select col1 y0_,
col2 y1_,
......
from .....
left join ....
on ....
order by
y41_ desc limit ? offset ?
the problem is that the count query fail due to a query time out .
and second one works perfect .
is there a way to prevent from hibernate to "made-up" this count query ?
I am trying to use HQL fetching my entity along with sub-entities using JOIN FETCH, this is working fine if I want all the results but it is not the case if I want a Page
My entity is
#Entity
#Data
public class VisitEntity {
#Id
#Audited
private long id;
.
.
.
#OneToMany(cascade = CascadeType.ALL,)
private List<VisitCommentEntity> comments;
}
and because I have millions of visits I need to use Pageable and I want to Fetch the comments in a single database query like :
#Query("SELECT v FROM VisitEntity v LEFT JOIN FETCH v.comments WHERE v.venue.id = :venueId and ..." )
public Page<VisitEntity> getVenueVisits(#Param("venueId") long venueId,...,
Pageable pageable);
That HQL call throws the following exception:
Caused by: java.lang.IllegalArgumentException: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=null,role=com.ro.lib.visit.entity.VisitEntity.comments,tableName=visitdb.visit_comment,tableAlias=comments1_,origin=visitdb.visit visitentit0_,columns={visitentit0_.visit_id ,className=com.ro.lib.visit.entity.VisitCommentEntity}}] [select count(v) FROM com.ro.lib.visit.entity.VisitEntity v LEFT JOIN FETCH v.comments WHERE v.venue.id = :venueId and (v.actualArrival > :date or v.arrival > :date)]
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1374)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1310)
at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:309)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
and once I remove the paging everything works fine
#Query("SELECT v FROM VisitEntity v LEFT JOIN FETCH v.comments WHERE v.venue.id = :venueId and ..." )
public List<VisitEntity> getVenueVisits(#Param("venueId") long venueId,...);
Obviously the problem is the count query from Spring-Data, but how can we fix it?
The easiest way is to use the countQuery attribute of the the #Query annotation to provide a custom query to be used.
#Query(value = "SELECT v FROM VisitEntity v LEFT JOIN FETCH v.comments …",
countQuery = "select count(v) from VisitEntity v where …")
List<VisitEntity> getVenueVisits(#Param("venueId") long venueId, …);
Alternatively in newest versions of Spring (supporting JPA 2.1 specification) you can use entity graph like this:
#EntityGraph(attributePaths = "roles")
#Query("FROM User user")
Page<User> findAllWithRoles(Pageable pageable);
Of course named entity graphs work as well.
You have to specify countQuery param for #Query and now you can use Page or List as return value.
#Query(value = "SELECT v FROM VisitEntity v LEFT JOIN FETCH v.comments WHERE v.venue.id = :venueId and ...",
countQuery = "SELECT count(v) FROM VisitEntity v LEFT JOIN v.comments WHERE v.venue.id = :venueId and ..." )
public Page<VisitEntity> getVenueVisits(#Param("venueId") long venueId,...,
Pageable pageable);
If you want completely control your query build by Specification with join fetch you can check CriteriaQuery return type and change join fetch logic according to query type like this:
public class ContactSpecification implements Specification<Contact> {
#Override
public Predicate toPredicate(Root<Contact> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
if(query.getResultType() == Long.class) {
root.join(Contact_.company);
} else {
root.fetch(Contact_.company);
}
return cb.equal(root.get(Contact_.company).get(Company_.name), "Company 123");
}
}
I was not able to find this info in documentation, but from SimpleJpaRepository.getCountQuery() method you can see query for count request first build for Long return type, and later fetch for expected class is running.
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Long> query = builder.createQuery(Long.class);
Root<S> root = applySpecificationToCriteria(spec, domainClass, query);
It can be not reliable since it is an implementation details which can be changed, but it works.
Try countProjection
#Query(value="SELECT v FROM VisitEntity v LEFT JOIN FETCH v.comments WHERE v.venue.id = :venueId and ..." ,
countProjection = "v.id")
public Page<VisitEntity> getVenueVisits(#Param("venueId") long venueId,...,
Pageable pageable);
I run following code intend to update the least record in the table on Hibernate 3.6.7 final (JPA 2.0?) :
Query query = em.createQuery("UPDATE MyTable a SET a.isEnable=1 WHERE a.isEnable=0 ORDER BY a.id DESC").setMaxResults(1);
query.executeUpdate();
but hibernate ignores ORDER BY when generating sql.
Is ORDER BY for SELECT use only in JPQL? How to execute UPDATE query with ORDER BY in JPA?
thanks for any help.
To update the record with the last ID in a table you do the following:
TypedQuery<MyEntity> query = em.createQuery("SELECT a FROM MyEntity a WHERE a.isEnable=0 ORDER BY a.id DESC", MyEntity.class);
query.setMaxResults(1);
List<MyEntity> resultList = query.getResultList();
if (resultList.size()>0) {
resultList.get(0).setEnabled(true);
//eventually you can to em.flush();
}
I have the following parametrised JPA, or Hibernate, query:
SELECT entity FROM Entity entity WHERE name IN (?)
I want to pass the parameter as an ArrayList<String>, is this possible? Hibernate current tells me, that
java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.lang.String
Is this possible at all?
ANSWER: Collections as parameters only work with named parameters like ":name", not with JDBC style parameters like "?".
Are you using Hibernate's Query object, or JPA? For JPA, it should work fine:
String jpql = "from A where name in (:names)";
Query q = em.createQuery(jpql);
q.setParameter("names", l);
For Hibernate's, you'll need to use the setParameterList:
String hql = "from A where name in (:names)";
Query q = s.createQuery(hql);
q.setParameterList("names", l);
in HQL you can use query parameter and set Collection with setParameterList method.
Query q = session.createQuery("SELECT entity FROM Entity entity WHERE name IN (:names)");
q.setParameterList("names", names);
Leaving out the parenthesis and simply calling 'setParameter' now works with at least Hibernate.
String jpql = "from A where name in :names";
Query q = em.createQuery(jpql);
q.setParameter("names", l);
Using pure JPA with Hibernate 5.0.2.Final as the actual provider the following seems to work with positional parameters as well:
Entity.java:
#Entity
#NamedQueries({
#NamedQuery(name = "byAttributes", query = "select e from Entity e where e.attribute in (?1)") })
public class Entity {
#Column(name = "attribute")
private String attribute;
}
Dao.java:
public class Dao {
public List<Entity> findByAttributes(Set<String> attributes) {
Query query = em.createNamedQuery("byAttributes");
query.setParameter(1, attributes);
List<Entity> entities = query.getResultList();
return entities;
}
}
query.setParameterList("name", new String[] { "Ron", "Som", "Roxi"}); fixed my issue