[英]Spring data JPA retrieve data with between two dates not working
Edited:编辑:
I am trying to get the data by filtering it by dates, But I am getting the following error.我试图通过按日期过滤数据来获取数据,但出现以下错误。 I am accepting date in LocalDateTimeFormat
.我接受LocalDateTimeFormat
中的日期。 However, the type of created_date
is Instant
in JPA entity.但是, created_date
的类型在 JPA 实体中是Instant
。 I cannot change the type in entity because it might break other APis我无法更改实体的类型,因为它可能会破坏其他 API
Parameter value [2021-12-23T13:00] did not match expected type [java.time.Instant (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value [2021-12-23T13:00] did not match expected type [java.time.Instant (n/a)]
Curl request Curl 请求
curl --location --request GET 'http://localhost:9202/movement-history?amd=xs&fromCreatedDate=2021-12-23T13:00&toCreatedDate=2021-12-23T15:10'
Response回复
{
"code": 0,
"message": "Success",
"data": {
"movementHistory": [],
"totalCount": 0,
"page": 1,
"limit": 20
}
}
Controller Controller
@GetMapping( value = "/movement-history")
public ResponseDto<RetrieveMovementHistoryResponse> retrieveMovementHistory(
@Valid RetrieveMovementHistoryRequest retrieveMovementHistoryRequest
) {
return responseUtil.prepareSuccessResponse( retrieveHistoryService.doAction( retrieveMovementHistoryRequest ),
null );
}
retrieveMovementHistoryRequest DTO检索MovementHistoryRequest DTO
package ai.growing.platform.inventory.dto;
import ai.growing.platform.library.dto.Request;
import lombok.*;
import lombok.experimental.FieldDefaults;
import org.hibernate.validator.constraints.Range;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Positive;
import java.time.Instant;
import java.util.List;
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@FieldDefaults( level = AccessLevel.PRIVATE )
public class RetrieveMovementHistoryRequest extends Request {
List<String> amd;
@DateTimeFormat( pattern = "yyyy-MM-dd'T'HH:mm" )
LocalDateTime fromCreatedDate;
@DateTimeFormat( pattern = "yyyy-MM-dd'T'HH:mm" )
LocalDateTime toCreatedDate;
@Positive( message = "limit can not be negative" )
@Builder.Default
@NotNull
Integer limit = 20;
@Builder.Default
@Range( min = 1, message = "page can not be zero or negative" )
@NotNull
Integer page = 1;
}
Search specification for JPA JPA的搜索规范
public class TransactionSearchSpecification {
private static final String AMD = "amd";
private static final String CREATED_DATE = "createdDate";
public static Specification<Transaction> getTransactionBySpecification(
TransactionSearchCriteria transactionSearchCriteria) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
if( !CollectionUtils.isEmpty( transactionSearchCriteria.getAmdList() ) ) {
predicates.add( root.get( SKU ).in( transactionSearchCriteria.getAmdList() ) );
}
if( !StringUtils.isEmpty( transactionSearchCriteria.getFromDate() ) && !StringUtils.isEmpty(
transactionSearchCriteria.getToDate() ) ) {
predicates.add( criteriaBuilder
.between( root.get( CREATED_DATE )
, transactionSearchCriteria.getFromDate(), transactionSearchCriteria.getToDate() ) );
}
return criteriaBuilder.and( predicates.toArray( new Predicate[ 0 ] ) );
};
}
}
entity实体
package ai.growing.platform.product.inventory.entity;
import java.io.Serializable;
import java.time.Instant;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@Builder
@Entity
@Table( name = "transaction" )
@Data
@NoArgsConstructor
public class Transaction implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue( strategy = GenerationType.IDENTITY )
@Column( name = "id", nullable = false )
private Long id;
@Column( name = "amd", nullable = false )
private String amd;
@Column( name = "request_type" )
private String requestType;
@CreationTimestamp
@Column( name = "created_date", nullable = false )
private Instant createdDate;
}
Data in DB数据库中的数据
id created_date request_type amd
850476 2021-12-23 13:01:19 COMPLETED xs
850480 2021-12-23 14:58:17 COMPLETED xs
850474 2021-12-23 13:00:41 INITIATED xs
850478 2021-12-23 14:58:08 INITIATED xs
Query issued by JPA JPA 发出的查询
select
transactio0_.id as id1_11_,
transactio0_.created_date as created_2_11_,
transactio0_.amd as amd3_11_,
from
transaction transactio0_
where
(
transactio0_.amd in (
?
)
)
and (
transactio0_.created_date between ? and ?
) limit ?
2022-01-24 09:02:00.918 TRACE [...true] 29314 --- [tp1324897979-85] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [xs]
2022-01-24 09:02:00.918 TRACE [...,true] 29314 --- [tp1324897979-85] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [TIMESTAMP] - [2021-12-23T13:00:00Z]
2022-01-24 09:02:00.920 TRACE [...,true] 29314 --- [tp1324897979-85] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [TIMESTAMP] - [2021-12-23T15:10:00Z]
As you can see in your stacktrace, the error is related to the conversion between the value you provided for your LocalDateTime
to Instant
, probably when Spring Data executes the JPA Criteria query you created in your Specification
.正如您在堆栈跟踪中看到的那样,该错误与您为LocalDateTime
提供的值之间的转换有关Instant
,可能是当 Spring Data 执行您在Specification
中创建的 JPA Criteria 查询时。
To solve the problem you could try manually converting from LocalDateTime
to Instant
in your Specification
code - I assume that both transactionSearchCriteria.getFromDate()
and transactionSearchCriteria.getToDate()
are String
.要解决该问题,您可以尝试在Specification
代码中手动从LocalDateTime
转换为Instant
- 我假设transactionSearchCriteria.getFromDate()
和transactionSearchCriteria.getToDate()
都是String
。 For example:例如:
ublic class TransactionSearchSpecification {
private static final String AMD = "amd";
private static final String CREATED_DATE = "createdDate";
public static Specification<Transaction> getTransactionBySpecification(
TransactionSearchCriteria transactionSearchCriteria) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
if( !CollectionUtils.isEmpty( transactionSearchCriteria.getAmdList() ) ) {
predicates.add( root.get( SKU ).in( transactionSearchCriteria.getAmdList() ) );
}
LocalDateTime fromDate = transactionSearchCriteria.getFromDate();
LocalDateTime toDate = transactionSearchCriteria.getToDate();
if( !StringUtils.isEmpty( fromDate ) && !StringUtils.isEmpty(
toDate ) {
Instant fromInstant = this.fromString(fromDate);
Instant toInstant = this.fromString(toDate);
predicates.add(
criteriaBuilder.between(
root.get( CREATED_DATE ), fromInstant, toInstant
)
);
}
return criteriaBuilder.and( predicates.toArray( new Predicate[ 0 ] ) );
};
}
// This method is suitable for being defined probably in a common, utility class
// Following, for example, the advice provided in this SO question
// https://stackoverflow.com/questions/50299639/converting-string-to-instant
private Instant fromString(final String localDateTimeString) {
if (StringUtils.isEmpty(localDateTimeString) {
return null;
}
// Convert String to LocalDateTime
LocalDateTime localDateTime = LocalDateTime.parse(
localDateTimeString,
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm" , Locale.US )
);
// Provide the appropriate ZoneId
Instant result = localDateTime
.atZone(ZoneId.of("Europe/Madrid"))
.toInstant();
// Return the obtained instant
result
}
}
I found the issue, Well it might be a way of how JPA interacts with MySQL or MySQL itself.我发现了这个问题,这可能是 JPA 与 MySQL 或 MySQL 本身交互的一种方式。 The issue was the fromCreatedDate
and toCreatedDate
was get converted into localDateTime
at the of execution.问题是fromCreatedDate
和toCreatedDate
在执行时被转换为localDateTime
。 For example I was sending date as instant 2021-12-20T13:00:41Z
and 2021-12-29T15:10:08Z
which was getting converted to UAE localtime 2021-12-23 17:00:41.0
and 2021-12-23 19:10:08.0
.例如,我将日期作为即时2021-12-20T13:00:41Z
和2021-12-29T15:10:08Z
发送,它们被转换为阿联酋2021-12-23 17:00:41.0
和2021-12-23 19:10:08.0
。 So the code was working fine but since there was not record on that day.所以代码工作正常,但因为那天没有记录。 I was giving me empty result.我给了我空的结果。
I enable MySql general logs to see what query is actually being hit to MySql.我启用 MySql 常规日志以查看 MySql 实际遇到的查询。 I took reference from this video for enabling logs in MySql.我参考了这个视频来启用 MySql 中的日志。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.