How to fetch a association on the other side of a ManyToMany? - spring

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);

Related

JPA/Hibernate ignoring JOIN FETCH with condition on fetched data

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?

Left Join Fetch returns null with Where condition

I have two entities with Many-to-Many relationship: Author and Book.
I want to get a non-deleted author with all his non-deleted books (these entities have field deleted). And I try this #Query:
select a from Author a left join fetch a.books b
where a.id = :id and a.deleted = false and b.deleted = false
But it returns nothing, for example, if an author has only deleted books.
Of course, without b.deleted = false author is returned. But I want a query to return author without any deleted books.
How can I do that?
Maybe, with a native query?

Using "having" on join clause of Spring JPA specification

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);

Spring Roo doesn't add FetchType.LAZY for fields in .aj files, Should I do it manually?

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

Using lookup values in linq to entity queries

I'm a total LINQ noob so I guess you'll probably have a good laugh reading this question. I'm learning LINQ to create queries in LightSwitch and what I don't seem to understand is to select an entity based on a value in a lookup table. Say I want to select all employees in a table that have a job title that is picked from a related lookup table. I want the descriptive value in the lookup table for the user to pick from a list to use as a parameter in a query, not the non-descriptive id's.
Can someone point me to an article or tutorial that quickly explains this, or give me a quick answer? I AM reading books and have a Pluralsight account but since this is probably the most extensive knowledge I will need for now a simple tutorial would help me more that watching hours of videos and read thousands of pages of books.
Thanks in advance!
Edit: this is the code. As far as I know this should but won't work (red squigly line under EmployeeTitle, error says that EmployeeContract does not contain a definition for EmployeeTitle even though there is a relationship between the two).
partial void ActiveEngineers_PreprocessQuery(ref IQueryable<Employee> query)
{
query = from Employee e in query
where e.EmployeeContract.EmployeeTitle.Description == "Engineer"
select e;
}
Edit 2: This works! But why this one and not the other?
partial void ActiveContracts_PreprocessQuery(ref IQueryable<EmployeeContract> query)
{
query = from EmployeeContract e in query
where e.EmployeeTitle.Description == "Engineer"
select e;
}
The red squiggly line you've described is likely because each Employee can have 1-to-many EmployeeContracts. Therefore, Employee.EmployeeContracts is actually an IEnumerable<EmployeeContract>, which in turn does not have a "EmployeeTitle" property.
I think what you're looking for might be:
partial void ActiveEngineers_PreprocessQuery(ref IQueryable<Employee> query)
{
query = from Employee e in query
where e.EmployeeContract.Any(x => x.EmployeeTitle.Description == "Engineer")
select e;
}
What this is saying is that at least one of the Employee's EmployeeContracts must have an EmployeeTitle.Description == "Engineer"
Try something like this:
partial void RetrieveCustomer_Execute()
{
Order order = this.DataWorkspace.NorthwindData.Orders_Single
(Orders.SelectedItem.OrderID);
Customer cust = order.Customer;
//Perform some task on the customer entity.
}
(http://msdn.microsoft.com/en-us/library/ff851990.aspx#ReadingData)
Assuming you have navigation properties in place for the foreign key over to the lookup table, it should be something like:
var allMonkies = from employee in context.Employees
where employee.EmployeeTitle.FullTitle == "Code Monkey"
select employee;
If you don't have a navigation property, you can still get the same via 'manual' join:
var allMonkies = from employee in context.Employees
join title in context.EmployeeTitles
on employee.EmployeeTitleID equals title.ID
where title.FullTitle == "Code Monkey"
select employee;

Resources