I use Spring (no Boot), Spring Data JPA, Hibernate and H2 in-memory DB.
There are 2 entities: User and Post.
Post has attribute Set<User> users.
I have method that should get posts with users that have a name and then print all the posts and all their properties.
When i get posts and try to print users of these posts in the main (testing) class, it ends with LazyInitializationException. That is expected.
Using FetchType.EAGER works, as well as doing the printing in the service withing transaction or using OpenSessionInViewFilter.
However, all these solutions have their drawbacks, so I wanted to created separate method in a repository that would JOIN FETCH the users.
I need users that have a name, so my code looks like this:
#Query("SELECT p FROM Post p JOIN FETCH p.users u WHERE u.name is not null")
Collection<Post> findAllWithNamedUsers();
In this case, the JOIN FETCH is probably ignored, because I get the LazyInitializationException again:
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize
a collection of role: example.entity.Post.users, could not initialize proxy - no Session
I tried experimenting with the query (no matter if it does what I need or not)...
#Query("SELECT p FROM Post p JOIN FETCH p.users u WHERE u.is not null") does not work
#Query("SELECT p FROM Post p JOIN FETCH p.users u") does not work
#Query("SELECT p FROM Post p LEFT JOIN FETCH p.users u") DOES work
#Query("SELECT p FROM Post p LEFT JOIN FETCH p.users u WHERE u.is not null") does not work
What happens here, why does it behave like this and what can I do to make it working with the condition?
Related
I have the following model: a "classical" many-to-many book/author association but with 2 peculiarities.
Author does not have an association to Books.
The Author personal data is stored on other associated entity, called Person.
The model:
public class Book {
#ManyToMany
List<Author> authors;
}
public class Author {
#OneToOne
Person person;
}
public class Person {
String name;
}
I would like to write a service which, given a book, will return all the authors associated with it. I would also like to retrieve the personal data avoiding the N+1 problem.
Inside my Book repo I wrote a custom query like that:
#Query("select b.authors from Book b join b.authors a join fetch a.person where b = :book")
List<Author> listAuthors(#Param("book") Book book);
But I have the folloiwing error:
query specified join fetching, but the owner of the fetched
association was not present in the select list (...)
I could manage to solve it by adding the association to Books on the Author side and fetching Person from it. Unfortunately , in my case, it is really desirable that Authors do not know about Books.
Is there any way to fetch Person data from a Book?
This is actually a Hibernate Bug HHH-14116 - I recently stumbled across a related bug and in the discussion in the Hibernate Chat this bug was filed too. There is already a fix, but AFAIK it has not been released yet.
One way to circumvent this would be to have the relationship be bi-directional and query from the Author side.
Otherwise you could do it with a subquery, but I'm not sure whether that works with Spring Data JPA:
select a from Author a join fetch a.person where a.id in
(select a.id from Book b join b.authors a where b = :book)
Have you tried selecting Person with your #Query as well?
#Query("select b.authors, a.person from Book b join b.authors a join fetch a.person where b = :book")
List<Author> listAuthors(#Param("book") Book book);
I solved it using an EXISTS clause:
#Query("select a from Author a join fetch a.person " +
"where exists (select 1 from Book b where b = :book and a member of b.authors)")
List<Author> listAuthors(#Param("book") Book book);
I have a repository of Users; each User has a OneToMany relationship with a collection of Posts.
How can I filter (and page) the repository of users by the number of posts they have using JPA specifications?
My initial attempt looked as such:
public static Specification<User> receiptNumberGreaterThanOrEqualTo(int numberToFilterBy) {
return (users, query, cb) -> greaterThanOrEqualTo(cb.count(users.get("posts")), Long.valueOf(numberToFilterBy));
}
This however caused an error to the effect of:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')>=9 limit 100'
This is because the #OneToMany join expects a HAVING clause to be used rather than WHERE, i.e. select * from users outer left join posts on users.id = posts.id having(count(posts) > someNumber) where user.id = "some-id"
vs
select * from users outer left join posts on users.id = posts.id where user.id = "something" and count(posts) > someNumber;
I can generate a query with the having keyword by creating a join between user and posts, and then doing as follows:
query.having(cb.greaterThanOrEqualTo(cb.count(joinOfPosts), Long.valueOf(numberToFilterBy))), but the type of this is CriteriaQuery, and I'm unsure how to turn this into a Specification.
I solved this by using the size() method on the criteriaBuilder,
ie.
(users, query, criteriaBuilder) -> criteriaBuilder.greaterThanOrEqualTo(criteriaBuilder.size(users.get("posts")), numberToFilterBy);
I have a following problem with executing quite simple query with querydsl. Imagine that we have two entities:
CAR ----< OWNERS
I would like to execute query which returns all cars and fetches all its owners which are mapped lazily. In other words, I would like to fetch those to be used outside of transaction.
My query looks like:
List<Car> cars = new JPAQuery<Car>(em)
.select(car).from(car)
.leftJoin(car.owners, owner)
.where(car.make.eq(make))
.orderBy(new OrderSpecifier<>(Order.ASC, car.id))
.distinct()
.fetch();
Similar query worked fine in QueryDSL 3, but after upgrade to 4 I am getting LazyInitializationException, which means that 'owners' are not fetched properly. Could you please shed some light on how to solve this problem?
For example when I write this query manually it works completely fine:
List<Car> cars = em.createQuery(
"SELECT DISTINCT c FROM Car c LEFT JOIN FETCH c.owners WHERE c.make = :make ORDER BY c.id ASC")
.setParameter("make", make).getResultList();
I am using spring-boot 2 with querydsl 4.1.4
BTW, query which worked fine in querydsl 3
List<Car> car = new JPAQuery(em)
.from(car)
.leftJoin(car.owners)
.fetch()
.distinct()
.where(car.make.eq(make))
.orderBy(new OrderSpecifier<>(Order.ASC, car.id))
.list(car);
after multiple attempts I have found a solution, here is the code:
new JPAQuery<Car>(em)
.select(car)
.distinct()
.from(car)
.leftJoin(car.owners, owner).fetchJoin()
.where(car.make.eq(make))
.orderBy(new OrderSpecifier<>(Order.ASC, car.id))
.fetch();
I had the same problem and i had to change new JPAQuery(em) to new JPAQuery<Foo>(em)
I have the following query which joins over multiple tables for getting the necessary output:
public interface UserRepositoryJpaRepository <User, Long> {
#Query("SELECT o.id, u.id, u.name, i.name,i.id FROM User u, Item i LEFT JOIN u.order o LEFT JOIN o.payment p WHERE p.id=:id AND o.id=i.order")
List<MixedResult>getUsersWithOrderByPayment_id(#Param("id") Long id);
However, when running Spring Boot 1.4.1 with Spring Data JPA 1.10.5.BUILD-SNAPSHOT, I am getting the following error message :
org.springframework.dao.InvalidDataAccessApiUsageException: No aliases
found in result tuple! Make sure your query defines aliases!; nested
exception is java.lang.IllegalStateException: No aliases found in
result tuple! Make sure your query defines aliases!
However, I see that the generated SQL is fine and it works
These are a couple of question, should Spring Roo through reverse engineering adding FetchType.LAZY for Set fields in .aj files or should I do it manually?
If FetchType.LAZY is not present at .aj files, I could make it through the queries " SELECT st1 FROM parentTable t1 JOIN FETCH t1.childTable st1" into the select, right ?
The point here is that I can add FetchType.LAZY to the files manually (Refactor .aj file > Push In..) and then on .java file (high risk if I want Roo to keep the control on my domain classes).
About to make it through queries, I can't do this because I'm getting:
> 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=ii,role=...domain.Industry.i18nIndustries,
tableName=..I18nIndustry,
tableAlias=i18nindust3_,
origin=..Industry industry2_,
columns={industry2_.IdIndustry ,className=..domain.I18nIndustry}}]
[select u.idUserName, u.isActiveInd, u.employeeNbr, u.name, c.name as CompanyName, ii.name as IndustryName from Users u JOIN u.idCompany c JOIN c.idIndustry i JOIN FETCH i.i18nIndustries ii WHERE u.idUserName = :username ]
Here the .aj files generated by Roo:
privileged aspect Users_Roo_DbManaged {
...
#ManyToOne
#JoinColumn(name = "IdCompany", referencedColumnName = "IdCompany", nullable = false)
private Company Users.idCompany; ...
..
privileged aspect Company_Roo_DbManaged {
...
#ManyToOne
#JoinColumn(name = "IdCity", referencedColumnName = "IdCity", nullable = false)
private City Company.idCity;
#ManyToOne
#JoinColumn(name = "IdIndustry", referencedColumnName = "IdIndustry", nullable = false)
private Industry Company.idIndustry; ..
..
privileged aspect Industry_Roo_DbManaged { ..
#OneToMany(mappedBy = "idIndustry")
private Set<I18nIndustry> Industry.i18nIndustries; ...
Can you please give me a clue, about what is happening here ?
Spring MVC 3.2,
Roo 1.2.4,
MSSql database
Thanks folks!
--JR
About
These are a couple of question, should Spring Roo through reverse engineering adding FetchType.LAZY for Set fields in .aj files or should I do it manually?
This is not needed because this is the default for this kind of relationship (see https://stackoverflow.com/a/13765381/2295657).
If FetchType.LAZY is not present at .aj files, I could make it through the queries " SELECT st1 FROM parentTable t1 JOIN FETCH t1.childTable st1" into the select, right ?
This makes a eager loading of the relationship data. I think you are in a mistake about the meaning of LAZY and EAGER modes:
EAGER: Requires to load value when instance is loaded into memory (default for basic and no-collections-relationship properties)
LAZY: when instance is loaded a proxy is stored in property and, when anything tries to get data, loads it form DB (default for collections-relationship properties)
On the other hand, to load a LAZY property requires a DB Connection alive when anything tries to get the data, so, you must execute this code inside a no-static, #Transactional annotated, method (read #Transactional JavaDoc for more info).
I advice you to make a look in the JPA specification. In my experience, I so useful.
I found whats happening, This is your HQL
[select u.idUserName, u.isActiveInd, u.employeeNbr, u.name, c.name as CompanyName, ii.name as IndustryName from Users u JOIN u.idCompany c JOIN c.idIndustry i JOIN FETCH i.i18nIndustries ii WHERE u.idUserName = :username ]
What i understand from this hql is u want to fetch list of i18nIndustries ii along with that perticuler idIndustry i correct?
But when you use JOIN FETCH on any object you have to select the main object to hold child object, in your case main object is idIndustry. JOIN FETCH i.i18nIndustries ii means "when fetching idIndustry, also fetch the i18nIndustries, linked to the idIndustry". But your query doesn't fetch idIndustry So the fetch makes no sense.
In select either u have to fetch i also so that the owner (i) of the fetched association (i.i18nIndustries ii) will present in the select list. as your error is
query specified join fetching, but the owner of the fetched
association was not present in the select list
or remove JOIN FETCH just use JOIN
So keeping in mind u want list of i18nIndustries ii, Your query will become :(added i in select)
select u.idUserName, u.isActiveInd, u.employeeNbr, u.name, c.name as
CompanyName, i,ii.name as IndustryName from Users u JOIN u.idCompany c
JOIN c.idIndustry i JOIN FETCH i.i18nIndustries ii WHERE u.idUserName
= :username