JPA: how to select data by child ID in OneToMany relation - java

I have two models Quality and ExpertsData:
#Entity(name = "QualityDepartmentData")
#Table(name = "quality")
public class QualityDepartmentData {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "qualityId")
private List<ExpertsData> ListOfExpertsData;
#Entity(name = "ExpertsData")
#Table(name = "experts")
public class ExpertsData {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private Long qualityId;
private Integer expertId;
I need to fetch list of QualityDepartmentData by the expertId. Right now I can do it by native MySQL query, like this:
#Query(value = "SELECT * FROM quality INNER JOIN experts ON quality.id = experts.quality_id WHERE experts.expert_id = ?1", nativeQuery = true)
Page<QualityDepartmentData> findAllForExpertId(long id, Pageable pageable);
That is working, but the problem is that the native query can't be dynamically sorted, so I need to write query in JPQL for ability to use org.springframework.data.domain.Pageable.
Can't find any examples for such event in official JPA docs. Also I tryed many vatiants like this, but it's not working:
#Query(value = "SELECT d FROM QualityDepartmentData d INNER JOIN ExpertsData c ON d.id = c.qualityId WHERE c.expertId = ?1")

#Query(value = "SELECT distinct q FROM QualityDepartmentData q JOIN q.ListOfExpertsData e WHERE e.expertId = ?1")
Page<QualityDepartmentData> findAllForExpertId(Integer id, Pageable pageable);
That is working. Thanks for link to example to JB Nizet!

Related

How to use Hibernate to combine efficient queries on #ManyToMany association?

I have 2 entities with a ManyToMany association between them - FeedbackApp & FeedbackAppProfile and each of them has a tenant-id FK to Tenant entity.
FeedbackApp entity:
public class FeedbackApp {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "tenant_id")
private Tenant tenant;
/*
Marked as the owner side.
*/
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "feedbackApp_profile_mapping",
joinColumns = #JoinColumn(name = "feedbackApp_id"),
inverseJoinColumns = #JoinColumn(name = "profile_id"))
Set<FeedbackProfile> profiles;
}
The FeedbackProfile entity:
public class FeedbackProfile {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "tenant_id")
private Tenant tenant;
#ManyToMany(mappedBy = "profiles", fetch = FetchType.EAGER)
Set<FeedbackApp> feedbackApps;
}
The feedbackApp_profile_mapping join table has 2 columns and looks like this:
My question: I need to create a query that gets all feedback apps for a specific feedback profile and tenant id. Is it possible to get it with Hibernate/JPA OR I have to manually query my join table?
Let Jpa worry about the optimal Sql query to generate. Your Jpa/criteria/specification query should be something like
select fp.feedbackApps from FeedbackProfile fp LEFT JOIN FETCH fp.feedbackApps where fp.id=:feedback_profile_id and fp.tenant.id=:tenant_id
Since you are asking about efficiency, better remove fetch = FetchType.EAGER from the two many-to-many mappings and use join fetch or named entity graphs to do the joins only when you need to.
Thanks to #GabiM direction, I created a join fetch query which did the job for what I needed:
#Query(value = "SELECT f FROM FeedbackApp f JOIN FETCH f.profiles p WHERE p.id = ?1 AND p.tenant.id = ?2")
Set<FeedbackApp> getFeedbackAppsByProfileId(long profileId, long tenantId);

Select from table using parameters from related tables

I have 3 tables (Entities) (postgreSQL):
#Entity
#Table(name = "application", schema = "applications")
public class Application implements Serializable {
#Id
#GeneratedValue
#Column(name = "application_id")
private long applicationId;
#Column(length = 400)
#Size(min=50, max=400)
private String applicationDescribing;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "customerId")
private Customer Customer;
.....
Also I have the abstract class User with child class Customer, which has its own table in DB.
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private Long userId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "localitiesId")
private Localities userLocality;
.....
#Entity
#AttributeOverride(name="userId", column=#Column(name="customerId"))
public class Customer extends User {
#Column(length = 8)
private String userType;
.....
User and also Customer table has many to one related table Locality, connected via id.
#Entity
#Table(name = "localities", schema = "resource")
public class Localities implements Serializable {
#Id
#GeneratedValue
#Column(name = "id")
private long localitiesId;
#Column(name = "region", length = 50)
private String region;
....
I try to realize seaching via Application, using (String) keyWord and locality_id (long) locality.
First approach:
#Query(value = "SELECT u FROM Application u WHERE u.applicationDescribing LIKE %:keyWord% " +
"AND userLocality IN (SELECT c FROM User c WHERE c.userLocality IN " +
"(SELECT l FROM Localities l WHERE l.localitiesId = :locality))")
List<Application> getApplicationsByKeyWordsAndRegion(#Param("keyWord") String keyWord, #Param("locality") long locality);
Second approach (recomended by richyen):
#Query(value = "SELECT a FROM Application a " +
"JOIN Customer c ON a.customerid = c.userid " +
"JOIN Localities L ON c.localitiesid = L.id " +
"WHERE a.applicationDescribing LIKE %:keyWord% AND L.id = :locality")
List<Application> getApplicationsByKeyWordsAndRegion(#Param("keyWord") String keyWord, #Param("locality") long locality);
Both dont work. As a result I need List <Application>, which have "keyWord" in describing field and locality in Application.Customer.locality_id. Please help
Is this what you're looking for?
SELECT a.*
FROM application a
JOIN (SELECT * FROM customer c UNION ALL SELECT * FROM executor e) as u on a.customer_id=u.id
JOIN locality l on u.locality_id=l.id
WHERE a.description like '%<keyWord>%'
AND l.region = <region>;
I guess one question I have for you is: how do you differentiation between Customer ID and Executor ID?
Correct answer:
SELECT app FROM Application app
INNER JOIN app.Customer customer
INNER JOIN customer.userLocality
locality WHERE customer.userLocality.id
= :locality AND app.applicationDescribing
LIKE CONCAT('%',:keyWord,'%')

Top 5 records by size of collection

I have following entity:
public final class Stock implements Serializable {
#JsonIgnore
#ManyToMany(mappedBy = "stocks", fetch = FetchType.LAZY)
private Set<User> users = new HashSet<>();
[Other fileds]
[getters/setters]
}
And i would like write query in jpql to get top5 Stock entity based on size of set users. So far i write native query in sql and it looks like:
SELECT s.ticker, COUNT(s.ticker)
FROM t_stock s INNER JOIN t_user_stocks us ON s.id = us.stock_id
INNER JOIN t_user u on us.user_id = u.id GROUP BY s.ticker ORDER BY count DESC
And i want a jqpl query which return top 5 stocks entity. Could someone help me?
Assuming your entity is mapped as follows.
#Entity
public class Stock {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column
private String ticker;
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "stock_user", joinColumns = { #JoinColumn(name = "STOCK_ID", nullable = false, updatable = false) }, inverseJoinColumns = { #JoinColumn(name = "USER_ID", nullable = false, updatable = false) })
private Set<User> users = new HashSet<User>();
}
I did the following using native SQL to get the result.If you insist on using JPQL, the answer here is your friend.
public interface StockRepository extends JpaRepository<Stock, Integer> {
#Query(value = "SELECT s.ticker, COUNT(s.ticker) FROM stock s INNER JOIN "
+ "stock_user us ON s.id = us.stock_id INNER JOIN user u on us.user_id = u.id GROUP BY s.ticker order by count(*) desc limit 1", nativeQuery = true)
public List<Object[]> findStock();
}

How to join fetch two associations with JPA/Hibernate

I am new to Hibernate and really need help from you guys....
I have following class: UserActionPerUserResult
#Entity
#Table(name = "user_action_per_user_result")
public class UserActionPerUserResult {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "user_email", nullable = false)
private String userEmail;
#ManyToOne
#JoinColumn(name = "user_action_id", nullable = false)
private UserAction userAction;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name="user_action_per_user_result_email_message",
joinColumns={#JoinColumn(name="user_action_per_user_result_id", referencedColumnName="id")},
inverseJoinColumns={#JoinColumn(name="email_message_id", referencedColumnName="id")})
#JsonIgnore
private List<EmailMessage> emailMessages;
....getters/setter....
}
In MYSQL, the UserActionPerUserResult is one table and for email Messages, there is another table called as UserActionPerUserResultEmailMessage which has email messages associate with id of the table UserActionPerUserResult.
I have all the data stored in the MySQL table, however I am not able to query it. I need to write a query to fetch the list of emailMessages. I am trying the following, but it is throwing exception.
TypedQuery<UserActionPerUserResult> messagesQuery = entityManager.createQuery(
"SELECT e from UserActionPerUserResult e JOIN UserActionPerUserResult.emailMessages e1 WHERE e.id = 1 and e.userAction = 1", UserActionPerUserResult.class);
List<UserActionPerUserResult> resultList = messagesQuery.getResultList();
Try writing the query like this:
TypedQuery<UserActionPerUserResult> messagesQuery = entityManager.createQuery(
"SELECT e from UserActionPerUserResult e JOIN FETCH e.emailMessages em WHERE e.id = 1 and e.userAction.id = 1", UserActionPerUserResult.class);
List<UserActionPerUserResult> resultList = messagesQuery.getResultList();
The join is using the root entity alias
The userAction.id is used against userAction, when matching against a numerical value

NamedQuery miss places table in OnetoOne relation

i have a problem with named query in my project. I have 2 entities with OneToOne relation.
#Entity
#Table(name = "SL_BRANCH_PARAMS")
public class BranchParams implements Identifiable {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.AUTO, generator = "SL_BRANCH_PARAM_SEQ_GEN")
#SequenceGenerator(name = "SL_BRANCH_PARAM_SEQ_GEN", sequenceName = "SL_BRANCH_PARAM_SEQ")
private Long id;
#JoinColumn(name = "INTEREST_ACCOUNT_ID")
#OneToOne(fetch = FetchType.EAGER)
private AccountDef interestAccount;
}
and second class with named query
#Entity
#Table(name = "SL_ACCOUNT_DEF")
#NamedQueries({
#NamedQuery(name = "AccountDef.getAvaibleInterestAccountsForBranch",
query = "SELECT ad FROM AccountDef ad LEFT JOIN FETCH ad.branchParamsInterest WHERE ad.branchParamsInterest = NULL ORDER BY ad.id ASC"),
})
public class AccountDef implements Identifiable {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.AUTO, generator = "SL_ACCOUNT_DEF_SEQ_GEN")
#SequenceGenerator(name = "SL_ACCOUNT_DEF_SEQ_GEN", sequenceName = "SL_ACCOUNT_DEF_SEQ")
private Long id;
#OneToOne(fetch = FetchType.EAGER, mappedBy = "interestAccount")
private BranchParams branchParamsInterest;
}
When i execute the named query I get this query to database
select
accountdef0_.ID as ID1_10_0_,
branchpara1_.ID as ID1_13_1_,
branchpara1_.INTEREST_ACCOUNT_ID as INTERES16_13_1_
from
SL_ACCOUNT_DEF accountdef0_
left outer join
SL_BRANCH_PARAMS branchpara1_
on accountdef0_.ID=branchpara1_.INTEREST_ACCOUNT_ID
where
accountdef0_.ID is null //this is incorrect
order by
accountdef0_.ID ASC
which is not correct because it gives me no rows as it checks if the ID in AccountDef is null instead in BranchParams.
The correct query should look like this
select
accountdef0_.ID as ID1_10_0_,
branchpara1_.ID as ID1_13_1_,
branchpara1_.INTEREST_ACCOUNT_ID as INTERES16_13_1_
from
SL_ACCOUNT_DEF accountdef0_
left outer join
SL_BRANCH_PARAMS branchpara1_
on accountdef0_.ID=branchpara1_.INTEREST_ACCOUNT_ID
where
branchpara1_.ID is null //this is correct
order by
accountdef0_.ID ASC
and such query returns the rows i want. And the question from me is, why named query checks null id value for AccountDef instead for BranchParams?
Perhaps something like this?
#NamedQuery(name = "AccountDef.getAvaibleInterestAccountsForBranch",
query = "SELECT ad FROM AccountDef ad LEFT JOIN FETCH ad.branchParamsInterest bp WHERE bp.interestAccount = NULL ORDER BY ad.id ASC")

Categories