spring JPA java.sql.Date is accepting invalid Dates - mysql

I am writing rest apis for my project to create, retrieve and modify values to my database.
The model has an sql.Date field which while doing POST request if I give date as for example 2018-01-35(yyyy-MM-dd), it is auto converting to 2018-02-04.
I want to avoid this so that it can tell me that the given date is invalid.
I searched in SO and tried this approach,
#DateTimeFormat(pattern = "yyyy-MM-dd")
private Date dob;
but this did nothing, so then I tried another method
#JsonFormat(shape= JsonFormat.Shape.STRING, pattern="yyyy-MM-dd")
but this only checks for format like if i give value as just "1993", it gives error but does not check for "1993-01-35".
I also tried,
public static boolean dateChecker(Date date){
Calendar calendar = Calendar.getInstance();
calendar.setLenient(false);
calendar.setTime(date);
try{
calendar.getTime();
}catch(Exception e)
{
System.out.println(e.getMessage());
return false;
}
return true;
}
But when I debug it, this method is already getting "1993-01-35" as "1993-02-04".
The model is,
#JsonFormat(shape= JsonFormat.Shape.STRING, pattern="yyyy-MM-dd")
private Date dob;
This is my POST method,
#RequestMapping(value = "/patient",method = RequestMethod.POST,consumes = "application/json")
public ResponseEntity<?> createPatient(#RequestBody Patient patient)
{
if(patient.getPatientId()!=null)
{
Patient patient1 = patientRepository.findOne(patient.getPatientId());
if(patient1!=null)
{
return new ResponseEntity("Patient for the given patient Id already exists.",HttpStatus.BAD_REQUEST);
}
}
System.out.println(patient.getDob());//Here I am getting 1993-02-04 when i give date as 1993-01-35
Please help me solve this.

Try util.date package for date.

You could try doing something like this if the calendar.setLenient isn't working for you.
I just added some random number for the day for testing.
int day = 21;
Calendar gmtCal =
Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(Calendar.MONTH, Calendar.JANUARY);
if(day <= gmtCal.getActualMaximum(Calendar.DAY_OF_MONTH)){
gmtCal.set(1993, Calendar.JANUARY, day );
System.out.println(gmtCal.get(Calendar.YEAR));
System.out.println(gmtCal.get(Calendar.MONTH));
System.out.println(gmtCal.get(Calendar.DAY_OF_MONTH));
System.out.println(gmtCal.getTime());
}
else{
System.out.println("Out of bounds");
}
The First thing you would have to do is to set the month for the calendar and then do a test on the day that was entered. If it passes then then you can set the whole date and if not print out an error.
Note: when using Calendar.get, the year and day will be printed out correctly. However, the month will start at zero. So in this example the dot gets print out:
1993 (year)
0 (month)
21 (day).
Also the the sysout for getTime() will print out some extra information that you might not need.
I used this post as a reference, it might give you some extra help: Detect invalid date on Calendar.set()
I hope this helps you. Let me know if this needs tweeking

Related

Spring data reactive repository - r2dbc not working

The query is getting executed but not getting any result.
router :- api/v1/service/appt/usr/{usr_id}
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
public Mono<ServerResponse> retrieveProjectsByUsr(ServerRequest request) {
final String userIdStr = request.pathVariable(USER_ID_PARAM);
final Optional<String> stDateStr = request.queryParam("stDate");
final Optional<String> endDateStr = request.queryParam("endDate");
final LocalDateTime stDate = LocalDateTime.parse(stDateStr.get(), DATE_TIME_FORMATTER);
final LocalDateTime endDate = LocalDateTime.parse(endDateStr.get(), DATE_TIME_FORMATTER);
long userId = Long.parseLong(userIdStr);
return secContext.retrieveUser().flatMap(usr -> {
Flux<Appt> appts = projectRepository.findApptsBetween(stDate, endDate, userId, usr.getOrgId());
return ServerResponse.ok().contentType(APPLICATION_JSON).body(appts, Project.class);
});
}
Repository code,
#Repository
public interface ApptRepository extends ReactiveCrudRepository<Appt, Long> {
#Query("select * from appt where usr_id = :usrId and org_id = :orgId and start_time BETWEEN :stDate and :endDate")
Flux<Appt> findApptsBetween(LocalDateTime stDate, LocalDateTime endDate, long usrId, int orgId);
}
Query from the log,
Executing SQL statement [select * from appt where usr_id = :usrId and org_id = :orgId and start_time BETWEEN :stDate and :endDate]
Data in project table,
Postman request,
http://localhost:9090/api/v1/service/appt/usr/2?stDate=2021-01-24 03:20&endDate=2021-03-25 05:23
Not sure what is wrong with this. It doesn't return the record.
The problem here is that reactive code needs to be subscibed to, to start execution. The following statement only describes what should happen:
Flux<Appt> appts = projectRepository.findApptsBetween(stDate, endDate, userId, usr.getOrgId());
To initate execution one needs to add .subscribe() operator to the reactive call. But here you dont't want that because that will start execution in a different context/thread and you won't be able to return the value to the outer method. This is why one should write reactive code as chain of reactive calls.
(Note: controller methods and router functions have an implicit .subscribe() at the end of your code so you don't need to add it)
You could rewite this code to something like this:
return secContext.retrieveUser().flatMap(usr ->
projectRepository.findApptsBetween(stDate, endDate, userId, usr.getOrgId())
.collectList()
.map(appts -> ServerResponse.ok().contentType(APPLICATION_JSON).body(appts, Project.class));
The following code works. Answer was modified from the above posts.
return secContext.retrieveUser()
.flatMap(usr -> apptRepository.findApptsBetween(userId, usr.getOrgId(), stDate, endDate).collectList()
.flatMap(appts -> ServerResponse.ok().contentType(APPLICATION_JSON).bodyValue(appts)));

How to take Date from sql db as a single result and compare with current date

i have a complex problem with Date field. Describe what i want to do:
I have field date1 as Date in my db.
#Temporal(TemporalType.DATE)
private Date date1;
I want to take data from this field and compare with current date.
#Query("SELECT date1 FROM Table io WHERE io.date1 >= DATE_FORMAT(CURRENT_DATE, '%Y-%m-%e')")
Date findAll2();
public boolean CheckDate1(){
currentDate = new Date();
date1 = getInterimOrdersRepo().findAll2();
if(currentDate.before(date1) || currentDate.equals(date1)){
System.out.println("TRUE");
System.out.println("currentDate = "+currentDate);
return true;
}
else{
System.out.println("FALSE");
return false;
}
}
but i have an error:
result returns more than one elements; nested exception is javax.persistence.NonUniqueResultException
When method return false i want do Update field data1 with " " empty data.
I using jsf, what i must to do?
It seems that you are trying to read several values from the table into a single variable, and that is the error.
findall2 returns an array (most likely) and u should read one of it's values - try reading first one.
Furthermore, I believe that you can skip the "DATE_FORMAT" in your query, and this is a very strange way to write a code. Not clear what u are trying to achieve here

Weird behaviour encountered using java.sql.TimeStamp and a mysql database

The weird behavior is that a java.sql.Timestamp that I create using the System.currentTimeMillis() method, is stored in my MySQL database as 1970-01-01 01:00:00.
The two timestamps I am creating are to mark the beginning and end of a monitoring task I am trying to perform, what follows are excepts from the code where the behavior occurs
final long startTime = System.currentTimeMillis();
while(numberOfTimeStepsPassed < numTimeStep) {
/*
* Code in here
*/
}
final long endTime = System.currentTimeMillis();
return mysqlConnection.insertDataInformation(matrixOfRawData, name,Long.toString(startTime),
Long.toString(endTime), Integer.toString(numTimeStep),
Integer.toString(matrixOfRawData[0].length), owner,
type);
And here is the code used for inserting the time stamps and other data into the MySQL database
public String insertDataInformation(final double [][] matrix,
final String ... params) {
getConnection(lookUpName);
String id = "";
PreparedStatement dataInformationInsert = null;
try {
dataInformationInsert =
databaseConnection.prepareStatement(DATA_INFORMATION_PREPARED_STATEMENT);
id = DatabaseUtils.createUniqueId();
int stepsMonitored = Integer.parseInt(params[STEPS_MONITORED]);
int numberOfMarkets = Integer.parseInt(params[NUMBER_OF_MARKETS]);
dataInformationInsert.setNString(ID_INDEX, id);
dataInformationInsert.setNString(NAME_INDEX, params[0]);
dataInformationInsert.setTimestamp(START_INDEX, new Timestamp(Long.parseLong(params[START_INDEX])));
dataInformationInsert.setTimestamp(END_INDEX, new Timestamp(Long.parseLong(params[END_INDEX])));
dataInformationInsert.setInt(STEPS_INDEX, stepsMonitored);
dataInformationInsert.setInt(MARKETS_INDEX, numberOfMarkets);
dataInformationInsert.setNString(OWNER_INDEX, params[OWNER]);
dataInformationInsert.setNString(TYPE_INDEX, params[TYPE]);
dataInformationInsert.executeUpdate();
insertRawMatrix(matrix, id, Integer.toString(stepsMonitored), Integer.toString(numberOfMarkets));
} catch (SQLException sqple) {
// TODO Auto-generated catch block
sqple.printStackTrace();
System.out.println(sqple.getSQLState());
} finally {
close(dataInformationInsert);
dataInformationInsert = null;
close(databaseConnection);
}
return id;
}
The important lines of code are :
dataInformationInsert.setTimestamp(START_INDEX, new Timestamp(Long.parseLong(params[START_INDEX])));
dataInformationInsert.setTimestamp(END_INDEX, new Timestamp(Long.parseLong(params[END_INDEX])));
The JavaDocs on the TimeStamp ( http://docs.oracle.com/javase/1.5.0/docs/api/java/sql/Timestamp.html ) says that it takes in time in milliseconds since 1st January 1970 and a simple print test confirms this.
What I am looking for is:
A reason for this behavior when trying to store timestamps in a MySQL database through java.sql.Timestamp?
Any solutions to this behavior?
Any possible alternatives?
Any possible improvements?
EDIT:
Been asked to include what START_INDEX and END_INDEX are:
private static final int END_INDEX = 4;
private static final int START_INDEX = 3;
Apologises for not putting them in the original post.
Okay, look at your call:
insertDataInformation(matrixOfRawData, name, Long.toString(startTime),
Long.toString(endTime), Integer.toString(numTimeStep),
Integer.toString(matrixOfRawData[0].length), owner,
type);
So params will have values:
0: name
1: start time
2: end time
3: numTimeStep
4: matrixOfRowData[0].length
5: owner
6: type
Then you're doing:
dataInformationInsert.setTimestamp(START_INDEX,
new Timestamp(Long.parseLong(params[START_INDEX])));
... where START_INDEX is 3.
So you're using the value corresponding to numTimeStep as the value for the timestamp... I suspect you don't want to do that.
I would strongly advise you to create a simple object type (possibly a nested type in the same class) to let you pass these parameters in a strongly typed, simple to get right fashion. The string conversion and the access by index are both unwarranted, and can easily give rise to errors.

Dapper And System.Data.OleDb DbType.Date throwing 'OleDbException : Data type mismatch in criteria expression'

Not sure if I should raise an issue regarding this, so thought I would ask if anybody knew a simple workaround for this first. I am getting an error when I try to use Dapper with OleDbConnection when used in combination with MS Access 2003 (Jet.4.0) (not my choice of database!)
When running the test code below I get an exception 'OleDbException : Data type mismatch in criteria expression'
var count = 0;
using (var conn = new OleDbConnection(connString)) {
conn.Open();
var qry = conn.Query<TestTable>("select * from testtable where CreatedOn <= #CreatedOn;", new { CreatedOn = DateTime.Now });
count = qry.Count();
}
I believe from experience in the past with OleDb dates, is that when setting the DbType to Date, it then changes internally the value for OleDbType property to OleDbTimeStamp instead of OleDbType.Date. I understand this is not because of Dapper, but what 'could' be considered a strange way of linking internally in the OleDbParameter class
When dealing with this either using other ORMs, raw ADO or my own factory objects, I would clean up the command object just prior to running the command and change the OleDbType to Date.
This is not possible with Dapper as far as I can see as the command object appears to be internal. Unfortunately I have not had time to learn the dynamic generation stuff, so I could be missing something simple or I might suggest a fix and contribute rather than simply raise an issue.
Any thoughts?
Lee
It's an old thread but I had the same problem: Access doesn't like DateTime with milliseconds, so you have to add and extension method like this :
public static DateTime Floor(this DateTime date, TimeSpan span)
{
long ticks = date.Ticks / span.Ticks;
return new DateTime(ticks * span.Ticks, date.Kind);
}
And use it when passing parameters:
var qry = conn.Query<TestTable>("select * from testtable where CreatedOn <= #CreatedOn;", new { CreatedOn = DateTime.Now.Floor(TimeSpan.FromSeconds(1)) });
Unfortunately, with current Dapper version (1.42), we cannot add custom TypeHandler for base types (see #206).
If you can modify Dapper (use the cs file and not the DLL) merge this pull request and then you do not have to use Floor on each parameters :
public class DateTimeTypeHandler : SqlMapper.TypeHandler<DateTime>
{
public override DateTime Parse(object value)
{
if (value == null || value is DBNull)
{
return default(DateTime);
}
return (DateTime)value;
}
public override void SetValue(IDbDataParameter parameter, DateTime value)
{
parameter.DbType = DbType.DateTime;
parameter.Value = value.Floor(TimeSpan.FromSeconds(1));
}
}
SqlMapper.AddTypeHandler<DateTime>(new DateTimeTypeHandler());

How to work with zero dates ("0000-00-00") in Hibernate?

I have MySql table that has a date field with zeroes ("0000-00-00") as its default value (field cannot be null, I can't change table structure). Hibernate doesn't like zero dates and throws exception during read or save.
I managed to make it read records by setting MySql connection setting "zeroDateTimeBehavior=convertToNull" that converts zero dates to nulls while retrieving records. It is all working fine until I try to save the record that has null date - it throws exception that date cannot be null.
So the question is - how to save record through Hibernate so date will appear as zeroes in a table?
Thanks.
I'd try to add an Hibernate Interceptor (API, Doc) and try to implement something in the onSave() method.
The following code may work:
static final Date ZERO_DATE = //0000-00-00
public boolean onSave(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types)
throws CallbackException {
for(int i = 0; i< propertyNames.length; i++) {
if(propertyNames[i].equals("dateFieldName") && state[i]==null) {
state[i] = ZERO_DATE;
return; //or may continue, if there are several such fields.
}
}
}
Ready and working solution for DATE '0000-00-00' and TIME '00:00:00': How to map MySQL DATE '0000-00-00' & TIME '00:00:00' with Hibernate
Thanks Preston for the code and ChssPly76 for useful comments.