Criteria Query with multiple cascaded joins involved - java

I have three entities: Block, Viewand TargetBlockand want to create a JPA Criteria query for the following sql.
select * from Block INNER JOIN TargetBlock tb ON tb.blockID = b.blockID INNER JOIN view v ON tb.viewID = v.viewID
Block has many views and many TargetBlocks.

For documentation of the Criteria API see,
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/Criteria#Join

Related

CriteriaBuilder join two tables with a custom condition

I want to write this SQL query
SELECT *
FROM A
LEFT OUTER JOIN B
ON A.IDRESOURCE=B.IDRESOURCE AND B.IDLANGUAGE=22;
with the JPA Criteria Builder.
I wrote the first part of the join simply with:
CriteriaQuery<A> searchQuery = criteriaBuilder.createQuery(A.class);
Root<A> aRoot = searchQuery.from(A.class);
Join<A, B> bJoin= aRoot.join("mappedB", JoinType.LEFT);
but I don't know how to implement the condition B.IDLANGUAGE=22.
Is there any way to accomplish this in Criteria Builder?
Use JPA 2.1 ON to specify it in the JOIN part, which is not the same as in the WHERE
CriteriaQuery<A> searchQuery = criteriaBuilder.createQuery(A.class);
Root<A> aRoot = searchQuery.from(A.class);
Join<A, B> bJoin= aRoot.join("mappedB", JoinType.LEFT);
bJoin.on(criteriaBuilder.equal(bJoin.get("idLanguage"), 22));
Honestly, after spending few days with this problem, we have decided to solve it via database views.
So there is a little alternative solution:
Create view that joins needed tables together according wanted conditions
Create new entity, representing data from this view and annotate it #Immutable, to make it read only
Create criteria builder only above this one entity
Also be aware that you entity needs to have UNIQUE Id, so if you use i.e. LEFT JOINs in you view, it might happen that there will be not unique id, and hibernate will have problems with that. So create new Id in you view as compound key, or random generated value.
It is a little workaround solutions, but it worked well for us.
Use where and CriteriaBuilder.equal.
em.select(aRoot).where( criteriaBuilder.equal(bJoin.get("IdLanguage"), 22));

Why this hql statement is like an inner join?

Why this HQL statement is like an inner join?
select
user.id,
user.allocationVersion,
user.tx.statusId,
user.userId,
user.nameFirst,
user.nameLast,
user.email1,
user.statusId,
user.tx.name,
user.note1,
user.note2
from
module.bb.jpa.User as user
where user.clientId = :clientId
order by user.id DESC
The TX class which is referenced in the User class can be null. Why do I get a result as if I did an inner join? I just get Users with a TX but I want all of them.
The HQL reference talks about this in section 14.4:
HQL supports two forms of association joining: implicit and explicit.
The queries shown in the previous section all use the explicit form,
that is, where the join keyword is explicitly used in the from clause.
This is the recommended form.
The implicit form does not use the join keyword. Instead, the
associations are "dereferenced" using dot-notation. implicit joins can
appear in any of the HQL clauses. implicit join result in inner joins
in the resulting SQL statement.
from Cat as cat where cat.mate.name like '%s%'
[highlighting by me].
What your HQL is basically equivalent to is:
select
user.id,
user.allocationVersion,
user.tx.statusId,
user.userId,
user.nameFirst,
user.nameLast,
user.email1,
user.statusId,
user.tx.name,
user.note1,
user.note2
from
module.bb.jpa.User as user
join module.bb.jpa.Tx as tx -- This is an abbreviated inner join
where user.clientId = :clientId
order by user.id DESC
To "correct" this behaviour you will have to explicitly specify a left outer join or left join for short.
Because you have referenced tx.name in user. It should not work if you access it in Java but in HQL to get the value for this column Hibernate make implicit join. To get all values you should do it explicitly and include outer join option.

Hibernate Criteria aPI Multiselect

If I use Hibernate Criteria API like:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery criteriaQuery = criteriaBuilder.createQuery();
Root<OneEntity> entityOneRoot = criteriaQuery.from(OneEntity.class);
Root<TwoEntity> entityTwoRoot = criteriaQuery.from(TwoEntity.class);
criteriaQuery.multiselect(OneEntity, TwoEntity);
Do I need to use EQUAL-restriction (as ON-restriction in SQL) for equaling IDs between both tables (entities)? Because of Cartesian product of both tables?
criteriaQuery.where(criteriaBuilder.equal(OneEntity.get("fk_id"), TwoEntity.get("id")));
I mean... In SQL using join we need to use on-clausule like:
select * from table_1 t1, table_2 t2 where t1.t2_id=t2.t1_id;
But I can't find information about it in API Criteria.
From JPA 2.1 specification, chapter 4.4.5 Joins:
An inner join may be implicitly specified by the use of a cartesian
product in the FROM clause and a join condition in the WHERE
clause. In the absence of a join condition, this reduces to the
cartesian product.
The main use case for this generalized style of join is when a join
condition does not involve a foreign key relationship that is mapped
to an entity relationship.
Example:
SELECT c FROM Customer c, Employee e WHERE c.hatsize = e.shoesize
In general, use of this style of inner join (also referred to as
theta-join) is less typical than explicitly defined joins over
relationships.
Since JPQL is works the same way as Criteria API then equal restriction is needed in your query.

How to select in Hibernate criteria if there is no relationship between the tables? [duplicate]

I'd like to use Hibernate's criteria api to formulate a particular query that joins two entities. Let's say I have two entities, Pet and Owner with a owner having many pets, but crucially that association is not mapped in the Java annotations or xml.
With hql, I could select owners that have a pet called 'fido' by specifying the join in the query (rather than adding a set of pets to the owner class).
Can the same be done using hibernate criteria? If so how?
Thanks,
J
This is indeed possible with criteria:
DetachedCriteria ownerCriteria = DetachedCriteria.forClass(Owner.class);
ownerCriteria.setProjection(Property.forName("id"));
ownerCriteria.add(Restrictions.eq("ownername", "bob"));
Criteria criteria = getSession().createCriteria(Pet.class);
criteria.add(Property.forName("ownerId").in(ownerCriteria));
Update: This actually performs a sub-query instead of a join but it allows you to use Criteria on two entities that do not have a hibernate relationship defined.
My understanding is that if you do this using HQL, you are creating a Cartesian join with a filter, rather than an inner join. Criteria queries do not support doing this.
In NHibernate you can use subqueries which are defined as DetachedCriteria. Not sure if it works the same in Java, most probably it is the same:
DetachedCriteria pets = DetachedCriteria.For<Pet>("pet")
.SetProjection(Projections.Property("pet.ownername"))
.Add(/* some filters */ );
session.CreateCriteria(typeof(Owner))
.Add(Subqueries.PropertyIn("name", pets);
Assumed that it is joined using the name of the owner.
Criterion ownerCriterion = Restrictions.sqlRestriction(SELECT ownerId FROM Owner WHERE ownerName ='bob');
Criteria criteria = getSession().createCriteria(Pet.class);
criteria.createCriteria("ownerId").add(ownerCriterion);
There's a SQLCriterion, which you can give arbitrary SQL, and add to your Criteria. In the SQL string, the token {alias} "will be replaced by the alias of the root entity."

Hibernate criteria: Joining table without a mapped association

I'd like to use Hibernate's criteria api to formulate a particular query that joins two entities. Let's say I have two entities, Pet and Owner with a owner having many pets, but crucially that association is not mapped in the Java annotations or xml.
With hql, I could select owners that have a pet called 'fido' by specifying the join in the query (rather than adding a set of pets to the owner class).
Can the same be done using hibernate criteria? If so how?
Thanks,
J
This is indeed possible with criteria:
DetachedCriteria ownerCriteria = DetachedCriteria.forClass(Owner.class);
ownerCriteria.setProjection(Property.forName("id"));
ownerCriteria.add(Restrictions.eq("ownername", "bob"));
Criteria criteria = getSession().createCriteria(Pet.class);
criteria.add(Property.forName("ownerId").in(ownerCriteria));
Update: This actually performs a sub-query instead of a join but it allows you to use Criteria on two entities that do not have a hibernate relationship defined.
My understanding is that if you do this using HQL, you are creating a Cartesian join with a filter, rather than an inner join. Criteria queries do not support doing this.
In NHibernate you can use subqueries which are defined as DetachedCriteria. Not sure if it works the same in Java, most probably it is the same:
DetachedCriteria pets = DetachedCriteria.For<Pet>("pet")
.SetProjection(Projections.Property("pet.ownername"))
.Add(/* some filters */ );
session.CreateCriteria(typeof(Owner))
.Add(Subqueries.PropertyIn("name", pets);
Assumed that it is joined using the name of the owner.
Criterion ownerCriterion = Restrictions.sqlRestriction(SELECT ownerId FROM Owner WHERE ownerName ='bob');
Criteria criteria = getSession().createCriteria(Pet.class);
criteria.createCriteria("ownerId").add(ownerCriterion);
There's a SQLCriterion, which you can give arbitrary SQL, and add to your Criteria. In the SQL string, the token {alias} "will be replaced by the alias of the root entity."

Categories

Resources