How can I persist a jodatime YearMonth object to a sql database?
#Entity
public class MyEntity {
private YearMonth date; //how to persist?
//some more properties
}
I want later to be able to pick all entities by a specific month.
The better question is how do you intend to use the data in the database? Generally you want to store dates in databases using data types supported by the target database as opposed to strings or composite integers. That would allow you to use the built in database date/time functions and validations as opposed to needing to implement that logic in your application.
You can certainly add methods to your entity to convert to/from jodatime values to your entity for the parts of your application that need it.
You need a javax.persistence.Converter for this (see How to convert java.util.Date to Java8 java.time.YearMonth):
#Entity
public class MyEntity {
#Convert(converter = YearMonthConverter.class)
private YearMonth date; //how to persist?
//some more properties
}
The following is an example converter for Java8 java.time.YearMonth to java.utilDate. You need to change java.time.YearMonth to org.joda.time.YearMonth and use the appropriate methods to convert to/from the desired type:
(imports omitted):
#Converter(autoApply = true)
public class YearMonthConverter implements AttributeConverter<YearMonth, Date> {
#Override
public Date convertToDatabaseColumn(YearMonth attribute) {
// uses default zone since in the end only dates are needed
return attribute == null ? null : Date.from(attribute.atDay(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
}
#Override
public YearMonth convertToEntityAttribute(Date dbData) {
// TODO: check if Date -> YearMonth can't be done in a better way
if (dbData == null) return null;
Calendar calendar = Calendar.getInstance();
calendar.setTime(dbData);
return YearMonth.of(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1);
}
}
Related
Similar to this question. I have an interface DateRangeModel:
I use this to automatically validate dates in implementers:
public interface DateRangeModel {
#ApiModelProperty(value = "From date. Must be less than to date.")
#Column()
Date getFromDate();
void setFromDate(Date date);
#ApiModelProperty(value = "To date. Must be greater than to date.")
#Column()
Date getToDate();
void setToDate(Date date);
/**
* Checks that if both dates are populated, a valid date range is used.
*
* #return true if the date is a valid range.
*/
#AssertTrue(message = "To date must be greater than or equal to from date.")
#JsonIgnore
default boolean areDatesValid() {
if (getToDate() != null && getFromDate() != null) {
return !getFromDate().after(getToDate());
}
return true;
}
}
I implement it like this:
#EqualsAndHashCode
#Data
#Builder
public class BirthdayParty implements DateRangeModel {
Date fromDate;
Date toDate;
String name;
}
Which compiles and seems to work, but I get that error when running PMD:
Returning a reference to a mutable object value stored in one of the object's fields exposes the internal representation of the object.
How can I either accomplish what I want (an interface with to/from date validation) without having to implement the setDate methods in all implementers (which I think would defeat the purpose)?
The problem is that java.util.Date is mutable and you return it in your getters. So someone could do this:
BirthdayParty party = ...;
Date fromDate = party.getFromDate();
...
Date someDate = fromDate;
...
// you might not be aware that this also changes the fromDate in party
someDate.setTime(12345678);
There are four things you can do:
Disable the PMD rule.
Suppress the warning everywhere you use one of these a classes.
Don't use Lombok and copy the dates in your setters and getters instead of just storing/returning the reference to a date.
Use java.time.ZonedDateTime (or LocalDateTime) instead of Date. ZonedDateTime is immutable and should not lead to this warning.
I suggest the fourth option. Not only because it gets rid of the PMD warning but also because the new time API in Java 8 is so much better and easier to use than java.util.Date.
I'd like to convert a Java POJO into a Map without using JSON serialization for the properties (e.g. that Date's are converted to a long or a ISO8601 String). I just want the fields to be retained as they are.
For example if I have a POJO defined like this:
public class MyPojo {
private Date date;
private String x;
public MyPojo(Date date, String x) {
this.date = date;
this.x = x;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getX() {
return x;
}
public void setX(String x) {
this.x = x;
}
}
I know I can use Jackson as described in this Stackoverflow question but the result is not what I want. For example if I do:
Map<String, Object> x = new ObjectMapper().convertValue(new MyPojo(new Date(), "x"), new TypeReference<Map<String, Object>>() {});
System.out.println(x);
I get this result:
{date=1528521584984, x=x}
whereas I would simply like a Map retaining the java.util.Date instance without being serialized.
Note that I obviously would like this work for nested Pojo's such as:
public class MyPojo {
private MyOtherPojo otherPojo;
...
}
How can I achieve this?
First off, going from a POJO to a Map seems like a bad idea. But I guess that you have reasons for wanting to do it ...
The simple approach is to just create a HashMap and populate it using a sequence of put calls. That gives you a map that contains the same information as the original POJO, but is not "connected" to is.
If you want the Map to be a view of the original POJO, there are 3rd-party libraries that can be used to do this. For example, org.apache.commons.beanutils has classes for wrapping an POJO that follows the JavaBeans conventions as a DynaBean. You can then adapt that as a Map using DynaBeanMapDecorator.
Mapping nested POJOs to nested maps would require more work. (And it contradicts your requirement that you can get the value of getData() as a Date POJO rather than as a Map!)
Jackson will serialize the Date to a timestamp format by default (number of milliseconds since January 1st, 1970, UTC). #JsonFormat annotation can be used to control the date format on individual classes.
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
private Date date;
In my Spring application I need to design a data model which is having date range. Is there any data type which can be used?
Currently I did something like this.
#Entity
#Table(name = "scheduled_period")
public class ScheduledPeriod {
private Date oneDay;
private Date fromDate;
private Date toDate;
}
I want to put fromDate and toDate together in to something like dateRange
Best solution is yours, by using LocalDate.
By using two LocalDate to Start Date and End Date.
and working with them, see:
HQL / JPA find available items between date range
some DBMS's like PostgreSQL has datatype named: daterange
for example:
CREATE TABLE reservation (room int, during daterange);
INSERT INTO reservation VALUES
(1108, '[2010-01-01 14:30, 2010-01-01 15:30)');
but Java (JPA) does not support datarange data type.
If you generate that table into Entity, it generated to Object.
private Object during;
public Object getDuring() {
return this.during;
}
public void setDuring(Object during) {
this.during = during;
}
finally, if you persist to use rangedate, you can define a new class (Entity) named RangeDate with two LocalDates or use array of LocalDate (if your DMBS support it).
I have a rest service which validates date now i need to modify it to take two dates, but i don't know if to use #PathParam or #QueryParam and if i can insert it between two #PathParam
This it the original code :
#Path("isDateValid/{date}/{itemId}")
public boolean isDateValid(#PathParam("date") Date date, #PathParam("itemId") Long itemId) {
Should i do like this :
#Path("isDateValid/{startDate}/{endDate}/{itemId}")
public boolean isDateValid(#PathParam("startDate") Date startDate, #PathParam("endDate") Date endDate, #PathParam("itemId") Long itemId) {
If you do not want to use third party stuff, I suggest you define a format for the text-date. You can use the SimpleDateFormat class (avoid the space). The you can use the following code.
#Path("isDateValid/{itemId}")
public boolean isDateValid(#PathParam("itemId") Long itemId) {
#QueryParam("begin") String sBegin;
#QueryParam("end") String sEnd;
SimpleDateFormat sdf = new SimpleDateFormat(/* Your patern, for example "yyMMddHHmmssZ"*/);
Date dBegin = sdf.parse(sBegin);
Date dEnd = sdf.parse(sEnd);
/*
...
*/
}
Date Class is cannot serialize using JAX-RS as it is not a simple type. You need to develop the same using MessageBodyReader/Writer .
Click Here for more
Or you can use some third party stuff to configure to get it done.
Click Here for more
We have a case, where we require only the day and the month and thus would use the java.time.MonthDay (Javadocs) to represent that information.
We are aware that we could create our own JPA object for persistence or just use the java.sql.Date object, but that generally requires an unrequired year information.
Another way is to call the method .atYear(int) (Javadoc) (with a fictitious year) on it and receive a java.time.LocalDate (Javadoc), which can be easily converted to java.sql.Date. But this is prone to missunderstandings in the future (and also persist the year information).
Is there some "elegant"/supposed solution for this case? Or is there a replacement for SQL that supports the whole new date and time API for Persistence.
Another case would be java.time.YearMonth (Javadoc).
Thanks for reading!
Since SQL databases don't have a type compatible with MonthDay, use a VARCHAR columns, and simply use toString() and MonthDay.parse().
Or use a custom DateTimeFormatter, if you don't like the --12-03 format.
The default format will correctly sort, as a string.
here are the code snippets:
// column define:
#Column(name = "date", columnDefinition = "mediumint", nullable = false)
#Convert(converter = MonthDayIntegerAttributeConverter.class)
protected MonthDay date;
// converter define:
public class MonthDayIntegerAttributeConverter implements AttributeConverter<MonthDay, Integer> {
#Override
public Integer convertToDatabaseColumn(MonthDay attribute) {
return (attribute.getMonthValue() * 100) + attribute.getDayOfMonth();
}
#Override
public MonthDay convertToEntityAttribute(Integer dbData) {
int month = dbData / 100;
int day = dbData % 100;
return MonthDay.of(month, day);
}
}