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.
Related
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.
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.
I have an entity (Person) having a #ManyToOne relation with another entity (Availability) and other entities. When I get the Persons, I don't have the persons where the Availability is Null as Hibernate do an inner join (If I have an eager fetch), or a Select if Lazy fetch.
In the same time, I try to create another bean from the result so I use:
query.select(builder.construct(MyPerson.class,root.get("availability").get("date").....)
This will generate
select a.date, ... from Person p, Availability a Where p.availId = a.id.
As I need a Left join, I have added to my code :
Join<Availability, person> avail = root.join("availability", JoinType.LEFT);
Strange, it will generate an LEFT OUTER join but still use the old request
select **a2**.date, ...
from Person p,
LEFT OUTER JOIN Availability a1 on a1.id = p.availId
,**Availability a2**
**Where p.availId = a2.id.**
What is wrong with it?
The Only case when it generate only the Left Join is when I construct the new bean with the root. (with a Lazy Loading), but it will generate too many other queries.
query.select(builder.construct(MyPerson.class,root)
Finally, I find the Solution. In fact, I don't need to use
Join avail = root.join("availability", JoinType.LEFT);
So I have removed it, and while creating my new bean I do :
query.select(builder.construct(MyPerson.class,
root.join("availability", JoinType.LEFT).get("date"))
Now I have only one generated query with the LEFT OUTER JOIN.
I have a JPA 2 query that is driving me nuts. A SurveyQuestion may have an Organization. Here's the mapping within SurveyQuestion
#ManyToOne( optional=true )
#JoinColumn( name="organization_key" )
private Organization organization;
Organization has a field named key. I create a TypedNamedQuery with the following jpql
SELECT q from SurveyQuestion q
where q.organization IS NULL
or q.organization.key = :organizationKey
with that query I only get the questions that have the given key. If I remove the q.organization.key = :organizationKey from the query, then I get all those that have no organization, but I can't for the life of me get it to return the combination of those that have no organization or have the given organization.
JPA2 with EclipseLink provider.
This is JPA standard.
q.organization.key
resolves to an inner join on q.organization, not to a left join!
You have to do this:
SELECT q
from SurveyQuestion q
left join q.organization o
where o is null OR o.key = :organizationKey