Need help in creating CriteriaQuery - jpa

First of all, I would like to know if it is possible to do?
Below is my query and I am trying to build using criteria.
SELECT CONCAT('record-', rl.record_id) AS tempId,
'sloka' AS type,
rl.record_id AS recordId,
rl.title AS title,
rl.locale as locale,
rl.intro AS intro,
rl.title AS localetitle,
NULL AS audioUrl,
lp.name AS byName,
lp.person_id AS byId,
lp.name AS onName,
lp.person_id AS onId
FROM record_locale rl
LEFT JOIN record r ON rl.record_id = r.record_id
LEFT JOIN locale_person lp ON r.written_on = lp.person_id
WHERE rl.title LIKE :title
AND rl.locale = :locale
AND lp.locale = :locale
UNION
SELECT CONCAT('lyric-', s.song_id) AS tempId,
'bhajan' AS type,
s.song_id AS recordId,
s.title,
l.locale as locale,
NULL AS intro,
l.title AS localetitle,
s.audio_url AS audioUrl,
lpb.name AS byName,
lpb.person_id AS byId,
lpo.name AS onName,
lpo.person_id AS onId
FROM song s
LEFT JOIN locale_person lpb
ON (s.written_by = lpb.person_id AND lpb.locale = :locale)
LEFT JOIN locale_person lpo
ON (s.written_on = lpo.person_id AND lpo.locale = lpb.locale)
INNER JOIN lyric l
ON (l.locale = lpb.locale AND l.song_id = s.song_id)
WHERE s.title LIKE :title AND s.approved_by IS NOT NULL
ORDER BY localeTitle ASC
// END
Based on few conditions, I might need to have union of both queries or just individual query without union.

Converting the SQL to JPQL is usually a good first step, as we can't quite tell what these tables map to, or what you are expecting to get back. If it is possible to do in JPQL, it should be possible with a criteria query. Except in this case: JPA/JPQL does not have the union operator so it won't work in straight JPA, but some providers such as EclipseLink have support. See:
UNION to JPA Query
and
http://www.eclipse.org/eclipselink/documentation/2.5/jpa/extensions/j_union.htm

Related

JPA Criteria orderBy: unexpected AST node

I have the following criteria query, which retrieves some fields from Anfrage and Sparte entities and also the translated string for the sparte.i18nKey.
This works as expected if I dont use orderBy.
Now I have the requirement to sort by the translated string for sparte.i18nKey and using the orderBy as shown below, results in QuerySyntaxException: unexpected AST node
So the problem must be the subselect in the orderBy clause!
select distinct new
my.domain.model.dto.AnfrageDTO(
anfrage0.id,
anfrage0.name,
anfrage0.sparte.id,
anfrage0.sparte.i18nKey,
-- retrieve translated string for sparte.i18nKey
(select rb0.value from at.luxbau.mis2.domain.model.ResourceBundleEntity as rb0
where (anfrage0.sparte.i18nKey = rb0.key) and (rb0.language = 'de'))
)
from my.domain.model.impl.Anfrage as anfrage0
left join anfrage0.sparte as sparte
order by (
-- sort by translated string for sparte.i18nKey
select rb1.value
from my.domain.model.ResourceBundleEntity as rb1
where (anfrage0.sparte.i18nKey = rb1.key) and (rb1.language = 'de')
) asc
My Java code looks like this:
private List<AnfrageDTO> getAnfragen() {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<AnfrageDTO> query = cb.createQuery(AnfrageDTO.class);
Root<Anfrage> anfrage = query.from(Anfrage.class);
anfrage.join(Anfrage_.sparte, JoinType.LEFT);
query.select(cb.construct(AnfrageDTO.class,
anfrage.get(Anfrage_.id),
anfrage.get(Anfrage_.name),
anfrage.get(Anfrage_.sparte).get(Sparte_.id),
anfrage.get(Anfrage_.sparte).get(Sparte_.i18nKey),
// create subquery for translated sparte.i18nKey
createResourceBundleSubQuery(cb, query, anfrage.get(Anfrage_.sparte).get(Sparte_.i18nKey)).getSelection()));
TypedQuery<AnfrageDTO> tq = entityManager
.createQuery(query)
// use subquery to sort by translated sparte.i18nKey
.orderBy(cb.asc(createResourceBundleSubQuery(cb, query, anfrage.get(Anfrage_.sparte).get(Sparte_.i18nKey))));
tq.setMaxResults(10);
List<AnfrageDTO> anfragen = tq.getResultList();
return anfragen;
}
public Subquery<String> createResourceBundleSubQuery(CriteriaBuilder cb, CriteriaQuery<?> query, <String> expr) {
Subquery<String> subquery = query.subquery(String.class);
Root<ResourceBundleEntity> rb = subquery.from(ResourceBundleEntity.class);
subquery
.select(rb.get(ResourceBundleEntity_.value))
.where(cb.and(
cb.equal(expr, rb.get(ResourceBundleEntity_.key)),
cb.equal(rb.get(ResourceBundleEntity_.language), "de")));
return subquery;
}
Using a native SQL query with subselect in orderBy works also as expected.
select distinct
anfrage0_.id,
anfrage0_.name,
anfrage0_.sparte_id,
sparte4_.i18n_key,
(select rb3.i18n_value from resource_bundle rb3 where rb3.language_code = 'de' and rb3.i18n_key = sparte4_.i18n_key) as sparte_i18n_value
from
mis2.anfrage anfrage0_
left outer join mis2.sparte sparte4_ on anfrage0_.sparte_id = sparte4_.id
order by (
select rb.i18n_value
from mis2.resource_bundle rb
where sparte4_.i18n_key = rb.i18n_key and rb.language_code = 'de'
) asc
Also using an alias in the native SQL query works also as expected.
select distinct
anfrage0_.id,
anfrage0_.name,
anfrage0_.sparte_id,
sparte4_.i18n_key,
(select rb3.i18n_value from resource_bundle rb3 where rb3.language_code = 'de' and rb3.i18n_key = sparte4_.i18n_key) as sparte_i18n_value
from
mis2.anfrage anfrage0_
left outer join mis2.sparte sparte4_ on anfrage0_.sparte_id = sparte4_.id
order by sparte_i18n_value
asc
It would be great if JPA Criteria API would support using an alias in orderBy clause!
Any hints welcome - Thank you!
My environment is WildFly 11 and PostgreSQL 9.6.
JPA doesn't support passing parameter in order by clause, your problem has been asked before: Hibernate Named Query Order By parameter

ALL query in Ecto

I need to translate the following SQL to Ecto Query DSL.
SELECT otc.*
FROM users u
INNER JOIN one_time_codes otc ON u.id = otc.user_id
LEFT OUTER JOIN one_time_code_invalidations otci ON otci.one_time_code_id = otc.id
WHERE u.id = 3 AND
otc.code = 482693 AND
otci.inserted_at IS NULL AND
otc.inserted_at > all(SELECT otc.inserted_at
FROM one_time_codes otc2
WHERE otc2.user_id = 3) AND
otc.inserted_at > (now() - '180 seconds'::interval);
In the third AND statement of WHERE clause, notice there is an ALL query. Ecto seems to not have corresponding function in Ecto.Query.API.
How to apply such aggregate? What is the correct way to implement this, though it could be implemented by having a lookup on debug logs of the Ecto, do you have another idea (or suggestion)?
Thank you.
Ecto does not allow subqueries in expressions, and you're right that there's no all in Ecto's Query API, but you can use fragment like this:
where: ...
and otc.inserted_at > fragment("all(SELECT otc.inserted_at FROM one_time_codes otc2 WHERE otc2.user_id = ?", 3)
and ...

How to return specific properties from a JPA repository?

I'm getting a ClassCastException when retrieving an entity into a resource. It occurs only when the JPA repository statement explicitly specifies the properties to be returned.
For example, this works just fine and there is no exception triggered:
#Query("SELECT r FROM Rollout r, RolloutAdmin ra WHERE r.id = ra.rollout.id AND ra.admin = :admin")
And it renders as:
select rollout0_.id as id1_8_, rollout0_.version as version2_8_, rollout0_.bts_nb_all as bts_nb_a3_8_, rollout0_.country_id as country_9_8_, rollout0_.creation_datetime as creation4_8_, rollout0_.description as descript5_8_, rollout0_.end_datetime as end_date6_8_, rollout0_.name as name7_8_, rollout0_.operator_id as operato10_8_, rollout0_.start_datetime as start_da8_8_ from rollout rollout0_ cross join rollout_admin rolloutadm1_ where rollout0_.id=rolloutadm1_.rollout_id and rolloutadm1_.admin_id=1 order by rollout0_.name asc limit 10;
But this triggers the exception:
#Query("SELECT DISTINCT r.id, r.country, r.operator, r.name, r.description, r.creationDatetime, r.startDatetime, r.endDatetime FROM Rollout r, RolloutAdmin ra, BTS b WHERE b.rollout.id = r.id AND r.id = ra.rollout.id AND ra.admin = :admin GROUP BY r.id")
And it renders as:
select distinct rollout0_.id as col_0_0_, rollout0_.country_id as col_1_0_, rollout0_.operator_id as col_2_0_, rollout0_.name as col_3_0_, rollout0_.description as col_4_0_, rollout0_.creation_datetime as col_5_0_, rollout0_.start_datetime as col_6_0_, rollout0_.end_datetime as col_7_0_, country3_.id as id1_4_0_, operator4_.id as id1_6_1_, country3_.version as version2_4_0_, country3_.code as code3_4_0_, country3_.list_order as list_ord4_4_0_, country3_.name as name5_4_0_, operator4_.version as version2_6_1_, operator4_.country_id as country_9_6_1_, operator4_.description as descript3_6_1_, operator4_.image as image4_6_1_, operator4_.messenger as messenge5_6_1_, operator4_.name as name6_6_1_, operator4_.operator_id as operator7_6_1_, operator4_.url as url8_6_1_ from rollout rollout0_ inner join country country3_ on rollout0_.country_id=country3_.id inner join operator operator4_ on rollout0_.operator_id=operator4_.id cross join rollout_admin rolloutadm1_ cross join bts bts2_ where bts2_.rollout_id=rollout0_.id and rollout0_.id=rolloutadm1_.rollout_id and rolloutadm1_.admin_id=1 group by rollout0_.id order by rollout0_.name asc limit 10;
How come such a difference ? Any way to specify the properties to be returned without fetching the whole dependent parent properties ?
EDIT: I now understand, thanks to Nizet, that I need to return an object of the same type as my repository method.
It's quite simple:
SELECT r FROM Rollout r
means: I want this query to return r, of type Rollout. The query returns a List<Rollout>.
Whereas
SELECT DISTINCT r.id, r.country, r.operator, r.name, r.description, r.creationDatetime, r.startDatetime, r.endDatetime FROM Rollout r
means: I want this query to return many different things: an ID, a country, an operator, etc. Since the query returns many different things, of different types, and potentially from several different entities (not the case in this specific example, but you could also return properties from ra), the query returns a List<Object[]>, where the first element of the array of objects is ID, the second is country, the third is operator, etc.
Not that even your first query is more complex than it should. The association between the two entities already says how the two entities are joined. So all you need is
SELECT r FROM RolloutAdmin ra join ra.rollout r WHERE ra.admin = :admin

JPA Criteria Equivalent Query Subquery greatest or max

This SQL query does exactly what is needed when executed. I am looking for the JPA Criteria equivalent? Basically gets the Device entity and the LATEST(greatest) GPS coord from a One-To-Many relationship
SELECT DISTINCT t0.DEVICE_I, t0.ACTIVE_S, t0.APP_CONFIG_I, t0.PREV_APP_CONFIG_I,
t0.APP_CONFIG_CONFIRMED_S, t0.APP_CONFIG_RECEIPT_D, t0.APP_CONFIG_SENT_S, t0.CHANNEL_X,
t0.CREATION_TS, t0.CREATION_USER_I, t0.DEST_QUEUE_C, t0.DIVISION_C, t0.LAN_IP_X,
t0.LAST_UPDATE_TS, t0.LAST_UPDATE_USER_I, t0.MOBILE_IP_X, t0.ORIGIN_MP_I,
t0.PREV_CHANNEL_X, t0.TELEPHONE_NUMBER_X, t0.VERSION_X, t1.DEVICE_I, t1.COORDINATE_I,
t1.CREATION_TS, t1.CREATION_USER_I, t1.GENERATED_TS, t1.GPGGA_X, t1.GPGSA_X,
t1.GPGSV_X, t1.GPRMC_X, t1.GPVTG_X, t1.LAST_UPDATE_TS, t1.LAST_UPDATE_USER_I,
t1.WORK_ORDER_NUMBER_I
FROM DEVICE t0, GPS_COORD t1
WHERE t0.DEVICE_I = t1.DEVICE_I AND
t1.GENERATED_TS IN ( select max(GENERATED_TS)
from GPS_COORD
group by DEVICE_I )
ORDER BY t1.DEVICE_I ASC, t1.GENERATED_TS DESC
The java code below is almost there and generates the following query which is missing joined fields needed from GpsCoord. These fields are included if a fetch is done, but the Join is necessary later in the where clause of the Subquery:
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Device> cq = criteriaBuilder.createQuery(Device.class);
Root<Device> device = cq.from(Device.class);
cq.distinct(true);
Join<Device, GpsCoord> j = device.join(Device_.gpsCoords, JoinType.LEFT);
//Fetch<Device,GpsCoord> f = device.fetch(Device_.gpsCoords);
CriteriaQuery<Device> select = cq.select(device);
Subquery<Timestamp> sq = cq.subquery(Timestamp.class);
Root<GpsCoord> gpsCoord = sq.from(GpsCoord.class);
sq.select(criteriaBuilder.greatest(gpsCoord.get(GpsCoord_.generatedTs)));
sq.groupBy(gpsCoord.get(GpsCoord_.device).get(Device_.deviceI));
select.where(j.get(GpsCoord_.generatedTs).in(sq));
TypedQuery<Device> query = this.getEntityManager().createQuery(cq);
Query that is generated is missing the GpsCoord fileds from the Join oepration
SELECT DISTINCT t0.DEVICE_I, t0.ACTIVE_S, t0.APP_CONFIG_I, t0.PREV_APP_CONFIG_I,
t0.APP_CONFIG_CONFIRMED_S, t0.APP_CONFIG_RECEIPT_D, t0.APP_CONFIG_SENT_S,
t0.CHANNEL_X, t0.CREATION_TS, t0.CREATION_USER_I, t0.DEST_QUEUE_C, t0.DIVISION_C,
t0.LAN_IP_X, t0.LAST_UPDATE_TS, t0.LAST_UPDATE_USER_I, t0.MOBILE_IP_X, t0.ORIGIN_MP_I,
t0.PREV_CHANNEL_X, t0.TELEPHONE_NUMBER_X, t0.VERSION_X
FROM SPW_OWN.DEVICE t0, SPW_OWN.GPS_COORD t1
WHERE (t1.GENERATED_TS IN (SELECT MAX(t2.GENERATED_TS) FROM SPW_OWN.GPS_COORD t2,
SPW_OWN.DEVICE t3 WHERE t2.DEVICE_I = t3.DEVICE_I GROUP BY t3.DEVICE_I))
AND t0.DEVICE_I = t1.DEVICE_I(+)
Using a multiselect from the CriteriaQuery like this generates the proper SQL when executed, but the following exception
Caused by: java.lang.Exception: java.lang.RuntimeException: Can not find constructor for "class Device" with argument types "[class java.lang.String ... java.sql.Timestamp]" to fill data.
gets thrown after the call:
CriteriaQuery<Device> select = cq.multiselect(
//"deviceI",
device.get(Device_.deviceI),
//"activeS",
device.get(Device_.activeS),
//"appConfigConfirmedS",
device.get(Device_.appConfigConfirmedS),
//"appConfigReceiptD",
device.get(Device_.appConfigReceiptD),
//"appConfigSentS",
device.get(Device_.appConfigSentS),
//"channelX",
device.get(Device_.channelX),
//"destQueueC",
device.get(Device_.destQueueC),
//"lanIpX",
device.get(Device_.lanIpX),
//"mobileIpX",
device.get(Device_.mobileIpX),
//"prevChannelX",
device.get(Device_.prevChannelX),
//"telephoneNumberX",
device.get(Device_.telephoneNumberX),
//"versionX"
device.get(Device_.versionX),
//"device",
j.get(GpsCoord_.device),
//"gpggaX",
j.get(GpsCoord_.gpggaX),
//"gprmcX",
j.get(GpsCoord_.gprmcX),
//"gpgsaX",
j.get(GpsCoord_.gpgsaX),
//"gpgsvX",
j.get(GpsCoord_.gpgsvX),
//"gpvtgX"
j.get(GpsCoord_.gpvtgX),
j.get(GpsCoord_.generatedTs)
);
A few issues,
you call, sq.from(GpsCoord.class); twice
using device.fetch(Device_.gpsCoords); will also fetch the object, and will join it twice
you IN is incorrect it should be,
select.where(criteriaBuilder.get(path).in(sq));

Forming JPQL query from native SQL query for multiple views with CASE condition and join statements

Kindly helping in converting native query ti JPQL query for multiple views with CASE condition and join statements.Table c1 and c3 are views. I am trying the get the current and pending information from c1.
Please find the query mentioned below
SELECT c3.eqip_id AS EQIP_ID,
CASE WHEN c1.inst_ts IS NULL OR c1.sent_ts > c1.inst_ts THEN c1.ver_nm END AS PEND,
CASE WHEN c1.sent_ts IS NULL OR c1.sent_ts > c1.inst_ts THEN c1.ver_nm END AS CURRENT,
c1.trm_ver_hist_id AS TRM_VER_HIST_ID
FROM trm_ver_hist_vw c1
JOIN(SELECT Max(trm_ver_hist_id) AS TRM_VER_HIST_ID, dvc_id, status
FROM trm_ver_hist_vw
WHERE ver_typ_id = 1 AND status IN( 'C', 'P' )
GROUP BY dvc_id, status) c2
ON c1.trm_ver_hist_id = c2.trm_ver_hist_id
JOIN trm_dtl_vw c3 ON c1.dvc_id = c3.trm_id
WHERE c3.co_actv_ind = 'Y' AND c3.mach_hdwr_asscn_ind = 'Y' AND pin = 'ABC'
Can anyone please help me to make this query in JPA/JPQL?
It seems very complex and data specific. I would recommend using a native SQL query.
The JPA spec does not allow selects in the FROM clause, although EclipseLink does have some support for this.
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL#Sub-selects_in_FROM_clause