How to convert my native SQL query into a JPQL query - sql

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).

Related

Select rows using mapping table with JPA

I do not know how to implement the query below in jpa, so please help.
select t.*
from team t
join team_memeber tm
on t.team_id = tm.team_id and tm.member_id = ?
The related entities are as follows.
public class Team {
#Id
int teamId;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "teamId")
Set<TeamMember> members = new HashSet<>();
}
public class TeamMember {
#Id
int id;
int teamId;
int memberId;
}

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.

Hiberate search for child entities

I have three classes defined as:
1)Category class:-
#Entity
#Table(name = "CATEGORY")
#Data
#NoArgsConstructor
#AllArgsConstructor
#Indexed
public class Category {
#Id
#GeneratedValue
private Long id;
#Field(index = Index.YES, store = Store.YES, analyzer = #Analyzer(definition = "ngram"))
private String categoryName;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "categoryId", referencedColumnName = "id")
#LazyCollection(LazyCollectionOption.TRUE)
#IndexedEmbedded
private List<SubCategory> subCategories;
private Long creationTime;
private Long updationTime;
}
2) SubCategory class:-
#Entity
#Table(name = "SUB_CATEGORY")
#Data
#NoArgsConstructor
#AllArgsConstructor
#Indexed
public class SubCategory {
#Id
#GeneratedValue
private Long id;
#Field(index = Index.YES,store = Store.YES,analyzer = #Analyzer(definition = "ngram1"))
private String subCategoryName;
#Field(index = Index.YES,store = Store.YES)
private Long categoryId;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "subCategoryId", referencedColumnName = "id")
#LazyCollection(LazyCollectionOption.TRUE)
#IndexedEmbedded
private List<Pages> pages;
private Long creationTime;
}
3) Pages class:-
#Entity
#Table(name = "PAGES")
#Data
#NoArgsConstructor
#AllArgsConstructor
#Indexed
public class Pages {
#Id
#GeneratedValue
private Long id;
#Field(index = Index.YES,store = Store.YES,analyzer = #Analyzer(definition = "ngram2"))
private String pageName;
#Field(index = Index.YES,store = Store.YES,analyzer = #Analyzer(definition = "ngram2"))
private String pageDescription;
#Field(index = Index.YES,store = Store.YES,analyzer = #Analyzer(definition = "ngram2"))
private Long subCategoryId;
private Long creationTime;
}
Now the data is defined like:-
Category SubCategory Pages
-------- ------------ -------
Vehicle Car BMW
Electricals MotorCycle Hero
........... ........ Audi
........... ........ ......
Lapzz Laptop ......
Dell
Now I am stuck at getting the query that will search in all three classes using hibernate search(i.e.-If I search Lap*) I should get result from Categories,Subcategories and Pages and only the rows matching the query only not the complete object of Category.
eg-I should get in the result set the row containing Lapzz in categories and row containing Laptop in subcategories when I search for Lap*.
Please help my find this solution.
This should do what you want, as far as I understand:
String searchTerms = ...;
QueryBuilder categoryQb = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity( Category.class ).get();
QueryBuilder subCategoryQb = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity( SubCategory.class ).get();
QueryBuilder pagesQb = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity( Pages.class ).get();
Query categoryQuery = categoryQb.keyword()
.onField( "categoryName" )
.matching( searchTerms )
.createQuery();
Query subCategoryQuery = subCategoryQb.keyword()
.onField( "subCategoryName" )
.matching( searchTerms )
.createQuery();
Query pagesQuery = pagesQb.keyword()
.onFields(
"pageName",
"pageDescription"
)
.matching( searchTerms )
.createQuery();
Query luceneQuery = categoryQb.bool()
.should( categoryQuery )
.should( subCategoryQuery )
.should( pagesQuery )
.createQuery();
FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery(
luceneQuery,
Category.class, SubCategory.class, Pages.class
);
List<?> results = fullTextQuery.getResultList();
Several warnings are necessary here:
since you are using ngram filters, you may need to use a different analyzer when querying; see this answer
your #Field annotation on Pages.subcategoryId is highly suspicious: it doesn't make sense to analyze a numeric value. For that matter, it's suspicious to store the ID of another entity in Pages, you'd generally want to have a field whose type is SubCategory instead and annotate it with #ManyToOne.
if you want your query to match a category that doesn't have the search terms in its description, but contains a page whose name or description contains the search terms, you will have to add an IndexedEmbedded and add the embedded fields ("pages.pageName", "pages.pageDescription", ...) to your search queries.

Inheritance JPA 2.1

I got this entities:
#Entity
#Table(name = "PROCESS_VERSION")
public class ProcessVersion implements Serializable {
private static final long serialVersionUID = 1L;
private static final String SEQ_NAME = "PROCESS_VERSION_id_SEQ";
#Id
#Basic(optional = false)
#NotNull
#SequenceGenerator(name = "seq", sequenceName = SEQ_NAME, allocationSize = 1)
#GeneratedValue(generator = "seq", strategy = GenerationType.SEQUENCE)
#Column(name = "ID")
private BigDecimal id;
#OneToMany(mappedBy = "idProcessVersion", cascade = CascadeType.ALL)
private List<Stage> stageList;
#OneToMany(mappedBy = "idProcessVersion", cascade = CascadeType.ALL)
private List<Kpi> kpiList;
//Getters and Setters
}
#Entity
#Table(name = "KPI")
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(
name="ID_CATEGORY", discriminatorType=DiscriminatorType.INTEGER)
public class Kpi implements Serializable {
private static final long serialVersionUID = 1L;
private static final String SEQ_NAME = "KPI_id_SEQ";
#Id
#Basic(optional = false)
#NotNull
#SequenceGenerator(name = "seq", sequenceName = SEQ_NAME, allocationSize = 1)
#GeneratedValue(generator = "seq", strategy = GenerationType.SEQUENCE)
#Column(name = "ID")
private BigDecimal id;
#JoinColumn(name = "ID_PROCESS_VERSION", referencedColumnName = "ID")
#ManyToOne(optional = false)
private ProcessVersion idProcessVersion;
#JoinColumn(name = "ID_CATEGORY", referencedColumnName = "ID", insertable = false, updatable = false)
#ManyToOne(optional = false)
private KpiCategory idCategory;
//Getters and Setters
}
#Entity
#Table(name = "KPI_TEMPORARY")
#DiscriminatorValue("1")
public class KpiTemporary extends Kpi implements Serializable {
private static final long serialVersionUID = 1L;
#JoinColumn(name = "ID_STAGE_SOURCE", referencedColumnName = "ID")
#ManyToOne(optional = false)
private Stage idStageSource;
#JoinColumn(name = "ID_STAGE_TARGET", referencedColumnName = "ID")
#ManyToOne(optional = false)
private Stage idStageTarget;
//Getters and Setters
}
#Entity
#Table(name = "KPI_COUNTER")
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorValue(KpiCategory.CATEGORY_COUNTER)
public class KpiCounter extends Kpi implements Serializable {
private static final long serialVersionUID = 1L;
#JoinColumn(name = "ID_STAGE", referencedColumnName = "ID")
#ManyToOne(optional = false)
private Stage idStage;
//Getters and Setters
}
When I try to get the kpis list from ProcessVersion I always get 0. I check the sql generated by hibernate and I see this sentece wich is very strage:
select kpilist0_.ID_PROCESS_VERSION as ID_PROCESS_VERSION5_14_0_, kpilist0_.ID as ID2_5_0_,
kpilist0_.ID as ID2_5_1_, kpilist0_.DESCRIPTION as DESCRIPTION3_5_1_,
kpilist0_.ID_CATEGORY as ID_CATEGORY1_5_1_, kpilist0_.ID_PROCESS_VERSION as ID_PROCESS_VERSION5_5_1_,
kpilist0_.NAME as NAME4_5_1_,
kpilist0_2_.ID_STAGE as ID_STAGE2_7_1_,
kpilist0_3_.ID_STAGE_SOURCE as ID_STAGE_SOURCE2_9_1_, kpilist0_3_.ID_STAGE_TARGET as ID_STAGE_TARGET3_9_1_,
kpicategor1_.ID as ID1_6_2_, kpicategor1_.NAME as NAME2_6_2_,
stage2_.ID as ID1_15_3_, stage2_.DESCRIPTION as DESCRIPTION2_15_3_,
stage2_.ID_PROCESS_VERSION as ID_PROCESS_VERSION5_15_3_,
stage2_.ID_RESPONSABLY as ID_RESPONSABLY6_15_3_, stage2_.ID_STAGE as ID_STAGE3_15_3_,
stage2_.NAME as NAME4_15_3_, processver3_.ID as ID1_14_4_,
stage10_.ID as ID1_15_11_, stage10_.DESCRIPTION as DESCRIPTION2_15_11_,
stage10_.ID_PROCESS_VERSION as ID_PROCESS_VERSION5_15_11_, stage10_.ID_RESPONSABLY as ID_RESPONSABLY6_15_11_,
stage10_.ID_STAGE as ID_STAGE3_15_11_, stage10_.NAME as NAME4_15_11_, stage11_.ID as ID1_15_12_,
stage11_.DESCRIPTION as DESCRIPTION2_15_12_, stage11_.ID_PROCESS_VERSION as ID_PROCESS_VERSION5_15_12_,
stage11_.ID_RESPONSABLY as ID_RESPONSABLY6_15_12_, stage11_.ID_STAGE as ID_STAGE3_15_12_,
stage11_.NAME as NAME4_15_12_
from KPI kpilist0_
left outer join KPI_COUNTER kpilist0_2_ on kpilist0_.ID=kpilist0_2_.ID
left outer join KPI_TEMPORARY kpilist0_3_ on kpilist0_.ID=kpilist0_3_.ID
inner join KPI_CATEGORY kpicategor1_ on kpilist0_.ID_CATEGORY=kpicategor1_.ID
inner join STAGE stage2_ on kpilist0_3_.ID_STAGE_SOURCE=stage2_.ID
left outer join PROCESS_VERSION processver3_ on stage2_.ID_PROCESS_VERSION=processver3_.ID
inner join STAGE stage10_ on kpilist0_3_.ID_STAGE_TARGET=stage10_.ID
inner join STAGE stage11_ on kpilist0_2_.ID_STAGE=stage11_.ID
where kpilist0_.ID_PROCESS_VERSION=?;
The question is why the sentece has 3 inner with stage..Two from KpiTemporary and One from KpiCounter the problem is this will never have the 3 values because the kpi is temporary or is counter but not both.
Can anyone tell me something about?. Is there any extra config to apply. I am using JPA 2.1 specification with hinerbate 4.3.5.Final
The problem is that you have 3 mappings of:
#ManyToOne(optional = false)
On both counter and temporary. The false means that the mapping is 1..M -> 1, while true would mean the mapping is 0..M ->1, I think you would want the latter. Change those to true and see if you get the sql you desire.

JPA Order by with two columns

I have two objects
Objects: Objeto, Comunicado and ComunicadoTramite
#Entity
#Table(name = "objetos")
#Inheritance(strategy = InheritanceType.JOINED)
public class Objeto extends Fact {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_objeto", unique = true, nullable = false)
private Integer id;
#NotNull
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "dt_criacao")
private Date dataCriacao;
}
#Entity
#Table(name = "comunicados")
#PrimaryKeyJoinColumn(name = "cd_comunicado")
public class Comunicado extends Objeto {
#OneToMany(mappedBy = "comunicado")
private List<ComunicadoTramite> tramites;
}
#Entity
#Table(name = "comunicados_tramites")
#PrimaryKeyJoinColumn(name = "cd_tramite")
public class ComunicadoTramite extends Objeto {
#NotNull
#ManyToOne
#JoinColumn(name = "cd_comunicado")
private Comunicado comunicado;
The problem is, i want a list of Comunicado that is ordered by dataCriacao, the two objects (Comunicado and ComunicadoTramite) extends Objeto that contains dataCriacao.
if Comunicado.tramites.size() > 0 then get dataCriacao from the last ComunicadoTramite inserted
if Comunicado.tramites.size() = 0 then get dataCriacao from Comunicado.
SQL I did this
SELECT c.cd_comunicado, tb_tram.dt_criacao FROM comunicados AS c
INNER JOIN (
SELECT ct.cd_comunicado, MAX(obj.dt_criacao) AS dt_criacao FROM comunicados_tramites AS ct, objetos obj
WHERE ct.cd_tramite = obj.id_objeto GROUP BY ct.cd_comunicado
) AS tb_tram
ON c.cd_comunicado = tb_tram.cd_comunicado
)
UNION
(SELECT c.cd_comunicado, obj.dt_criacao FROM comunicados c, objetos obj
WHERE c.cd_comunicado = obj.id_objeto
AND c.cd_comunicado NOT IN (SELECT tram.cd_comunicado FROM comunicados_tramites tram))
ORDER BY dt_criacao DESC
I dont know how to do it in JPA... can anyone help me?
Based on Jorge' ideas i did this
SELECT obj FROM Objeto obj
WHERE obj.id IN (select comunicado.id from Comunicado comunicado
where comunicado NOT IN (SELECT tramite.comunicado from ComunicadoTramite tramite))
OR obj.id IN(select tramite.id from ComunicadoTramite tramite
where tramite.dataCriacao = (select MAX(tram.dataCriacao) from ComunicadoTramite tram WHERE tram.comunicado.id = tramite.comunicado.id)
group by tramite.comunicado)
ORDER BY obj.dataCriacao DESC