Database Oracle, Query Date String JPA Repository Crud Repository, Java 8 - oracle

NOTE: I can't put some privated code (Database or Java Code).
I have in the Database
CREATE TABLE "SCHEMA"."ENTITY"
(
"HCODFECREGISTR" DATE,
... BLABLA
)
The Entity
import java.util.Date;
public class Entity implements java.io.Serializable {
private Date hcodfecregistr;
....
}
In the Repository Interface
using
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
public interface EntityRepository extends CrudRepository<Entity, Long>,
JpaRepository<Entity, Long> {
public static final String USING_STRING
= " SELECT enti FROM Entity enti WHERE "
+ " (enti.hcodfecregistr BETWEEN TO_DATE(:stringIni,'dd/MM/yyyy hh24:mi') AND TO_DATE(:stringEnd,'dd/MM/yyyy hh24:mi'))";
#Query(value = USING_STRING)
List<Object[]> getEntityUsingString(
#Param("stringIni") String stringIni, #Param("stringEnd") String stringEnd);
public static final String USING_DATE
= " SELECT enti FROM Entity enti WHERE "
+ " (enti.hcodfecregistr BETWEEN :dateIni AND :dateIni) ";
#Query(value = USING_DATE)
List<Object[]> getEntityUsingDate(
#Param("dateIni") Date dateIni, #Param("dateEnd") Date dateEnd);
}
Now I want to perform a query.
Date fecha = //some java.util.Date
Calendar calendar = Calendar.getInstance();
calendar.setTime(fecha);
calendar.set(Calendar.HOUR, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
Date dateIni = calendar.getTime();
calendar.set(Calendar.HOUR, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
Date dateEnd = calendar.getTime();
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
String stringIni = dateFormat.format(fecha) + " 00:00";
String stringEnd = dateFormat.format(fecha) + " 23:59";
Works!
List<Object[]> listaObjects = theRepository.getEntityUsingString(stringIni, stringEnd);
Fails! It does not bring me results (but it also does not show any error).
List<Object[]> listaObjects = theRepository.getEntityUsingDate(dateIni, dateEnd);
Question:
I want to understand Why using the same fecha (java.util.Date) the method getEntityUsingString works, but using the method getEntityUsingDate fails (
When I set the range using Date it does not bring me results, whereas when I set the range with String it does.).
In my opinion, it should yield the same result that complies with the range of dates.
What is the problem?

You did not write what error it was, so it's only my guess that the most likely error in this case is:
ORA-01861: literal does not match format string.
If this is the case, then the reason is that you are passing to the query two values of date type:
Date dateIni = calendar.getTime();
.....
Date dateEnd = calendar.getTime();
.....
theRepository.getEntityUsingDate(dateIni, dateEnd);
these values are bind to this parameters :stringIni and :stringEnd
BETWEEN TO_DATE(:stringIni,'dd/MM/yyyy hh24:mi') AND TO_DATE(:stringEnd,'dd/MM/yyyy hh24:mi')
Since TO_DATE( char, [format] ) functions expects CHAR (string) as a first parameter, but gets a date, then according to rules explained in this documentation see section: implicit conversion
The following rules govern implicit data type conversions: .... ....
When you use a SQL function or operator with an argument of a data
type other than the one it accepts, Oracle converts the argument to
the accepted data type.
it does an implicit conversion from date to string using TO_CHAR function.
This function uses the default date language from a session to format strings, so depending of this settings the date may be converted to something like 12/15/54, 19-06-11 12:23 PM, JUN 23, 2019 13:20 AM +01 etc.
This string 12/15/54 is then passed to TO_DATE(:stringIni,'dd/MM/yyyy hh24:mi'), and because this sting doesnt match format string, the error is thrown: ORA-01861: literal does not match format string

Related

how to extract the today's date out of the date column using SQL query?

I assume I would need to change query in order to sort the data with today's date.
Please tell me how to change it though...
SQL QUERY in ToDoDao
#Query("SELECT * FROM todo_table WHERE date(date) = date('now')")
fun getTodayList(): Flow<List<ToDoTask>>
DATABASE
#Entity(tableName = DATABASE_TABLE)
data class ToDoTask(
#PrimaryKey(autoGenerate = true) val id: Int = 0,
#ColumnInfo(name = "title") val title: String,
#ColumnInfo(name = "description") val description: String,
#ColumnInfo(name = "priority") val priority: Priority,
#ColumnInfo(name = "date") val date: String,
#ColumnInfo(name = "favorite") var favorite: Boolean)
date val in ViewModel class
val date : MutableState<String> = mutableStateOf("")
datas inserted
enter image description here
I have tried the code below and I was able to activate the function as the query as I intented, so I think the query is the issue here.
#Query("SELECT * FROM todo_table WHERE date = '2023-2-14'")
fun getTodayList(): Flow<List<ToDoTask>>
The Issue
The issue is that the SQLite date function expects the date to be in an accepted format.
YYYY-M-DD is not such a format and will result in null rather than a date. YYYY-MM-DD is an accepted format (see https://www.sqlite.org/lang_datefunc.html#time_values). That is leading zeros are used to expand single digit numbers to 2 digit numbers for the month and day of month values.
The Fix (not recommended)
To fix the issue you have shown, you could use (see the However below):-
#Query("SELECT * FROM todo_table WHERE date(substr(date,1,5)||CASE WHEN substr(date,7,1) = '-' THEN '0' ELSE '' END ||substr(date,6)) = date('now');")
If the month was 2 numerics i.e. MM (e.g. 02 for February) then the above would not be necessary.
The CASE WHEN THEN ELSE END construct is similar to IF THEN ELSE END. see https://www.sqlite.org/lang_expr.html#the_case_expression. This is used to add the additional leading 0, when omitted, to the string used by the date function.
However, the above would not cater for days that have the leading 0 omitted for the first 9 days of the month. This due to the 4 permutations of the format (YYYY-MM-DD, YYYY-MM-D, YYYY-M-D and YYYY-M-DD) would be more complex e.g.
#Query("SELECT * FROM todo_table WHERE date(CASE WHEN length(date) = 8 THEN substr(date,1,5)||'0'||substr(date,6,1)||'-0'||substr(date,8) WHEN length(date) = 9 AND substr(date,7,1) = '-' THEN substr(date,1,5)||'0'||substr(date,6) WHEN length(date) = 9 AND substr(date,8,1) = '-' THEN substr(date,1,8)||'0'||substr(date,9) ELSE date END) = date('now');")
Recommended Fix
The recommended fix is to store values using one of the accepted formats rather than try to manipulate values to be an accepted date to then be worked upon using the date and time functions.

Error: Hexadecimal string contains non-hex character for enumerated field

I've got an error
Caused by: org.h2.jdbc.JdbcSQLDataException: Hexadecimal string contains non-hex character: "SWEDISH_MARKET"; SQL statement:
when I pass an enum as parameter to query
Entity has this enum as field.
Field has bellow annotations
#Column(name = "market", nullable = false)
#Enumerated(EnumType.STRING)
private Market type;
query:
#Query(value = "SELECT count(*) " +
"FROM Statements statements WHERE " +
"market = :market",
nativeQuery = true)
long countStatements(#Param("licenseMarket") Market market);
Previously everything was parsed to String and casted in postgres market = (cast :market as text)
Invoking countStatements(Market.SWEDISH_MARKET) throws the exception
You have to use JPQL, then it parse enums automatia
The problem is enum was parsed automatically to the number, regardless column type is text.
Solutions are:
invoke on enum method .name() and pass a param as String,
use SpEL :#{#market?.name()},

How to receive a SELECT VALUE COUNT query with Java?

When I use the Cosmos website to execute the query
SELECT VALUE COUNT(1) FROM c WHERE 1623736778 <= c.timestamp AND 1623736779 <= c.timestamp
I get
[
4
]
in the "Results" window. This makes sense, since executing
SELECT * FROM c WHERE 1623736778 <= c.timestamp AND 1623736779 <= c.timestamp
returns 4 entries from my database.
How do I receive the result "4" (from SELECT VALUE COUNT(1) ...) in my backend code using Java with Spring Boot? In other words, given any Cosmos repository, how does one access the return from a SELECT VALUE COUNT ... query using Java?
For context, I have a repository class that looks like this:
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import com.azure.spring.data.cosmos.repository.Query;
import com.azure.spring.data.cosmos.repository.ReactiveCosmosRepository;
import reactor.core.publisher.Flux;
import myownpackage.MyEntity;
#Repository
public interface MyRepository extends ReactiveCosmosRepository<MyEntity, String> {
...
#Query(value = "SELECT * FROM c WHERE #startTime <= c.timestamp AND c.timestamp <= #endTime")
Flux<MyEntity> findInRange(
#Param("startTime") Long startTime,
#Param("endTime") Long endTime
);
#Query(value = "SELECT VALUE COUNT(1) FROM c WHERE #startTime <= c.timestamp AND c.timestamp <= #endTime")
Flux<MyEntity> countInRange(
#Param("startTime") Long startTime,
#Param("endTime") Long endTime
);
}
In my service class I try to do
#Autowired private MyRepository repo;
int totalCount = repo.countInRange(1623736778, 1623736779).collectList().block().get(0);
so that totalCount == 4, but this causes an error saying "incompatible types: myownpackage.MyEntity cannot be converted to int." Changing the return type of countInRange to int gives "int cannot be dereferenced." There's no issue accessing results from findInRange() in this manner. I used a logger (from slf4j) to run
LOGGER.info("\n\nHere is the type we need to know: {}\n\n",
repo.countInRange(1623736778,1623736779).getClass().getCanonicalName());
which logs "Here is the type we need to know: reactor.core.publisher.FluxMap", but Googling reactor.core.publisher.FluxMap doesn't reveal any documentation similar to Flux's documentation.
The result is of primitive type int and not an Object. Dereferencing is the process of accessing the value referred to by a reference . Since, int is already a value (not a reference), it can not be dereferenced. Try changing the return type as Flux<Integer>

Does JodaTime or Java 8 have special support JD Edwards Date and Time?

The topic at hand is a messy domain-specific problem working with dates in Oracle's ERP software called JD Edwards. Its detail is documented in this question.
Before writing wrapper classes for handling the dates and times from JD Edwards, I want to know if JodaTime or Java 8 introduced any special support for this unique time format, or if I'll have to do significant string manipulation regardless of the libraries I use.
This is an obscure problem, so please only respond if you have specific knowledge of this problem, and/or JodaTime/Java 8/JSR 310.
ADDITION:
Per Basil Bourque's request, adding example of timestamps that accompany said dates. Here are two example of date/time fields from different tables:
JCSBMDATE:115100, JCSBMTIME:120102.0
RLUPMJ:114317, RLUPMT:141805.0
Also, the date variable is being cast as a BigDecimal and the time is a Double. So, I'll probably keep the string parsers around, but also write factory methods that take the BigDecimal/Double values natively as well.
It seems that the time field is actually the number of Milliseconds (not seconds) from the start of the day, and the ".0" can be ignored. So, one will have to perform a conversion and calculation like so:
localDate.atTime(LocalTime.ofNanoOfDay(Long.parseLong(jdeTime) * 1000000))
JD Edwards date defined
Actually the detail of a JD Edwards date is not so gory, according to this simple description on a page at Oracle.com:
About the Julian Date Format
Date fields in JD Edwards World files are stored in the Julian format. …
The Julian (*JUL) date format is CYYDDD, where:
C is added to 19 to create the century, i.e. 0 + 19 = 19, 1 + 19 = 20. YY is the year within the century, DDD is the day in the year.
Terms:
I would call the C part a “century-offset”, how many centuries to add to 19. Use 0 for 19xx years, and 1 for 20xx years.
The java.time framework calls the DDD a “DayOfYear”, and “ordinal date” is another term. The use of “Julian” for a day-number-within-a-year is common but not correct, conflicting with a Julian Day.
The java.time framework does not include direct support for parsing or generating strings of this format, not that I can find.
JulianFields
There is the java.time.temporal.JulianFields but those are for an redefined version of Julian dates where we count the number of days from an epoch (1970-01-01 (ISO) rather than the historic November 24, 4714 BC (proleptic Gregorian)), while ignoring years altogether. So this has nothing to do with the JD Edwards definition, contrary to some incorrect advice on that page linked in the Question.
Ordinal Date
This JD Edwards date is a version of an ordinal date. The ordinal date is sometimes referred to casually (and incorrectly) as a "julian" date only because it shares the idea of counting a sequence of days. But an ordinal date counts days from the beginning of the year to end of year for a number always between 1 and 365/366 (leap year), not counting since some epoch and growing into a number into the thousands.
Back to the Question, handling the JD Edwards date in java.time…
No, I do not find any direct or indirect support the JD Edwards date built into java.time.
The java.date.format package seems unaware of the century of a date, only the year and the era. So no way that I can find to define the C part of a JD Edwards date.
The last part of a JD Edwards date, the ordinal number of days in the year, is well-handled with within both the date-time classes and the formatting classes.
Wrap LocalDate
Since a JD Edwards date apparently has the same logic as the ISO chronology used by java.time, the only real issue at hand is parsing and generating String objects according to this particular format. All other behavior can be leveraged from a LocalDate.
Since I cannot find a way to define a java.time.format.DateTimeFormatter for this purpose, I suggest writing a utility class to handle these chores.
Ideally we would extend the LocalDate class, overriding its parse and toString methods. And perhaps a getCenturyOffset method. But the LocalDate class is marked final and cannot be extended. So I would create something like this class shown below, wrapping a LocalDate.
CAVEAT: Use at your own risk. Fresh code, barely run, hardly tested. Meant as an example, not for use in production. Use according to terms of the ISC License.
package com.example.whatever;
import java.time.LocalDate;
import java.time.ZoneId;
/**
* Wraps a 'LocalDate' to provide parsing/generating of strings in format known
* as JD Edwards date.
*
* Format is CYYDDD where C is the number of centuries from 1900, YY is the year
* within that century, and DDD is the ordinal day within the year (1-365 or
* 1-366 in Leap Year).
*
* Immutable object. Thread-safe (hopefully! No guarantees).
*
* I would rather have done this by extending the 'java.time.LocalDate' class, but that class is marked 'final'.
*
* Examples: '000001' is January 1 of 1900. '116032' is February 1, 2016.
*
* © 2016 Basil Bourque. This source code may be used according to terms of the ISC License at https://opensource.org/licenses/ISC
*
* #author Basil Bourque
*/
public class JDEdwardsLocalDate {
private LocalDate localDate = null;
private int centuryOffset;
private int yearOfCentury;
private String formatted = null;
// Static Factory method, in lieu of public constructor.
static public JDEdwardsLocalDate from ( LocalDate localDateArg ) {
return new JDEdwardsLocalDate ( localDateArg );
}
// Static Factory method, in lieu of public constructor.
static public JDEdwardsLocalDate parse ( CharSequence charSequenceArg ) {
if ( null == charSequenceArg ) {
throw new IllegalArgumentException ( "Passed CharSequence that is null. Message # 0072f897-b05f-4a0e-88d9-57cfd63a712c." );
}
if ( charSequenceArg.length () != 6 ) {
throw new IllegalArgumentException ( "Passed CharSequence that is not six characters in length. Message # eee1e134-8ec9-4c92-aff3-9296eac1a84a." );
}
String string = charSequenceArg.toString ();
// Should have all digits. Test by converting to an int.
try {
int testAsInteger = Integer.parseInt ( string );
} catch ( NumberFormatException e ) {
throw new IllegalArgumentException ( "Passed CharSequence contains non-digits. Fails to convert to an integer value. Message # 0461f0ee-b6d6-451c-8304-6ceface05332." );
}
// Validity test passed.
// Parse.
int centuryOffset = Integer.parseInt ( string.substring ( 0 , 1 ) ); // Plus/Minus from '19' (as in '1900').
int yearOfCentury = Integer.parseInt ( string.substring ( 1 , 3 ) );
int ordinalDayOfYear = Integer.parseInt ( string.substring ( 3 ) );
int centuryStart = ( ( centuryOffset + 19 ) * 100 ); // 0 -> 1900. 1 -> 2000. 2 -> 2100.
int year = ( centuryStart + yearOfCentury );
LocalDate localDate = LocalDate.ofYearDay ( year , ordinalDayOfYear );
return new JDEdwardsLocalDate ( localDate );
}
// Constructor.
private JDEdwardsLocalDate ( LocalDate localDateArg ) {
this.localDate = localDateArg;
// Calculate century offset, how many centuries plus/minus from 1900.
int year = this.localDate.getYear ();
int century = ( year / 100 );
this.yearOfCentury = ( year - ( century * 100 ) ); // example: if 2016, return 16.
this.centuryOffset = ( century - 19 );
// Format as string.
String paddedYearOfCentury = String.format ( "%02d" , this.yearOfCentury );
String paddedDayOfYear = String.format ( "%03d" , this.localDate.getDayOfYear () );
this.formatted = ( this.centuryOffset + paddedYearOfCentury + paddedDayOfYear );
}
#Override
public String toString () {
return this.formatted;
}
public LocalDate toLocalDate () {
// Returns a java.time.LocalDate which shares the same ISO chronology as a JD Edwards Date.
return this.localDate;
}
public int getDayOfYear () {
// Returns ordinal day number within the year, 1-365 inclusive or 1-366 for Leap Year.
return this.localDate.getDayOfYear();
}
public int getYear () {
// Returns a year number such as 2016.
return this.localDate.getYear();
}
public int getYearOfCentury () {
// Returns a number within 0 and 99 inclusive.
return this.yearOfCentury;
}
public int getCenturyOffset () {
// Returns 0 for 19xx dates, 1 for 20xx dates, 2 for 21xx dates, and so on.
return this.centuryOffset;
}
public static void main ( String[] args ) {
// '000001' is January 1, 1900.
JDEdwardsLocalDate jde1 = JDEdwardsLocalDate.parse ( "000001" );
System.out.println ( "'000001' = JDEdwardsLocalDate: " + jde1 + " = LocalDate: " + jde1.toLocalDate () + " Should be: January 1, 1900. " );
// '116032' is February 1, 2016.
JDEdwardsLocalDate jde2 = JDEdwardsLocalDate.parse ( "116032" );
System.out.println ( "'116032' = JDEdwardsLocalDate: " + jde2 + " = LocalDate: " + jde2.toLocalDate () + " Should be: February 1, 2016." );
// Today
LocalDate today = LocalDate.now ( ZoneId.systemDefault () );
JDEdwardsLocalDate jdeToday = JDEdwardsLocalDate.from ( today );
System.out.println ( "LocalDate.now(): " + today + " = JDEdwardsLocalDate: " + jdeToday + " to LocalDate: " + jdeToday.toLocalDate () );
}
}
When run.
'000001' = JDEdwardsLocalDate: 000001 = LocalDate: 1900-01-01 Should be: January 1, 1900.
'116032' = JDEdwardsLocalDate: 116032 = LocalDate: 2016-02-01 Should be: February 1, 2016.
LocalDate.now(): 2016-05-09 = JDEdwardsLocalDate: 116130 to LocalDate: 2016-05-09
JD Edwards time-of-day
As for JD Edwards time-of-day formats, I searched and could not find any documentation. If you know of some, please edit your Question to add links. The only mentions of JDE times seemed to be a count of seconds from midnight.
If that is the case (a count since midnight), the java.time.LocalTime class has you covered. A LocalTime can be instantiated and read as either:
Whole seconds since start of day ( withSecond, ofSecondOfDay )
Fractional seconds since start of day, with a resolution of nanoseconds ( withNano, ofNanoOfDay )
Nanosecond resolution means up to nine digits of a decimal fraction. No problem handling the six digits you mentioned. Just do the math, multiply/divide by 1_000L. Just be aware that means possible data loss as you could be truncating those last three digits of fraction (7th, 8th, 9th digits of decimal fraction) if the LocalTime value came from outside of JD Edwards data. [FYI, the old java.util.Date/.Calendar classes, as well as Joda-Time, are limited to milliseconds resolution, for three digits of decimal fraction.]
Not recommended: You could do some kind of combo class, composed of a LocalDate and a LocalTime. Or use a LocalDateTime. The key issue is time zone. If a JD Edwards date-time is always in a certain time zone such as UTC, then it might make sense to combine and use an OffsetDateTime . But if it has no specific time zone context, if the values are just a fuzzy idea of a date-time rather than specific points on the timeline, then use LocalDateTime as it has no time zone. If a JDE is always in UTC, use OffsetDateTime set to ZoneOffset.UTC. If you want to specify a time zone (an offset plus rules for handling anomalies such as DST), use ZonedDateTime.
Recommended: Use a LocalTime separately. I do not think you want to be using my JDEdwardsLocalDate class in your business logic, especially because it is not a full implementation fitting into the java.time framework. My intention is to use that class to immediately convert to LocalDate when you encounter a JDE date. Same goes for a JDE time-of-day, convert to LocalTime immediately. If their context is always UTC, create an OffsetDateTime with UTC, and then pass that around your business logic. Only go back to a JDE date & time when necessary (persisting to database column of that JDE type, or reporting to user expecting that JDE presentation).
OffsetDateTime odt = OffsetDateTime.of( myLocalDate , myLocalTime , ZoneOffset.UTC );
If the JDE date & time has some other context implied, then assign the intended time zone.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.of( myLocalDate , myLocalTime , zoneId );
Time zone is crucial here. You must understand the concepts in general. Be clear that LocalDate and LocalTime and LocalDateTime are not a moment on the timeline. They have no specific meaning until you adjust them into a time zone (or at least an offset-from-UTC).
My diagram of date-time types included on this Answer may help you if not familiar with the java.time types.
And you must understand the meaning of JDE date & time and their use in your apps/databases. As I could not find anything about JDE time, I could not learn anything about the JD Edwards intentions towards time zones. So I cannot suggest anything more specific.
No: Neither Joda Time nor Java 8 have support for JD Edwards time representations.

Java Oracle date

String sql="select id from period where '"+systemDate+"' between startDate and endDate";
I need to execute this query and I am using oracle 10g. I want to get the id which falls between these dates. Problem I am facing is in oracle my date format is"dd-mon-yy" and systemDate is in format yyyy-mm-dd.So is ther any way to convert java date to dd-mon-format or any other way to execute the above query...
Kindly help.
You should convert your java.util.Date to a java.sql.Date and use a PreparedStatement as shown below:
java.util.Date jStartDate = ...; //your "java" date object
java.sql.Date startDate = new java.sql.Date(jStartDate.getTime());
java.util.Date jEndDate = ...;
java.sql.Date endDate = new java.sql.Date(jEndDate.getTime());
PreparedStatement p = connection.prepareStatement("select id from period where systemDate between ? and ?");
p.setDate(1, startDate);
p.setDate(2, endDate);
ResultSet rs = p.executeQuery();
Java has date formatting classes. DateFormat is the abstract parent class for these, SimpleDateFormat here is probably the one you want
SimpleDateFormat oracleStyle = new SimpleDateFormat("dd-MM-yy");
String dString = oracleStyle.format(systemDate);
String sql="select id from period where '"+systemDate+"' between startDate and endDate";
I havent tested this. The format call might need new string buffers as arguments, and my format string could be off, but this is the idea. Read the documentation

Resources