I would like to fetch data with one-to-many relation with inner join only.I can easily achieve the above using written direct HQL like this:
#Query("select distinct b from Batch b inner join fetch b.transactions")
But i dont want to write query as i am using JpaRepository interface which gives us very useful feature of Query By Method.
I also tried this.
#EntityGraph(attributePaths = { "transactions" } )
List<Batch> findDistinctByIdNotNull();
This avoids N+1 problem but performs left outer join between batch & transaction entity. Can any one help how to perform inner join with using Query method feature??
Related
Is there any way to force hibernate to use an inner-join for an association using annotations? I am using Hibernate 5 via Spring Boot JPA
#Data
class Entity {
private Integer id;
#OneToMany
#JoinColumn(name="entityId", referencedColumnName="id")
private Set<ComplexObject> complexObjects;
I would like the join for complexObjects be an inner-join rather than a left join.
You shouldn't see a join by default at all since this is a #OneToMany association which is lazy by default. There must be a query explictly using LEFT JOIN FETCH or just LEFT JOIN. Other possible reasons for left joins to happen are the use of an entity graph or the use of the fetch method in the JPA CriteriaBuilder API.
In general, the left join here is correct as it is necessary to retain the expected cardinality. Fetching with an inner join would eliminate the main entity if there were no children which is probably not what one would expect when applying e.g. an entity graph.
I don't understand why you need an INNER JOIN, but depending on how the join is created, you might have to adapt your query to get the desired result.
Is it possible to perform a join with CriteriaBuilder on a table that is not referenced by the selected entity? Since CriteriaBuilder.join() expects as parameter the attribute name, it seems like it won't work.
To be a bit clearer, the original query looks like this:
select Vehicle v left join VehicleStatus vs on v.id = vs.vehicleId...
Vehicle does not define a relationship to VehicleStatus. And changes to the database are currently undesired though possible if needed.
Currently the code I have
final Join<Vehicle, VehicleStatus> vs = vehicle.join("vs", JoinType.LEFT);
vs.on(cb.equal(vs.get("vehicleId"), vehicle.get("id")));
fails with java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [vs] on this ManagedType
No, you need a mapped association to create a join clause with the CriteriaBuilder.
With Hibernate, you can join 2 unassociated entities in a JPQL query. The syntax is almost identical to SQL. But it's a Hibernate-specific feature and not part of the JPA standard.
I want to join three tables in a query but I do not know how to do that.
I tried:
List<Subclasses> list=em.createNativeQuery("select a.*,b.*,c.* from Subclasses a,Subs b,Klasses c where Subclasses.subid=Subs.subid and Subclasses.Klassid=Klasses.klassid");
But, I did not work.
Situation: "Parent" entity has multiple "Child" entities (#OneToMany, #Lazy) - two way relationship. No foreign key ("Child#parentId") field on entity.
Goal: Avoid N+1 problem by retrieving fully loaded Parent collection using sub-selects. If I understand theory of Subselect, this is my goal (2 resulting SQL queries):
select * from Parent ...;
select * from Child where parent_id in ...;
Question 1: What is the best practice to achieve this? Could you provide examples in both JPQL/HSQL and Criteria?
Question 2 (bonus): Can API manage second query division into "batches" - e.g. limit batches to 500: if 1st query loads 1000 Parents, 2a. loads Children for 500 Parents, 2b. loads for next 500.
I have tried:
Both result in SQL JOINs, it seems that I cannot use Child's foreign key without JOIN.
// 2nd query:
criteria
.createAlias("parent", "p")
.add(Property.forName("p.id")
.in(parentCriteria.setProjection(Projections.property("id"))))
.list();
// 2nd query (manual):
criteria
.createAlias("parent", "p")
.add(Property.forName("p.id").in(parentIdList))
.list();
Update (2015-04-05)
I checked that what it indeed works in EclipseLink via hints:
query.setHint("eclipselink.batch.type", "EXISTS");
This link http://blog.ringerc.id.au/2012/06/jpa2-is-very-inflexible-with-eagerlazy.html suggests that this is not possible via Hibernate and suggests manual fetching. However I cannot understand how to achieve it via HQL or Criteria, specifically how to get child.parent_id column that is not on Entity, but exists only on Database. That is, avoiding JOIN that would result from child.parent.id.
To avoid N+1 queries you can annotate relationship with
#BatchFetch(BatchFetchType.JOIN) //in eclipselink or
#BatchSize //in hibernate.
Inside queries, you can add fetch to join clause:
select p from Parent p join fetch p.children c where ...
You can add also query hints
query.setHint("eclipselink.batch", "p.children");
Or use EntityGraphs.
I have the following entities:
Machines {
id,
name
}
Favorites {
userId,
objectId,
objectType
}
Now I want to return list of machines ordered by favorites, name.
Favorites does not have any relation with Machines entities, its a generic entity which can hold various favoritable objects.
I got the sorting to work by the following raw sql. Is there any way to get this working using hibernate criterias. Basically ability to add alias for Favorites though Machines doesn't have any reference to it.
select m.* from Machines m left outer join Favorites f on m.id=f.objectId and f.userId =#userId order by f.userId desc, m.name asc
There are two ways to do this:
Use an SQL query or an SQL restriction for a criteria query.
Map the relation between Favorites and Machines as an Any type association.
Since you already have the query, executing it as a native SQL query through hibernate is the simplest solution:
sess
.createSQLQuery("SELECT ... ")
.addEntity(Machine.class)
.list();
The Any type association however, will probably benefit you in other situations where you want to query favoritables.
As said by #Maarten Winkels, you can use native SQL query that you have. You can do that but, if you want change your database then syntax may differs. So, it is recommended not to use native SQL query.
You cannot perform outer join using Hibernate criteria without any association between tables.
If you change your mind & want to add an association between these tables, then You can do something like below using criteria
List machines = session.createCriteria( Machines.class )
.createAlias("Machines", "m")
.createAlias("Favorites", "f", Criteria.LEFT_JOIN,
Restrictions.and(Restrictions.eq("m.id", "f.objectId"),
Restrictions.eq("f.userId", "#userId")))
.addOrder(Order.desc("f.userId"))
.addOrder(Order.asc("m.name"))
.list();
If for some reason you don't want to add any relationship between these tables, then you can use INNER JOIN or CROSS JOIN to get all machines with whatever criteria you want.
from Machines m, Favorites f where m.id = f.objectId and f.userId = #userId order by f.userId desc, m.name asc;
With inner joins there is one problem, if there is outer join relation in database then it doesn't work.
You can also write subqueries or separate queries & perform manual conditional checks between the results returned by those queries.
And one thing I want to ask you, What is the meaning of #userId in your SQL query ? I keep it as it is in my answer but, I didn't understand for what purpose # is there ?