How to use '::' in spring boot? - postgresql

how to use a query from the Postgre to Spring boot?
I used this query to get data in postgre
"SELECT * FROM mytable WHERE delivery_date::date = '2019-10-28' limit 10;"
I'm trying to make a rest fire with an entity like this
#Entity
#Table(name ="mytable")
public class DeliveryDataEntity implements Serializable {
#Column(name="delivery_date")
private Date delivery_date;
#Column(name="delivery_name")
private String delivery_name;
#Id
#Column(name="delivery_id")
private int delivery_id;
}
and my repo code like this
#Repository
public interface DeliveryDataRepository extends PagingAndSortingRepository<DeliveryDataEntity, Date>{
#Query(value="SELECT * FROM mytable WHERE delivery_date = ?1", nativeQuery = true)
Page<DeliveryDataEntity> findByUserAndStatusOrderByCreatedAtDesc(Date delivery_date, PageRequest pageRequest
);
}
If I run there are no errors, but the data isn't readable.
I tried changing the query to be like this
#Query(value="SELECT * FROM mytable WHERE delivery_date >= ?1", nativeQuery = true)
and
Query(value="SELECT * FROM accounts_data WHERE updated \\= ?1", nativeQuery = true)
but, if I run it will get null results.
how do i solve this problem?
thanks

Assuming that the likely problem be the double colon (::) cast operator, which Spring Boot doesn't like, you could try refactoring your query to use a range instead:
SELECT *
FROM mytable
WHERE delivery_date >= '2019-10-28' AND delivery_date < '2019-10-29'
ORDER BY <some column>
LIMIT 10;
The logic in the WHERE clause simply states to find all records whose delivery dates occur on or after midnight of 28-OCT-2019, and strictly before midnight of the following day.
JPA/Hibernate might also support using the CAST function, since Postgres does implement CAST, so this version might also work:
SELECT *
FROM mytable
WHERE (CAST delivery_date AS date) = '2019-10-28'
ORDER BY <some column>
LIMIT 10;

I think you do not need the (::) operator. If you want to consider only dates (without times), you can use the #Temportal annotation, as explained in this Baeldung tutorial.
Also, I think you don't need to use native queries.

Related

Group by date intervals using JPA's Criteria API

I'm trying to group entities by date intervals using JPA's Criteria API. I use this way of querying for entities as this is a part of the service that serves API requests which may ask for any field of any entity, including sorting, filtering, grouping and aggregations. Everything works fine except for grouping by date fields. My underlying DBMS i PostgreSQL.
To give a minimal example, here's my entity class:
#Entity
#Table(name = "receipts")
public class DbReceipt {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Date sellDate;
// Many other fields
}
This example discusses grouping my "month" interval (therefore grouping by year+month), but in the end I'm looking for a solution that would let me group by any interval, such as "year", "day" or "minutes".
What I'm trying to achieve is the following query, but using Criteria API:
SELECT TO_CHAR(sell_date, 'YYYY-MM') AS alias1 FROM receipts GROUP BY alias1;
My attempt to do so is this:
#Service
public class ReceiptServiceImpl extends ReceiptService {
#Autowired
private EntityManager em;
#Override
public void test() {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
Root<?> root = query.from(DbReceipt.class);
Expression<?> expr = cb.function("to_char", String.class, root.get("sellDate"), cb.literal("YYYY-MM"));
query.groupBy(expr);
query.multiselect(expr);
TypedQuery<Object[]> typedQuery = em.createQuery(query);
List<Object[]> resultList = typedQuery.getResultList();
}
}
The reason I use to_char function and not MONTH and similar is that I need entities like 2019-05 and 2020-05 to not be grouped together. I also narrow this example down to only year and month to keep things short, but the goal is to group by any date interval.
The code above creates the following query (SQL logging enabled) which results in an error:
Hibernate: select to_char(dbreceipt0_.sell_date, ?) as col_0_0_ from receipts dbreceipt0_ group by to_char(dbreceipt0_.sell_date, ?)
24-05-2020 12:16:30.071 [http-nio-1234-exec-5] WARN o.h.e.jdbc.spi.SqlExceptionHelper.logExceptions - SQL Error: 0, SQLState: 42803
24-05-2020 12:16:30.071 [http-nio-1234-exec-5] ERROR o.h.e.jdbc.spi.SqlExceptionHelper.logExceptions - ERROR: column "dbreceipt0_.sell_date" must appear in the GROUP BY clause or be used in an aggregate function
Position: 16
which to me is caused by the fact that the whole expression is put into the 'group by' part of the query, rather than just an alias. Now, I've tried to assign an alias to the expression (which returns Selection<T> and groupBy accepts expressions, therefore I can only really use that in the multiselect), but that didn't affect how the query is performed - nothing changed.
How do I achieve grouping by year and month as described above using Criteria API? Maybe there's a different way other than using to_char? Maybe there's a way to give an alias to the groupBy method that would cause it to group by an alias instead of the whole expression?
I think it's a bug in PostgreSQL (the error comes from there, not from Hibernate). I have tried a slightly modified version of your code with EclipseLink + Derby and works perfectly.
Note that I had to use numbers instead of strings because Derby DB doesn't have an equivalent of TO_CHAR function.
Expression<Integer> year = cb.function("YEAR", Integer.class, root.get("sellDate"));
Expression<Integer> month = cb.function("MONTH", Integer.class, root.get("sellDate"));
Expression<Integer> expr = cb.sum(month, cb.prod(12, year));
query.groupBy(expr);
query.multiselect(expr);
This returns the following SQL:
SELECT (MONTH(MY_DATE) + (12 * YEAR(MY_DATE)))
FROM MY_DATE_TABLE
GROUP BY (MONTH(MY_DATE) + (12 * YEAR(MY_DATE)))
Note that there are no portable solutions for manipulating dates in JPA criteria queries. If the number of groups to be queried simultaneously is not too high I'd go with a more practical approach where you find the dates in Java and pass them as literals to the query builder.
Another workaround is to query with a groupBy(root.get("sellDate")) and then aggregate the results in Java according to the desired time period.
Post Scriptum: I don't think it's relevant, however I modified the query's return type from Object[] to Object.

how to use query parameters with limit and interval in spring data jpa with Query annotation

I have a native query to be used in #Query annotation in spring. The problem is that I cannot pass parameters for non standard clauses like LIMIT AND INTERVAL.
1) Is it possible to do pass the parameters for LIMIT in the annotation.
2) What is the programmatic equivalent in JPA. Does it have any query interface which can be used ?
#Query(value = "SELECT * FROM scheduler sch where timestamp < (CURRENT_TIMESTAMP + interval '1 seconds') FOR UPDATE SKIP LOCKED LIMIT 5",nativeQuery = true)
Also, the limit and interval clause are not to be changed for every query but should be set when the jvm starts up.
Two Step
1. Use Pageable pageable as a parameter in your method signature
2. Create object of PageRequest and call repository method by passing the pageRequest Object.
PageRequest pageRequest=new PageRequest(0,5); // 0 is page (used to calculate offset) and 5 is size (limit).
Pass the pageRequest object to the method.
For INTERVAL, if you are using seconds, use like below.
#Query(value = "SELECT * FROM scheduler sch where timestamp < (CURRENT_TIMESTAMP + ((interval '1 seconds') * :seconds)) FOR UPDATE SKIP LOCKED",nativeQuery = true)
Pass :seconds as another parameter.
For the parameters to be constant, you can create a public static final variable.

Count Date in JPA or JPQL

I need to count registered users in current month.
I use a primitive method
I use H2 Database.
In Model(Reader) i have a field: private String LocalDate
because i save the date filed in database as String:
#Override
public String getCurrentDate() {
LocalDate localDate = LocalDate.now();
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String formatDate = localDate.format(format);
return formatDate;
}
In ReaderRepository I have this:
Integer getCountDatesCurrentMonth();
List<String> allDates();
Where in List I get all dates from table.
In ReaderServiceImpl I have this method:
#Override
public Integer getCurrentMonthRegisteredReaders() {
List<String> dates = readerRepository.allDates();
Integer date = LocalDate.now().getMonthValue();
String s;
int count=0;
for(int i =0;i<dates.size();i++){
s = dates.get(i).charAt(5)+""+dates.get(i).charAt(6);
if(s.equals(date.toString()))
count++;
}
return count;
Where I get the count of users registered in current month
The code works perfect and shows me on the web page the readers registered in current month,
but I want do this in one line or two with JPA or JPQL and I cant do that.
I've tried with JPA, but doesn't work:
Integer countByDateMonthValue(int currentMonth);
I get error:
Caused by: java.lang.IllegalStateException: Illegal attempt to dereference path source [null.date] of basic type
Or with JPQL:
#Query("select count(date) from Reader where... ")
but I don't know further what should I do...
The challenge here is that JPQL doesn't define many useful methods to manipulate dates. So the query will either depend on your JPA implementation (probably Hibernate or EclipseLink) or on the database you are using or both.
This one should work with Hibernate
#Query("select count(date) from Reader r where month(r.date) = :month")
int countForMonth(int month);
This one should work with Oracle
#Query("select count(date) from Reader r where EXTRACT(month FROM r.date) = :month", nativeQuery = true)
int countForMonth(int month);
Or if you always want the current month as a basis
#Query("select count(date) from Reader r where EXTRACT(month FROM r.date) = EXTRACT(month FROM sysdate)", nativeQuery = true)
int countForMonth();
Your current logic an the queries above ignore the year, so rows from all years get counted, if the month matches.
If you really want only the rows from the current month counted, or if it doesn't matter because you have rows from a single row anyway, you could create the date range using a SpEL expression.
You could register custom functions that provide the beginning and end of the current month an do something like this in the query:
#Query("select count(date) from Reader r where r.date between :#{startOfMonth(month)} and :#{endOfMonth(month)}")
int countForMonth(int month);
See this blog article how to use SpEL expressions in queries and how to register custom extensions to the evaluation context.
Done.
I did this select, and works perfect. Now i don't need to send
LocalDate().now().getMonthValue as parameter in my method.
#Query(value = "select count(date) from Reader r where extract(month from r.date) = extract(month from sysdate)" +
"and extract(year from r.date) = extract(year from sysdate)",nativeQuery = true)
int findAllByMonth();
Danke Schön Jens Schauder !

JPQL get difference in days between a column and now

I'm using hibernate and jpa in a spring boot application.
I have a hibernate entity with 2 columns, type (string) and date (LocalDate).
I want to select all the columns and the difference of days between my date column and now.
I succeeded in PostgreSQL: select date, date - current date from table;
I'm using an interface which extends JpaRepository. Until now i was successfully to insert new queries through #Query annotations.
I can't figure it out how to select my columns and a column containing the difference of days in jpql.
Kind regards,
Indeed you can use query creator. You have to create a new entity which has the same columns as the original table and in addition the calculated difference between the date column and now. You have to be careful which date type is your column.
In my experience only Date from java.util can be used for differences with CURRENT_DATE (i may be wrong).
public List<NewEntity> getTest() {
String queryStr = "select new com.entity.NewEntity(n.type, n.date - CURRENT_DATE as
time) from Table as n"
TypedQuery<PunteInspectieTimpRamas> query = entityManager.createQuery(queryStr,
NewEntity.class);
return query.getResults()
}
Not sure you can do this with jpql.
But you can use your query as a native query, directly in you repository:
#Query(value = "SELECT ... ", nativeQuery = true)
ClassToReturn methodName();
see https://www.baeldung.com/spring-data-jpa-query

JPA - Query with 'composite values' in where-clause

I want to realize the following (for this example: MySQL) statement in JPA 2.1 with Eclipselink as the implementation:
select *
from account
where (mailing_zip_code, id) > (56237, 275)
order by mailing_zip_code asc, id asc
limit 10;
What I want to achieve is an implementation of the seek method for endless scrolling without using an offset in the query. With focus on the where clause, I am not able to find a correct name for this construct. In some places it is called 'composite value', but I am not able to find results by this name, but it seems to be compliant with the SQL-92 standard.
This statement will be extended by filter parameters, so naturally I would like to build it with the JPA criteria API.
I searched the API for some time now, does anybody now if and how this is possible?
After realizing that
select *
from account
where (mailing_zip_code, id) > (56237, 275)
order by mailing_zip_code asc, id asc
limit 10;
is just a shorter way to write
select *
from account
where mailing_zip_code > '56237'
or (mailing_zip_code = '56237' AND id > 276)
order by mailing_zip_code asc, id asc
limit 10;
the criteria query was not that hard after all (appended just the predicate):
if (null != startId) {
Predicate dateGreater = cb.greaterThan(entity.get(sortBy), startValue);
Predicate dateEqual = cb.equal(entity.get(sortBy), startValue);
Predicate idGreater = cb.greaterThan(entity.get("id"), startId);
Predicate dateEqualAndIdGreater = cb.and(dateEqual, idGreater);
cb.or(dateGreater, dateEqualAndIdGreater);
}
If there is a nicer way, I would be very happy to learn about it.