Timestamp invalid hour in Java - java

I am using JPA with my Java project, and the timestamp is not working very well : it only shows 2015-08-12 00:00:00.0 (the day is correct but the hour is not)
#Entity
public class Session implements Serializable {
..
#Temporal(TemporalType.DATE)
private Date timestamp;
..
public Session(String sessionId) {
super();
this.sessionId = sessionId;
this.timestamp = new Date();
}
public Session() {
super();
this.timestamp = new Date();
}
}
Do you know how to fix this?

You should use TemporalType.TIMESTAMP that will map the field to a java.sql.Timestamp, hence it will contain also time related info, not only regarding date. In comparison, the type you used, TemporalType.DATE are mapped to java.sql.Date, class containing information like day, month year.
So, your code will transform in:
#Entity
public class Session implements Serializable {
..
#Temporal(TemporalType.TIMESTAMP)
private Date timestamp;
..
public Session(String sessionId) {
this.sessionId = sessionId;
this.timestamp = new Date();
}
public Session() {
this.timestamp = new Date();
}
}

Related

Hibernate getting and saving a date with an extra day than the record on the database

Im having an issue when I fetch data from the DB with HIBERNATE and SpringMVC, everything works fine except for the dates. When I fetch a field of Date type, I receive the date but with a previous day.
For example, Im fetching the date as "2022-08-28", but im receiving "2022-08-27"
Also have an issue when I update the data, Hibernate saves on the next day that the date that is stored on the DB.
On the same example Im fetching the date as "2022-08-28", but when saving the record appears as "2022-08-29"
This is my architechture
Entity
#Entity
#Table(name = "cliente_tickets")
public class ClienteTicket {
... other fields...
#DateTimeFormat(pattern = "yyyy-MM-dd")
#Temporal(TemporalType.DATE)
#Column(name = "created_on")
private Date createdOn;
#DateTimeFormat(pattern = "yyyy-MM-dd")
#Temporal(TemporalType.DATE)
#Column(name = "updated_on")
private Date updatedOn;
... other GettersAndSetters...
public Date getCreatedOn() {
return createdOn;
}
public void setCreatedOn(Date createdOn) {
this.createdOn = createdOn;
}
public Date getUpdatedOn() {
return updatedOn;
}
public void setUpdatedOn(Date updatedOn) {
this.updatedOn = updatedOn;
}
}
This is the method in my DAO
#Override
public ClienteTicket buscarId(long id) {
try {
return sessionFactory.getCurrentSession().createQuery("from ClienteTicket ct where ct.idTicket = :id", ClienteTicket.class)
.setParameter("id", id).getSingleResult();
} catch (NoResultException e) {
return null;
}
}
This is the Service
#Override
#Transactional
public void guardar(ClienteTicket clienteTicket) {
if (clienteTicket.getCreatedBy() == null || clienteTicket.getCreatedBy().length() == 0) {
clienteTicket.setCreatedOn(new Date());
clienteTicket.setCreatedBy(Utilidades.currentUser());
} else {
clienteTicket.setUpdatedOn(new Date());
clienteTicket.setUpdatedBy(Utilidades.currentUser());
}
this.clienteTicketDao.guardar(clienteTicket);
}
This is my controller for fetching the Object
#GetMapping("/editar")
ModelAndView editar(#RequestParam("id") String id) throws ParseException {
ClienteTicket ticket = this.ticketsService.buscarId(Long.parseLong(id));
super.mv = new ModelAndView("/cliente/ticket_formulario");
super.mv.addObject("clienteEncontrado", true);
super.mv.addObject("editar", true);
super.mv.addObject("clienteTicket", ticket);
super.mv.addObject("idClienteActual", ticket.getCliente().getIdCliente());
super.mv.addObject("estadosIncidencia", this.ticketEstadosService.listar());
super.mv.addObject("tiposIncidencia", this.ticketTiposService.listar());
super.mv.addObject("titulo", "Tickets");
//this.ticketsService.guardar(ticket);
return super.mv;
}
When I Debug I get this JAVA_DEBUG
These are the properties of the DB TABLE_PROPERTIES
Current data on DB DATA_TABLE
Date in JSP is hidden DATA_JSP
HTML EDGE_DEVTOOLS
Other data is fetched correctly OTHER_DATA
I've solved!, It was related with the Timezone.The timezone on my pc was right, the issue was the configuration on the jdbc.url. I was using serverTimezone=UTC, Im from Mexico so i needed to use CST. adding serverTimezone=CST to jdbc.url solved my issue. I hope this helps someone in the future. TY
Change
jdbc.url=jdbc:mysql://localhost:3306/sge?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true&zeroDateTimeBehavior=convertToNull
To
jdbc.url=jdbc:mysql://localhost:3306/sge?useSSL=false&serverTimezone=CST&allowPublicKeyRetrieval=true&zeroDateTimeBehavior=convertToNull

Converting date from timestamp to human readable in entity constructor

Currently, the format of the Date requestDate variable stored looks like: 2017-02-17 00:00:00.0. I want to convert this into, for example: Friday, February 17, 2017. I would like to do the conversion here in my entity and return it so that when it's displayed it is more human readable. This will likely happen in the constructor, at this line: this.setRequestDate(doDateConversion(requestDate));. How can I make this conversion?
My Request entity:
#Entity
#Table(name = "Request")
public class RequestDO implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="request_id")
private Long id;
private Date requestDate;
private String description;
private RequestStatus status;
/*private Boolean read;*/
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="user_id", nullable = false)
private Users users;
public RequestDO() {}
public RequestDO(Users user, Date requestDate) {
this.setUsers(user);
this.setRequestDate(requestDate);
}
#Override
public String toString() {
return String.format(
"RequestDO[id=%d, inital='%s', requestDate='%s']",
getId()
, getUsers().getInitialName()
, getRequestDate());
}
public Date getRequestDate() {
return requestDate;
}
public void setRequestDate(Date requestDate) {
this.requestDate = requestDate;
}
}
You can use SimpleDateFormat to convert your Date to a readable String of your choice.
The time format String for your example is EEEE, MMMM, dd, yyyy. You have to create a new SimpleDateFormat object and format your date to a String. Examples...
But Spring provides some specials out of the box. For example you can use Jackson for date format: #JsonFormat(pattern="yyyy-MM-dd") more. It is also possible to add a data format in application.properties file : spring.jackson.date-format
Using SimpleDateFormat:
java.sql.Date date = new Date(System.currentTimeMillis());
System.out.println(new SimpleDateFormat("EEEE, MMMM dd, YYYY").format(date));
See this for more details.
I solved the problem by changing the dates as they are read in my controller, using SimpleDateFormat:
#RequestMapping(value = "/requests", method = RequestMethod.GET)
public String getAllRequests(Model model, RequestModel requestModel) throws ParseException {
List<RequestDO> requestDOArrayList = new ArrayList<RequestDO>();
for (RequestDO requestDO : requestRepository.findAll()) {
log.info(requestDO.toString());
// Display all dates in Requests list in human-readable form
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse(requestDO.getRequestDate().toString());
log.info(String.valueOf(date));
requestDO.setRequestDate(date);
requestDOArrayList.add(requestDO);
}
model.addAttribute("requests", requestDOArrayList);
log.info(requestDOArrayList.toString());
return "requests";
}

Java 303 / 349 start date before end date validation

Is there a better way of writing a Java validator which ensures that a start date is before an end date than writing a class level ConstraintValidator in the following manner:
// VALIDATOR IMPLEMENTATION
public class StartBeforeEndDateValidator implements ConstraintValidator<StartBeforeEndDateValid, Object> {
// cannot use LocalDate here...
private String start;
private String end;
#Override
public void initialize(final StartBeforeEndDateValid annotation) {
start = annotation.start();
end = annotation.end();
}
#Override
public boolean isValid(final Object bean, final ConstraintValidatorContext context) {
try {
final String startDateStr = BeanUtils.getProperty(bean, start);
final String endDateStr = BeanUtils.getProperty(bean, end);
final LocalDate startDate = new LocalDate(startDateStr);
final LocalDate endDate = new LocalDate(endDateStr);
return !startDate.isAfter(endDate);
} catch (final Exception e) {
return false;
}
}
}
// USAGE
#StartBeforeEndDateValid(start = "startDate", end = "endDate")
#Entity
public class MyBean {
#NotNull
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDate")
private LocalDate startDate;
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDate")
private LocalDate endDate;
...
}
I don't really like the fact that I have to use reflection to extract the 2 date objects from the bean. Unfortunately afaik the validation spec does not specify a way to set only the values you want to validate from the bean.
One way would be to add an interface to MyBean
public interface StartEndDateable {
public LocalDate getStartDate();
public LocalDate getEndDate();
}
public class MyBean implements StartEndDatable {
...
Then you can set the generic type on ConstraintValidator to the new interface instead of Object.
public class StartBeforeEndDateValidator implements ConstraintValidator<StartBeforeEndDateValid, StartEndDatable> {
#Override
public void initialize(StartBeforeEndDateValid annotation) {
}
#Override
public boolean isValid(StartEndDatable bean, ConstraintValidatorContext context) {
final LocalDate startDate = bean.getStartDate();
final LocalDate endDate = bean.getEndDate();
return !startDate.isAfter(endDate);
}
}
Obviously any class you then want to validate with the start and end date will have to implement the StartEndDateable (Not the best name, I know, but I'm sure you can think of something better) and define the getStartDate and getEndDate methods.

How to format #param String in Spring Data Rest

I have to convert the incoming parameter value to Repository interface into desired format, is it possible to do it. My Domain Class,
#DynamoDBTable(tableName = "test")
public class Test implements Serializable{
#Id
private String id;
private String name;
private String date;
#DynamoDBHashKey(attributeName = "id")
#DynamoDBAutoGeneratedKey
public String getId() {
return id;
}
#DynamoDBAttribute(attributeName = "name")
public String getName() {
return name;
}
#DynamoDBAttribute(attributeName = "date")
#JsonSerialize(using = StringDateSerializer.class)
public String getDate() {
return date;
}
public void setId(String id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
#JsonDeserialize(using = StringDateDeserializer.class)
public void setDate(String date) {
this.date = date;
}
}
And my repository interface,
#EnableScan
#RestResource(path="test", rel="test")
public interface TestRepository extends PagingAndSortingRepository<Test, String>{
#RestResource(path="testsearch", rel="test")
public Page<Test> findByNameAndDateLessThan(#Param("name") String name, #Param("date") String date, Pageable pageable);
}
Here I have to convert the incoming date String to time using getTime() method of Java. Is it possible to achieve this without using controller and am not interested in sending from client side because timezone problem may occur.
My Convertors:
public class StringDateSerializer extends JsonSerializer<String> {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
#Override
public void serialize(String time, JsonGenerator gen,
SerializerProvider provider) throws IOException,
JsonProcessingException {
Date date = new Date(Long.parseLong(time));
String formattedDate = dateFormat.format(date);
gen.writeString(formattedDate);
}
}
public class StringDateDeserializer extends JsonDeserializer<String> {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
#Override
public String deserialize(JsonParser parser, DeserializationContext context)
throws IOException, JsonProcessingException {
String dateReceived = parser.getText();
Date date = null;
try {
date = dateFormat.parse(dateReceived);
} catch (ParseException e) {
e.printStackTrace();
}
return String.valueOf(date.getTime());
}
}
Here I have to use, GET /test/search/test?name=xx&date=14-06-2014. I need to get all the names with date less than 14-06-2014 and left the datas with or after 14-06-2014.
While POST and GET, I have converted the incoming and outgoing string using JsonSerialize and JsonDeserialize annotations but if I want to fetch any data using finder method its not converting as I thought.
For example, If I save {"name": "Test", "date": "08-10-2014"}, in DB it will be saved by its equivalent time and If I want to search it using 08-10-2014 not the time constant. I am new to springs and I cant find a way for it. Thanks in advance.
What's the reason you use String as the type for the date in the first place. That's quite suboptimal (to phrase it politely) API design.
Spring Data REST support the usage of #DateTimeFormat on query method parameters to turn the String base representation you get from the HTTP request into a Date. So your repository interface might look something like this:
public interface TestRepository extends PagingAndSortingRepository<Test, String>{
public Page<Test> findByNameAndDate(#Param("name") String name,
#Param("date") #DateTimeFormat(iso = ISO.DATE) Date date, Pageable pageable);
}
This will cause Strings like 2014-06-08 to be turned into the appropriate Date.
If I'm understanding your issue correctly, there are two areas of concern - how Spring-Data-Rest handles date mapping, and how Spring-Data-DynamoDB handles date mapping.
With regard to Spring-Data-DynamoDB:
DynamoDB stores dates as Strings, so if you have a date attribute as part of your date model you can either represent them as Strings in your data model ( as I think you are doing currently ), or you can represent them as Dates, and configure Spring-Data-DynamoDB so that it maps the Dates to Strings. This can be done using Custom Marshallers from amazon-aws-sdk, and support has been added to handle this in the Spring Data DynamoDB module.
You can read about marshallers here : http://java.awsblog.com/post/Tx1K7U34AOZBLJ2/Using-Custom-Marshallers-to-Store-Complex-Objects-in-Amazon-DynamoDB
Note that this marshalling is separate from any mapping that you may be requiring Spring-Data-Rest to perform from JSON to objects - for this you will still need the #DateTimeFormat annotation.
If you want to represent the date as a java.util.Date in your data model, simply annotate the getter for the attribute in your domain class with #DynamoDBMarshalling, and pass in the class of marshaller you wish to use, eg:
#DynamoDBRangeKey(attributeName = "ReplyDateTime")
#DynamoDBMarshalling(marshallerClass=DefaultDynamoDBDateMarshaller.class)
public Date getReplyDateTime() {
...
DefaultDynamoDBDateMarshaller here is a support class from Spring-Data-DynamoDB, but you can implement your own for custom date/string mapping.
With this in place, you can now change your repository finder methods so they expect Date parameters rather than Strings:
public Page<Reply> findByReplyDateTimeAfter(Date replyDateTime,Pageable pageable);
Hope this helps,
Cheers,
Michael

JPA PostLoad and PreUpdate with Embedded columns

Situation: NotificationProfile entity has collection of NotificationProfileIntegration entities which has embedded IntegrationNotificationCutOff. It is legacy database and i cannot modify it. Someone thought it is a good idea to store time as "HH:mm" strings, but i need to work with date objects. That is the reason why i have these convert callbacks and transient date fields.
When i do (for existing profile entity)
entityManager.merge(profile);
I expect that #PreUpdate is called on NotificationProfileIntegration and dates are converted to their string representation and persisted to DB. Instead #PostLoad method is called first and #PreUpdate is called after.
So if i set something in cutOff instance it is never persisted, because in #PostLoad method new instance of cutOffs is created because it is null due to this feature of hibernate
When all of the values in an #Embedded object are null, Hibernate will
set the field in the parent object to null.
How can i handle this situation? Thank you.
#Entity
#Table(name = "NOTIFICATION_PROFILES")
public class NotificationProfile extends AbstractEntity<Long> {
#OneToMany(mappedBy = "notificationProfile", cascade = CascadeType.ALL, orphanRemoval = true)
private Collection<NotificationProfileIntegration> profileIntegrations;
....
}
#Entity
#Table(name = "NOTIFICATION_PROFILE_INTEG")
public class NotificationProfileIntegration extends AbstractEntity<Long> {
#Embedded
private IntegrationNotificationCutOff cutOffs;
#Embedded
private IntegrationNotificationAverageCount averageShipments;
#PostLoad
public void initEmbeded() {
if (cutOffs == null) {
cutOffs = new IntegrationNotificationCutOff();
}
if (averageShipments == null) {
averageShipments = new IntegrationNotificationAverageCount();
}
cutOffs.convertToDates();
}
#PreUpdate
#PrePersist
private void formatCutOffs() {
if (cutOffs != null) {
cutOffs.convertToValues();
}
}
}
#Embeddable
public class IntegrationNotificationCutOff {
#Column(name = "NPI_CUT_OFF_TIME_MON")
private String monday;
#Column(name = "NPI_CUT_OFF_TIME_TUE")
private String tuesday;
#Column(name = "NPI_CUT_OFF_TIME_WED")
private String wednesday;
#Column(name = "NPI_CUT_OFF_TIME_THU")
private String thursday;
#Column(name = "NPI_CUT_OFF_TIME_FRI")
private String friday;
#Column(name = "NPI_CUT_OFF_TIME_SAT")
private String saturday;
#Column(name = "NPI_CUT_OFF_TIME_SUN")
private String sunday;
#Transient
private Date mondayDate;
#Transient
private Date tuesdayDate;
#Transient
private Date wednesdayDate;
#Transient
private Date thursdayDate;
#Transient
private Date fridayDate;
#Transient
private Date saturdayDate;
#Transient
private Date sundayDate;
public void convertToDates() {
SimpleDateFormat dateFormat = getDateFormat();
mondayDate = nullSafeConvert(monday, dateFormat);
tuesdayDate = nullSafeConvert(tuesday, dateFormat);
wednesdayDate = nullSafeConvert(wednesday, dateFormat);
thursdayDate = nullSafeConvert(thursday, dateFormat);
fridayDate = nullSafeConvert(friday, dateFormat);
saturdayDate = nullSafeConvert(saturday, dateFormat);
sundayDate = nullSafeConvert(sunday, dateFormat);
}
public void convertToValues() {
SimpleDateFormat dateFormat = getDateFormat();
monday = nullSafeFormat(mondayDate, dateFormat);
tuesday = nullSafeFormat(tuesdayDate, dateFormat);
wednesday = nullSafeFormat(wednesdayDate, dateFormat);
thursday = nullSafeFormat(thursdayDate, dateFormat);
friday = nullSafeFormat(fridayDate, dateFormat);
saturday = nullSafeFormat(saturdayDate, dateFormat);
sunday = nullSafeFormat(sundayDate, dateFormat);
}
private String nullSafeFormat(Date date, SimpleDateFormat dateFormat) {
if (date == null) {
return null;
}
return dateFormat.format(date);
}
private SimpleDateFormat getDateFormat() {
return new SimpleDateFormat("HH:mm");
}
private Date nullSafeConvert(String day, SimpleDateFormat dateFormat) {
if (day == null) {
return null;
}
try {
return dateFormat.parse(day);
} catch (ParseException e) {
return null;
}
}
}
EDIT
It seems that embedding does not have any efect on this behavior. When i refactor it to single entity problem is still here: after calling createOrUpdate - select is triggered before an update and my change to entity is somewhere "lost"

Categories

Resources