How to solve duplicated rows in the JPQL query with many-to-one? - jpa

My query almost works but it returns duplicate rows, because the team name is duplicated. How can I solve this problem?
I have : "team glass", "team foot", and "team swim".
TeamUserCompany has a many-to-one relationship to Team.
When I query for team names containing "team" I get duplicated rows because it contains "team glass", "team foot", and "team swim"!
In the front end I need to show companies!
#Query("SELECT distinct teamUserComp FROM TeamUserCompany teamUserComp WHERE teamUserComp.team.name like %?1% ");
Entity
public class TeamUserCompany extends AbstractEntity {
#ManyToOne(optional = false)
private Company company;
#ManyToOne
private User user;
#ManyToOne
private Team team;
}

If you want distinct companies you have to select distinct companies and not distinct TeamUserCompany:
SELECT distinct c
FROM TeamUserCompany tuc
JOIN tuc.company c
WHERE tuc.team.name like %?1%

I used a Group by it solved the problem.
#Query("SELECT distinct teamUserComp FROM TeamUserCompany teamUserComp WHERE teamUserComp.team.name like %?1% group by (teamUserInter.company)");

Related

JPA Query Left Join with A non-declared relation

I need to query an Entity with a left join with another entity. But I can only declare the ManyToOne relation on the left table:
First table:
#Entity
public class ShipyardModel {
private Long id
....etc
}
Second table (LEFT one):
#Entity
public class Boat{
private Long id;
...
#ManyToOne
#JoinColumn(name = "shipyard_model_id")
private ShipyardModel shipyardModel;
}
Because of serialization reasons I can't declare the #OneToMany relation in the first entity. But they are related, because in the Boat table, i have the shipyard_mode_id. So I created a DTO to query this guy for me and created a JPA query:
Query(" SELECT new br.com.easymarine.dto.ShipyardModelDTO" +
"(sm.id, sm.name, sm.commercialLength, sm.length, sm.width, sm.height, sm.beam, sm.weightWithoutMotor, sm.weightWithMotor, sm.modelYear, sm.shipType,s.id, s.name,count(b.id)) " +
"from ShipyardModel sm " +
"join sm.shipyard s " +
"left join Boat b on b.shipyardModel = sm GROUP BY sm.id")
List<ShipyardModelDTO> findAllShipyardModelDTO();
The Spring is giving me
Path expected for join!
error
Is it possible to execute this query the way it is?

Spring Data query: how to express 'at least one of the element of this list is member of...'

For now, I have a Repository class, that has a Department and a list of Users.
For a connected user, I want to retrieve all repositories from a dedicated department or repositories where the user is listed.
Here is how it is managed using Spring Data query:
#Query(value = "select distinct repository " +
"from Repository repository " +
"left join repository.users " +
"where analysis.department =:department " +
"or :connectedUser member of repository.users ",
countQuery = "select count(distinct repository) from Repository repository")
Page<Analysis> findAllByUser(Pageable pageable,
#Param("department") Department department,
#Param("connectedUser") User connectedUser);
It is working well.
I have now to integrate a new feature.
The repository can be linked to a list of UserGroup.
I would like to update my method with something like this:
#Query(value = "select distinct repository " +
"from Repository repository " +
"left join repository.users " +
"left join repository.userGroups " +
"where analysis.department =:department " +
"or :connectedUser member of repository.users " +
"or (at least one element of :userGroupList) member of repository.userGroups ",
countQuery = "select count(distinct repository) from Repository repository")
Page<Analysis> findAllByUser(Pageable pageable,
#Param("department") Department department,
#Param("connectedUser") User connectedUser,
#Param("userGroupList") Set<UserGroup> userGroupList;
Is there a way to do that? If so, what should I use instead of (at least one element of userGroupList)?
Maybe, member of is not the best approach... If so, could you advise another one? My main objective is to be able to manage that in a single query to get page results.
Thanks in advance for all help and advice you could provide! :)
If it can help, here are entities used (high level, not in detail):
public class Repository {
private String name;
private Department department;
#ManyToMany
private Set<User> users = new HashSet<>();
#ManyToMany
private Set<UserGroup> userGroups = new HashSet<>();
public class UserGroup {
private String name;
private Department department;
private Set<User> users = new HashSet<>();

Rewrite SQL query with JOINS in JPA

I have this SQL query for MariaDB.
select #ref:=id as id, unique_id, reference_id
from mytable
join (select #ref:=id from mytable WHERE unique_id = 55544)tmp
where reference_id=#ref
https://www.db-fiddle.com/f/jKJodfVfvw65aMaVDyFySd/0
How this query can be implemented in HQL query? I would like to use it in JPA?
(Answer largely re-written after comments below)
JPA doesn't have built-in support for hierarchical queries. The main option is a native query.
E.g. with this entity class:
#Entity
public class MyTable {
#Id
#GeneratedValue
private int id;
private int uniqueId;
#ManyToOne
private MyTable reference;
// ... getters and setters ...
}
The following is an example of a native hierachical SQL query (actually against MySQL, just in case):
Query query = entityManager.createNativeQuery(
"select #ref\\:=id as id, unique_id, reference_id\r\n" +
"from my_table\r\n" +
"join (select #ref\\:=?)tmp\r\n" +
"where reference_id=#ref",
MyTable.class);
query.setParameter(1, 1);
query.getResultList();
This was chasing down a chain of references successfully.
(Other alternatives)
There probably aren't too many other options that can do this as a single query. If scalability is less of a concern, adding a back reference would be a simple way to navigate the model:
#OneToMany(mappedBy = "reference")
private Set<MyTable> backReferences;
Those would then be straightforward to recursively navigate. Clearly the relation defaults to lazy loading, so would add little overhead until used.
With #df778899's MyTable in spring-data it could look like:
#Repository
public interface MyRepository extends ...
#Query("select #ref:=id as id, unique_id, reference_id "+
"from mytable join (select #ref:=id from mytable WHERE unique_id = :pUid) tmp "+
"where reference_id=#ref", //just copy paste the query, use :pUid instead of constant...
nativeQuery = true) // and this!
List<MyTable> myCustomHirachicalQuery(#Param("pUid") Integer uid/*String/Long/...*/);
...

Constraint the number of rows returned by JOIN FETCH in JPA

I'm having a Product object with a list of related products (which are also product objects). The field of related products is annotated like this:
public class Product {
#JoinTable(name = "RELATED_PRODUCT", joinColumns = {
#JoinColumn(name = "PRODUCT_ID", referencedColumnName = "id", nullable = false)}, inverseJoinColumns = {
#JoinColumn(name = "RELATED_PRODUCT_ID", referencedColumnName = "id", nullable = false)})
#ManyToMany(fetch = FetchType.LAZY)
List<Product> relatedProducts;
}
As you can see the list is fetched lazy, which is what I want in most cases. In some cases however, I want the list of related products to be filled immediatly. I created a query for this with a LEFT JOIN FETCH. However, I want only the related products to be added that have a certain rating, let's say a rating of > 3.
I tried the following:
SELECT DISTINCT p FROM Product p LEFT JOIN FETCH p.comparableProducts cp WHERE p.id = :id AND cp.rating > 3 AND CURRENT_DATE BETWEEN p.commenceDate AND p.removeDate
But this doesn't work. It always returns back ALL related products in the database, not just the ones that have a rating above 3. How is this fixable?
The easiest way to solve this problem is to load related products separately instead of trying to fit them into relatedProducts field.
It also makes perfect sense from object oriented point of view. I suppose you have something like "Product page" that contains the selected product and "recommended products". If so, such a page is a separate concept that deserves its own class:
public class ProductPage {
private Product product;
private List<Product> recommendedProducts;
...
}
Then you can fill such a class either by a single query:
SELECT DISTINCT p, cp FROM Product p LEFT JOIN p.comparableProducts cp WHERE p.id = :id AND cp.rating > 3 AND CURRENT_DATE BETWEEN p.commenceDate AND p.removeDate
or by two separate queries.
Unfortunately, this approach doesn't allow you to receive an instance of ProductPage directly from JPA, you need to write conversion code manually.

JPA: How to filter an association?

My model:
#Entity
class Person {
#Id
long id;
#OneToMany
Set<Employment> employments = new HashSet<Employment>();
}
#Entity
class Employment {
#Id
long id;
#ManyToOne
Company company;
Date from;
Date until;
}
It's an association between a Person and a Company that's restricted by a time interval.
I'm looking for a JPA criteria query to select all Persons and their employments at a given time.
The expected result is a List<Person> containing all people, where the collection employments of each Person contains only employments that match certain criteria (or no emplyments at all).
#Where is not a sensible approach because the filter criteria for employments is variable, e.g. I would want to select all employments of a person at any given time.
Is this even a reasonable thing to do? Any suggestions how to do this differently?
No, it's not a reasonable thing to do. An entity is supposed to represent the data that is in the database, and not the date returned by a particular query.
I would simply add a ManyToOne association from Employment to Person, and search for employments. If you need the set of persons, just iterate through the employments and add each employment's person to a Set. If you want the persons associated with their employments at this date, you could use a custom class, or a MultiMap<Person, Employment>.
String hql = "select employment from Employment employment"
+ " left join fetch employment.person"
+ " where :date between employment.startDate and employment.endDate";
List<Employment> employments = session.createQuery(hql)
.setDate("date", date)
.list();