My team recently migrated one of our main database from Oracle to Azure SQL database. In this and upcoming posts, I’m going to share a few things I have learned during the process. Specifically, in this post, I share an issue we ran into with storing date and time, after migrating the schema and data from our Oracle into azure SQL database.
Our oracle database contains tables which have columns of Date type. Values of the date columns originate from a java application, using oracle JDBC driver . In the database, the values contain both date and time information. After migrating the schema and data to azure SQL, and updating the app to connect to the new database using Microsoft JDBC Driver for SQL, new date values no longer contain time component, only the date.
Below simplified snippet demonstrates a typical way we used to set a date value via a prepared statement.
Connection conn = getConnection(); PreparedStatement stmt = conn.prepareStatement("Update DocumentTable.Document Set column_a = ?, date_added_on = ? where id = ?"); stmt.setString(1, "new value"); java.sql.Date dtAddedOn= new java.sql.Date(new java.util.Date().getTime()); stmt.setDate(2, dtAddedOn); stmt.setInt(3, 123);
In the above snippet, notice that dtAddedOn has the type java.sql.Date. The value
contains both date and time info (e.g. 2022-05-27 9:43 PM). After insertion, using Oracle developer with configured date time format, the resulting value in the oracle database has this format: 2022-MAY-27 09:43:00 PM.
After migrating the schema and data, without changing codes in the java app besides changing the JDBC driver and connection string, the hour, minute, and second of the resulting value in the azure SQL is all 0: 2022-05-27 00:00:00.
By definition, SQL date does not contain time information. Since we use java.sql.Date to store the date as shown in the above snippet, when the Microsoft JDBC driver sends the statement to the database for execution, it sets all the time parts to 0.
To conform with the definition of SQL
java.sql.DateDATE
, the millisecond values wrapped by ajava.sql.Date
instance must be ‘normalized’ by setting the hours, minutes, seconds, and milliseconds to zero in the particular time zone with which the instance is associated.
In fact, if you look at the class definition of java.sql.Date, you will see that all the methods and constructor that have to do with time have been deprecated.
Therefore, Microsoft JDBC driver behaves as expected by normalizing the time values to 0. However, I was intrigued as to why we are able to store both date and time when using the Oracle JDBC driver and Oracle database. It turns out the reason is because the Oracle JDBC driver converts the date value to java.sql.Timestamp before sending to the database.
Beginning with [Oracle JDBC 11.1], the driver maps SQL DATE columns to
Displays same DATA_TYPE value for TIMESTAMP and DATE, while using ojdbc6 — oracle-techjava.sql.Timestamp
by default
Once we understand the problem, the solution is simple. We update the app to use java.sql.Timestamp, which represents both date and time.
Connection conn = getConnection(); PreparedStatement stmt = conn.prepareStatement("Update DocumentTable.Document Set column_a = ?, date_added_on = ? where id = ?"); stmt.setString(1, "new value"); java.sql.Timestamp dtAddedOn = Timestamp.from(Instant.now()); stmt.setTimestamp(2, dtAddedOn); stmt.setInt(3, 123);
Notice the changes in the above snippet compared to the one at the beginning:
Upon making the change, executing the update statement results in the the date_added_on column in azure SQL database to contain both date and time as expected.
Difference Between java.sql.Time, java.sql.Timestamp and java.sql.Date in Java – GeeksforGeeks
java.util.Date vs java.sql.Date | Baeldung
Date (Java SE 11 & JDK 11 ) (oracle.com)
Using basic JDBC data types – JDBC Driver for SQL Server | Microsoft Docs
Using MSAL angular to authenticate a user against azure ADB2C via authorization code flow with Proof Key for Code Exchange.
Using Azure Application Insights for centralized logging
Building multitenant application – Part 3: Authentication
Building multitenant application – Part 2: Storing value into database session context from ASP.NET core web API
Building multitenant application – Part 1: Multitenant database using Row Level Security
Migration from Oracle to azure SQL caveat – Azure SQL does not support time zone settings
Migrating from Oracle to Azure SQL caveat – prepared statement set string causes implicit conversion
Azure AD authentication in angular using MSAL angular v2 library