CriteriaBuilder join two tables with a custom condition - java

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));

Related

How to fetch Data using JPA for dynamic Where Clause

I am new to JPA. I am currently using JPA2.0 in WAS 8.5.5
I have a search screen where we have lot of search criteria's. Now, I have to create Query in JPA such a way that any criteria user has selected, it automatically check for that particular column in DB.
I am not able to find any solution on that. It seems to me that for every search criteria, I have to write new named Query.
Any suggestion or pointers will be appreciated.
You could do it in named query, but you would have to check every single possible criteria if it is null in order to avoid null values affect the results. Beware of unnecessary inner joins, each nullable relation should be included as left join
select e from Employee e left join e.department d
where (:name is null or e.name = :name)
and (:email is null or e.email = :email)
and (:deptId is null or d.id = :deptId)
...
However, depending on complexity of possible combinations, better option could be to not use named queries, but dynamically construct a JPQL depending on selected criteria.

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 and multiple join

is possible with Hibernate criteria do it?
select A.something, B.something, C.something, D.something
from A JOIN B on A.id = B.id_fk
JOIN C ON B.id = C.id_fk
JOIN D ON C.id = D.id_fk;
I have got exactly the same problem, and was able to resolve it like this:
return criteria.createCriteria(A.class)
.createCriteria("b", "join_between_a_b")
.createCriteria("c", "join_between_b_c")
.createCriteria("d", "join_between_c_d")
.add(Restrictions.eq("some_field_of_D", someValue));
Note: "b", "c" and "d" in code above refer to attribute names in A, B and C classes, correspondingly (class A has attribute b and so on).
For this solution you don't even need to have lazy and fetch parameters to be set in your A.hbm.xml.
There are some good examples in the Hibernate Reference material that show to use setFetchMode to fetch associations with an outer join.
An example is:
List books = sess.createCriteria(Book.class)
.setFetchMode("chapters", FetchMode.EAGER)
.setFetchMode("reviews", FetchMode.EAGER)
.list();
There is also information there about different fetching stragies that may be of use to you.
Try setting the fetch mode in your criteria, like:
criteria.setFetchMode(..., FetchMode.EAGER)
This creates a join query.
You may find more details here.
Yes, in fact there are several ways of doing this:
When mapping the association, set its lazyness to false and its fetch mode to join. This will affect all criteria queries.
Use setFetchMode as detailed by the other answers.
Use criteria.createAlias (or createCriteria). This also allows you to further restrict the rows you want joined.

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