JPQL inner join query - java

I am stuck in a tricky situation while writing a JPQL query, following are my tables:-
Order_
order_id quotation_id
1 11
2 12
Quotation
q_id qr_id
11 101
12 102
QRequest
qr_id name
101 Name 1
102 Name 2
#Entity
#Table(name = "Order_")
public class Order {
#Id
#GeneratedValue
private long id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "q_id", unique = true)
private Quotation quotation;
}
#Entity
public class QRequest {
#Id
#GeneratedValue
private long id;
#Column(nullable = false)
private String name;
}
#Entity
public class Quotation {
#Id
#GeneratedValue
private long id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "qr_id", nullable = false)
private QRequest qRequest;
}
public List<QRequest> getQRequestForOrders(List<Long> orderIds) {
String query = "Select qr from QRequest qr, Quotation q, Order o " +
"where o.quotation.qRequest.id = qr.id " +
"and o.id in (:orderIds) ";
TypedQuery<QRequest> typedQuery = entityManager.createQuery(query, QRequest.class);
typedQuery.setParameter("orderIds", orderIds);
return typedQuery.getResultList();
}
I am trying to get a List<QRequest> from a List of order_id.
This is the SQL equivalent query:-
select qr.* from QRequest qr
inner join Quotation q on q.qr_id = qr.id
inner join Order_ o on o.quotation_id = q.id
where o.id in (1,2);
I am looking for a equivalent JPQL query.

In this case it may be worth to set a bi-directional relationship to make it easier to query, like this for example:
#Entity
public class QRequest {
#Id
#GeneratedValue
private long id;
#Column(nullable = false)
private String name;
#OneToMany(mappedBy = "qRequest")
private Quotation quotation;
}
#Entity
public class Quotation {
#Id
#GeneratedValue
private long id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "qr_id", nullable = false)
private QRequest qRequest;
}
"Select qr from QRequest qr " +
"join qr.quotation q "
if you want to avoid it you can instead
"Select qr from QRequest qr, Quotation q, Order o " +
"where o.quotation.qRequest.id = qr.id " +
"and o.quotation.id = q.id " +
"and o.id in (:ids) "
and .setParameter("ids", your_list);
In both cases the query will return a collection of QRequest

Related

Return specified related records with #JoinTable

I have a User entity (table user_entity), which is linked with role (table keycloak_role) through reference table user_role_mapping. User can have multiple roles. I need to recieve a user with client_role = false. Since my reference table is named differently than hibernate expects, I've used #JoinTable to let it know correct ref table name. But when I run it, I recieve a User with all roles. I've added #WhereJoinTable to roles list, but Hibernate builds an incorrect query and tries to access client_role column in ref table user_role_mapping. How can I fix it?
User class
#Entity
#Table(name = "user_entity")
#Getter
#Setter
#EqualsAndHashCode
public class UserEntity {
#Id
#Column(name = "id")
private String id;
private String email;
#Column(name = "email_constraint")
private String emailConstraint;
#Column(name = "email_verified")
private String emailVerified;
private boolean enabled;
#Column(name = "federation_link")
private String federationLink;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
private String username;
#Column(name = "created_timestamp")
private Long createdAt;
#OneToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "user_group_membership",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "group_id")
)
private List<Group> groups;
#OneToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "user_role_mapping",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "role_id", referencedColumnName = "id")
)
#WhereJoinTable(clause = "client_role = false")
private List<Role> realmRoles;
}
Role class
#Entity(name = "keycloak_role")
#Table(name = "keycloak_role")
#Getter
#Setter
public class Role {
#Id
private String id;
private String name;
}
Hibernate query from logs. Last WHERE is what is wrong
Hibernate:
select
*
from
user_entity ue
join
user_group_membership ugm
on ugm.user_id = ue.id
join
keycloak_group kg
on kg.id = ugm.group_id
join
realm r
on r.id = ue.realm_id
where
username = ?
and r.id = 'CSP'
and kg.name in (
?, ?
)
Hibernate:
select
userattrib0_.id as id1_2_,
userattrib0_.name as name2_2_,
userattrib0_.user_id as user_id4_2_,
userattrib0_.value as value3_2_
from
user_attribute userattrib0_
left outer join
user_entity userentity1_
on userattrib0_.user_id=userentity1_.id
where
userentity1_.id=?
Hibernate:
select
groups0_.user_id as user_id1_4_0_,
groups0_.group_id as group_id2_4_0_,
group1_.id as id1_0_1_,
group1_.name as name2_0_1_
from
user_group_membership groups0_
inner join
keycloak_group group1_
on groups0_.group_id=group1_.id
where
groups0_.user_id=?
Hibernate:
select
realmroles0_.user_id as user_id1_5_0_,
realmroles0_.role_id as role_id2_5_0_,
role1_.id as id1_1_1_,
role1_.name as name2_1_1_
from
user_role_mapping realmroles0_
inner join
keycloak_role role1_
on realmroles0_.role_id=role1_.id
where
(
realmroles0_.client_role = false
)
and realmroles0_.user_id=?
If what I'm trying to do is impossible yet, I guess I'll have to use native query in my repo. But it does not work as intended and still returns all roles. Yet, if I run this exact query through the console, it returns 3 records with exact roles I need.
Repository
#Repository
public interface UserRepository extends JpaEntryRepository<UserEntity> {
#Query(value = "select * from user_entity ue " +
"join user_group_membership ugm on ugm.user_id = ue.id " +
"join keycloak_group kg on kg.id = ugm.group_id " +
"join realm r on r.id = ue.realm_id " +
"join user_role_mapping urm on ue.id = urm.user_id " +
"join keycloak_role kr on urm.role_id = kr.id " +
"where username = :username " +
"and r.id = 'REALM' " +
"and kr.client_role = false " +
"and kg.name in :groups ",
nativeQuery = true)
Optional<UserEntity> findByUsernameAndGroups(#Param("username") String username,
#Param("groups") List<String> groups);
}
Using #Where instead of #WhereJoinTable in User class solves the problem

HQL or JPQL - using CASE WHEN THEN END structure (Spring Boot/Java)

I have entity
#Data
#Entity
#Table(name = "event")
#DynamicInsert
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode
public class Event {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "accountable_unit_id")
private Long accountableUnitId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "type_id")
private EventType type;
#Column(name = "name")
private String name;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "status_id")
#Enumerated(EnumType.STRING)
private EventStatus status;
#Column(name = "user_info")
private String userInfo;
#Column(name = "deadline_dttm")
private Instant deadlineOn;
#Column(name = "completed_dttm")
private Instant completedOn;
#Column(name = "status_modify_dttm")
private Instant statusUpdatedOn;
#Column(name = "create_dttm")
private Instant createdOn;
#Column(name = "modify_dttm")
private Instant updatedOn;
#Version
#Column(name = "version")
private int version;
#Column(name = "status_name")
private String statusName;
And I have repository with one method
#Repository
public interface EventRepository extends JpaRepository<Event, Long>, JpaSpecificationExecutor<Event> {
#Query(value = "select e.*,\n" +
" CASE \n" +
" WHEN es.code = 'in_progress' and e.deadline_dttm < now()\n" +
" THEN 'Просрочено' \n" +
" ELSE es.\"name\"\n" +
" END status_name \n" +
"from public.\"event\" e join public.event_status es on e.status_id = es.id",
countQuery = "select count(*) from public.\"event\"",
nativeQuery = true)
Page<Event> findAllWithDynamicStatusName(Specification<Event> spec, Pageable pageable);
}
But unfortunately Specification doesn't work with native query.
Does anyone know how to rewrite this SQL to JPQL or HQL? The main problem is I can't use SQL CASE structure like I am using it in the native query.
Or maybe you can give me an advise on how to make it works with Specification and native queries?
Thank you very much for your answers!
Below query might help you
select
CASE (es.code = 'in_progress' and
es.deadline_ddtm = now()) THEN 'Просрочено'
ELSE es.name END status_name
from public.event e join public.event_status es on
e.status_id=es.id
I hope this helps!

JPQL TypedQuery - setParamer does not work

I am trying to fetch an entity which has a one-to-one relation using a named Query. My "where" condition is on the relation entity. I am giving a named parameter. When I execute the query it ignores the parameter passed and giving me all the records.
I tried with positional parameter, it too didn't work.
Query
#NamedQuery(
name = "country.by.region",
query = " select c from Country c join Region r on r.id = :regid"
)
Country Entity
public class Country {
#Id
#Column(name = "COUNTRY_ID")
private String id;
#Column(name = "COUNTRY_NAME")
private String name;
#OneToOne(targetEntity = Region.class, cascade = CascadeType.ALL)
#JoinColumn(name = "REGION_ID")
private Region region;
// ...
}
Region Entity
public class Region {
#Id
#Column(name = "REGION_ID")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "regSeq")
private int id;
#Column(name = "REGION_NAME")
private String name;
// ...
}
DAO Impl
#Override
public List<Country> findBy(Region region) {
TypedQuery<Country> query = getEntityManager().createNamedQuery("country.by.region", Country.class);
query.setParameter("regid", region.getId());
query.setMaxResults(30);
return query.getResultList();
}
Try to correct your query in this way:
select c from Country c join c.region r where r.id = :regid
See also this section of the documentation.

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,'%')

jpql select object from table based on some property from another table?

I am new to jpql.
I have two mySql tables.
Table advert: with columns:
id, name, description, phone, category etc..
And table advert_property with following columns:
id int 11
advert_id int 11
name varchar 255
value varchar 255
descr varchar 255
My goal is to choose object from table advert which has a property category = "flats" written in table advert property with name number_rooms = "234" written in table advert_property.
I'm a little bit confused with jpql syntaxis i came to this solution:
Query q = em.createQuery("SELECT ap FROM AdvertProperty as ap, Advert as a "
+ " where a.category= 'flats' and ap.advertId = a.id and ap.name='number_rooms' ");
List<Advert> ads = q.getResultList();
But it doesn't work as i needed..
Please suggest,
Thanks
Advert entity :
public class Advert implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
private Integer id;
#Size(max = 255)
private String title;
#Lob
#Size(max = 65535)
private String content;
private Integer price;
#Size(max = 255)
#Column(name = "contact_person")
private String contactPerson;
// #Pattern(regexp="[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", message="Invalid email")//if the field contains email address consider using this annotation to enforce field validation
#Size(max = 255)
private String email;
// #Pattern(regexp="^\\(?(\\d{3})\\)?[- ]?(\\d{3})[- ]?(\\d{4})$", message="Invalid phone/fax format, should be as xxx-xxx-xxxx")//if the field contains phone or fax number consider using this annotation to enforce field validation
#Size(max = 255)
private String phone;
#Column(name = "address_id")
private Integer addressId;
#Column(name = "category_id")
private Integer categoryId;
#Basic(optional = false)
#NotNull
#Column(name = "company_type")
private boolean companyType;
#Basic(optional = false)
#NotNull
private boolean approved;
#Column(name = "user_id")
private Integer userId;
#Column(name = "who_can_watch")
private Integer whoCanWatch;
#Basic(optional = false)
#NotNull
#Column(name = "creation_date")
#Temporal(TemporalType.TIMESTAMP)
private Date creationDate;
#Size(max = 255)
private String razdel;
public Advert() {
}
public Advert(Integer id) {
this.id = id;
}
AdvertProperty Entity:
#Entity
#Table(name = "advert_property")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "AdvertProperty.findAll", query = "SELECT a FROM AdvertProperty a"),
#NamedQuery(name = "AdvertProperty.findById", query = "SELECT a FROM AdvertProperty a WHERE a.id = :id"),
#NamedQuery(name = "AdvertProperty.findByAdvertId", query = "SELECT a FROM AdvertProperty a WHERE a.advertId = :advertId"),
#NamedQuery(name = "AdvertProperty.findByName", query = "SELECT a FROM AdvertProperty a WHERE a.name = :name"),
#NamedQuery(name = "AdvertProperty.findByValue", query = "SELECT a FROM AdvertProperty a WHERE a.value = :value"),
#NamedQuery(name = "AdvertProperty.findByDescr", query = "SELECT a FROM AdvertProperty a WHERE a.descr = :descr")})
public class AdvertProperty implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
private Integer id;
#Column(name = "advert_id")
private Integer advertId;
#Size(max = 255)
private String name;
#Size(max = 255)
private String value;
#Size(max = 255)
private String descr;
depending on how you implemented your entities:
SELECT a FROM Advert a JOIN a.properties ap where a.category = 'flats' and ap.name='number_rooms'
or
SELECT a FROM AdvertPropery ap JOIN ap.advert where a.category = 'flats' and ap.name='number_rooms'
However post your entities to have an exact answer.
That's not how JPA is supposed to do a mapping, you should use relational annotations (#OneToMany, #ManyToOne, ...) and mapping annotations (#JoinColumn, #JoinTable, ...)
without relations (and indexes!!) the only query you can do is similar to what you've just done:
SELECT DISTINCT ap FROM AdvertProperty ap, Advert a where a.category= 'flats' and ap.advertId = a.id and ap.name='number_rooms'
nevertheless this query is not optimized (no indexes...) and has a very bad performace.
EDIT
Since you already have a foreign key from advert_property to advert, you can model your entities accordingly
In Advert entity
...
#OneToOne(mappedBy = "advert")
private AdvertProperty property;
// or this, in case it is one to many
#OneToMany(mappedBy = "advert")
private List<AdvertProperty> properties;
...
In AdvertProperty entity
#ManyToOne // or #OneToOne
#JoinColumn("advert_id")
private Advert advert;
In case it is #OneToMany, the query would look like this
Query q = em.createQuery("SELECT a FROM Advert a join a.properties p where a.category = 'flats' and p.name='number_rooms' ");
In case it is #OneToOne, the query would look like this
Query q = em.createQuery("SELECT a FROM Advert a where a.category = 'flats' and a.property.name='number_rooms' ");

Categories