FetchTypes in Spring Data JPQL Query (MultipleBagFetchException) - spring

I am using Spring Data and defining following query:
#Query("SELECT u FROM AppUser u LEFT OUTER JOIN fetch u.userRights a LEFT OUTER JOIN fetch u.userGroups g LEFT OUTER JOIN fetch u.userGroups ug LEFT OUTER JOIN FETCH ug.groupRights where u.login = :login")
public Optional<AppUser> findOneWithCompleteRights(#Param("login") String login);
As you might see, I want to get back the logged in user with all his access rights. While starting the spring application, it runs into:
Caused by: javax.persistence.PersistenceException: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
I have checked following:
Multiple fetches with EAGER type in Hibernate with JPA
If I change all#XXXToMany Types to java.util.Set, it works, but I would like to decide the type on my own...
The other annotations of linked solution (see bottom) seem to be ignored if attached to the #Query method. Second would not make sense, anyway.
Load each collection separately using subselect #Fetch(FetchMode.SELECT)
Force usage of list instead of bag by adding index column #IndexColumn(name="LIST_INDEX")
Does anybody have another solution rather than setting the type to Set?

I had that problem also.
It happens when the class to load has more than on property of type List mapped.
You can resolve that,
by changing the type of
AppUser.userRights and AppUser.userGroups
from java.util.List to java.util.Set.

Not using Set here instead of List is exactly what Vlad Mihalcea is describing in this post and in some answers on StackOverflow. In his post he says to use separate queries.
But he is giving the solution not with the #Query-Annotation provided by the Spring-Data Project rather using JPQL-statements and the entityManager.
So to achieve the separate Query-Execution I would have thought, that the #Fetch(FetchMode.SELECT) (or SUBSELECT, not yet sure about the difference) would be a proper solution. But not for the #Query-Annotation but for the attribute in the class.
So in your case maybe:
class AppUser {
....
#OneToMany
#Fetch(FetchMode.SELECT)
List<UserRights> userRights
....
}
But trying that in my own project did not work, either. So another solution might be to somehow create multiple queries in the Statement of the #Query-Annotation, to act according Vlad's suggestion Not sure if that is possible in the Annotation-Parameter itself or if there has to be two Methods with two annotations.
#Query(
"SELECT u FROM AppUser u
LEFT OUTER JOIN fetch u.userRights a
LEFT OUTER JOIN fetch u.userGroups g
LEFT OUTER JOIN fetch u.userGroups ug
LEFT OUTER JOIN FETCH ug.groupRights where u.login = :login"
)
public Optional<AppUser> findOneWithCompleteRights(#Param("login") String login);

Related

How to use multiple JOINs with CriteriaBuilder to build Predicate?

I have table A that has an attribute that is an entity in table B that in turn is connected to a table C.
The (working) SQL query looks like this:
SELECT a.* from A a
LEFT JOIN B b ON a.b_id=b.id
LEFT JOIN C c ON b.c_id=c.id where c.attribute=VALUE;
Basically the VALUE is what Im filtering on. There is a one-to-one relationship from A->B and a one-to-one relationship from B->C.
There are other parameters Im also filtering on so I have a Specification class that generates a Predicate for each parameter that is passed in to build a list of predicates which is then ANDed together at the end. The Specification is being called from my Repository using something like findAll(MySpecificationClass.search(params)).
Im having a hard time understanding how to write this SQL query using CriteriaBuilder inside my Specification class.
NOTES:
This is in a Spring Boot application written in Kotlin.
My specification class is a singleton so I can't autowire EntityManager for example.
I tried to use a chain of get()s to navigate from A, through B to C but that doesn't work - it seems to return all records regardless.
My project is using spring-data-jpa 2.2.6
Through the help of a colleague I was able to build this (also should have been INNER JOINS).
The specific predicate returned from my Specification class is:
val joinToB: Join<A, B> = root.join("B", JoinType.INNER)
val joinToC: Join<B, C> = joinToB.join("C", JoinType.INNER)
cb.equal(joinToC.get<Long>("attribute"), VALUE)

Run complex SQL Query with spring boot repositories

i have a little complex scenario using spring data and jpa currently.
My data structure is like:
And i like to create a filter: give me all events which belongs to a list of structures, within a given period and is assigned to a list of categories.
I was able to create a sample SQL statement:
select * from event e inner join period p on e.period_id=p.id
inner join category_item_ids ci on e.id=ci.item_ids
inner join category c on ci.category_id=c.id
inner join event_assignment_structure_ids es on es.event_assignment_id=e.id
where p.start between '2020-05-01 12:00:00.000000' and '2020-11-14 12:00:00.000000' and
c.id in (1) and
es.structure_ids in (1,2)
But my objects are currently not wired together by all the JPA annotations.
e.g. the "Same ID" is something i did by convention to make the parts a little more independend.
So using the JPA way is currently no option i guess due to the missing relations.
Introducing them will be also quite a lot of work.
So i was wondering if there is a possiblity to run the sql query directly (i could use native query, but thats also no option, cause the filter values are not always given, so i need 13 native queries)
I used enityManager and query Builder in the past, but thats also no option due to the missing jpa relations.
Any ideas are much welcome :-)
Regards
Oliver

How to make left join on two un-related tables in hibernate query

I am trying to write a left join in hibernate query. I am not able to finish it because the two tables have no relationship.
How to make left join query in hibernate query?
If the tables have no relationship how do you expect to join them? They have to be related somehow.
Basically you need to join ON SOMETHING, if you just want data from both tables then you can query them separately.
Some more information is definitely warranted here.
Anywho, pretending that we have two tables TA and TB, if TA is connected to TB as a TA.tbData where tbData is a #OneToMany relationship, you can do something similar to:
You should have your Root from TA, let's call it here rootTA.
Join<TA, TB> fromTA = rootTA.join('tbData', JoinType.LEFT);
// your business logic here
Either way, this is very generic, without any code information or telling us what you tried, it means next nothing.
You can use DetachedCriteria that performs subquery
an example follows
DetachedCriteria personCriteria = DetachedCriteria.forClass(Person.class);
personCriteria.setProjection(Property.forName("id"));
personCriteria.add(Restrictions.eq("personLastName", "Smith"));
Criteria criteria = getSession().createCriteria(Account.class);
criteria.add(Property.forName("personId").in(personCriteria));

join more than one table in spring jparepository

I am trying to fetch record by doing a join. I am new to spring jparepository.
I understand that there is separate repository for each entity(table) where when i implement i need to define the entity and datatype of primary key.
Could anyone please suggest how can I fetch record by joining two tables.
I have two repo as below:
public interface AEntityRepository extends JpaRepository<AEntity, Integer>
public interface BEntityRepository extends JpaRepository<BEntity, Integer>
I want to join above two entity(AEntity, BEntity).
I know I can have custom query using something like below:
#Query("SELECT ****** FROM AEntity ae")
AEntity findCustomrRecords();
However can I write the same kind of query (join query) with join.
Do i need to have a separate repository implementing some other class.
Can anyone please help.
I am using mysql.
I understand that there is separate repository for each entity(table)
This is a very common misunderstanding. You do not want to have a repository per entity, but per aggregate root. See http://static.olivergierke.de/lectures/ddd-and-spring/
Regarding your specific problem at hand: Creating a custom method in your repository interface and annotating it with a JPQL should do the trick. So you get something like:
#Query("select a from BEntity b join b.a a where b.foo = :foo")
AEntity getAllFooishAs(String foo);
You can use any join syntax JPQL offers in the query.

Using referencing class field as filter in Spring Data Repository

Context: I have three models, Owner, Property and Community. Property has a reference to its Owner, and another one to the Community.
I need to make the following query: find all the owners in a community which meet some criteria (floor number, property letter, etc, all the fields of the criteria are inside the Property class)
Is there any way to implement this in a repository without creating a bidirectional relationship or writing a native query? Something like:
Set<Owners> findAllByCommunityAndProperty_floorNumberAndProperty_letter(Community community, Property property);
(I would need a bidirectional relationship to make the query above)
You can use a query like this
SELECT o
FROM Property p
INNER JOIN property.owners o
WHERE p. ...
See http://www.objectdb.com/java/jpa/query/jpql/from#INNER_JOIN_ for various examples of join syntax.
In Spring Data JPA you will probably use the #Query annotation to bind that query to a method.

Resources