Mapstruct LocalDateTime to Instant - java-time

I am new in Mapstruct. I have a model object which includes LocalDateTime type field. DTO includes Instant type field. I want to map LocalDateTime type field to Instant type field. I have TimeZone instance of incoming requests.
Manually field setting like that;
set( LocalDateTime.ofInstant(x.getStartDate(), timeZone.toZoneId()) )
How can I map these fields using with Mapstruct?

You have 2 options to achieve what you are looking for.
First option:
Use the new #Context annotation from 1.2.0.Final for the timeZone property and define your own method that would perform the mapping. Something like:
public interface MyMapper {
#Mapping(target = "start", source = "startDate")
Target map(Source source, #Context TimeZone timeZone);
default LocalDateTime fromInstant(Instant instant, #Context TimeZone timeZone) {
return instant == null ? null : LocalDateTime.ofInstant(instant, timeZone.toZoneId());
}
}
MapStruct will then use the provided method to perform mapping between Instant and LocalDateTime.
The second option:
public interface MyMapper {
#Mapping(target = "start", expression = "java(LocalDateTime.ofInstant(source.getStartDate(), timezone.toZoneId()))")
Target map(Source source, TimeZone timeZone);
}
My personal option would be to use the first one

Related

JPA Projection spring boot; repository mapping entity instant to do date

I have an entity that has a date modelled as an Instant
I have an DO object that has a date modelled as a Date
when i do the conversion myself in the constructor of the DO it works:
public class DO {
private Date someTimePoint;
public DO(Instant CreatedAt) {
this.someTimePoint = Date.from(CreatedAt);
}
}
and my repo works call:
List<DO> findBySomeField(UUID someField);
Give result.
However: The DO is generated and I do not have access to it, so the constructor is actually:
public DO(Date CreatedAt) {
So the question is:
Is there a way to have the conversion from Instant to Date done on the fly by Spring using the Projection methodology?
Reading https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections
doesn't give me any clues...

java.time.LocalDate not supported in native queries by latest Spring Data/Hibernate?

Problem: Native queries with Spring Data returning dates return java.sql.Date not java.time.LocalDate, despite the setup.
Context: A new project with Spring Boot 2.0.0.M5 (the latest), Hibernate 5.2.11, Hibernate-Java8 5.2.12 (which gives support for JSR310 classes as long as it's on the classpath).
Anonymized example below (the app is not really about birthdays):
public interface BirthdayRepository<T, ID extends Serializable> extends Repository<T, ID> {
#Query(value = "select day from birthdays", nativeQuery = true)
Iterable<java.sql.Date> getBirthdays(); //the return type should ideally be java.time.LocalDate
}
In the database (SQL Server), the day field is DATE and values are like 2017-10-24.
The problem is that at runtime, the Spring Data repository (whose implementation I cannot control, or is there a way?) returns java.sql.Date not java.time.LocalDate (Clarification: the return type appears to be decided by Spring Data and remains java.sql.Date even if I change the return type to be java.time.LocalDate, which is how I started to).
Isn't there a way to get LocalDate directly? I can convert it later, but (1) that's inefficient and (2) the repository methods have to return the old date/time classes, which is something I'd like to avoid. I read the Spring Data documentation, but there's nothing about this.
EDIT: for anyone having the same question, below is the solution, the converter suggested by Jens.
public class LocalDateTypeConverter {
#Converter(autoApply = true)
public static class LocalDateConverter implements AttributeConverter<LocalDate, Date> {
#Nullable
#Override
public Date convertToDatabaseColumn(LocalDate date) {
return date == null ? null : new Date(LocalDateToDateConverter.INSTANCE.convert(date).getTime());
}
#Nullable
#Override
public LocalDate convertToEntityAttribute(Date date) {
return date == null ? null : DateToLocalDateConverter.INSTANCE.convert(date);
}
}
It looks like you found a gap in the converters. Spring Data converts out of the box between java.util.Date and java.time.LocalDate but not between java.time.LocalDate and java.sql.Date and other date and time-related types in the java.sql package.
You can create your own converter to do that. You can use Jsr310JpaConverters as a template.
Also, you might want to create a feature request and if you build a converter for your use, you might even submit a pull request.
I know this is an older question, but my solution to this problem does not require a custom converter.
public interface BirthdayRepository<T, ID extends Serializable> extends Repository<T, ID> {
#Query(value = "select cast(day as date) from birthdays", nativeQuery = true)
Iterable<java.time.LocalDate> getBirthdays();
}
The CAST tells JPQL to use available java date\time types rather than java.sql.Date

mapStruct: map list to other list?

I have a list List<Payment> which I'd like to map to another list List<PaymentPlan>. These types look like this:
public class Payment {
#XmlElement(name = "Installment")
#JsonProperty("Installment")
private List<Installment> installments = new ArrayList<>();
#XmlElement(name = "OriginalAmount")
#JsonProperty("OriginalAmount")
private BigDecimal originalAmount;
//getters setters, more attributes
}
and....
public class PaymentPlan {
//(Installment in different package)
private List<Installment> installments;
#XmlElement(name = "OriginalAmount")
#JsonProperty("OriginalAmount")
private BigDecimal originalAmount;
//getters setters, more attributes
}
I expect that something like this is working...
#Mappings({
#Mapping(//other mappings...),
#Mapping(source = "payments", target = "paymentInformation.paymentPlans")
})
ResultResponse originalResponseToResultResponse(OrigResponse originalResponse);
...but I get:
Can't map property java.util.List<Payment> to java.util.List<PaymentPlan>.
Consider to declare/implement a mapping method java.util.List<PaymentPlan> map(java.util.List<Payment> value);
I don't know how to apply this information. First I though I need to declare some extra mapping (in the same mapper class) for the lists, so MapStruct knows how to map each field of the List types like this:
#Mappings({
#Mapping(source = "payment.originalAmount", target = "paymentInformation.paymentPlan.originalAmount")
})
List<PaymentPlan> paymentToPaymentPlan(List<Payment> payment);
...but I get error messages like
The type of parameter "payment" has no property named "originalAmount".
Obviously I do something completely wrong, since it sound like it does not even recognize the types of the List.
How can I basically map from one List to another similar List? Obviously I somehow need to combine different mapping strategies.
btw: I know how to do it with expression mapping, like...
#Mapping(target = "paymentPlans",expression="java(Helper.mapManually(payments))")
but I guess MapStruct can handle this by iself.
I presume you are using version 1.1.0.Final. Your extra mapping is correct, the only difference is that you need to define a mapping without the lists MapStruct will then use that to do the mapping (the example message is a bit misleading for collections).
PaymentPlan paymentToPaymentPlan(Payment payment);
You don't even need the #Mappings as they would be automatically mapped. You might also need to define methods for the Instalment (as they are in different packages).
If you switch to 1.2.0.CR2 then MapStruct can automatically generate the methods for you.

Swagger Model Schema Response: alternate label for LocalDate in SpringFox

We are using Swagger 2.x and SpringFox 2.0 to document our REST service created with Spring MVC.
We have a REST response with a property List<LocalDate> dates.
In the Model Schema of the response, the label for dates is shown as 'LocalDate'. That is not intended: we would like to have 'date' or 'yyyy-MM-dd' instead.
We have this class:
public class SayHelloResponse {
private List<LocalDate> dates;
private String message;
public SayHelloResponse(String message, LocalDate... dates) {
this.message = message;
this.dates = ImmutableList.copyOf(dates);
}
public List<LocalDate> getDates() {
return dates;
}
public String getMessage() {
return message;
}
}
That results in this Model Schema:
{
"dates": [
"LocalDate"
],
"message": "string"
}
In the Model Schema, I would like to have LocalDate as 'date' or 'yyyy-MM-dd'. The way to do this seems to be with com.wordnik.swagger.annotations.ApiModelProperty but this does not have any effect (it is being picked up, as when I add #ApiModelProperty(hidden=true) it is hidden).
I created a sample rest project that shows the issue.
Any ideas how I can change LocalDate to 'date' or 'yyyy-MM-dd' in the Model Schema of Swagger?
There is a method in Docket object to replace models called directModelSubstitute(). You can use it like this to substitute LocalDate to Date object:
Docket#directModelSubstitute(LocalDate.class, Date.class)
The only problem with it that I found is that you can't change the date format.
See A/Q section in the official Springfox documentation, specifically question "How do we use Java 8 types esply. LocalDateTime?"
This is recommended in the official Springfox documentation, but doesn't effect:
Docket(DocumentationType.SWAGGER_2)..build().directModelSubstitute(LocalDate.class, java.sql.Date.class)
This effect but change format to date-time instead of date:
Docket(DocumentationType.SWAGGER_2)..build().directModelSubstitute(LocalDate.class, java.util.Date.class);
That's why I use the last one and ignore time part.

Jersey Marshall Map<Date,List>

I start understanding how jersey works with JAXB. But today i faced a particular case where i want to marshall a Map of (Date,List) entries:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class MyClass{
#XmlJavaTypeAdapter(MapAdapter.class)
private Map<Date,List<MyObject>> = new TreeMap<Date,List<MyObject>>(new DateCompareDesc());
}
The goal here is to marshall a Map whose entry is a Date with its corresponding list of MyObject. the map is sorted in desc order.
For this i implemented an Adapter for Map (MapAdapter, following #Blaise Doughan's tutorial, http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret-weapon.html). The problem is on the Date key. I have an Error : Unable to marshall java.util.Date. So i tried this new Date Adapter :
public class DateAdapter extends XmlAdapter<String, Date> {
#Override
public Date unmarshal(String v) throws Exception {
//not implemented
}
#Override
public String marshal(Date v) throws Exception {
return v.toString();
}
}
Where can i add #XmlJavaTypeAdapter(DateAdapter.class) so that Jersey could marhsall Date as key to my TreeMap?
Thanks.
JAXB supports the marshalling/unmarshalling of java.util.Date to the standard XML schema types: date, time, dateTime. You can control the type used with the #XmlSchemaType annotation.
http://blog.bdoughan.com/2011/01/jaxb-and-datetime-properties.html
If your date information is not represented as one of the standard XML schema types, you can use an XmlAdapter similar to the one I used the following answer to a similar question:
jaxb unmarshal timestamp
If you need to use the XmlAdapter approach, the #XmlJavaTypeAdapter annotation would be placed on the Date field of the adapted object representing the entry in the Map. Below is what this might look like based on my blog: http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret-weapon.html.
import javax.xml.bind.annotation.XmlValue;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
public class MyMapEntryType {
#XmlAttribute
#XmlJavaTypeAdapter(DateAdapter.class)
public Date key;
public List<MyObject> value;
}