Hibernate Inner Join on Association Through Annotation - java

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.

Related

Criteria Builder JOIN on unreferenced table

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.

How to perform inner join with Jpa Query methods/JpaRepository

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??

Duplicated results in Hibernate OneToMany List

I have mapped an 1:N relation with a #OneToMany List, but when I access the list, the results are duplicated due to an OUTER JOIN.
This is how the mapping looks like:
#Entity
public class Programmer
#ElementCollection(fetch=FetchType.EAGER)
#CollectionTable(name="emails", joinColumns=#JoinColumn(name="id", nullable=false))
#Column(name="email", nullable=false)
protected Set<String> emails = new HashSet<String>();
#OneToMany(mappedBy="programmer", fetch=FetchType.EAGER)
private List <Game> games = new ArrayList<Game>();
When I get the attribute with prog.getGames(), the results comes duplicated because the Hibernate SQL makes an OUTER JOIN:
from programmer
left outer join emails on programmer.id=emails.id
left outer join game on programmer.id=game.id
where programmer.id=?
Is there any solution without transforming the List into a Set? I need to get the games with prog.getGames(), can not use a custom HQL or Criteria.
While the use of Set<> fundamentally resolves your issue, I'd argue that is simply a bandaid to get the expected results you're after but it doesn't technically address the underlying problem.
You should ultimately be using the default lazy fetch strategy because I'm of the opinion that eagerly loading any associations, particularly collection-based ones, are specific to a query and therefore should be toggled when you construct specific queries and not influenced as a part of your entity mapping model as you're doing.
Consider the future where you add a new query but you're only interestesd in attributes from the aggregate root entity. Your mapping model will still impose eagerly fetching those associations, you'll consume additional resources to by having a larger persistence context which means more memory consumption and impose unnecessary database joins for something which you aren't going to use.
If there are multiple collections that you need to hydrate, I would instead recommend you consider using FetchMode.SUBSELECT instead.
If we assume your query has 10 entities being returned, the default lazy strategy with 2 collections would issue 21 queries (1 for the base result set and 2 for each loaded entity).
The benefit of SUBSELECT is that Hibernate will actually only issue 3 queries (1 for the base result set and 1 for each collection to load all collection elements for all entities). And obviously, depending on certain queries, breaking one query with left-joins into 3 queries could actually perform better at the database level too.
Ive resolved this problem with #Fetch(FetchMode.SUBSELECT)
#OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Fetch(FetchMode.SUBSELECT)
private List<CompanyUserEntity> companyUserRelations;
I had the same problem. companyUserRelations had duplicate objects (I mean the same pointers to the same object, not duplicated data)
So after reading #dimitry response, I added #Fetch(FetchMode.SUBSELECT) and it worked

JPA/Hibernate query: load eagerly with subselect

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.

Hibernate - force inner join between parent and subclass

I have a parent class Party and a subclass LiveParty along with others (see below) and this is always a one to one relationship in the database. Whenever I use the Criteria API it does a Left Outer Join between the two. I want to force an Inner Join between the parent and child as this affects performance for some queries but can't find a way to do this in the documentation, can anyone suggest a way?
#Entity
#Table(name = "Party", schema = "dbo")
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class Party{
...
}
#Entity
#Table(name = "LiveParty", schema = "dbo")
#PrimaryKeyJoinColumn(name = "partyId")
public class LiveParty extends Party {
...
}
Whenever I use the Criteria API it does a Left Outer Join between the two.
That's indeed what you'll get when using a JOINED strategy, Hibernate will perform an outer join on all the tables corresponding to descendants of a given node of the inheritance tree in order to deduce and return the actual type for a given row (see this great previous answer by ChssPly76 for details).
In other words, I don't think you can force Hibernate to use an inner join, at least not without changing the mapping (using a secondary table or a OneToOne relation, I'm not sure what your constraints are exactly).
Related questions
Hibernate Inheritance Strategy and Why
How does the JPA handle partial, non-disjoint inheritance (using InheritanceType.JOINED in class-per-table scenario) along with EntityManager.find()?

Categories