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

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.

Related

criteria query for adding interval to a column in spring boot

I need to add an interval to a column value in using a criteria query.
My raw query is something like
select * from classes where classtime + INTERVAL '0.3 hour' <= CURRENT_TIME
I have tried with
builder.sum(root
.get(classtime), builder.literal("INTERVAL 0.5 HOUR"));
builder.function("INTERVAL", LocalDateTime.class, "0.5 HOUR");
Both are not working.

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 '::' in spring boot?

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.

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.

Lambda expression for Entity Framework query

I'm stuck trying to map this sql:
select dt.Id, dateadd(dd,datediff(dd,0,dt.CreatedAt),0) as Ct, DId, Amount
from dt, ad
where dt.ADId = ad.ADId and ad.Id = '13B29A01-8BF0-4EC9-80CA-089BA341E93D'
order by dateadd(dd,datediff(dd,0,dt.CreatedAt),0) desc, DId asc
Into an Entity Framework-compatible lambda query expression. I'd appreciate any help.
I think something like the code below should work.
Guid dtId = new Guid("13B29A01-8BF0-4EC9-80CA-089BA341E93D");
DateTime compareDt = new DateTime(...);
var q = from dt in dts
where dt.id == dtId
orderby dt.Ad.CreatedAt, Did
select new
{
dt.Ad,
(dt.CreatedAt - compareDt).Days,
DId,
Amount
};
dtId has to be outside of the query, because the entity framework mapper doesn't understand constructors that take parameters, similar for the DateTime. I couldn't completely figure out the datediff/dateadd part, looks like you're determining the total amount of days since a given datetime, which is what I assumed.
It could be that the datetime subtraction followed by TimeSpan.Days doesn't work in the query. If that's the case, pull it outside like so:
var q = from dt in dts
where dt.id == dtId
orderby dt.Ad.CreatedAt, Did
select new
{
dt.Ad,
dt.CreatedAt,
DId,
Amount
};
This creates an IEnumerable of objects that have a DateTime CreatedAt property. If you call .ToList() now, the query is executed, and you can calculate stuff that Entity Framework doesn't support. Again, if the first attempt worked, that's the best solution if the number of days since X is what you need.
from item in q.ToList()
select new
{
dt.Ad
(dt.CreatedAt - compareDt).Days,
DId,
Amount
};