Spring Batch - Meta tables - Oracle 12C - Entries in UTC timezone - spring

We are using Oracle 12C DB for Spring Batch Application.
System timezone is PST.
but i want the Job and step related entries in metatables in UTC timezone.
Any suggetions?

following the best practice, preset time as UTC is considered as best practice and to avoid future bugs With Spring Boot JPA, use the below code in your application. properties file and obviously you can modify the timezone to your choice :
spring.jpa.properties.hibernate.jdbc.time_zone = UTC
or you can attach it directly as :
spring.datasource.url=jdbc:oracle:thin://localhost:3306/linkedin?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
But to solve the problem you have two options:
1- JPA Native Query to select and cast an array of objects, for instance :
you can make use of the oracle feature to convert PST to UTC
select cast(coltime as timestamp) at time zone 'UTC' from ...
JPA repository :
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
#Query(value = "select cast(DD.Ch_Status_Validfrom as timestamp) at time zone 'UTC', za.first_name, za.birth_date, ts.salary\n" +
"from employees za, salaries ts \n" +
"where za.emp_no= :id" +
" LIMIT 10 OFFSET 20"
,nativeQuery = true)
List<Object[]> findWithSalary(#Param("id")Integer id);
}
CommandLineRunner
#Component
public class LoaderBootStrap implements CommandLineRunner {
private final EmployeeRepository employeeRepository;
#Override
public void run(String... args) throws Exception {
List<Object[]> withSalary = employeeRepository.findWithSalary(10001);
}
}
2- Convert date and time between timezone with java for instance (UTC+8:00) Asia/Singapore - Singapore Time to (UTC-5:00) America/New_York - Eastern Standard Time:
private final void demo(){
ZoneId assiaZone = ZoneId.of("Asia/Singapore");
ZoneId americaZone = ZoneId.of("America/New_York");
ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("UTC"));
System.out.println("Current Time As UTC : " + zonedDateTime);
System.out.println("Current time As America time : " + dateConvertWithZoneId(zonedDateTime, americaZone));
System.out.println("Current time As Asia time : " + dateConvertWithZoneId(zonedDateTime, assiaZone));
}
private final LocalDateTime dateConvertWithZoneId(ZonedDateTime actualDate, ZoneId withZone){
ZonedDateTime date = actualDate;
return date.withZoneSameInstant(withZone).toLocalDateTime();
}

Related

ZonedDateTime retrieved with one hour shift in Hsqldb with 2.6.x

I use Spring boot and JPA and my Order domain object have this field:
#Column(name = "TIMESTAMP")
private ZonedDateTime timestamp;
I create a date with this method:
protected static ZonedDateTime createZonedDateTime(final String date) {
LocalDateTime ldt = LocalDateTime.parse(date + " 00:00", DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm"));
return ldt.atZone(ZoneId.systemDefault());
}
When I insert this in database throw JPA, with showsql to true, I can see that the inserted date is: [2020-01-01T00:00+01:00[Europe/Paris]]
when I retrieve the data throw JPA repository, the date I get is: 2020-01-01T01:00+01:00[Europe/Paris]
How come I have one hour shift? It's not working since hsqldb 2.6.0. Works perfectly with 2.5.0.
I use Liquidbase to create the field with:
<column name="TIMESTAMP" type="TIMESTAMP WITH TIME ZONE"
remarks="The timestamp of the order"/>

Spring JPA Equal conditon with derived query for LocalDate not working

I have a Oracle DB with type of DATE my Entiy class have LocalDate parameter as shown in here
#Entity
#Table(name = "CONTRACT")
public class ContractEntity {
private Long id;
private LocalDate contractStartDate;
private LocalDate contractEndDate;
...
For the testing I created derived query to find Entity by contractStartDate
public interface ContractRepository extends JpaRepository<ContractEntity, Long>, JpaSpecificationExecutor<ContractEntity> {
ContractEntity getContractEntityByContractStartDateIs(LocalDate date)
}
Then I created Two Tests as follows;
#DisplayName("Should get Exact Date Entity")
#Test
void test1(){
LocalDate date=LocalDate.of(2021,03,9);
ContractEntity entity= searchService.getByDate(date); // getByDate is mapped to getContractEntityByContractStartDateIs() in service layer
assertEquals(date,entity.getContractStartDate());
}
#DisplayName("Should match ID:1 Entity's Date with Local Date")
#Test
void test2(){
ContractEntity entity=searchService.getById(1l).get();
assertEquals(1l,entity.getId());
LocalDate date=LocalDate.of(2021,03,9);
assertEquals(date,entity.getContractStartDate());
}
}
My problem is test1 fails with null error which means equality condition fails to give existence of that Entity even though test2 passed which date is equal to id:1 entity's Start-date
p.s: I have set Contract Table's ID:1 Contract Start Date value to 2021,03,9 as shown in image here
So what would be the reason for this Why test1 fails to get equal entity and why test2 pass that assert equity of date is true
LocalDate doesn't contain a time component, but the data type you used in the database does and it is not 0 for the data in the database.
In order to make the comparison (which happens in the database) the LocalDate gets converted with a date + time where the time is probably 0, which is not equal to the date+times present in the database.
When the entity gets loaded the time part of the database value gets dropped and the result is equal to your LocalDate value.

Initialize field with current year and assigned ID

I've got an entity that I am persisting. Its ID is automatically assigned when storing it into the database via Spring Repository.
In the same entity, I have a field build from the Id and the current year: "<current_year>-<id>".
In a method annotated with #PrePersist, the ID has not been assigned yet, so I wrote some code in a #PostPersist method:
#PostPersist
protected void setupOrderNumber() {
this.orderNumber = Calendar.getInstance().get(Calendar.YEAR) + "-" + id;
}
This code does not store the orderNumber into the database, because the entity was stored already.
How can I achieve such a result with JPA directly within the entity?
If not possible with JPA, I could use Hibernate with a #Formula annotation, but I am not sure how to write it: #Formula("extract(year from current_date) + '-' + id") does not seem to work.
As you've already noticed: In #PrePersist a generated ID is not available - just because the ID is set afterwards when persisting into the database. And no changes made in #PostPersist are persisted, just because the persist has already taken place...
You can use a #Formula, as long you don't need the value in the database. But I wouldn't use extract(year from current_date) - as this would change the orderNumber when the year changes - what is different to your experiment with #PostPersist.
Instead use a year field, which you initialize in #PrePersist and reference that one in your formula:
#Entity
public class MyEntity {
#Id
#GeneratedValue(...)
private Long id;
private int year;
#Formula("concat(id, '-', year)")
private String orderNumber;
#PrePersist
private void prePersist() {
this.year = Calendar.getInstance().get(Calendar.YEAR);
}
#PostPersist
private void postPersist() {
this.orderNumber = id + "-" + year;
}
}
I initialize the orderNumber in postPersist() as well, to have a valid value immediately after EntityManager.persist().

Spring Controller converts time to it's Local TimeZone

I have spring rest service project and I am using JPA hibernate and I am facing a strange issue in my controller. Below is code snippet:
controller:
#RequestMapping(method = RequestMethod.POST, value=PATH_SEPERATOR+ CREATE_OR_UPDATE_EVENT_METHOD, headers = ACCEPT_APPLICATION_JSON, produces = APPLICATION_JSON)
#ResponseStatus(HttpStatus.CREATED)
#ResponseBody
ResponseBean createOrUpdateEvent(#RequestBody Event eventBean)
{
ResponseBean response = new ResponseBean();
try {
String sysId = eventService.createOrUpdateEvent(eventBean);
response.setStatus(OK);
response.setData(sysId);
} catch(Exception ex) {
response = handleException(CREATE_OR_UPDATE_EVENT_METHOD, ex);
return response;
}
return response;
}
Event.java
#Entity
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "sysId", scope = Event.class)
#Table(name = "event")
public class Event {
#Column(name = "date_time")
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss")
private Date dateTime;
public Date getDateTime() {
return dateTime;
}
public void setDateTime(Date dateTime) {
this.dateTime = dateTime;
}
}
When I pass date to Event bean in createOrUpdateEvent method as String "2014-04-17T17:15:56" which is in IST timezone, the controller convert it to Date with datetime "2014-04-17T22:45:56" IST considering previous time as UTC time. I don't understand this behaviour of auto conversion. I assume that it's because I am accepting parameter as bean, where bean is JPA Entity. Please help me to fix it.
There are several things you must take into consideration. First and foremost you are lacking a time zone information in the provided JSON serialization format "yyyy-MM-dd'T'HH:mm:ss". There's a format character that adds it - Z. Using it should be something like "yyyy-MM-dd'T'HH:mm:ssZ" depending on your preferences. Another thing you should consider is the fact that java.util.Date is not TimeZone aware and when you are creating a new Date(long) it always assumes that the passed date is in the current time zone.
So in order to fix this issue you have to add (and pass) the time zone as I told you and the Json parser will do the rest.

JPA 2.0 and Oracle with TemporalType.TIME

I'm Using Oracle 11g and JPA 2.0 (hibernate in JBoss 6.0.0).
I need to represent a time range in an entity, so I defined those fields:
#Temporal(TemporalType.TIME)
private Date startTime;
#Temporal(TemporalType.TIME)
private Date endTime;
The generated tables use two DATE fields, and this is ok since Oracle doesn't have a type representing just the time part.
When loading the entity from db, just the time part is loaded (the field contains a java.sql.Time).
I've seen instead that if I set a complete date+time in the fields, the date part will be persisted to the db.
Is there a way to ensure that the date part will not be persisted to the db?
You can write setter methods which remove the date component. Quick and dirty example:
public void setStartTime(Date startTime)
{
this.startTime = new Time(startTime.getTime() % 86400000L);
}
Though you'd be better off using Joda Time to do your date/time calculations (see this question). I didn't test this to make sure it's correct, but it should show you the basic idea:
public void setStartTime(Date startTime)
{
this.startTime = new Time(LocalTime.fromDateFields(startTime).millisOfDay().get());
}

Resources