HQL Query with LEFT JOIN and WHERE not giving results - java

Working with HQL, on this simplified scenario:
String query = "SELECT new CustomUser(" +
"user.userID AS id," +
"user.username AS username)" +
"FROM User AS user" +
" LEFT JOIN user.friends as friend " +
" where user.username like (:query)" +
" OR " +
" friend.username like (:query) ";
This is giving back only those Users that have at least one friend, but I want to get Users by a condition, beyond of having Friends or not.
Dynamic instantiation is used because of domain requirements
I've noticed that it gives all Users, having Friends or not, if there is no condition on the joined table (friend.username like (:query))
These are my tables:
User
#Id...
protected Integer userID;
#Column(name = "username")
private String username;
#OneToMany(mappedBy = "user", cascade = CascadeType.REMOVE)
private List<Friend> friends;
Friend
#Id
private Integer friendID;
#Column(name = "username")
private String username;
#ManyToOne(cascade = CascadeType.DETACH)
#JoinColumn(name="userid")
private User user;
Note: it works as I expect on native SQL

You can try this query.
String query = "SELECT new CustomUser(" +
"user.userID AS id," +
"user.username AS username)" +
"FROM User AS user" +
" LEFT OUTER JOIN user.friends as friend " +
" where user.username like (:query)" +
" OR " +
" friend.username like (:query) ";

Related

Spring JPQL Query creating N+1 Query

My JPQL Query returns returns same results 5 more times than I expected.
I think there's 'N+1' problems maybe, so I used fetch join at the very first join of my query but it doesn't work.
code from domain Lv2
#ManyToOne
#JoinColumn(name = "level_3_id")
private Lv3 lv3;
code from domain Lv3
#ManyToOne
#JoinColumn(name = "level_2_id")
private Lv2 lv2;
code from domain Title
#ManyToOne
#JoinColumn(name = "title_info_id")
private TitleInfo titleInfo;
jpql query
#Repository
public class Lv4Repository {
#PersistenceContext
private EntityManager em;
public List<Lv4> findLv4ByLv3Id(String level_2_Title, String level_3_Title) {
return em.createQuery("select l4 from Lv4 l4 join Lv3 as l3 on l4.lv3 = " +
"(select l3 from Lv3 l3 join Lv2 as l2 on l3.lv2 = l2 " +
"join TitleInfo as ti on l2.metaInfo = ti.metaInfo " +
"join Title as t on ti.id = t.titleInfo.id " +
"where t.titleText like concat('%',:level_2_Title,'%'))" +
"join TitleInfo as ti on l3.metaInfo = ti.metaInfo " +
"join Title as t on ti.id = t.titleInfo.id " +
"where t.titleText =: level_3_Title", Lv4.class)
.setParameter("level_2_Title", level_2_Title)
.setParameter("level_3_Title", level_3_Title)
.getResultList();
}
}
Result
My Expectation
[[94], [ITKC_MO_1237A_0010_010_0010, ITKC_MO_1237A_0010_010_0020,...,ITKC_MO_1237A_0010_010_0940]]
My Result
[[470], [ITKC_MO_1237A_0010_010_0010,...,ITKC_MO_1237A_0010_010_0940,ITKC_MO_1237A_0010_010_0010,...,ITKC_MO_1237A_0010_010_0940,ITKC_MO_1237A_0010_010_0010,...,ITKC_MO_1237A_0010_010_0940,ITKC_MO_1237A_0010_010_0010,...,ITKC_MO_1237A_0010_010_0940,
ITKC_MO_1237A_0010_010_0010,...,ITKC_MO_1237A_0010_010_0940]]

#Transient can't get data from Entity

In my project, in the repository, I have a select that displays all the data I need, including those months for which I don’t have data. To do this, in my select, I create a temporary column, which I named tneDate .
#Query(value = "SELECT \n" +
" theDate, asl.id, asl.interest_payment, asl.interest_rate, asl.principal_payment, asl.total_payment, asl.actual_delta, \n" +
" asl.date, asl.modified, asl.interest_payment_modified, asl.amortization_schedule_initial_id, asl.tranche_id, asl.outstanding_usd,\n" +
" asl.disbursement, asl.floating_index_rate, asl.upfront_fee, asl.commitment_fee, asl.other_fee, asl.withholding_tax, \n" +
" asl.default_fee, asl.prepayment_fee, asl.total_out_flows, asl.net_flows, asl.user_id, asl.outstanding_principal, asl.new_row\n" +
"FROM\n" +
" GENERATE_SERIES\n" +
" (\n" +
" (SELECT MIN(ams.date) FROM amortization_schedules ams),\n" +
" (SELECT MAX(ams.date) + INTERVAL '1' MONTH FROM amortization_schedules ams),\n" +
" '1 MONTH'\n" +
" ) AS tab (theDate)\n" +
"FULL JOIN amortization_schedules asl on to_char(theDate, 'yyyy-mm') = to_char(asl.date, 'yyyy-mm')", nativeQuery = true)
List<AmortizationSchedule> findAllByDate();
Now, through the getter in Entity , I want to get only this tneDate , that is, the date that I form. I don't want it to be written to the database. Therefore, I put the #Transient annotation, but as I understand it, this annotation ignores the getter of this entity and I cannot get this data, since I get NULL. In this part :
else {
BigDecimal swaprate = getRate(previousAmortiz.getDate(), previousAmortiz.getTranche().getLocalCurrency().getId());
childReports.add(createChild(previousAmortiz.getOutstandingPrincipal(), swaprate, previousAmortiz.getTheDate()));
My Entity
#Getter
#Setter
#ToString
#Entity
#Table(name = "amortization_schedules")
public class AmortizationSchedule {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Transient
private Date theDate;
#Column(name = //)
private BigDecimal //;
how do i get the tneDate data?

Spring boot jpa query can't update field

I have an update query on hibernate on this table
class PackEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String status;
private String oldStatus;
#ManyToOne
#JoinColumn(name = "order_id")
private OrderEntity order;
...
}
And on OrderEntity I have there another relationship to another table when I have machine names.
On the JPA repository, I have the query. Basically first I search by machine and status and then I want to update the old status to put the current value of the status field and in status to put the new status. This is it:
#Transactional
#Modifying(clearAutomatically = true)
#Query("UPDATE PackEntity p " +
"SET p.oldStatus= p.status, p.status = ?3 " +
"WHERE p.id IN " +
" ( SELECT p2" +
" FROM PackEntity p2" +
" JOIN p2.order " +
" JOIN p2.order.machine" +
" WHERE p2.order.machine.name = ?1 AND p2.status = ?2)")
List<PackEntity > updateAllWithStatusByMachineNameAndStatus(String machineName, String status, String newStatus);
Now I'm having this error .QueryExecutionRequestException: Not supported for DML operations [UPDATE com.pxprox.entities.PackEntity with root cause ...
Why not create a method that does this for you? Initializing the entity and updating everything, changes will be flushed automatically at the end of the transaction. You can have a look at updating entity with spring-data-jpa
It should basically be something like:
#Autowired
private PackEntityRepository packEntityRepository;
public void updatePackEntity(PackEntity newPE) {
PackEntity packEntity = packEntityRepository.findById(newPE.getId());
packEntity.setOldStatus = packEntity.getStatus();
packEntity.setStatus = newPE.getStatus();
packEntityRepository.save(packEntity);
}
The return type of the method is wrong and also the query should be a little adapted. Use the following:
#Transactional
#Modifying(clearAutomatically = true)
#Query("UPDATE PackEntity p " +
"SET p.oldStatus = p.status, p.status = ?3 " +
"WHERE EXISTS " +
" ( SELECT 1" +
" FROM PackEntity p2" +
" JOIN p2.order o " +
" JOIN o.machine m" +
" WHERE m.name = ?1 AND p2.status = ?2 AND p2.id = p.id)")
void updateAllWithStatusByMachineNameAndStatus(String machineName, String status, String newStatus);
or even better
#Transactional
#Modifying(clearAutomatically = true)
#Query("UPDATE PackEntity p " +
"SET p.oldStatus = p.status, p.status = ?3 " +
"WHERE p.status = ?2 AND EXISTS " +
" ( SELECT 1" +
" FROM p.order o " +
" JOIN o.machine m" +
" WHERE m.name = ?1)")
void updateAllWithStatusByMachineNameAndStatus(String machineName, String status, String newStatus);

Spring Data JPA Distinct Returning Duplicate Values

In my Spring boot application I have a query which should return a distinct List of Focus' (works perfectly in MySQL)
#Query(value = "SELECT DISTINCT * FROM Focus F " +
"JOIN FrameworkFoci FF on FF.focusID = F.focusID " +
"JOIN FocusGroups FG on FF.frameworkID = FG.frameworkID " +
"JOIN GroupMembers GM on FG.groupID = GM.groupID " +
"JOIN Users U on GM.userID = U.userID " +
"WHERE U.userID = :userID", nativeQuery = true)
List<Focus> findByUserID(#Param("userID") Long userID);
However this does not return distinct values, duplicates are contained in the resulting list. Another issue is that I can't return a whole entity using #Query annotation - changing my query to SELECT DISTINCT(F) FROM Focus F gives the error java.sql.SQLSyntaxErrorException: Unknown column 'F' in 'field list'.
Furthermore, I tried changing adjusting the query to the following
#Query(value = "SELECT DISTINCT * FROM FrameworkFoci FF " +
"JOIN FocusGroups FG on FF.frameworkID = FG.frameworkID " +
"JOIN GroupMembers GM on FG.groupID = GM.groupID " +
"JOIN Users U on GM.userID = U.userID " +
"WHERE U.userID = :userID", nativeQuery = true)
however this produced the error java.sql.SQLException: Column 'focusCategory' not found.
Why is the query not returning distinct values? And why can't I return a whole entity nor use the second query?
Focus Entity:
#Entity
public class Focus {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long focusID;
#Column(name = "focusCategory")
private String focusCategory;
private String focusName;
private String focusExplanation;
#OneToMany(mappedBy = "focus")
private Set<Rating> ratings;
#ManyToMany
#JoinTable(name = "FrameworkFoci",
joinColumns = #JoinColumn(
name = "focusID"),
inverseJoinColumns = #JoinColumn(
name = "frameworkID"))
private Set<Framework> frameworks;
//image
protected Focus(){}
public Focus(String focusName, String focusCategory, String focusExplanation) {
this.focusCategory = focusCategory;
this.focusName = focusName;
this.focusExplanation = focusExplanation;
}
public Focus(String focusCategory, String focusName, String focusExplanation, Set<Rating> ratings){
this.focusCategory = focusCategory;
this.focusName = focusName;
this.focusExplanation = focusExplanation;
this.ratings = ratings;
}
public Long getFocusId() {
return focusID;
}
public void setFocusId(Long focusID) {
this.focusID = focusID;
}
public String getFocusCategory() {
return focusCategory;
}
public void setFocusCategory(String focusCategory) {
this.focusCategory = focusCategory;
}
EDIT:
I've switched from SQL to JPQL with the following query:
#Query(value = "SELECT DISTINCT focus FROM Focus focus " +
"WHERE focus.frameworks.groups.groupMembers.user.userID =:userID ")
I now get an error org.hibernate.QueryException: illegal attempt to dereference collection [focus0_.focusID.frameworks] with element property reference [groups]
Framework entity:
#Entity
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "frameworkID")
public class Framework {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long frameworkID;
private String frameworkName;
#ManyToMany
#JoinTable(name = "FrameworkFoci",
joinColumns = #JoinColumn(
name = "frameworkID"),
inverseJoinColumns = #JoinColumn(
name = "focusID"))
private Set<Focus> frameworkFoci = new HashSet<>();
#OneToMany(mappedBy = "framework", fetch = FetchType.EAGER, cascade = CascadeType.REMOVE)
private Set<Group> groups;
public Framework(){}
The following query solves the issue
#Query(value = "SELECT DISTINCT focus FROM Focus focus " +
"JOIN focus.frameworks frameworks " +
"JOIN frameworks.groups groups " +
"JOIN groups.groupMembers groupMembers "+
"WHERE groupMembers.userID =:userID ")
List<Focus> findByUserID(#Param("userID") Long userID);
Frameworks and GroupMembers are collections and hence needed to be joined, otherwise illegal attempt to dereference collection [focus0_.focusID.frameworks] with element property reference [groups] was produced
you should write your query like this:
'
SELECT DISTINCT f FROM Focus F '
The problem stems from you using SQL by specifying nativeQuery = true. SQL doesn't know about entities, just tables.
Since you presumably have many FrameworkFoci rows (and rows in all the other tables) for each Focus row, each Focus row gets repeated for each matching row in FrameworkFoci. This kind of duplicates the Focus row but the resulting rows are still distinct, because they differ in the columns from the other tables.
And then each row gets turned into a Focus entity, probably with a single element in the framework set.
So therefore query doesn't so much return duplicate results as results split into multiple entities.
Fortunately the solution should be fairly simple: Use JPQL which should be perfectly possible, since you're using only simple joins.
The following should give you a start:
#Query(value = "SELECT DISTINCT * FROM Focus F " +
"WHERE F.framework.groupMembers.user.id=:userID")
List<Focus> findByUserID(#Param("userID") Long userID);

Which join operation to use to combine several tables with JPA/SQL query?

I have a JPA database setup like the following:
#Entity
public class Contact {
#Id
private Long id;
#Column(length = 64)
private String firstname;
#Column(length = 64)
private String surname;
#OneToMany(mappedBy = "contact")
private List<Address> adresses = new ArrayList<Address>();
#OneToMany(mappedBy = "contact")
private List<Telephone> telephones = new ArrayList<Telephone>();
}
#Entity
public class Address {
#Id
private Long id;
#Column(length = 128)
private String street;
#Column(length = 16)
private String plz;
#ManyToOne
private Contact contact;
}
#Entity
public class Telephone {
#Id
private Long id;
#Column(length = 32)
private String number;
#ManyToOne
private Contact contact;
}
Now I have the following query for my search form:
em.createQuery("SELECT DISTINCT c FROM Contact c LEFT JOIN c.addresses a LEFT JOIN c.telephones t "
+ "WHERE c.surnameLIKE '%"+var_surname+"%' "
+ "AND c.firstname LIKE '%"+var_firstname+"%' "
+ "AND a.street LIKE '%"+var_street+"%'"
+ "AND t.nummer LIKE '%"+var_telephone+"%'"
).getResultList();
My question is now how can I join all 3 tables in a single JPA-query to look up for the firstname, street and number for example? I tried already with LEFT JOIN, but I don't get any results when for example the Telephone table is empty (it properly works if all tables have appropriate entries). I would like to have a result list with the Contacts even when there are no phone numbers and just one result even if a contact has more than one phone number for example.
Many thanks for your help in advance.
Did you try using "LEFT OUTER JOIN" e.g. below?
"SELECT DISTINCT c FROM Contact c LEFT OUTER JOIN c.addresses a LEFT OUTER JOIN c.telephones t "
+ "WHERE c.surnameLIKE '%"+var_surname+"%' "
+ "AND c.firstname LIKE '%"+var_firstname+"%' "
+ "AND a.street LIKE '%"+var_street+"%'"
+ "AND t.nummer LIKE '%"+var_telephone+"%'"
).getResultList();
The DISTINCT should remove the duplicates,
for getting results if there is no relationship you need to use an OR,
+ "AND ((a.id is null) OR (a.street LIKE '%"+var_street+"%'))"
Thanks for your helpful answers. Finally I had to solve the problem with a dynamic buildup of my search string, selectively add/remove some parts of the join operands depending on which fields the user left empty.
public List<Person> querySearch(String snachname, String svorname, String srechtsform, String sadresse, String sort, String sland, String stelefon, String semail, LoadGroup group) {
if(srechtsform.equals("Alle")) {
srechtsform = "";
}
if(sland.equals("Alle")) {
sland = "";
}
String q = "SELECT DISTINCT * FROM Person p LEFT OUTER JOIN Adresse a ON (a.PERSON_ID=p.ID) LEFT OUTER JOIN Telefon t ON (t.PERSON_ID=p.ID) LEFT OUTER JOIN Email e ON (e.PERSON_ID=p.ID) WHERE p.nachname1 LIKE '%"+snachname+"%'AND p.vorname LIKE '%"+svorname+"%' AND p.rechtsform LIKE '%"+srechtsform+"%' ";
if(sadresse.equals("") && sort.equals("") && sland.equals("")) {
q += "AND (a.ID IS NULL OR (a.strasse LIKE '%"+sadresse+"%' AND a.ort LIKE '%"+sort+"%' AND a.land LIKE '%"+sland+"%')) ";
} else {
q += "AND a.strasse LIKE '%"+sadresse+"%' AND a.ort LIKE '%"+sort+"%' AND a.land LIKE '%"+sland+"%' ";
}
if(stelefon.equals("")) {
q += "AND (t.ID IS NULL OR t.nummer LIKE '%"+stelefon+"%') ";
} else {
q += "AND t.nummer LIKE '%"+stelefon+"%' ";
}
if(semail.equals("")) {
q += "AND (e.ID IS NULL OR e.mailadresse LIKE '%"+semail+"%') ";
} else {
q += "AND e.mailadresse LIKE '%"+semail+"%' ";
}
q += "GROUP BY p.ID";
Query query = em.createNativeQuery(q, Person.class);
if(group!=null) { query.setHint(QueryHints.LOAD_GROUP, group); }
return query.getResultList();
}
"LEFT OUTER JOIN a ON b" style syntax is not supported in JPA (see http://chrisiecorner.blogspot.fi/2012/12/jpa-and-outer-joins.html)
You must do native query.

Categories

Resources