I'm using latest spring and hibernate. I need to get the date and time form jsp page and want to insert into mysql database. I'm using TIMESTAMP as dataType of one fields. When I try to save, there is no error, but showing "HTTP Status [400] – [Bad Request]".
Finally I find out, there is a problem in Date and Time format (may be in annotation or MySQL datatype or jsp page). Because I try to update the form without changing valuei of date time (path="startDateTime"). It was successfully updated. When I try to change the value in date time (path="startDateTime"), it shows the "HTTP Status [400] – [Bad Request]".
I need to update Date and Time to database. I tried a lot of ways, but failed.
Model class
public class Survey{
//other declaration
#Column(name="startDateTime",columnDefinition="TIMESTAMP")
#Temporal(TemporalType.TIMESTAMP)
#DateTimeFormat(pattern="yyyy-MM-dd'T'HH:mm:ss")
private Date startDateTime;
}
jsp page
<spring:url value="/survey/save" var="saveURL"></spring:url>
<form:form action="${saveURL}" method="POST" modelAttribute="surveyForm">
//other stuffs
<form:input type="datetime-local" path="startDateTime" />
</form:form>
Controller
public ModelAndView saveSurvey(#ModelAttribute("surveyForm") Survey survey) {
surveyService.saveOrUpdate(survey);
return new ModelAndView("redirect:/survey/list");
}
In order to adopt good practice, i suggest using JAVA 8 LocalDateTime class.
So modify your model class like this
public class Survey{
//other declaration
#Column(name="startDateTime",columnDefinition="TIMESTAMP")
private LocalDateTime startDateTime;
}
When using JAVA 8 LocalDateTime class, you no longer need to add #Temporal(TemporalType.TIMESTAMP)
After that,you need to create a LocalDateTimeConverter class by implement Converter interface like this
import org.springframework.core.convert.converter.Converter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public final class LocalDateTimeConverter implements Converter<String, LocalDateTime> {
private final DateTimeFormatter formatter;
public LocalDateTimeConverter(String dateFormat) {
this.formatter = DateTimeFormatter.ofPattern(dateFormat);
}
#Override
public LocalDateTime convert(String source) {
if (source == null || source.isEmpty()) {
return null;
}
return LocalDateTime.parse(source, formatter);
}
}
Then register the LocalDateTimeConverter class in your configuration class like this
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#Configuration
#EnableWebMvc
class WebMvcContext extends WebMvcConfigurerAdapter {
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new LocalDateTimeConverter("yyyy-MM-dd'T'HH:mm:ss.SSS"));
}
}
That's all and i hope it solves your problem.
You can learn more here
Well, i will still like to suggest that instead of leaving the responsibility to your site user to do the date time update why not allow hibernate to do that for you by using the #CreationTimestamp and #UpdateTimestamp annotations of hibernate or preferably by using JPA #PrePersist and #PreUpdate annotations like this
#Column(name="startDateTime")
#PrePersist
private LocalDateTime startDateTime;
#Column(name="updatedDateTime")
#PreUpdate
private LocalDateTime updatedDateTime;
With this approach, you no longer need to add startDateTime field in your form because hibernate will automatically insert and update those columns for you.
Note: Form input type datetime-local is not supported in all browers.
The problem is the parameter send to spring mvc is String, your dateToday type is date. Spring mvc don't know how to convert it.
You need a #InitBinder
Consult below
#InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
binder.registerCustomEditor(Timestamp.class, new CustomDateEditor(dateFormat, false));
}
In your hibernate configuration file .xml, is it set to update? Try checking on it.
<property name="hbm2ddl.auto">update</property>
Use initbinder in your controller class
#InitBinder
public void dataBinding(WebDataBinder binder) {
SimpleDateFormat dateFormat = new
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
dateFormat.setLenient(false);
binder.registerCustomEditor(LocalDateTime.class, "startDateTime", new CustomDateEditor(dateFormat, true));
}
Hope this solves it
Related
I using RestController in Spring Boot and I have problem with Jackson auto converting time zone. When I try to send date to RestController, the date is auto convert to my current time zone and I don't want to prevent it to happened.
If I send
"NGAY": "2022-07-29T17:00:00.000+00:00",
I want to receive
2022-07-29
no matter what the time zone is
Not
2022-07-30
This is code in model class
#Column(name = "NGAY")
private java.util.Date ngay;
And this is code in my RestController
#RestController
#RequestMapping("/api/sanluong/ngay")
public class Controller {
#PostMapping("/savedata")
public ResponseEntity<?> uploadFile(#RequestBody List<MLoadprofileView> lstData) {
ResponseData result = sanLuongNgayService.onSaveData(lstData);
return ResponseEntity.ok(result);
}
}
I tried in application.properties but it has no effect
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false
spring.jackson.deserialization.ADJUST_DATES_TO_CONTEXT_TIME_ZONE = false
EDIT: I want to applied to all java.ulti.Date field in all class
In a spring project, I'd like to create a LocalDate from an #Autowired constructor parameter whose value is in a .properties file. Two things I'd like to do:
1. If the property file contains the property my.date, the parameter should be created by parsing the property value
When the property is set, and when I use the following:
#DateTimeFormat(pattern = "yyyy-MM-dd") #Value("${my.date}") LocalDate myDate,
...
I get this error:
java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'java.time.LocalDate': no matching editors or conversion strategy found
I have also used the iso = ... to use an ISO date with the same result.
2. If the property is not in the properties file, the parameter should be created using LocalDate.now()
I tried using a default value as such:
#Value("${my.date:#{T(java.time.LocalDate).now()}}") LocalDate myDate,
...
But I get the same error.
Forgive my ignorance with Spring, but how can I achieve the two objectives here?
I know two ways. One is generic for any object - to use #Value annotation on custom setter
#Component
public class Example {
private LocalDate localDate;
#Value("${property.name}")
private void setLocalDate(String localDateStr) {
if (localDateStr != null && !localDateStr.isEmpty()) {
localDate = LocalDate.parse(localDateStr);
}
}
}
The second is for LocalDate/LocalDateTime
public class Example {
#Value("#{T(java.time.LocalDate).parse('${property.name}')}")
private LocalDate localDate;
}
Sample property:
property.name=2018-06-20
Spring Boot 2.5, works perfect:
application.yaml
my.date: 2021-08-14
my.time: "11:00"
#Service
public class TestService {
#Value("${my.date}")
#DateTimeFormat(pattern = "yyyy-MM-dd")
LocalDate myDate;
#Value("${my.time}")
#DateTimeFormat(iso = DateTimeFormat.ISO.TIME)
LocalTime myTime;
}
If you want to specify the date format as well then use following on the field:
#Value("#{T(java.time.LocalDate).parse('${date.from.properties.file}', T(java.time.format.DateTimeFormatter).ofPattern('${date.format.from.properties.file}'))}")
Try to add this into your properties file:
spring.jackson.date-format=com.fasterxml.jackson.databind.util.ISO8601DateFormat
spring.jackson.time-zone=UTC
and remove #DateTimeFormat annotation
Concerning LocalDate.now() initialization. Try to use field injection this way:
#Value("${my.date}") LocalDate myDate = LocalDate.now();
As mentioned in other answer by Pavel there are two ways.
I am providing similar two ways with modification to handle 2nd Point by OP.
If the property is not in the properties file, the parameter should be
created using LocalDate.now()
#Component
public class Example {
private LocalDate localDate;
#Value("${property.name}")
private void setLocalDate(String localDateStr) {
if (localDateStr != null && !localDateStr.isEmpty()) {
localDate = LocalDate.parse(localDateStr, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}else{
localDate = LocalDate.now();
}
}
}
I Prefere 2nd way though...
public class Example {
#Value("#{T(java.time.LocalDate).parse('${property.name}', T(java.time.format.DateTimeFormatter).ofPattern('yyyy-MM-dd')) ?: T(java.time.LocalDate).now()}")
private LocalDate localDate;
}
Edit:- Fixed 2nd Way
#Value("#{ !('${date:}'.equals('')) ? T(java.time.LocalDate).parse('${date:}', T(java.time.format.DateTimeFormatter).ofPattern('MM-dd-yyyy')) " +
":T(java.time.LocalDate).now()}")
private LocalDate asOfDate;
For the first bit, you could create a converter:
#Component
#ConfigurationPropertiesBinding
public class LocalDateConverter implements Converter<String, LocalDate> {
#Override
public LocalDate convert(String s) {
if (s == null) {
return null;
}
return LocalDate.parse(s);
}
}
Your config Class will automatically use this for conversion.
For the 2nd you can just do:
if(my.date == null) iso = LocalDate.now()
There's an example of initializing a LocalDateTime value using annotations and configuration properties. I've tested that it works in Spring Boot 2.4.
MyComponent.kt fragment:
#Value("\${my.date}")
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
val myDate: LocalDateTime
application.yml:
my:
date: "2023-01-23T00:00:00"
Thanks to this thread, I was able to register and use a custom Converter for org.joda.time.DateTime using JPA EclipseLink. Here is a sample use (only the relevant parts):
#Converter(name = "jodaTimeConverter", converterClass = JodaDateTimeConverter.class)
public class MyEntity{
#Column(name = "creationdate")
#Temporal(TemporalType.TIMESTAMP)
#Convert("jodaTimeConverter")
private DateTime creationdate;
}
I have many entity classes an most of them have a DateTime field. My question is thus: is it possible to register the converter once somewhere, so that all DateTime fields are automatically converted ?
I could obviously copy-paste the annotations everywhere, but a more DRY method would be appreciated.
What you're trying to use is a proprietary mechanism that would only work in EclipseLink, so leaving your code non-portable.
A better option, if using JPA 2.1, is to make use of AttributeConverter, and set the converter itself to "autoApply". This means that it will be applied to all fields of the specified type without having to annotate every field. And with that you get portability too
if you are using spring boot and the AttributeConverter.
in Application.java
#EntityScan(basePackageClasses = {Application.class, JpaConverters.class})
public class JpaConverters {
#Converter(
autoApply = true
)
public static class DateTimeOffsetToOffsetDateTimeConverter implements AttributeConverter<OffsetDateTime,
DateTimeOffset> {
#Override
public OffsetDateTime convertToEntityAttribute(DateTimeOffset dateTimeOffset) {
if (dateTimeOffset == null) {
return null;
}
OffsetDateTime utc = OffsetDateTime.ofInstant(dateTimeOffset.getTimestamp().toInstant(), UTC);
int offsetSeconds = Math.toIntExact(MINUTES.toSeconds(dateTimeOffset.getMinutesOffset()));
ZoneOffset offset = ZoneOffset.ofTotalSeconds(offsetSeconds);
OffsetDateTime offsetDateTime = utc.withOffsetSameInstant(offset);
return offsetDateTime;
}
#Override
public DateTimeOffset convertToDatabaseColumn(OffsetDateTime date) {
if (date == null) {
return null;
}
Timestamp timestamp = Timestamp.from(date.toInstant());
int offsetSeconds = date.getOffset().getTotalSeconds();
DateTimeOffset dateTimeOffset = DateTimeOffset.valueOf(timestamp, Math.toIntExact(SECONDS.toMinutes(offsetSeconds)));
return dateTimeOffset ;
}
}
}
I am trying to use an AttributeConverter to store the new Java 8 ZonedDateTime values in a MySQL database (DATETIME field) using Hibernate 4.3.0.
When I try to execute an update I get this error:
...Data truncation: Incorrect datetime value: '\xAC\xED\x00\x05sr\x00\x0Djava.time.Ser\x95]\x84\xBA\x1B"H\xB2\x0C\x00\x00xpw\x0D\x06\x00\x00\x07\xE0\x02\x11\x0B&\xEE\xE0\x08\x' for column...
I have read many SO answers saying to use the converter approach, so I wrote one:
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.sql.Date;
import java.time.LocalDate;
import java.time.ZonedDateTime;
#Converter(autoApply = true)
public class ZonedDateTimeConverter implements AttributeConverter<ZonedDateTime, Date> {
#Override
public Date convertToDatabaseColumn(ZonedDateTime zonedDateTime) {
if (zonedDateTime == null) return null;
return java.sql.Date.valueOf(zonedDateTime.toLocalDate());
}
#Override
public ZonedDateTime convertToEntityAttribute(Date date) {
if (date == null) return null;
LocalDate localDate = date.toLocalDate();
return ZonedDateTime.from(localDate);
}
}
... but it never gets called.
I have even added jpaProperties.put("hibernate.archive.autodetection", "class, hbm"); to my jpaProperties, but no luck. The ZonedDateTimeConverter class is in the same package as my Entities, so it should get scanned.
What am I missing here?
Reading JPA 2.1 spec :
"The conversion of all basic types is supported except for the following: Id attributes (including the
attributes of embedded ids and derived identities), version attributes, relationship attributes, and
attributes explicitly annotated as Enumerated or Temporal or designated as such in the XML descriptor."
any chance your ZonedDateTime field is annotated with #Id or #Temporal in your entity ?
OK, this was my mistake. Part of my application is managed by Hibernate, but part of it is NOT. The object I'm having problems with is NOT managed by Hibernate, so it makes sense that the JPA stuff isn't getting called.
I tried to use the DataTime in my entity class. Adding #Temporal(TemporalType.DATE) above the field, I got the error saying "The persistent field or property for a Temporal type must be of type java.util.Date, java.util.Calendar or java.util.GregorianCalendar". I can introduce the conversion back and forth; using setters and getters as follows:
#Temporal(TemporalType.DATE)
private Calendar attendanceDate;
public DateTime getAttendanceDate() {
return new DateTime(this.attendanceDate);
}
public void setAttendanceDate(DateTime attendanceDate) {
this.attendanceDate = attendanceDate.toCalendar(Locale.getDefault());
}
but I hope eclipselink to do it for me. I have gone thro' Persist Joda-time's DateTime via Hibernate. The answer suggesting to use hibernate, but I have to use eclipselink. I can use the DateTime object in my entity class with DB representation as BLOB, but I need it as Date. Is there anything like jodatime-eclipselink? Or any other suggestion? Thanks for the help.
Basic the link defines an EclipseLink Converter to convert from Joda DateTime to java.sql.Timestamp or Date.
You could use it, or define your own converter and use it through #Convert, #Converter in EclipseLink.
For DDL creation, the converter should define the initialize method and set the type on the mapping's field to java.sql.Timestamp.
Please log a bug (or vote for the existing one) on this in EclipseLink, we should have support for Joda.
I Try use joda-time-eclipselink-integration, but don't work, problably I made something wrong,
So I made more researchs and i found this link http://jcodehelp.blogspot.com.br/2011/12/persist-joda-datetime-with-eclipselink.html, they use #Converter annotation to convert the Joda Date Time.
I Try and works for me, I hope, works for you too.
I wanted to do the same thing, and Googling around actually led me here. I was able to accomplish this using the #Access annotation. First, you annotate the class like this
#Access(AccessType.FIELD)
public class MyClass
{
....
This provides field access to everything so you don't have to annotate the fields individually. Then you create a getter and setter for the JPA to use.
#Column(name="my_date")
#Temporal(TemporalType.DATE)
#Access(AccessType.PROPERTY)
private Date getMyDateForDB()
{
return myDate.toDate();
}
private void setMyDateForDB(Date myDateFromDB)
{
myDate = new LocalDate(myDateFromDB);
}
The #Access(AccessType.PROPERTY) tells JPA to persist and retrieve through these methods.
Finally, you'll want to mark your member variable as transient as follows
#Transient
private LocalDate myDate = null;
This stops JPA from trying to persist from the field as well.
That should accomplish what you're trying to do. It worked great for me.
Ahamed, you mentioned it wasn't working for you. Additionally you need to override the initialize method of the converter to define the desired field type:
#Override
public void initialize(DatabaseMapping mapping, Session session) {
((AbstractDirectMapping) mapping)
.setFieldClassification(java.sql.Timestamp.class);
}
The following is a working example based on the answers available in the topic
Basically the easiest approach is to use EclipseLink #Converter for a DateTime field in your Entity.
The converter usage looks as follows:
import org.eclipse.persistence.annotations.Convert;
import org.eclipse.persistence.annotations.Converter;
import javax.persistence.*;
import org.joda.time.DateTime;
#Entity
public class YourEntity {
#Converter(name = "dateTimeConverter", converterClass = your.package.to.JodaDateTimeConverter.class)
#Convert("dateTimeConverter")
private DateTime date;
And the converter itself:
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.converters.Converter;
import org.eclipse.persistence.sessions.Session;
import org.joda.time.DateTime;
import java.sql.Timestamp;
public class JodaDateTimeConverter implements Converter {
private static final long serialVersionUID = 1L;
#Override
public Object convertDataValueToObjectValue(Object dataValue, Session session) {
return dataValue == null ? null : new DateTime(dataValue);
}
#Override
public Object convertObjectValueToDataValue(Object objectValue, Session session) {
return objectValue == null ? null : new Timestamp(((DateTime) objectValue).getMillis());
}
#Override
public void initialize(DatabaseMapping mapping, Session session) {
// this method can be empty if you are not creating DB from Entity classes
mapping.getField().setType(java.sql.Timestamp.class);
}
#Override
public boolean isMutable() {
return false;
}
}
I am adding this for the purpose of easy copy-and-paste solution.
Solution is here
joda-time-eclipselink-integration
Answer from Atais works well. Below an upgrade to it.
You can omit #converter annotation by registering it globally.
At persistance.xml in persitence-unit add:
<mapping-file>META-INF/xyz-orm.xml</mapping-file>
and file META-INF/xyz-orm.xml with content:
<?xml version="1.0"?>
<entity-mappings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/orm" version="2.1">
<converter class="pl.ds.wsc.storage.converter.JodaDateTimeConverter"/>
</entity-mappings>
If your config file is META-INF/orm.xml then you can omit even first step because it is default confing for all persitence units.