Count entities in multiple tables - java

I Have a non-entity class
public class CountryStatistics {
public CountryStatistics(Long numTowns, Long numVillages) {
...
}
}
For a given country i want to construct a statistics object.
Country, Village and City are the Entitys, so i tried things in line with code below.
String queryString =
"SELECT NEW mypackage.CountryStatistics(count(t), count(v))
FROM Town t, Village v WHERE t.country = :country AND v.country = :country"
TypedQuery<CountryStatistics> query =
em.createQuery(queryString ,CountryStatistics.class).setParameter("country", country);
query.getSingleResult()
Question: What is the correct way to count some entities in different tables in the same query?
With the above query i end up with way to high numbers if i put distinct like below, number of towns will be correct.
"SELECT NEW mypackage.CountryStatistics(count(distinct t), count(v))
FROM Town t, Village v WHERE t.country = :country AND v.country = :country"
But if I set it for villages also i get:
java.sql.SQLSyntaxErrorException: Multiple DISTINCT aggregates are not supported at this time.

The following query (not tested) should do what you want:
select count(distinct t.id), count(distinct v.id) from Country c
left join c.towns t
left join c.villages v
where c.id = :countryId

Related

How to write "JPQL" query with "IN" between SELECTs?

I wrote this JPQL query and expect a result as List<Question>:
#Query("SELECT q FROM Question q WHERE q.id IN (SELECT qc.questions FROM QCard qc WHERE qc.id IN (SELECT ct.qCards FROM CTest ct WHERE ct.id=:id))")
These are my classes:
class CTest {
id, List<QCard>
}
class QCard{
id, List<Question>
}
class Question{
id
}
I expected all questions as return for given CTest.id.
But I got a compiler-error with message:
SQLSyntaxErrorException
I tried use ":" before "select" words but it had not helped.
What is wrong?
Test your SQL query in an SQL-client (e.g. Squirrel, DbVisualizer, etc.).
Assuming your foreign keys are named:
question_id in table QCard
card_id in table CTest
you could have an SQL with subselects like:
SELECT q.id
FROM Question q
WHERE q.id IN (
SELECT qc.question_id
FROM QCard qc
WHERE qc.id IN (
SELECT ct.card_id
FROM CTest ct
WHERE ct.id = 1 -- example test id
)
)
Does it return the expected result or are there syntax errors?
Then subsequently replace your subselects by JOINs.
Question and Cards
SELECT q.id, c.id
FROM Question q
JOIN QCard c ON c.question_id = q.id
Cards and Tests
SELECT c.id, t.id
FROM QCard c
JOIN CTest t ON t.card_id = c.id
All together
SELECT q.id, c.id, t.id
FROM Question q
JOIN QCard c ON c.question_id = q.id
JOIN CTest t ON t.card_id = c.id
Note: add WHERE clauses like WHERE t.id = 1 if needed.
Experiment with the FROM/JOIN order as it makes sense.
Then translate the running SQL query to JPQL. For example:
#Query("SELECT q"
+ " FROM CTest test"
// a test has many cards (1:n)
+ " JOIN QCard card ON card.id = test.card_id" // associated cards
// a card has many questions (1:n)
+ " JOIN Question q ON q.id = card.question_id" // associated questions
+ " WHERE test.id = :id")
public List<Question> findQuestionsByTestId(String id);
List<Question> findByIdIn(List<Long> idList); //In repository
or
String qlString = "select i from Item i where i.name IN :names";
Query q = em.createQuery(qlString, Item.class);
List<String> names = Arrays.asList("foo", "bar");
q.setParameter("names", names);
List<Item> actual = q.getResultList();
I did those example in my past work, check it, tnx
In your existing query, in inner queries you are selecting entities and checking IN against Id, which will definitely won't work. As you can't complete entry with the Id.
As you haven't shared your complete entity structure, assuming that you have two way relationship declared correctly in entities, here I am placing a reference query which uses the join:
#Query("SELECT q FROM Question q JOIN q.qCard qc WHERE q.qCardId = qc.id AND qc.cTestId = :id")
Where qCardId is the foreign key reference of QCard entity in Question entity and cTestId is the foreign key reference of CTest entity in QCard entity.
You can use this for your reference to update your query with joins.

How to join two Criteria Queries

I have a query that I want to make Criteria Query
select u.email, st.total_amount, st.company_total from users u
join (select user_id, SUM(balance) as total_amount,SUM(company_count) as company_total from subscription s
where s.is_active = 0
group by user_id) st on u.id = st.user_id
where u.is_active = 0
order by st.company_total
I have already made 1 criteria Query
CriteriaQuery<UserImpl> innerQuery = builder.createQuery(UserImpl.class);
Root<Subscription> subscriptionRoot = innerQuery.from(Subscription.class);
innerQuery.multiselect(subscriptionRoot.get("user").get("id"), builder.sum(subscriptionRoot.get("balance")),
builder.sum(subscriptionRoot.get("companyCount")));
I don't know how to make the outer query in spring hibernate JPA. Can some help.
Defining a JOIN clause is pretty simple. You first call the from method on your CriteriaQuery object to get a Root object. In the next step, you can call the join method to define your JOIN clause.
Following is example of Docs, you can manipulate it as per your example.
You can refer Hibernate docs for the join examples. following is sample code.
CriteriaQuery<String> q = cb.createQuery(String.class);
Root<Order> order = q.from(Order.class);
q.select(order.get("shippingAddress").<String>get("state"));
CriteriaQuery<Product> q2 = cb.createQuery(Product.class);
q2.select(q2.from(Order.class)
.join("items")
.<Item,Product>join("product"));
Docs to refer
Another quick example I found is listed below:
<Y> ListJoin<X, Y> join(ListAttribute<? super X, Y> list);
Quick example (assuming Employee has list of Tasks with many-to-many relation):
CriteriaQuery<Employee> query = criteriaBuilder.createQuery(Employee.class);
Root<Employee> employee = query.from(Employee.class);
ListJoin<Employee, Task> tasks = employee.join(Employee_.tasks);
query.select(employee)
.where(criteriaBuilder.equal(tasks.get(Task_.supervisor),
employee.get(Employee_.name)));
TypedQuery<Employee> typedQuery = entityManager.createQuery(query);
List<Employee> employees = typedQuery.getResultList();
Reference: here

Only last record from Custom query

The following query return a list but I am only interested in the last element of the list.
#Query("SELECT r FROM Reservation r WHERE r.reservationSeance.id=:seanceId AND r.seanceDate=:seanceDate")
public Reservation findReservationBySeanceDateAndSeanceId(#Param("seanceId") int seanceId, #Param("seanceDate") java.time.LocalDate seanceDate);
How shall I rewrite the SQL-Query in order to implement my idea?
One possible solution is to use ORDER BY r.id DESC :
#Query("SELECT r FROM Reservation r " +
"WHERE r.reservationSeance.id=:seanceId AND r.seanceDate=:seanceDate " +
"ORDER BY r.id DESC")
public Reservation findReservationBySeanceDateAndSeanceId(
#Param("seanceId") int seanceId,
#Param("seanceDate") java.time.LocalDate seanceDate, Pageable pageable);
and because there are no way to use limit in JPQL, you can use Pageable
Pageable pageable = new PageRequest(0, 1);
Reservation reservation = r.findReservationBySeanceDateAndSeanceId(seanceId, seanceDate, pageable);
Another possible solution without Query :
public Reservation findTop1ByReservationSeanceAndSeanceDateOrderByIdDesc(
ReservationSeanceEntity reservationSenace,
java.time.LocalDate seanceDate
)
In this second solution you have to pass the ReservationSeance Object and not the id of ReservationSeance, the query can be read as :
Find top 1 (first one) by `ReservationSeance` and `SeanceDate` order by `Id` Desc order
You need to provide a couple more parameters to your query, especially an ORDER BY clause.
To get the latest seanceId, you'll want to order your results by that id, but in reverse order. Then, just tell the query to return only the first result:
SELECT r FROM Reservation r
WHERE r.reservationSeance.id=:seanceId
AND r.seanceDate=:seanceDate
ORDER BY seanceId
DESC LIMIT 1;
You can try the following, if you are using mysql as your database:
SELECT r
FROM Reservation r
WHERE r.reservationSeance.id=:seanceId
AND r.seanceDate=:seanceDate
order by r.reservationSeance.id desc limit 0,1

How to flatten the results of a Hibernate query

Say I executed a theoretical HQL query like FROM Customer. And in Customer is a getOrders() getter returning a ManyToOne collection of Order objects. This executes a SQL statement selecting from Customer with a left join to Order.
Through the object model, I can programmaticly iterate over Customers and then iterate over Orders.
However, I want to convert the hierarchical object model to a flat tabular result of the left join so that the results would look much like that of this SQL query:
SELECT *
FROM Customer
LEFT JOIN Order on Customer.customerId = Order.customerId
Sample result:
Customer.customerId ... Order.orderId Order.customerId ...
1 200 1
2 201 2
2 202 2
3 NULL NULL
Is there an easy way to do this with Hibernate?
Depends what you want at the "scalar" level which you control through using an explicitl select clause.
select c, o
from Customer c left join c.orders o
returns you List of (Customer, Order) tuples. Or:
select c.id, c.name, o.id, ...
from Customer c left join c.orders o
which returns you a scalar projection of the atomic pieces.
In both cases you get back a List. You can use "dynamic instantiation" in both cases (though really its more useful in the second case imho):
select new CustomerOrderSummary( c.id, c.name, o.id, ... )
from Customer c left join c.orders o
where CustomerOrderSummary is just a plain class with matching constructor.
Possible you can do this via expressing of result set. http://docs.jboss.org/hibernate/orm/4.0/hem/en-US/html/query_native.html
From the example:
#SqlResultSetMapping(name="GetNightAndArea", entities={
#EntityResult(name="org.hibernate.test.annotations.query.Night", fields = {
#FieldResult(name="id", column="nid"),
#FieldResult(name="duration", column="night_duration"),
#FieldResult(name="date", column="night_date"),
#FieldResult(name="area", column="area_id")
}),
#EntityResult(name="org.hibernate.test.annotations.query.Area", fields = {
#FieldResult(name="id", column="aid"),
#FieldResult(name="name", column="name")
})
})
I think you can try retrieving results in Object[] e.g. below:
EntityManager entityManager = EntityManager.getEntityManager();
Query query= entityManager.createQuery("select cust, ord from Customer cust left outer join cust.orders ord where cust.customerId = :customerId");
tradeQuery.setParameter("customerId", aCustomerId);
List<Object[]> resultList = (List<Object[]>)query.getResultList();
The retrieved resultsList will be list of Object array containing Customer and Order objects in flat.
if(!resultList.isEmpty()){
Iterator<Object[]> iter = resultList.iterator();
while(iter.hasNext()){
Object[] resultObj = (Object[])iter.next();
Customer customer= (Customer )resultObj[0];
Order order = (Order)resultObj[1];
}
}
Hope this helps!

JPA: Query that returns multiple entities

I'm writing a JPQL query that joins across three tables. In my resultlist I would like to get all three entities per matching row (hope that makes sense).
Any ideas?
Hibernate 3.x is my JPA provider.
IIRC, you can do a SELECT o1, o2, o3 FROM EntityA o1, EntityB o2, EntityC o3 WHERE ...., and the result will be a List<Object[3]>, where the array contents will contain the o1,o2,o3 values.
This is a Spring Data sample, however its works the same way in JPA
//HQL query
#Query("SELECT c,l,p,u FROM Course c, Lesson l, Progress p, User u "
+ "WHERE c.id=l.courseId AND l.id = p.lessonId AND p.userId = u.id AND u.id=:userId AND c.id=:courseId")
public List<Object[]> getLessonsWithProgress(#Param("userId") Integer userId, #Param("courseId")Integer courseId);
Then, I call this method and print the results:
List<Object[]> lst = courseRepository.getLessonsWithProgress(userId, courseId);
for (Object o[] : lst) {
Course c = (Course) o[0];
Lesson l = (Lesson) o[1];
Progress p = (Progress) o[2];
User u = (User) o[3];
//all the classes: Course, Lesson, Progress and User have the toString() overridden with the database ID;
System.out.printf("\nUser: %s \n Lesson: %s \n Progress: %s \n Course: %s",u,l,p,c);
}
The output #Test is here:
User: com.cassio.dao.model.User[ id=1965 ]
Lesson: com.cassio.dao.model.Lesson[ id=109 ]
Progress: com.cassio.dao.model.Progress[ id=10652 ]
Course: com.cassio.dao.model.Course[ id=30 ]
Cheers
Since You are asking JPA: Query that returns multiple entities, EclipseLink too comes under it. And I reached on this question googling for EclipseLink. So here is my solution. Hope it works for you.
TypedQuery<Object[]> query = entityManager.createQuery("select p from Post p where p.publisher.pubId= :ID order by p.createdAt desc",
Object[].class);
query.setParameter("ID", publisherID);
Then you can loop through the result objects and cast them accordingly.
for (Object result : query.getResultList()) {
myList.add((Post) result);
}
You can also try this,
Query query = entityManager.createQuery("select p from Post p where p.publisher.pubId= :ID order by p.createdAt desc");
Reference:http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL
In case of many to one or one to many relationship how to get multiple records of one of the entities? lets say A is one entity and B is another entity but they have one to many relationship and when you get result you expect B has one record and A has more than 1 record? My query is as below but I don't know how shall I get multiple records of 2nd entity?
#Query("SELECT wl, gr FROM WatchList as wl, GeozoneReference gr " +
"WHERE wl.watchlistId = gr.objWatchList.watchlistId " +
"AND wl.watchlistId =:watchlistId")
List<Object[]> findWatchlistByWatchlistId(#Param("watchlistId") Long watchlistId);

Categories

Resources