I'm trying to use JPA Criteria API to filter a date between a specific range.
The problem is that I don't have directly the date in my entity, I obtain the date using a date field and adding a number of days (which are in another field) to this date .
I look around and I saw some responses which recommends to calculate the date in java before pass it to criteria builder, but the problem is that I don't know the number of days to add because it depends on the days in a specific column of the table.
The simplified table OBJECT has a field CREATION_DATE of type date and EXPIRATION_DAYS which is a number. I want to filter by a range of possible expiration date, I get the expiration date of each element in OBJECT table adding EXPIRATION_DAYS to CREATION_DATE, the SQL query is:
select * from OBJECT
where CREATION_DATE + EXPIRATION_DAYS
between to_date('01/01/2018','dd/MM/yyyy') and to_date('01/02/2018','dd/MM/yyyy')
In CriteriaBuilder I make some attempts, but I think the best approach I try to use is the follow:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Object> criteria = criteriaBuilder.createQuery(Object.class);
List<Predicate> predicates...
predicates.add(criteriaBuilder.between(
criteriaBuilder.function("AddDate", Date.class, Object.get("creationDate"), criteriaBuilder.literal("day"), Object.get("expirationDays")),
dateInit, dateEnd));
criteria.where(predicates.toArray(new Predicate[]{}));
...
TypedQuery<Object> query = entityManager.createQuery(criteria);
return query.getResultList();
I use function("addDate"...) method, the problem is that I thought JPA implementation translate "addDate" to specific database vendor but it's not, because it's throwing the following exception which is pretty clear:
javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.4.v20160829-44060b6): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLSyntaxErrorException: ORA-00904: "ADDDATE": invalid identifier
And the query trace:
(AddDate(t0.CREATION_DATE, ?, t0.EXPIRATION_DAYS) BETWEEN ? AND ?)).
How can I achieve this using ORACLE as a database? Or there is a better approach?
In Hibernate seems that there is the #Formula annotation which provides the functionality I'm looking for, however there is no such thing in JPA neither in specific implementation I'm using(eclipselink).
I want to avoid the necessity of declare a specific function, but unfortunately I do it this way, first I declare the function ADD_DAYS in Oracle as follows:
create or replace function ADD_DAYS(dateValue DATE, daysValue NUMBER)
return DATE is
begin
return dateValue + daysValue;
end;
Then I call this function using function method of criteriaBuilder, and passing as parameters the function name, the type of object returned by the oracle function, and the two arguments I define, the column with the date, and the column with the days to add:
criteriaBuilder.function("ADD_DAYS", Date.class, Object.get("creationDate"), Object.get("expirationDays"))
Related
I have coded the below code snippet, the values are storing in DB is correct but while fetching the carId after inserting its always returning 1 instead of actual value, I can't use order="AFTER" as already I have used one order in generating sequence, and cannot use annotations based way as our org code structure does not allows that. Can someone please identify what I am doing wrong in XML-based way of inserting and fetching data?
Note** - Using oracle DB
<insert id = "insertIntoCar" parameterType = "CarEntity" useGeneratedKeys="true" keyColumn="id" keyProperty="carId">
<selectKey keyProperty="carId" resultType="long" order="BEFORE">
SELECT CAR_ID_SEQ.nextval From DUAL
<selectKey>
INSERT INTO CAR (CAR_ID, CAR_TYPE, CAR_STATUS)
VALUES (#{carId}, #{carType}, #{carStatus})
<insert>
I got a transaction with a LocalDate as created attribute:
private LocalDate created;
I also got following query to retrieve every date out of my table transaction.
SELECT DISTINCT t.created FROM Transaction t
This however just gives me all dates and I am mapping those dates to all month that were found.
public List<Month> findAllUsesdMonth() {
return getEm().createNamedQuery(Transaction.FIND_ALL_MONTH, LocalDate.class).getResultList().stream().map(LocalDate::getMonth)
.collect(Collectors.toList());
}
This leads to duplicate month obviously because I selected distinct dates and not distinct months. A workaround would be to use a Set instead of a List.
I would like to retrieve only the month in the JPQL query instead of mapping them afterward. Is there any possibility to do so?
Date format: yyyy-MM-dd HH:mm:ss
Query: SELECT t FROM Transaction t GROUP BY (SUBSTRING(t.created, 6, 7))
By calling this NamedQuery you will get distinct month.
I'm starting to use EntityFramework 6 with Oracle (client 12.x).
I have severals tables with DATE columns and TIMESTAMP columns.
All queries generated by EF6 convert .NET DateTime type to TIMESTAMP. So the query performance is very poor.
I tried to add a precision to 0 for the DATETIME columns but nothing changes on the generated query.
Property(_ => _.MyDate).HasColumnName("DATE_COLUMN").HasPrecision(0);
How can I specify in my code to translate my where clause by a TO_DATE function and not by a TO_TIMESTAMP function ?
I've had a similar issue with DATE Oracle type and EF6.1.
My workaround was to use the CodeFirstFunctions library (available only for EF6.1 and later) and specify a conversion function first in Oracle:
create or replace FUNCTION TO_DATE_LOCAL
(
DATETIMETOCONVERT IN VARCHAR2
, CONVERTFORMAT IN VARCHAR2
) RETURN DATE AS
BEGIN
RETURN to_date(DATETIMETOCONVERT, CONVERTFORMAT);
END TO_DATE_LOCAL;
And later in my DbContext:
[DbFunction("CodeFirstDatabaseSchema", "TO_DATE_LOCAL")]
public static DateTime ToDateLocal(string dateTimeToConvert, string convertFormat)
{
// no need to provide an implementation
throw new NotSupportedException();
}
So I can force Entity Framework to use DATE type in a where condition:
var measurement =
context.Measurements
.Where(m => m.MeasuredAt ==
PlantContext.ToDateLocal("2016.01.01 10:00:00", "YYYY.MM.DD Hh24:MI:SS"))
.FirstOrDefault();
Be careful to use capital letters for the function name and for the schema name with CodeFirstFunctions if you use Oracle.
If you need more details I've written a blog post about this with an example project.
I am writing an SQL query where the query should first search the first value, and only if this value is missing the query should search for the second value.
I have two tables. One of these tables contains the modification date (this is not always filled and can be null) and a creation date which is always filled.
Now what I want is that the query first looks in the table with the modification date and only if it is null looks at the table with the creation date.
Example of the query:
Select *
from all_articles
where to_char(modification_date, 'YYYYMMDD') = to_char(sysdate, 'YYYYMMDD')-1
-- if this is an empty record then
to_char(creation_date, 'YYYYMMDD') = to_char(sysdate, 'YYYYMMDD')-1
Can anyone help me with this query?
Almost all the major RDBMS' available have in built functions to handle such a situation.
The Oracle DB has NVL function which works as follows:
NVL(Modified_dt, Create_dt);
The above will return Modified_dt column data by default. However, if that isn't available, it will return Create_dt.
See here for details:
http://www.w3schools.com/sql/sql_isnull.asp
I have a birt dataset for a db2 query. My query works fine without parameters with the following query...
with params as (SELECT '2014-02-16' enddate,'1' locationid FROM sysibm.sysdummy1)
select
t.registerid
from (
select
...
FROM params, mytable sos
WHERE sos.locationid=params.locationid
AND sos.repositorytype ='xxx'
AND sos.repositoryaccountability='xxx'
AND sos.terminalid='xxx'
AND DATE(sos.balanceDate) between date(params.enddate)-6 DAY and date(params.enddate)
GROUP BY sos.terminalid,sos.balancedate,params.enddate) t
GROUP BY
t.registerid
WITH UR
But when I change the top line to ...
with params as (SELECT ? enddate,? locationid FROM sysibm.sysdummy1)
And make the two input paramters of string datatype I get db2 errors sqlcode -418. But i know that it is not my querty because my query works.
What is the right way for me to set up the parameters so there is no error?
thanks
I'm not familiar with DB2 programming, but on Oracle the ? works anywhere in the query.
Have you looked at http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=%2Fcom.ibm.db2z9.doc.codes%2Fsrc%2Ftpc%2Fn418.htm?
Seems that on DB2 it's a bit more complicated and you should use "typed parameter markers".
The doc says:
Typed parameter marker
A parameter marker that is specified with its target data type. A typed parameter marker has the general form:
CAST(? AS data-type)
This invocation of a CAST specification is a "promise" that the data type of the parameter at run time will be of the data type that is specified or some data type that is assignable to the specified data type.
Apart from that, always assure that your date strings are in the format that the DB expects, and use explicit format masks in the date function, like this:
with params as (
SELECT cast (? as varchar(10)) enddate,
cast (? as varchar2(80)) locationid
FROM sysibm.sysdummy1
)
select
...
from params, ...
where ...
AND DATE(sos.balanceDate) between date(XXX(params.enddate))-6 DAY and date(XXX(params.enddate))
...
Unfortunately I cannot tell you how the XXX function should look on DB2.
On Oracle, an example would be
to_date('2014-02-18', 'YYYY-MM-DD')
On DB2, see Converting a string to a date in DB2
In addition to hvb answer, i see two options:
Option 1 you could use a DB2 stored procedure instead of a plain SQL query. Thus there won't be these limitations you face to, due to JDBC query parameters.
Option 2, we should be able to remove the first line of the query "with params as" and replace it with question marks within the query:
select
t.registerid
from (
select
sos.terminalid,sos.balancedate,max(sos.balanceDate) as maxdate
FROM params, mytable sos
WHERE sos.locationid=?
AND sos.repositorytype ='xxx'
AND sos.repositoryaccountability='xxx'
AND sos.terminalid='xxx'
AND DATE(sos.balanceDate) between date(?)-6 DAY and date(?)
GROUP BY sos.terminalid,sos.balancedate) t
GROUP BY
t.registerid
A minor drawback is, this time we need to declare 3 dataset parameters in BIRT instead of 2. More nasty, i removed params.endDate from "group by" and replaced it with "max(sos.balanceDate)" in select clause. This is very near but not strictly equivalent. If this is not acceptable in your context, a stored procedure might be the best option.