I have a view named V_ENTITY in my database which is populated with a few entries. Using SQL scrapbook in Eclipse I can easily do queries which return desired results.
However, when I try to retrieve the entries from my DAO class, I get nothing. My count returns 0 as number of entries, while there are 7 of them, which I can fetch with native SQL queries via scrapbook.
I have created a separate entity for that particular view:
#Entity
#Table(name = "V_ENTITY")
public class ViewEntity {
...
}
I use JPA2 CriteriaQuery API for dynamic queries I do. Here is the example for count that is bugging me:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<ViewEntity> transactionQuery = cb.createQuery(ViewEntity.class);
Root<ViewEntity> ViewEntityRoot = transactionQuery.from(ViewEntity.class);
Predicate[] predicates = new Predicate[<someSize>]
predicates[<someIndex>] = cb.equal(
root.get("foreignName").as(String.class),
"%" + foreignName + "%"
)
CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
countQuery.select(cb.count(ViewEntityRoot));
countQuery.where(predicates);
TypedQuery<Long> finalQuery = entityManager.createQuery(countQuery);
return finalQuery.getSingleResult().intValue();
Can anyone help me find a flaw in my code/thinking?
Use the <property name="show_sql">true</property> property to show us the generated sql pls
Related
The scenario is:
Entity Student
ID
Name
List Courses
Entity Course
ID
Name
Student
Now I need the list of students who are studying course 'Programming'
How can I achieve this using Criteria Query.
try this:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Student> cq = cb.createQuery(Student.class);
Root<Student> rootStudent = cq.from(Student.class);
Join<Student,Course> joinCourse = rootStudent.join("courses",JoinType.INNER);
cq.where(cb.equals(joinCourse.get("Name"),"Proggraming"));
cq.select(rootStudent);
List<Student> res = entityManager.createQuery(cq).getResultList();
In this example I assume that you have the JPA entities well modeled, including bidirectional relationships. It would also be recommended if it is for an API that you use pojos instead of returning a JPA entity to the web part, for this you should use multiselect instead of select and specify each of the fields you want to obtain, but as a first approximation it would be valid.
It would also be advisable to use metamodels for JPA entities instead of accessing the properties through a string with the name (join, get methods ..)
In this scenario, it does not make sense to make a subquery, I change the query to find the students of the courses Proggraming1 or Proggraming2 through a subquery(I would still prefer to do it by filtering the joins without using subquery), it would be something like this:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Student> cq = cb.createQuery(Student.class);
Root<Student> rootStudent = cq.from(Student.class);
Join<Student,Course> joinCourse = rootStudent.join("courses",JoinType.INNER);
Subquery<Long> subqueryIdCourse = cq.subquery(Long.class);
Root<Course> rootCourseSq = subqueryIdCourse.from(Course.class);
subqueryIdCourse.where(
cb.or(
cb.equals(rootCourseSq.get("Name"),"Proggraming1"),
cb.equals(rootCourseSq.get("Name"),"Proggraming2")))
subqueryIdCourse.select(rootCourseSq.get("ID"))
cq.where(joinCourse.get("ID").in(subqueryIdCourse.getSelection()));
cq.select(rootStudent);
List<Student> res = entityManager.createQuery(cq).getResultList();
Let's say, I have a query like
Select a.valA, b.valB
from tableA a join tableB b on a.joinCol = b.joinCol
where a.someCol = 1.
I want to execute it using Hibernate (and Spring Data) in one query to the database. I know, I can write just
Query query = em.createQuery(...);
List<Object[]> resultRows = (List<Object[]>)query.getResultList();
But my question would be - is it possible to do it in a typesafe way, using CriteriaQuery for example? The difficulty is, that, as you see, I need to select values from different tables. Is there some way to do this?
Simple example where an Employee has many to many relation to several jobs that he may have :
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Tuple> criteria = builder.createTupleQuery();
Root<TableA> root = criteria.from(TableA.class);
Path<Long> qId = root.get("id");
Path<String> qTitle = root.get("title");
Join<TableA, TableB> tableTwo = root.join("joinColmn", JoinType.INNER);
criteria.multiselect(qId, qTitle, tableTwo);
List<Tuple> tuples = session.createQuery(criteria).getResultList();
for (Tuple tuple : tuples)
{
Long id = tuple.get(qId);
String title = tuple.get(qTitle);
TableB tableB= tuple.get(tableTwo);
}
but saw that there is an alternate answer here :
JPA Criteria API - How to add JOIN clause (as general sentence as possible)
I am using hibernate 5 (I am not familiar with hibernate, just started with it) and I want to perform a simple select query, after searching I found the following code to select an element by id:
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Ord> query = cb.createQuery(MyClass.class);
Root<Ord> root = query.from(MyClass.class);
query.select(root);
query.where(cb.equal(root.get(MyClass.NUM),ordId));
Query<Ord> sessionQuery = session.createQuery(query);
return sessionQuery.getSingleResult();
I find it a heavy way to just get just one element.
The question is: Is the above the recommended/correct way to fetch data using hibernate (5)?
Thanks in advance,
JPQL provides us with a quicker and simpler implementation while using the Criteria API is more dynamic and robust.
public List<Student> findAllStudentsWithJpql() {
return session.createQuery("SELECT a FROM Student a", Student.class).getResultList();
}
You are using Criteria API
The Criteria API provides a dynamic approach for building JPA queries.
public List<Student> findAllStudentsWithCriteriaQuery() {
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Student> cq = cb.createQuery(Student.class);
Root<Student> rootEntry = cq.from(Student.class);
CriteriaQuery<Student> all = cq.select(rootEntry);
TypedQuery<Student> allQuery = session.createQuery(all);
return allQuery.getResultList();
}
Above two methods are the common way in Hibernate.
Documentation - https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html
You can perform something like this...
#Autowired
private SessionFactory session;
Query<Your Class> query = session.getCurrentSession().createQuery("select * from <tableName>);
I am trying to write following SQL query using JPA Criteria API
SELECT * FROM roles WHERE roles.name IN (SELECT users.role FROM users where name="somename");
and it is a bit to much for me (I have just started learing Criteria API). I got something like this:
CriteriaBuilder criteriaBuilder = manager.getCriteriaBuilder();
CriteriaQuery<RoleEntity> criteriaQuery = criteriaBuilder.createQuery(RoleEntity.class);
Root<RoleEntity> root = criteriaQuery.from(RoleEntity.class);
Subquery<UserEntity> subquery = criteriaQuery.subquery(UserEntity.class);
Root<UserEntity> subqueryRoot = subquery.from(UserEntity.class);
subquery.where(criteriaBuilder.equal(subqueryRoot.get(UserEntity_.username), username));
subquery.select(subqueryRoot);
And I have no idea how to put it all together.
Best regards,
Bartek
Fellow JPA learner here. Here's my attempt at setting it up:
// Get the criteria builder from the entity manager
CriteriaBuilder cb = manager.getCriteriaBuilder();
// Create a new criteria instance for the main query, the generic type indicates overall query results
CriteriaQuery<RoleEntity> c = cb.createQuery(RoleEntity.class);
// Root is the first from entity in the main query
Root<RoleEntity> role = criteriaQuery.from(RoleEntity.class);
// Now setup the subquery (type here is RETURN type of subquery, should match the users.role)
Subquery<RoleEntity> sq = cb.subquery(RoleEntity.class);
// Subquery selects from users
Root<UserEntity> userSQ = sq.from(UserEntity.class);
// Subquery selects users.role path, NOT the root, which is users
sq.select(userSQ.get(UserEntity_.role))
.where(cb.equal(userSQ.get(UserEntity_.username), username)); // test for name="somename"
// Now set the select list on the criteria, and add the in condition for the non-correlated subquery
c.select(role)
.where(cb.in(role).value(sq)); // can compare entities directly, this compares primary key identities automatically
Hopefully that helps!
I am implementing "Advanced Search" kind of functionality for an Entity in my system such that user can search that entity using multiple conditions(eq,ne,gt,lt,like etc) on attributes of this entity. I am using JPA's Criteria API to dynamically generate the Criteria query and then using setFirstResult() & setMaxResults() to support pagination. All was fine till this point but now I want to show total number of results on results grid but I did not see a straight forward way to get total count of Criteria query.
This is how my code looks like:
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Brand> cQuery = builder.createQuery(Brand.class);
Root<Brand> from = cQuery.from(Brand.class);
CriteriaQuery<Brand> select = cQuery.select(from);
.
.
//Created many predicates and added to **Predicate[] pArray**
.
.
select.where(pArray);
// Added orderBy clause
TypedQuery typedQuery = em.createQuery(select);
typedQuery.setFirstResult(startIndex);
typedQuery.setMaxResults(pageSize);
List resultList = typedQuery.getResultList();
My result set could be big so I don't want to load my entities for count query, so tell me efficient way to get total count like rowCount() method on Criteria (I think its there in Hibernate's Criteria).
Thanks Vladimir!
I took your idea and used separate count query to use my existing array of predicates in it. Final implementation looks like this:
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Brand> cQuery = builder.createQuery(Brand.class);
Root<Brand> from = cQuery.from(Brand.class);
CriteriaQuery<Brand> select = cQuery.select(from);
.
.
//Created many predicates and added to **Predicate[] pArray**
.
.
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
cq.select(builder.count(cq.from(Brand.class)));
// Following line if commented causes [org.hibernate.hql.ast.QuerySyntaxException: Invalid path: 'generatedAlias1.enabled' [select count(generatedAlias0) from xxx.yyy.zzz.Brand as generatedAlias0 where ( generatedAlias1.enabled=:param0 ) and ( lower(generatedAlias1.description) like :param1 )]]
em.createQuery(cq);
cq.where(pArray);
Long count = em.createQuery(cq).getSingleResult();
.
.
select.where(pArray);
.
.
// Added orderBy clause
TypedQuery typedQuery = em.createQuery(select);
typedQuery.setFirstResult(startIndex);
typedQuery.setMaxResults(pageSize);
List resultList = typedQuery.getResultList()
Though this is working fine but still I am not sure why I have to write
em.createQuery(cq);
to get it working. Any Idea?
Why don't you just use count?
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Long> cQuery = builder.createQuery(Long.class);
Root<Brand> from = cQuery.from(Brand.class);
CriteriaQuery<Long> select = cQuery.select(builder.count(from));
.
.
//Created many predicates and added to **Predicate[] pArray**
.
.
select.where(pArray);
// Added orderBy clause
TypedQuery<Long> typedQuery = em.createQuery(select);
typedQuery.setFirstResult(startIndex);
//typedQuery.setMaxResults(pageSize);
// here is the size of your query
Long result = typedQuery.getSingleResult();
If you're using Hibernate as your JPA-Provider have a look at projections, especially Projections.rowCount().
You might have to execute the query twice though, first get the count then get the results.
Note that for plain JPA you might need some other approach.
I guess both of the answers work. But none of them is optimal. The problem with ThinkFloyd's answer is that createQuery is used two times. And Vladimir Ivanov has created two instances of CriteriaQuery which I think is unnecessary.
val cb = entityManager.criteriaBuilder
val cq = cb.createQuery(ManualQuery::class.java)
val manualQuery = cq.from(ManualQuery::class.java)
val predicates = ArrayList<Predicate>()
/*
predications.....
*/
cq.select(manualQuery)
.where(*predicates.toTypedArray())
.orderBy(cb.desc(manualQuery.get<ZonedDateTime>("createdDate")))
val query = entityManager.createQuery(cq)
// total rows count
val count = query.resultList.size
val indexedQuery = query
.setFirstResult((currentPage - 1) * pageSize)
.setMaxResults(itemsPerPage)
Doing go it works. And it is done in Kotlin. You do it the same way in Java.