eclipse jpa jql not generating on clause for one to many - sql

I have two tables:
DIDPool
DIDRange
A pool can have many ranges.
The range table doesn't have a parent id (e.g. no foreign key back to the pool) as the range table is used by a number of other entities.
As such I've setup the join so that it uses an intermediate join table tbldidpooldidrange.
#Entity
#Table(name = "tbldidpool")
class DIDPool
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(updatable = false, nullable = false)
private long id;
#OneToMany
#JoinTable(name = "tbldidpooldidrange", joinColumns = #JoinColumn(name = "did_pool_id")
, inverseJoinColumns = #JoinColumn(name = "did_range_id"))
private ArrayList<DIDRange> didRanges = new ArrayList<DIDRange>();
}
#Entity
#Table(name = "tbldidrange")
public class DIDRange
{#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(updatable = false, nullable = false)
private long id;
#NotEmpty
#NotNull
#AttributeOverride(name = "phoneNumber", column = #Column(name = "start"))
private PhoneNumber start = new PhoneNumber();
}
The jpa correctly generates the intermediate join table.
My problem is in creating a jql expression that joins the two tables.
// Finds the pool that contains the given range or part range.
#SuppressWarnings("unchecked")
public List<DIDPool> findOverlapping(DIDRange didRange)
{
EntityManager em = EntityManagerProvider.getEntityManager();
Query query = em.createQuery("SELECT e FROM DIDPool e "
+ "join DIDRange r "
+ "where r.start.phoneNumber=:startNo"
+ " ) "
);
query.setParameter("startNo", didRange.getStartOfDIDRange().toCompactString());
return (List<DIDPool>) query.getResultList();
}
My understanding of jql is that you don't specify the join 'on' clause as jpa knows how to generate it.
However this is the sql the above clause generates:
SELECT t1.ID,
FROM tbldidrange t0, tbldidpool t1 WHERE t0.start = ?
bind => [61383208100]
As you can see there is no 'on' clause (and the where doesn't join the tables) and the intermediate join table isn't even mentioned.
What am I doing wrong here?
I've been the reading the jpa documentation and its really not very helpful.

Your query should be
Query query = em.createQuery("SELECT e FROM DIDPool e "
+ "join e.didRanges r "
+ "where r.start.phoneNumber=:startNo"
+ " ) "
);
You have to use the ToMany attribute in the JOIN.

Related

Nested query in JPQL

I have 2 entities -
#Table(name = "MM_MONITORING_CARD")
#Entity(name = "mm_MonitoringCard")
public class MonitoringCard extends StandardEntity {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "CLIENT_ID")
private Counterparty client;
#Column(name = "START_DATE")
private LocalDate startDate;
#Column(name = "END_DATE")
private LocalDate endDate;
...otherColumn
}
and
#DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.STRING)
#Table(name = "MM_COUNTERPARTY")
#Entity(name = "mm_Counterparty")
#DiscriminatorValue("COUNTERPARTY")
#NamePattern("%s|name")
public class Counterparty extends StandardEntity {
#Column(name = "TYPE", insertable=false, updateable=false)
private String type;
...otherColumn
I need get all examples monitoringCard with condition - 1) between start date and end date 2) with Counterparty type = 'someType'
I do method -
List<MonitoringCard> monitoringCardList = dataManager.load(MonitoringCard.class)
.query("select distinct cm from mm_MonitoringCard m join mm_Counterparty cm where (m.current_date between cm.startDate and cm.endDate) and cm.type = :type")
.parameter("type", "someType")
.list();
but i get error, how can i make a correct request?
thanks
Your query is not correct. It should use a join like this:
select distinct m.Counterparty from mm_MonitoringCard m
where (m.current_date between m.Counterparty.startDate and m.Counterparty.endDate)
and m.Counterparty.type = :type
In JPA you don't need to do an explicit join when navigating on a ToOne relationship.

Translate SQL Query to JPQL - #ManyToMany join

I'm trying to translate SQL native query usage in #Query("...", native=true) Spring Data Jpa annotation, to JPQL query usage within the same annotation.
SQL Query:
select d.id as doctorId, d.firstName as doctorFirstName, d.lastName as doctorLastName, d.title as doctorTitle,
d.email as doctorEmail, v.id as id, v.dateFrom as dateFrom, v.dateTo as dateTo, v.status as status,
md.id as medicalServicesId, md.service as medicalServicesService, md.price as medicalServicesPrice
from Visit v
left outer join Doctor d on v.doctor_id=d.id
left outer join visit_medical_services vms on v.id=vms.medical_services_id
left outer join MedicalService md on vms.visit_id=md.id
where d.id= :doctorId and v.status= :status and v.dateFrom>= :dateFrom and v.dateTo<= :dateTo
I'm using Spring Projected Interface, that's why columns aliases are named liked this.
Now i want to get exactly the same result with JPQL - list of items. What i did tried it's this:
#Query("select d.id as doctorId, d.firstName as doctorFirstName, d.lastName as doctorLastName, d.title as doctorTitle, d.email as doctorEmail, " +
"v.id as id, v.dateFrom as dateFrom, v.dateTo as dateTo, v.status as status, md.id as medicalServicesId, md.service as medicalServicesService, md.price as medicalServicesPrice \n" +
"from Visit v \n" +
"left outer join Doctor d on v.doctor.id=d.id \n" +
"left outer join v.medicalServices vms on vms.id=v.id \n" +
"left outer join MedicalService md on md.id=vms.id \n" +
"where d.id= :doctorId and v.status= :status and v.dateFrom>= :dateFrom and v.dateTo<= :dateTo")
List<VisitInfoWithPatientAndMedServices3joins> getAllVisitInfoWithPatientAndMedicalServicesJpqlQuery
(#Param("doctorId") Long doctorId, #Param("status") VisitStatus status, #Param("dateFrom") LocalDateTime dateFrom, #Param("dateTo") LocalDateTime dateTo, Pageable pageable);
And here is translated SQL from JPQL.
select doctor1_.id as col_0_0_, doctor1_.firstName as col_1_0_, doctor1_.lastName as col_2_0_, doctor1_.title as col_3_0_, doctor1_.email as col_4_0_, visit0_.id as col_5_0_, visit0_.dateFrom as col_6_0_, visit0_.dateTo as col_7_0_, visit0_.status as col_8_0_, medicalser4_.id as col_9_0_, medicalser4_.service as col_10_0_, medicalser4_.price as col_11_0_
from Visit visit0_
left outer join
(visit_medical_services medicalser2_ left outer join MedicalService medicalser3_ on medicalser2_.visit_id=medicalser3_.id) on visit0_.id=medicalser2_.medical_services_id
and (medicalser3_.id=visit0_.id)
left outer join Doctor doctor1_ on (visit0_.doctor_id=doctor1_.id)
left outer join MedicalService medicalser4_ on (medicalser4_.id=medicalser3_.id) where doctor1_.id=? and visit0_.status=? and visit0_.dateFrom>=? and visit0_.dateTo<=? limit ?
And the problem here is, that it's returning columns from #ManyToMany relations (visit_medical_services) but ONLY FOR THE FIRST OBJECT. Json response from Postman below:
[
{
"id": 1,
"status": "PAID",
"dateFrom": "2019-04-10T08:00:00",
"dateTo": "2019-04-10T08:30:00",
"medicalServicesId": 1,
"medicalServicesService": "Visit",
"medicalServicesPrice": 100.0,
"doctorLastName": "James",
"doctorFirstName": "Alex",
"doctorEmail": "james#gmail.com",
"doctorId": 1,
"doctorTitle": "dr n. md."
},
{
"id": 2,
"status": "PAID",
"dateFrom": "2019-04-10T09:00:00",
"dateTo": "2019-04-10T09:30:00",
"medicalServicesId": null,
"medicalServicesService": null,
"medicalServicesPrice": null,
"doctorLastName": "James",
"doctorFirstName": "Alex",
"doctorEmail": "james#gmail.com",
"doctorId": 1,
"doctorTitle": "dr n. md."
}
]
I tried to use translated SQL query using JPQL in Workbench, because I'm using MySQL database and the result is the same - first object is correct, and the rest have null values inside mapped #ManyToMany colums.
Here are my Entity classes if it would this issue a little bit easier:
public class Visit {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
LocalDateTime dateFrom;
LocalDateTime dateTo;
#Enumerated(EnumType.STRING)
VisitStatus status;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "doctor_id", referencedColumnName = "id", nullable = true)
#JsonManagedReference
Doctor doctor;
#ManyToOne(fetch = FetchType.LAZY)
#JsonManagedReference
#JoinColumn(name = "patient_id", referencedColumnName = "id", nullable = true)
Patient patient;
#ManyToMany
#JsonManagedReference
#JoinTable(
name = "visit_diseases",
joinColumns = #JoinColumn(
name = "disease_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(
name = "visit_id", referencedColumnName = "id"))
List<Disease> diseases;
#ManyToMany
#JsonManagedReference
#JoinTable(
name = "visit_medical_services",
joinColumns = #JoinColumn(
name = "medical_services_id"),
inverseJoinColumns = #JoinColumn(
name = "visit_id"))
Set<MedicalService> medicalServices;
String mainSymptoms;
String treatment;
String allergy;
String addiction;
String comment;
}
And Medical Services:
public class MedicalService {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
Long id;
String service;
Float price;
#ManyToMany(mappedBy = "medicalServices", fetch = FetchType.EAGER)
private Set<Visit> visits;
public MedicalService(Long id, String service, float price) {
this.id = id;
this.service = service;
this.price = price;
}
}
Can someone take a look please and explain to me what is not working correctly here? What i want to achive is to make JPQL generate THE SAME SQL query. It is even possible? Please help me...
You have a few errors in your query. Use the following instead
#Query("select d.id as doctorId, d.firstName as doctorFirstName, d.lastName as doctorLastName, d.title as doctorTitle, d.email as doctorEmail, " +
"v.id as id, v.dateFrom as dateFrom, v.dateTo as dateTo, v.status as status, md.id as medicalServicesId, md.service as medicalServicesService, md.price as medicalServicesPrice \n" +
"from Visit v \n" +
"left outer join v.doctor d \n" +
"left outer join v.medicalServices md \n"
"where d.id= :doctorId and v.status= :status and v.dateFrom>= :dateFrom and v.dateTo<= :dateTo")
List<VisitInfoWithPatientAndMedServices3joins> getAllVisitInfoWithPatientAndMedicalServicesJpqlQuery
(#Param("doctorId") Long doctorId, #Param("status") VisitStatus status, #Param("dateFrom") LocalDateTime dateFrom, #Param("dateTo") LocalDateTime dateTo, Pageable pageable);

EclipseLink- JPQL join tables through query

I have problem with my JPQl. this is a one to many relationship with TrainRoute and TrainRouteStation. I'm trying to create a inner join and fetch the data. native SQL query working when I used mysql workbeanch and I'm trying convert it to JPQL. also, I was trying to fix from 2 days.
Error : Object comparisons can only be used with OneToOneMappings. Other mapping comparisons must be done through query keys or direct attribute level comparisons.
Class: TrainRoute
#Basic(optional = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
#Column(name = "train_route_id", unique = true, nullable = false)
public Long getTrainRouteId() {
return this.trainRouteId;
}
public void setTrainRouteId(Long trainRouteId) {
this.trainRouteId = trainRouteId;
}
#OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER, mappedBy = "trainRoute")
public List<TrainRouteStationData> getTrainRouteStations() {
return this.trainRouteStations;
}
public void setTrainRouteStations(List<TrainRouteStationData> trainRouteStations) {
this.trainRouteStations = trainRouteStations;
}
Class: TrainRouteStation
#Basic(optional = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
#Column(name = "train_route_station_id", unique = true, nullable = false)
public Long getTrainRouteStationId() {
return this.trainRouteStationId;
}
public void setTrainRouteStationId(Long trainRouteStationId) {
this.trainRouteStationId = trainRouteStationId;
}
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "train_route_id", nullable = false)
public TrainRouteData getTrainRoute() {
return this.trainRoute;
}
JPQL :"SELECT s FROM TrainRouteData t inner join TrainRouteStationData s ON t.trainRouteId=s.trainRoute where s.stationSeqN >=1 AND s.stationSeqN <=3 AND t.trainRouteDescX='Test1-Test2' order by s.stationSeqN asc"
Native SQL : SELECT train_route_station.* FROM train_route inner join train_route_station ON train_route.train_route_id=train_route_station.train_route_id where train_route_station.station_seq_n >= 1 AND train_route_station.station_seq_n <= 3 AND train_route.train_route_desc_x='Test1-Test2' order by train_route_station.station_seq_n asc
And it throw an error:
Exception Description: Object comparisons can only be used with OneToOneMappings. Other mapping comparisons must be done through query keys or direct attribute level comparisons.
Mapping: [org.eclipse.persistence.mappings.DirectToFieldMapping[trainRouteId-->train_route.train_route_id]]
Expression: [
Query Key trainRouteId
How can I change that query?
That's not how joins work in JPQL.
The correct query is
select s from TrainRouteData t inner join t.trainRouteStations s
where s.stationSeqN >= 1
and s.stationSeqN <= 3
and t.trainRouteDescX = 'Test1-Test2'
order by s.stationSeqN asc

How to convert my native SQL query into a JPQL query

I'm trying to convert a native SQL query into a JPQL query. Presented first are the JPA entities and the repository interface:
LearnerActivity
#Entity
#Table(name = "learner_activity")
public class LearnerActivity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToMany
#JoinTable(name = "learner_activity_unit",
joinColumns = #JoinColumn(name="learner_activitys_id", referencedColumnName="ID"),
inverseJoinColumns = #JoinColumn(name="units_id", referencedColumnName="ID"))
private Set<Unit> units = new HashSet<>();
#ManyToMany
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JoinTable(name = "learner_activity_performance_criteria",
joinColumns = #JoinColumn(name="learner_activitys_id", referencedColumnName="ID"),
inverseJoinColumns = #JoinColumn(name="performance_criterias_id", referencedColumnName="ID"))
private Set<PerformanceCriteria> performanceCriterias = new HashSet<>();
}
LearnerJobOnSiteChecklistSectionItem
#Entity
#Table(name = "learner_job_on_site_checklist_section_item")
public class LearnerJobOnSiteChecklistSectionItem implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name = "learner_activity_id")
private LearnerActivity learnerActivity;
}
LearnerPortfolioPerformanceCriteriaAchievement
#Entity
#Table(name = "learner_portfolio_performance_criteria_achievement")
public class LearnerPortfolioPerformanceCriteriaAchievement implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name = "performance_criteria_id")
private PerformanceCriteria performanceCriteria;
#ManyToMany
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JoinTable(name = "learner_portfolio_performance_criteria_achievement_learner_job_on_site_checklist_section_item",
joinColumns = #JoinColumn(name="learner_portfolio_performance_criteria_achievements_id", referencedColumnName="ID"),
inverseJoinColumns = #JoinColumn(name="learner_job_on_site_checklist_section_items_id", referencedColumnName="ID"))
private Set<LearnerJobOnSiteChecklistSectionItem> learnerJobOnSiteChecklistSectionItems = new HashSet<>();
}
I'm looking to get all LearnerPortfolioPerformanceCriteriaAchievements which have the same PerformanceCriteria as a given LearnerJobOnSiteChecklistSectionItem's Activity.
LearnerPortfolioPerformanceCriteriaAchievementRepository
public interface LearnerPortfolioPerformanceCriteriaAchievementRepository extends JpaRepository<LearnerPortfolioPerformanceCriteriaAchievement,Long> {
#Query("select distinct lppca from LearnerJobOnSiteChecklistSectionItem si inner join si.learnerActivity la inner join LearnerPortfolioPerformanceCriteriaAchievement lppca where lppca.performanceCriteria member of la.performanceCriterias and si.id =:sectionItemId")
public List<LearnerPortfolioPerformanceCriteriaAchievement> findForSectionItem(#Param("sectionItemId") Long sectionItemId);
}
Native SQL
SELECT
a.id
FROM
learner_job_on_site_checklist_section_item AS i
JOIN
learner_activity_performance_criteria AS c
ON
c.learned_activitys_id = i.learned_activitys_id
INNER JOIN
learner_portfolio_performance_criteria_achievement AS a
ON
a.performance_criteria_id = c.performance_criterias_id
where i.id = 2
Understanding that I can replace my SQL ON with WHERE clauses, my best JPQL attempt so far is this:
SELECT
DISTINCT lppca
FROM
LearnerJobOnSiteChecklistSectionItem si
INNER JOIN
si.learnerActivity la
INNER JOIN
LearnerPortfolioPerformanceCriteriaAchievement lppca
WHERE
lppca.performanceCriteria MEMBER OF la.performanceCriterias
AND si.id =:sectionItemId
However, running this JPQL throws the following exception:
Caused by: java.lang.IllegalStateException: No data type for node: org.hibernate.hql.internal.ast.tree.IdentNode
\-[IDENT] IdentNode: 'lppca' {originalText=lppca}
The following query will work:
SELECT
a
FROM
LearnerPortfolioPerformanceCriteriaAchievement a
, LearnerActivity v
, LearnerJobOnSiteChecklistSectionItem i
WHERE
i.id = :sectionItemId
AND i.learnerActivity = v
AND a.performanceCriteria MEMBER OF v.performanceCriterias
AND i MEMBER OF a.learnerJobOnSiteChecklistSectionItems
Have created a sample application to test the query (I have used shorter and simpler class names to make it easy to read).

unable to update objects of one to one relation in hibernate

I have relation as shown bellow:
#Entity
#Table(name = "ORDER_", catalog = "smartorder")
public class Order implements Serializable {
/**
* serial version id
*/
private static final long serialVersionUID = 13875615L;
#Id
#Column(name = "ORDER_ID", unique = true, nullable = false)
#SequenceGenerator(name = "ORDER_ID_GEN", sequenceName = "ORDER_ID_SEQ")
#GeneratedValue(generator = "ORDER_ID_GEN")
private long orderId;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "INVOICE_ID", referencedColumnName = "INVOICE_ID")
private Invoice invoice;
// setters and getters
}
#Entity
#Table(name = "INVOICE_")
public class Invoice implements Serializable {
/**
* serial version id
*/
private static final long serialVersionUID = 13875612L;
#Id
#Column(name = "INVOICE_ID", unique = true, nullable = false)
#SequenceGenerator(name = "INVOICE_ID_GEN", sequenceName = "INVOICE_ID_SEQ")
#GeneratedValue(generator = "INVOICE_ID_GEN")
private int invoiceId;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "ORDER_ID", referencedColumnName = "ORDER_ID")
private Order order;
#Column(name = "SUB_TOTAL", precision = 6, nullable = false)
private double subTotal;
#Column(name = "SERVICE_TAX", precision = 6, nullable = false)
private double serviceTax;
#Column(name = "VAT", precision = 6, nullable = false)
private double vat;
#Column(name = "SURCHAARGE", precision = 6, nullable = false)
private double surChaarge;
#Column(name = "GRAND_TOTAL", precision = 6, nullable = false)
private double grandTotal;
//setters and getters
}
I am able to save the records properly. But when i am trying to update orders objects by setting invoice object to order object then the order object is nor persisting only invoice object is persisting.
Order o = getSession().load(Order.class,1L);
o.setInvoice(new Invoice(.........));
getSession().update(o);
in console I am able to see one SQL statement only,
insert into INVOICE_ (DISCOUNT, GRAND_TOTAL, ORDER_ID, ROUNDING, SERVICE_TAX, SUB_TOTAL, SURCHAARGE, VAT) values (?, ?, ?, ?, ?, ?, ?, ?)
Invoice Id is not getting update in Order table :(
Can anyone suggest whats the issue is.
Thanks in advance.....
This may depend on your unusual design.
With INVOICE_ID in ORDR_ and ORDER_ID in INVOICE_ you have both tables at the same time as parent and child of each other.
If your database uses foreign keys deleting and inserting will be hard.
You should use one type/table as parent, (e. g. Order, because it's normaly first) and the other as child (order_id will be in invoice_ table).
In your object model you can have both directions (see first example of http://docs.oracle.com/javaee/6/api/javax/persistence/OneToOne.html)
The issue is incorrect scenario in which you used your Entities/Tables and the one-to-one mapping style. The concept of One-To-One does not corresponed with your current design of both tables and entities.
Please, try to read more about one-to-one here: The concept for one-to-one mapping. Explain the mapping
And mostly take a deep look here: Hibernate – One-to-One example (Annotation), where you can find examples of the one-to-one mapping.
If you really would like to continue with one-to-one mapping you have to:
Remove the "INVOICE_ID" column from the "INVOICE_" table (surprising but a fact)
make the "ORDER_ID" column in the "INVOICE_" table as a primary key (another fact)
change the mapping of the Invoice entity to be more submissive (driven by Order entity)
Example of changes of the Invoice mapping:
// just a draft, to give you idea about the
// "submissive" side mapping.
// All the ID stuff of the Invoice is driven by its
// "Master" - Order
#GenericGenerator(name = "generator", strategy = "foreign",
parameters = #Parameter(name = "property", value = "order"))
#Id
#GeneratedValue(generator = "generator")
#Column(name = "ORDER_ID", unique = true, nullable = false)
public Integer getOrderId() {
return this.orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
#OneToOne(fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn
public Order getOrder() {
return this.order;
}
Please, take it as a draft, to show how different the one-to-one concept is.