How to fetch all data in one query - java

I have multiple entities that are queried via JPA2 Criteria Query.
I am able to join two of these entities and get the result at once:
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<LadungRgvorschlag> criteriaQuery = criteriaBuilder.createQuery(LadungRgvorschlag.class);
Root<LadungRgvorschlag> from = criteriaQuery.from(LadungRgvorschlag.class);
Join<Object, Object> ladung = from.join("ladung");
from.fetch("ladung", JoinType.INNER);
Then i try to join an additional table like that:
ladung.join("ladBerechnet");
ladung.fetch("ladBerechnet", JoinType.LEFT);
i get the following error:
org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=generatedAlias3,role=null,tableName=ladberechnet,tableAlias=ladberechn3_,origin=ladungen ladung1_,columns={ladung1_.id ,className=de.schuechen.beans.tms.master.LadBerechnet}}] [select generatedAlias0 from de.schuechen.beans.tms.master.LadungRgvorschlag as generatedAlias0 inner join generatedAlias0.ladung as generatedAlias1 inner join generatedAlias1.ladBerechnet as generatedAlias2 left join fetch generatedAlias1.ladBerechnet as generatedAlias3 inner join fetch generatedAlias0.ladung as generatedAlias4 where ( generatedAlias0.erledigt is null ) and ( generatedAlias0.belegart in (:param0, :param1) ) and ( generatedAlias1.fzadresse in (:param2, :param3) ) and ( generatedAlias1.zudatum<=:param4 ) and ( 1=1 ) order by generatedAlias0.belegart asc, generatedAlias1.fzadresse asc, generatedAlias1.zudatum asc, generatedAlias1.zulkw asc]
How can i tell JPA/Hibernate, that it should select all the entities at once?

With JPA 'some dialects of JPA' you can chain join fetches, but I don't think you can/should do both a join and a join fetch.
For instance, if we have a Program that has a one-to-many relation to a Reward that has a relation to a Duration, the following JPQL would get a specific instance with the rewards and duration pre-fetched:
SELECT DISTINCT
program
FROM
Program _program
LEFT JOIN FETCH
_program.rewards _reward
LEFT JOIN FETCH
_reward.duration _duration
WHERE
_program.id = :programId
}
With the equivalent Criteria code:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Program> criteriaQuery = criteriaBuilder.createQuery(Program.class);
Root<Program> root = criteriaQuery.from(Program.class);
Fetch<Program, Reward> reward = root.fetch("rewards", JoinType.LEFT);
Fetch<Reward, Duration> duration = reward.fetch("duration", JoinType.LEFT);
criteriaQuery.where(criteriaBuilder.equal(root.get("id"), programId));
TypedQuery<program> query = entityManager.createQuery(criteriaQuery);
return query.getSingleResult();
Note that the intermediate variables reward and duration are not needed here, but they're just for informational purposes. root.fetch("rewards", JoinType.LEFT).fetch("duration", JoinType.LEFT) would have the same effect.

What it comes to JPA, you cannot chain join fetches in Criteria API queries (citation from specification):
An association or attribute referenced by the fetch method must be
referenced from an entity or embeddable that is returned as the result
of the query. A fetch join has the same join semantics as the
corresponding inner or outer join, except that the related objects are
not top-level objects in the query result and cannot be referenced
elsewhere by the query.
And it is also not supported in JPQL queries:
The association referenced by the right side of the FETCH JOIN clause
must be an association or element collection that is referenced from
an entity or embeddable that is returned as a result of the query.
It is not permitted to specify an identification variable for the
objects referenced by the right side of the FETCH JOIN clause, and
hence references to the implicitly fetched entities or elements cannot
appear elsewhere in the query.
With HQL it seems to be possible: Hibernate documentation EclipseLink does not provide such a extension, so syntax of following query is accepted by Hibernate, but not by EclipseLink:
SELECT a FROM A a LEFT JOIN FETCH a.bb b LEFT JOIN FETCH b.cc
In EclipseLink same can be done via query hints.

Related

Hibernate criteria - Inner Join OR Condition

I want to ignore default Join Restriction in createAlias. I have a OnetoOne relationship.
My Problem is Hibernate generates default restriction for the join relationship.
Pojo
Note : No column for diagnosticTemplate in charge table.
Charge.java
#OneToOne(mappedBy = "charge")
private DiagnosticTemplate diagnosticTemplate;
DiagnosticTemplate.java
#OneToOne
#JoinColumn(name = "charge")
#Exclude
private Charge charge;
Query
select
*
from
charges c
inner join diagnostic_template dt
on (dt.charge = c.id and dt.status=1) or (dt.status=0)
Criteria
Criteria criteria = getSession().createCriteria(Charge.class, "charge")
.createAlias("charge.diagnosticTemplate", "diagnosticTemplate",
JoinType.INNER_JOIN,
Restrictions.or(
Restriction.and(Restrictions.eqProperty("charge.id",
"diagnosticTemplate.charge"),
Restrictions.eq("diagnosticTemplate.status",true)),
Restrictions.eq("diagnosticTemplate.status",false) ))
Hibernate Query
select
*
from
charges c
inner join diagnostic_template dt
on dt.charge = c.id and (dt.charge = c.id and dt.status=1) or (dt.status=0)
How to avoid this condition? or anything wrong with my relationship?
Please help..!
When you join a charge with charge.diagnosticTemplate, it means that Hibernate will try to lookup a DiagnosticTemplate that is linked with this charge. Thus, the generated condition dt.charge = c.id does make sense.
Remember that you have define the relationship using foreign key. So, soft relation like dt.status=0 cannot be understood by Hibernate.
If you still wish to achieve you query, you can consider joining the two instance indirectly (not using association path). Refer to How to join Multiple table using hibernate criteria where entity relationship is not direct?.

org.hibernate.hql.internal.ast.QuerySyntaxException: Path expected for join in hql query [duplicate]

I keep trying variations of this query and can't seem to make this happen. I've also referenced this post: Path Expected for Join! Nhibernate Error and can't seem to apply the same logic to my query. My User object has a UserGroup collection.
I understand that the query needs to reference entities within the object, but from what I'm seeing I am...
#NamedQuery(
name = "User.findByGroupId",
query =
"SELECT u FROM UserGroup ug " +
"INNER JOIN User u WHERE ug.group_id = :groupId ORDER BY u.lastname"
)
select u from UserGroup ug inner join ug.user u
where ug.group_id = :groupId
order by u.lastname
As a named query:
#NamedQuery(
name = "User.findByGroupId",
query =
"SELECT u FROM UserGroup ug " +
"INNER JOIN ug.user u WHERE ug.group_id = :groupId ORDER BY u.lastname"
)
Use paths in the HQL statement, from one entity to the other. See the Hibernate documentation on HQL and joins for details.
You need to name the entity that holds the association to User. For example,
... INNER JOIN ug.user u ...
That's the "path" the error message is complaining about -- path from UserGroup to User entity.
Hibernate relies on declarative JOINs, for which the join condition is declared in the mapping metadata. This is why it is impossible to construct the native SQL query without having the path.
You'll be better off using where clauses. Hibernate does not accept inner joins for tables where the PK/FK relationship is not there between Entities
do
SELECT s.first_name, s.surname, sd.telephone_number FROM Student s, StudentDetails sd WHERE s.id = sd.student_id
instead of
SELECT s.first_name, s.surname, sd.telephone_number FROM Student s INNER JOIN StudentDetails sd on s.id = sd.student_id
The latter will only work if Student's id (s.id) is referenced as FK on StudentDetails (sd.student_id)) table design / erd

Hibernate Native SQL Query retrieving multiple entities in join

Referencing with this answer to this correlated thread,
the trick posted by ehrhardt works fine.
But, what I have to do if I have to join with multiple entities? for example:
List<Person> peopleWithBooks = session.createSQLQuery(
"select {p.*}, {b.*}, {m.*} from person p, book b, magazine m where <complicated join>")
.addEntity("p", Person.class)
.addJoin("b", "p.books")
.addJoin("m", "p.magazines")
.addEntity("p", Person.class)
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
.list();
Hibernate aggregates right the first join, but not the second (magazine entities are not grouped).
There are any tricks or there is a limit to join with only one correlated entity?
And if I have to join with entities that have sub entities? (my goal is to retrieve all selected data with only one custom query)

Using the JPA Criteria API, can you do a fetch join that results in only one join?

Using JPA 2.0. It seems that by default (no explicit fetch), #OneToOne(fetch = FetchType.EAGER) fields are fetched in 1 + N queries, where N is the number of results containing an Entity that defines the relationship to a distinct related entity. Using the Criteria API, I might try to avoid that as follows:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<MyEntity> query = builder.createQuery(MyEntity.class);
Root<MyEntity> root = query.from(MyEntity.class);
Join<MyEntity, RelatedEntity> join = root.join("relatedEntity");
root.fetch("relatedEntity");
query.select(root).where(builder.equals(join.get("id"), 3));
The above should ideally be equivalent to the following:
SELECT m FROM MyEntity m JOIN FETCH myEntity.relatedEntity r WHERE r.id = 3
However, the criteria query results in the root table needlessly being joined to the related entity table twice; once for the fetch, and once for the where predicate. The resulting SQL looks something like this:
SELECT myentity.id, myentity.attribute, relatedentity2.id, relatedentity2.attribute
FROM my_entity myentity
INNER JOIN related_entity relatedentity1 ON myentity.related_id = relatedentity1.id
INNER JOIN related_entity relatedentity2 ON myentity.related_id = relatedentity2.id
WHERE relatedentity1.id = 3
Alas, if I only do the fetch, then I don't have an expression to use in the where clause.
Am I missing something, or is this a limitation of the Criteria API? If it's the latter, is this being remedied in JPA 2.1 or are there any vendor-specific enhancements?
Otherwise, it seems better to just give up compile-time type checking (I realize my example doesn't use the metamodel) and use dynamic JPQL TypedQueries.
Instead of root.join(...) you can use root.fetch(...) which returns Fetch<> object.
Fetch<> is descendant of Join<> but it can be used in similar manner.
You just need to cast Fetch<> to Join<> it should work for EclipseLink and Hibernate
...
Join<MyEntity, RelatedEntity> join = (Join<MyEntity, RelatedEntity>)root.fetch("relatedEntity");
...
Starting with JPA 2.1 you can use dynamic entity graphs for this. Remove your fetch and specify an entity graph as follows:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<MyEntity> query = builder.createQuery(MyEntity.class);
Root<MyEntity> root = query.from(MyEntity.class);
Join<MyEntity, RelatedEntity> join = root.join("relatedEntity");
query.select(root).where(builder.equal(join.get("id"), 3));
EntityGraph<MyEntity> fetchGraph = entityManager.createEntityGraph(MyEntity.class);
fetchGraph.addSubgraph("relatedEntity");
entityManager.createQuery(query).setHint("javax.persistence.loadgraph", fetchGraph);
Using root.fetch() on EclipseLink will create a SQL with INNER JOIN because have 3 types and the default is INNER.
INNER, LEFT, RIGHT;
The suggestion is to use CreateQuery.
TypedQuery<T> typedQuery = entityManager.createQuery(query);
EDIT: You can Cast the root to From like this:
From<?, ?> join = (From<?, ?>) root.fetch("relatedEntity");

How to implement with hibernate criteria Object the select query with inner join

I'm new with Hibernate and Criteria Query.
So I have implemented the query in HQL:
select A.mobilephone
B.userNick
C.creditCard
from mobile_table A
inner join user_table B
on A.codmobile=B.codmobile
inner join Credit C
on A.mobileCredit= C.mobileCredit
How can I implement it with Hibernate Criteria Object?
Your example is just a native SQL , not the HQL . Anyway , you can use the following methods from the Criteria API to construct the desired Criteria object :
Use the setProjection(Projection projection) to define the select clause
Use the createCriteria(String associationPath,String alias) to define the inner join
For example , if the SQL is :
select
TableA.columnA ,TableB.columnB ,TableC.columnC
from TableA
inner join TableB on TableA.tableB_id=TableB.id
inner join TableC on TableA.tableC_id=TableC.id
where TableA.columnAA="AAA"
and TableB.columnBB="BBB"
and TableC.columnCC="CCC"
Then the Criteria object will be:
List<Object[]>resultList= (List<Object[]>)session.createCriteria(TableA.class, "aliasOfTableA")
.add(Restrictions.eq("aliasOfTableA.columnAA", "AAA"))
.createCriteria("aliasOfTableA.tableBId" , "aliasOfTableB")
.add(Restrictions.eq("aliasOfTableB.columnBB", "BBB"))
.createCriteria("aliasOfTableA.tableCId" , "aliasOfTableC")
.add(Restrictions.eq("aliasOfTableC.columnCC", "CCC"))
.setProjection( Projections.projectionList()
.add( Projections.property("aliasOfTableA.columnA") )
.add( Projections.property("aliasOfTableB.columnB"))
.add( Projections.property("aliasOfTableC.columnC") )
).list();
for (Object[] obj : resultList) {
System.out.println(obj[0]); //print the value from TableA.columnA
System.out.println(obj[1]); //print the value from TableB.columnB
System.out.println(obj[2]); //print the value from TableC.columnC
}
Please note that all parameters in the Criteria use the property name and class name in the mapped Java entities.
Are you sure that's HQL? Looks like native SQL to me.
In order to use Criteria your entities need to be mapped and associated with each other (since Criteria only allows navigating association paths). I think you could get more help if you give some details about your mappings.

Categories