How to convert inner native sql to JPQL - java

Hi Here is my entity
#Entity
#Table(name = "COMMENTS")
public class Comment {
private static final long serialVersionUID = -1606236771730211148L;
#NotNull
private Instant created_On; // immutable
#Id
#NotNull
#Column(updatable = false)
private String uuid;
#NotNull
private DomainReference domainRef; // immutable
}
Here is my table looks like.
UUID Varchar Unique id
CREATED_ON TIMESTAMP Instant
Encounter Varchar encounter
I need a query that looks up the latest N Number of rows by each encounter. So if there are 3 encounters they each can have up to N Number rows.
Here is my native sql query
SELECT a.* FROM (
SELECT COMMENTS.UUID ,COMMENTS.DOMAINREF, COMMENTS.CREATED_ON
ROW_Number() OVER(PARTITION BY COMMENTS.DOMAINREF ORDER BY COMMENTS.CREATED_ON DESC) AS rn
FROM COMMENTS WHERE COMMENTS.DOMAINREF IN ('1','2')) a
WHERE a.rn <= 1;
Here is my partially converted JPQL query.
SELECT a.UUID,a.domainRef,a.CREATED_ON FROM
( SELECT COMMENT.UUID,COMMENT.domainRef,COMMENT.CREATED_ON,
ROW_Number() OVER(PARTITION BY IAW_COMMENT.domainRef ORDER BY COMMENT.CREATED_ON DESC) AS rn
FROM COMMENT WHERE COMMENT.domainRef IN :domainrefs) a
WHERE a.rn <= :maxNum";
This query will create table called "a" which is not my entity. I'm not sure if JPA handles inner queries and returns the matching entity.
Can we convert this query into JPQL query?

Related

Order by date not working with distinct using native query

Currently I have a repository where I'm using a native query to find out the first 10 unique ids and these ids should be fetched with the recent ones first. To do this I'm using the following query in my repository class:
#Repository
public interface HomePageRepository extends JpaRepository<TransferEntity, Integer> {
#Query(value="select DISTINCT transfer_to from transfers_table where transfer_from= :transfer_from ORDER BY transaction_date DESC LIMIT 10;", nativeQuery=true)
public ArrayList<Integer> getTransferRequests(Integer transfer_from);
}
The following is my entity class I'm using:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "transfers_table")
public class TransferEntity {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer Id;
private Integer transfer_from;
private Integer transfer_to;
private Double transaction_amount;
#Column(nullable = false, updatable = false)
#CreationTimestamp
private Date transaction_date;
}
Now this is giving me the following error when I try to run it:
java.sql.SQLException: Expression #1 of ORDER BY clause is not in SELECT list, references column 'db.transfers_table.transaction_date' which is not in SELECT list; this is incompatible with DISTINCT
This same query works when I remove the Order by clause from the statement, but then I cannot fetch the recent ids first.
use subquqries:
select DISTINCT transfer_to from (
select transfer_to, transaction_date from transfers_table where transfer_from= 6 ORDER BY transaction_date DESC LIMIT 10
) as x
Demo

Hibernate: sorting on an Embeddable field is not sorting for all multiple fields in an embeddable field

Hibernate: sorting on an Embeddable field is not sorting for multiple fields in an embeddable
Entity class
#Entity(name = "s.mainEntity")
#Table(name = "Main_Entity"
Public class MainEntity{
#Embedded
#NotNull
private CurrencyAmount cost;
....
}
Embeddable field
#Embeddable
public class CurrencyAmount implements Comparable<CurrencyAmount>, Serializable {
#Column(nullable = false, precision = 20, scale = 6)
private BigDecimal amount;
#Column(name = "currency_code", nullable = false, length = 3)
private String currencyCode;
}
HQL query with sorting on cost field
..... generatedAlias1 order by generatedAlias0.cost desc, generatedAlias0._id desc
Translated sql query
WHERE main.uuid=?
ORDER BY main.amount,
main.currency_code DESC,
main.id DESC
LIMIT ?
From the above sql query, the amount always sorts amount by ASC because the hibernate adds the sorting DESC only to the last param and not for all params.
But my expectation is to get
ORDER BY main.amount DESC, main.currency_code DESC, main.id DESC
Does someone know how to resolve this?
Try to use the following order by clause:
...
order by
generatedAlias0.cost.amount desc,
generatedAlias0.cost.currencyCode desc,
generatedAlias0._id desc

problems with OneToMany including a filter clause in spring jpa

I currently get unexpected results in my MYSQL8/H2 test-case when using on a #OneToMany relationship in spring jpa. I want to filter in a list of TKBColumn-tables inside my TKBData table using JPQL. I expect to get one TKBData-table with the filtered TKBColumn but I always get the TKBData-table with ALL TKBColumn (unfiltered). When I using a SQL command it works!
I got no Idea whats the problem here, why it always give me the TKBData-table with always ALL TKBColumn-tables inside.
Native Query (This works):
SELECT d.id,c.name FROM TKBDATA d LEFT JOIN TKBDATA_TKBCOLUMN dc ON d.ID = dc.TKBDATA_ID LEFT JOIN TKBCOLUMN c ON c.ID = dc.COLUMNS_ID WHERE c.name = 'column1';
Output
ID NAME
7b6ec910-3e53-40a3-9221-ee60e75c8d67 column1
JPQL Query (Not works):
select d from TKBData d LEFT JOIN d.columns c WHERE c.name = :name
Output:
id: e892bc28-c35f-4fc8-9b09-387f97a758d8, name:column1
id: 069cc76b-3487-4ad8-a4ae-6568694e2287, name:column2
Table 'TKBData'
public class TKBData {
#Id
#Builder.Default
private String id = UUID.randomUUID().toString();
...
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
#Builder.Default
private Set<TKBColumn> columns = Sets.newHashSet();
...
}
Table 'TKBColumn'
public class TKBColumn {
#Id
#Builder.Default
private String id = UUID.randomUUID().toString();
...
}
Spring Data Repository
#Service
public interface KBDataRepository extends CrudRepository<TKBData, String>, KBDataCustomRepository {
#Query("select d from TKBData d LEFT JOIN d.columns c WHERE c.name = :name")
public TKBData filterByColumn(#Param("name") String name);
}
Spring JPA Generated H2 Tables (relevant)
CREATE CACHED TABLE "PUBLIC"."TKBCOLUMN"(
"ID" VARCHAR(255) NOT NULL,
"NAME" VARCHAR(255),
...
)
CREATE CACHED TABLE "PUBLIC"."TKBDATA_TKBCOLUMN"(
"TKBDATA_ID" VARCHAR(255) NOT NULL,
"COLUMNS_ID" VARCHAR(255) NOT NULL
)
CREATE CACHED TABLE "PUBLIC"."TKBDATA"(
"ID" VARCHAR(255) NOT NULL,
...
)
Relevant Content of tables which are generated at the start of the test class
Table: TKBDATA
ID
726004cf-5cab-4b1d-bb3f-466ba22622e9
Table: TKBDATA_TKBCOLUMN
TKBDATA_ID COLUMNS_ID
726004cf-5cab-4b1d-bb3f-466ba22622e9 7b4e4ea8-4ff9-4668-8882-67ff93b595ca
726004cf-5cab-4b1d-bb3f-466ba22622e9 d670e813-0466-48a8-be54-ee992cf28462
Table: TKBCOLUMN
ID DATAORDER NAME OWNERID
d670e813-0466-48a8-be54-ee992cf28462 0 column1 16e01046-9a84-4651-98d8-4e3e358212eb
7b4e4ea8-4ff9-4668-8882-67ff93b595ca 1 column2 16e01046-9a84-4651-98d8-4e3e358212eb
For more informations you can find the github repository here: https://github.com/fo0/ScrumTool
Test class: https://github.com/fo0/ScrumTool/blob/master/ScrumTool/src/test/java/com/fo0/vaadin/scrumtool/test/data/TKBDataColumnFilterTest.java
Edit:
The solution for this was to use a native query, because of the design of JPA and how it works with objects, thats why my use-case has exactly this problem.
Meaning of select d from TKBData d JOIN d.columns c WHERE c.name = column1 is
Find a TKBData object where it has an associated column object for which name is column1
Once its decided which TKBData has at least one column object for which name is column1, then it will return all its associated column objects which you don't have control over in JPA. ( see My answer to another question ). Alternative is to write native sql and return custom non entity objects
For example, you have TKBDATA_1 with column1 and column2 associated, you also have TKBDATA_2 with column3 associated.
When you run your query, it will ignore TKBDATA_2 and decides to return TKBDATA_1 as it has atleast one column object with name= column2. But after that you don't have control over which associated column objects to return for TKBDATA_1 and JPA will return all associated column objects
If you are not sure of the reason, read about hibernate session.How it provides unique presentation of any associated entry in memory. It is the foundation for its dirty checking and repeatable read
Update your #OneToMany as follows
#OneToMany(fetch = FetchType.EAGER,
cascade = CascadeType.ALL, orphanRemoval = true)
#Builder.Default
#JoinTable(name = "TKBDATA_TKBCOLUMN",
joinColumns = #JoinColumn(name = "TKBDATA_ID"),
inverseJoinColumns = #JoinColumn(name = "COLUMNS_ID"))
private Set<TKBColumn> columns = Sets.newHashSet();
When it comes to JPA query language, I would like to think in terms of query a collection of in-memory objects.
So now try to describe the meaning of the following two queries in terms of objects.
select d from TKBData d LEFT JOIN d.columns c WHERE c.name = :name
vs
select d from TKBData d JOIN d.columns c WHERE c.name = :name
Don't forget unlike in sql where you are select any columns here you have said you want to select TKBData objects and restricting which TKBData objects to return.
So to achieve the same result as of your native sql, use the second JPA query
Note:
Even though you used a left join in your sql query, it is effectively an inner join sql query because you also applied a where condition to the most right table on that join.
Use the DISTINCT JPQL keyword
#Query("select distinct d from TKBData d LEFT JOIN d.columns c WHERE c.name = :name")
public TKBData filterByColumn(#Param("name") String name);
Or use JPA method naming query
public TKBData findByColumnsName(String name);

sqlException : column not found JPA #query

I'm using #query annotation but when I try to fetch count of records it throws
java.sql.SQLException: Column 'allowPartialPay' not found.
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1094) ~[mysql-connector-java-5.1.31.jar:na]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:997) ~[mysql-connector-java-5.1.31.jar:na]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:983) ~[mysql-connector-java-5.1.31.jar:na]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:928) ~[mysql-connector-java-5.1.31.jar:na]
at com.mysql.jdbc.ResultSetImpl.findColumn(ResultSetImpl.java:1162) ~[mysql-connector-java-5.1.31.jar:na]
at com.mysql.jdbc.ResultSetImpl.getBoolean(ResultSetImpl.java:1781) ~[mysql-connector-java-5.1.31.jar:na]
I'm writing my custom queries in repository.
InvoiceRepository.java
public interface InvoiceRepository extends JpaRepository<Invoice, Integer>{
Invoice findByInvoiceNumber(String invoiceNumber);
List<Invoice> findByUserId(int id);
#Query(value = "select c.id,c.business_name,count(i.id) from client c join invoice i on c.id = i.client_id where i.date <= :agingDate group by c.id",nativeQuery=true)
List<Invoice> findInvoiceCount(#Param("agingDate")Date agingDate);
}
ReportService.java
if(report.getReportBy().equals("invoiceCount")){
result = invoiceRepository.findInvoiceCount(report.getAgingDate());
}
Invoice.java
#Entity
#Table
public class Invoice {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id")
private int id;
#ManyToOne
private Client client;
#Column
private boolean allowPartialPay;
}
Database
It comes during mapping result set into java Invoice class (as you declared it as return type List for method findInvoiceCount() ). native query return in your case Object[] instead of List.
You can see it in log exception
ResultSetImpl.findColumn(ResultSetImpl.java:1162
So it happens in result mapping stage ,after query has executed.
#Query(value = "select c.id,c.business_name,count(i.id) from client
c join invoice i on c.id = i.client_id
where i.date <= :agingDate group by c.id",nativeQuery=true)
List<Invoice> findInvoiceCount(#Param("agingDate")Date agingDate);
spring data gets result set from query result and tries to map it into Invoice field by field (try to contruct Invoice class ). But actual type it's Object[].
If you need get some DTO as result your query , with fields like is result set : 'c.id,c.business_name,count(i.id)' use #SqlResultSetMapping (you can map result columns from select query into your dto). Or change return type from List to Object[] and iterate it as you need.
Here is example for Result Set Mapping: The Basics.

Native Query (JPA ) not reset and return the same old result

I have a native sql query as the following :
for (init i=0; i<=2 ; i++) {
String sql = "Select * from accounts where id = ?";
Query query = em.createNativeQuery(sql,AccountBean.class);
query.setParameter(1, i );
AccountBean accountBean = (AccountBean)query.getSingleResult();
}
For the first loop it works correctly but any loop after the first one returns the same result as the first one , i debug it, the parameter changed , it works correctly if i change
Query query = em.createNativeQuery(sql,AccountBean.class);
to
Query query = em.createNativeQuery(queryString);
Regards
Wish79
Every JPA entity must have a primary key. Your JPA entities may not properly reflect the primary key, if any, on the database table.
I ran into the same problem. In my model class I had only one class variable annotated with #Id. However, that was not an accurate reflection of the table itself, which has a composite primary key. Thus, my query results returned the correct number of rows, but each confoundingly contained the same values, even though the actual data was different in the db. For example, this query:
Query query = entityManager.createQuery
("SELECT tbl FROM Tbl tbl WHERE tbl.id = 100
and tbl.code in ('A','B','C')");
...returned 10 rows, each showing a code of 'A'. But in actuality 9 of those 10 rows had a different code value ('B' or 'C'). It seemed as if the results were being cached and/or the tbl.code predicate was ignored. (That happened whether I used JPQL or Native SQL.) Very confusing.
To fix this I added an additional #Id annotation to my model to reflect the composite primary key:
#Id
#Column(name = "Code")
public String getCode() {
return this.code;
}
Now the query returns the data correctly and the code select criteria is no longer effectively ignored.
Edit: Although the above worked for me, on further research it seems a better approach to configure a separate JPA Entity composite primary key class. See http://docs.oracle.com/cd/E16439_01/doc.1013/e13981/cmp30cfg001.htm.
For example, here's an Entity class with an embedded primary key (see #EmbeddedId):
/**
* The persistent class for the SOME_TABLE database table.
*/
#Entity
#Table(name = "SOME_TABLE")
public class SomeTable implements Serializable {
#EmbeddedId
private SomeTablePk id;
#Column(name = "NUMBER_HRS")
private BigDecimal numberHrs;
...
...and here's the composite primary key class (see #Embeddable):
#Embeddable
public class SomeTablePk implements Serializable {
#Column(name = "SOME_ID")
private String someId;
#Column(name = "ANOTHER_ID")
private BigDecimal anotherId;
public String getSomeId() {
return someId;
}
...

Categories

Resources