This is a sample entity:
public class Account{
#Id
Long id
Double remaining;
#ManyToOne
AccountType type
}
public class AccountType{
#Id
Long id;
String name;
}
Now i create a criteria query with Join as follwing :
CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
CriteriaQuery criteriaQuery = criteriaBuilder.createquery();
Root<Account> accountRoot = criteriaQuery.from(Account.class);
Join<Account, AccountType> typeJoin = accountRoot.join(Account_.type);
criteriaQuery.multiSelect(
typeJoin,
criteriaBuilder.sum(accountRoot.get(Account_.remaining))
);
criteriaQuery.groupBy(typeJoin);
Query query = getEntityManager().createQuery(criteriaQuery);
query.getResultList();
The above code generate Sql command like following:
select accType.id, accType.name, sum(acc.remaining)
from account acc join accType on acc.accounttype_id = accType.id
group by accType.id
Above code work in PosgreSQL but can't run in Oracle, because in it select accType.name that doesn't appear in the group by clause.
update :
I think my question isn't clear for you. My question isn't about PostgreSQL or Oracle behavior in group by. My question is this :
I use typeJoin in group by clause(this means I expect hibernate use all field of AccountType in group by), but why hibernate just use identity field on group by? if I will use just identity field in group by then I can use the following statement :
criteriaQuery.groupBy(typeJoin.get(AccountType_.id)) ;
JPA/Hibernate doesn't automatically include all entity properties in a group by clause, so you have to manually specify them:
CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
CriteriaQuery criteriaQuery = criteriaBuilder.createQuery();
Root<Account> accountRoot = criteriaQuery.from(Account.class);
Join<Account, AccountType> typeJoin = accountRoot.join(Account_.type);
criteriaQuery.multiSelect(
typeJoin.get("id"),
typeJoin.get("name"),
criteriaBuilder.sum(accountRoot.get(Account_.remaining))
);
criteriaQuery.groupBy(typeJoin.get("id"), typeJoin.get("name"));
Query query = getEntityManager().createQuery(criteriaQuery);
query.getResultList();
If using GROUP BY, Oracle requires every column in select list to be in the GROUP BY.PostgreSQL is the same, except when grouping by the primary key, then it allows you to select any column.
From Oracle docs
In a query containing a GROUP BY clause, the elements of the select
list can be aggregate functions, GROUP BY expressions, constants, or
expressions involving one of these.
From PostgreSQL docs
When GROUP BY is present, or any aggregate functions are present, it
is not valid for the SELECT list expressions to refer to ungrouped
columns except within aggregate functions or when the ungrouped column
is functionally dependent on the grouped columns, since there would
otherwise be more than one possible value to return for an ungrouped
column. A functional dependency exists if the grouped columns (or a
subset thereof) are the primary key of the table containing the
ungrouped column.
Related
I would like to return a list of Group objects using a JPA criteria query. A group can either be empty or contain participants. Each Participant has a reference to its Group. The group knows nothing about the participants. Now I would like to implement a criteria query to dynamically return either:
Only groups with participants.
All groups (including empty ones).
Basically, this is my Group class:
public class Group {
private long id;
}
And this is my Participant:
public class Participant {
#ManyToOne
#JoinColumn(name = "FK_GROUP")
private Group group;
}
I left out all properties, methods and annotations for sake of simplicity.
This will return both, empty groups and groups with participants, in plain SQL:
SELECT DISTINCT G.* FROM GROUP_TABLE G
LEFT JOIN PARTICIPANT_TABLE P ON P.FK_GROUP=G.ID;
Now I'd like to do the same thing with Criteria API:
if (includeEmptyGroups) {
// ...
Root<Group> root = criteria.from(Group.class);
Join<Participant, Group> join = root.join("id"); <-- This is my issue!
criteria.select(root).distinct(true);
//...
}
else {
// ...
}
The query should return a list of Group objects, so I chose that type for my Root object and the from() method. Now, most tutorials would define a Join<Group, Participant>, and join participants by a property of the Group class.
Due to my reversed class structure, however, I cannot do this. The code above does not work, because I reversed the types and JPA cannot join by simple types. I'm unsure, if I approach the problem correctly at all.
I could switch types around and base my Root on the Participant class, then join with Group. This, however, makes it impossible to conditionally return empty groups. Is it even possible to build the plain SQL query with Criteria API?
Since the JPA contracts do not allow for left joining entities, you can only use HQL or a query builder API on top of HQL like e.g. Blaze-Persistence which supports this. Either way, you will need at least Hibernate 5.1 which is the first version that added support for entity joins.
I have two pojo's
Endeavor
{
private Integer id;
private EndeavorPlan currentlyActiveEndeavorPlan;
private Set<EndeavorPlan> endeavorPlans;
}
EndeavorPlan{
private Integer id;
private Endeavor endeavor;
private Date creationTs;
private String status;
}
Query:
If I have currentlyActiveEndeavorPlan in endeavor then return that against their endeavor else I want the oldest creationTs endeavorPlan against their endeavor who does not have "D" status. any suggestions????
I tried this hql query:
SELECT
endeavor.id, COALESCE(endeavor.currentlyActiveEndeavorPlan.id,plans.id)
FROM
Endeavor as endeavor,
IN
(endeavor.endeavorPlans) as plans
WHERE
plans.endeavor.id = endeavor.id
and plans.id in (
SELECT
ep.id
FROM
EndeavorPlan as ep
WHERE
ep.endeavor.id = endeavor.id
and ep.status <> 'D'
ORDER BY
ep.creationTs asc
)
and endeavor.id in (:endeavorIds)
GROUP BY
endeavor.id,plans.id
but its giving me two or more endeavorPlans who have more than one EndeavorPlan in an Endeavor but I only want the oldest creationTs EndeavorPlan against their Endeavor.
Not sure I completely understand your use case, but it seems to me that what you want is a Top-N per category which can't be done efficiently with HQL yet because subqueries do not support limit/offset yet (this changes with Hibernate 6).
I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(Endeavor.class)
public interface EndeavorDto {
#IdMapping
Integer getId();
EndeavorPlanDto getCurrentlyActiveEndeavorPlan();
#Limit(limit = "1", orderBy = "creationTs ASC")
#Mapping("endeavorPlans[status <> 'D']")
EndeavorPlanDto getLatestEndeavorPlan();
#EntityView(EndeavorPlan.class)
interface EndeavorPlanDto {
#IdMapping
Integer getId();
Date getCreationTs();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
EndeavorDto a = entityViewManager.find(entityManager, EndeavorDto.class, id);
This will create a SQL query similar to the following:
select e.id, ep1.id, ep1.creation_ts, ep2.id, ep2.creation_ts
from endeavor e
left join endeavor_plan ep1 on e.active_plan = ep1.id
left join lateral (
select ep2.id, ep2.creation_ts
from endeavor_plan ep2
where ep2.endeavor = e.id
order by ep2.creation_ts asc
limit 1
) ep2 on 1=1
If your database does not support lateral joins, it will create a similar query involving a subquery in the ON clause.
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<EndeavorDto> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!
I want to write this query using Hibernate Criteria language. I am pretty new to Hibernate and not able to convert this query into Criteria form. I referred lots of answers available on SO but in my case I am using inner join on different columns rather than primary key/ foreign key column. I referred this but still can't make it right.
select TableA.columnA1, TableA.columnA2, TableA.columnA3, TableB.columnB1, TableC.columnC2 from TableA inner join TableB
on
cast(TableA.columnA3 as Integer) = TableB.columnB2
inner join
TableC
on
TableB.columnB3 = TableC.columnC1
To handle the joining logic, you are going to want to use from for each of the tables and include all of your conditions from the on-clauses in the where predicate.
Here is a JPA example that handles a parent-child relationship without having a foreign-key relationship:
EntityManager em = getDb().getEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Child> criteria = cb.createQuery(Child.class);
Root<Parent> p = criteria.from(Parent.class);
Root<Child> c = criteria.from(Child.class);
Predicate condition = cb.and(
cb.equal(c.get(Child_.parentId), p.get(Parent_.id)),
...
);
criteria.where(condition);
criteria.select(c);
criteria.orderBy(cb.asc(c.get(Child_.createDate)));
TypedQuery<Child> q = em.createQuery(criteria).setMaxResults(limit);
A JPA example is provided here, because the Hibernate criteria API is deprecated in favor of the JPA criteria API (see Legacy Hibernate Criteria Queries).
I have following query which is working fine:
public ContractorContractor findContractorByName(String contractorName) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<ContractorContractor> query = builder.createQuery(ContractorContractor.class);
Root<ContractorContractor> root = query.from(ContractorContractor.class);
query.select(root).distinct(true);
Predicate namePredicate = builder.like(root.get(ContractorContractor_.name), contractorName);
query.where(builder.and(namePredicate));
return em.createQuery(query).getSingleResult();
}
Above query gives me single contractor by name or throws exception.
Now I would like to do same thing but get more informations about contractor (add the fetch to another child of contractor) but with following query I do not get result (org.springframework.dao.EmptyResultDataAccessException: No result found for query is thrown). Query with fetch:
public ContractorContractor findContractorByName(String contractorName) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<ContractorContractor> query = builder.createQuery(ContractorContractor.class);
Root<ContractorContractor> root = query.from(ContractorContractor.class);
root.fetch(ContractorContractor_.countries);
query.select(root).distinct(true);
Predicate namePredicate = builder.like(root.get(ContractorContractor_.name), contractorName);
query.where(builder.and(namePredicate));
return em.createQuery(query).getSingleResult();
}
Can anybody tell me what I am doing wrong and why in above query?
It appears that you have accidentally limited the domain of your query by adding a inner fetch join with the addition of the following statement:
root.fetch(ContractorContractor_.countries);
As per JPA 2.1, JSR 338, section 6.5.4
[...] A fetch join has the same join semantics as the corresponding inner or outer join [...]
Thus changing the implicit inner fetch join (JoinType.INNER) to an (outer) left fetch join should solve your problem:
root.fetch(ContractorContractor_.countries, JoinType.LEFT);
This side effect might seem a little unexpected which is probably why the authors of the specification added the following comment (although in context with standard joins, section 4.4.7):
Application developers should use caution in defining identification variables because the domain of the query can depend on whether there are any values of the declared type.
I'd like to change the default join type from the criteriaBuilder from INNER to LEFT. Is this possible in some way? Atm I'm doing things like:
Join<Request, Ship> requestShipJoin = requestRoot.join("ship", JoinType.LEFT);
Join<Ship, Visit> shipVisitJoin = requestShipJoin.join("visit", JoinType.LEFT);
I've got a lot of nullable foreign keys, but if the FK is null the result still has to return (used with ordening). I'm hoping that this is possible since all the joins that I have to do are of type LEFT.