1

I am using Spring Boot and Spring JPA. I have a repository class with custom @Query methods.

public interface MarketForceRepository extends CrudRepository<MarketForceComment, Integer> {

    @Query("SELECT c FROM MarketForceComment c WHERE c.property.id = ?1 and c.commentDate >= '?1' AND  c.commentDate <= '?2'")
    List<MarketForceComment> getByPropAndDate(Integer propID, LocalDate start, LocalDate end);

    @Query("SELECT c FROM MarketForceComment c WHERE c.property.id IN ?1 and c.commentDate >= '?2' AND c.commentDate <= '?3'")
    List<MarketForceComment> getByPropsAndDates(List<Integer> propIDs, LocalDate start, LocalDate end);
}

The MarketForceComment class follows (in part):

@Table(name = "MarketForceComment", schema = "dmb")
@Entity
public class MarketForceComment {
    @ManyToOne
    @JoinColumn(name = "PropertyID")
    private Property property;

    @Column(name = "CommentDate")
    private LocalDate commentDate;

    @Column(name = "Level")
    private int level;

    @Column(name = "Subject")
    private String subject;

    @Column(name = "Details", columnDefinition = "text")
    private String details;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Integer id;  

    .... 
}

Whenever I attempt to run my query method:

@Override
public List<CommentDTO> getCommentsByStore(int storeID, LocalDate date) {
    List<CommentDTO> dtoList = new ArrayList<>();
    marketRepo.getByPropAndDate(storeID, date.plusMonths(1), date)
            .forEach(c -> dtoList.add(mapper.map(c, CommentDTO.class)));
    guestRepo.getByPropAndDate(storeID, date.plusMonths(1).atStartOfDay(), date.atStartOfDay())
            .forEach(c -> dtoList.add(mapper.map(c, CommentDTO.class)));
    return dtoList;
}

I receive the following error: com.microsoft.sqlserver.jdbc.SQLServerException: Conversion failed when converting date and/or time from character string.

I am a bit perplexed. It appears to be a format issue (the DBMS wants a 'yyyy-MM-dd' or similar format), yet I thought Java's LocalDate would be expressed/converted into a format that could be parsed by database system.

Any advice would be appreciated. Thanks.

EDIT:

For reference, the underlying fault was the order of the parameters (?1, ?2, ?3):

public interface MarketForceRepository extends CrudRepository<MarketForceComment, Integer> {

    @Query("SELECT c FROM MarketForceComment c WHERE c.property.id = ?1 and c.commentDate >= ?2 AND  c.commentDate <= ?3")
    List<MarketForceComment> getByPropAndDate(Integer propID, LocalDate start, LocalDate end);

    @Query("SELECT c FROM MarketForceComment c WHERE c.property.id IN ?1 and c.commentDate >= ?2 AND c.commentDate <= ?3")
    List<MarketForceComment> getByPropsAndDates(List<Integer> propIDs, LocalDate start, LocalDate end);
}
alexrnov
  • 2,346
  • 3
  • 18
  • 34
KellyM
  • 2,472
  • 6
  • 46
  • 90

3 Answers3

2

If you are using JPA version lower than 2.2, it doesn't directly support Java8 LocalDate & LocalTime conversion from SqlDate, as it was released before Java8. You will have to write your custom date converter by implementing JPA AttributeConverter interface. Then either you can autoApply it for all entity classes or you can selectively annotate it to LocalDate entity field.

import java.sql.Date;
import java.time.LocalDate;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

@Converter(autoApply = true)
public class LocalDateConverter implements AttributeConverter<LocalDate, Date> {

    @Override
    public Date convertToDatabaseColumn(LocalDate date) {        
        return Date.valueOf(date);
    }

    @Override
    public LocalDate convertToEntityAttribute(Date value) {
        return value.toLocalDate();
    }
}

With autoApply = true JPA will automatically associate this converter whereaever Entity fields are LocalDate.

Or if you choose autoApply = false, you will have to associate converter class via annotation with the field.

@Column(name = "CommentDate")
@Convert(converter = LocalDateConverter.class)
private LocalDate commentDate;

Refer JPA 2.2 release spec

Amith Kumar
  • 4,400
  • 1
  • 21
  • 28
  • Thanks. However, whenever I add the class, I received the following error on startup: `Caused by: org.hibernate.QueryException: AttributeConverter domain-model attribute type [java.time.LocalDate] and JDBC type [java.util.Date] did not match query literal type [java.lang.String]` – KellyM Jun 18 '18 at 20:09
  • `@Query("SELECT c FROM MarketForceComment c WHERE c.property.id = ?1 and c.commentDate >= '?1' AND c.commentDate <= '?2'") List getByPropAndDate(Integer propID, LocalDate start, LocalDate end);` shouldn't it be `c.commentDate >= '?2'`, a typo error. – Amith Kumar Jun 18 '18 at 20:15
  • I updated my answer for conversion directly between LocalDate & SqlDate, that's precise & quicker. – Amith Kumar Jun 18 '18 at 20:41
  • Thanks, but I am afraid I am still getting an error: `Caused by: org.hibernate.QueryException: AttributeConverter domain-model attribute type [java.time.LocalDate] and JDBC type [java.sql.Date] did not match query literal type [java.lang.String] [SELECT c FROM com.midamcorp.dmb.model.MarketForceComment c WHERE c.property.id = ?1 and c.commentDate BETWEEN '?1' AND '?2']`. – KellyM Jun 18 '18 at 20:49
  • 1
    I think you have a typo error here, you are not using the correct placeholder# `[SELECT c FROM com.midamcorp.dmb.model.MarketForceComment c WHERE c.property.id = ?1 and c.commentDate BETWEEN '?1' AND '?2']` whereas it should be: `[SELECT c FROM com.midamcorp.dmb.model.MarketForceComment c WHERE c.property.id = ?1 and c.commentDate BETWEEN '?2' AND '?3']` – Amith Kumar Jun 18 '18 at 21:00
  • Thanks so much, @Amith Kumar. Part of me hates that it was so simple, but the type/misplaced parameters were the underlying problem. When I placed them correctly, the `@Query` worked correctly; I just had to remove the `'` marks. – KellyM Jun 18 '18 at 21:07
  • oh! yeah I didn't pay attention too on the quotes you are using. Yeah you didn't need it at first place :). You should update your question block as well, so it doesn't create confusion to anybody refering this in future. – Amith Kumar Jun 18 '18 at 21:09
0

Please have a look at: How to persist JSR-310 types with Spring Data JPA?

Depending on the framework versions you use, you have to add additional configuration to your project to use JSR-310 types like LocalDate in your entities.

Chris_LE
  • 31
  • 2
  • Sorry, I should have mentioned the versions. I am using Spring Boot 2 with the corresponding `spring-boot-starter-data-jpa`. Thanks. – KellyM Jun 18 '18 at 20:10
0

Either use converter or column definition

for example

@Column(name = "local_time", columnDefinition = "TIME")
private LocalTime localTime;

@Column(name = "local_date", columnDefinition = "DATE")
private LocalDate localDate;

@Column(name = "local_date_time", columnDefinition = "TIMESTAMP")
private LocalDateTime localDateTime;

using converter

@Convert(converter = LocalDateConverter.class)
@Column(name = "createdDate")
private LocalDate createdDate;
Ritunjay kumar
  • 261
  • 3
  • 6