【问题标题】:How to use springframework.data.jpa.repository.Query to find all results between two dates ignoring time如何使用 springframework.data.jpa.repository.Query 查找两个日期之间忽略时间的所有结果
【发布时间】:2020-08-02 04:39:21
【问题描述】:

我正在为这样一个简单的任务而苦苦挣扎,但我不知道自己做错了什么。

根据个人研究,我似乎正在编写我应该编写的代码,以便使用 Spring Data 搜索两个日期之间的所有记录。据我了解,“@Temporal(TemporalType.DATE)”具有“魔力”。

存储库

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

import javax.persistence.TemporalType;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.jpa.repository.Temporal;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import com.mybank.accountmanagement.model.Transaction;

    @Repository
    public interface TransactionRepository extends JpaRepository<Transaction, Long> {
    
        
@Query(value = "SELECT * FROM transactions WHERE account_id=:idAccount AND CAST(created_at AS date) BETWEEN :fromDate AND :toDate ", nativeQuery = true)
List<Transaction> findByAccountIdWithCreationDateBetween(@Param("idAccount") Long idAccount,
        @Param("fromDate") @Temporal(TemporalType.DATE) Date fromDate,
        @Param("toDate") @Temporal(TemporalType.DATE) Date toDate);
        
    }

型号:

交易

package com.mybank.accountmanagement.model;

import java.math.BigDecimal;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;

import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import org.springframework.format.annotation.NumberFormat;

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonIdentityReference;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;

@Entity
@Table(name = "transactions")
public class Transaction  extends AuditModel {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "account_id", nullable = false)
    @OnDelete(action = OnDeleteAction.CASCADE)
    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
    @JsonIdentityReference(alwaysAsId = true)
    @JsonProperty("accoout_id")
    private Account account;

    @NotNull
    @NumberFormat(pattern = "#,###,###,###.##")
    private BigDecimal amount;

    @NotNull
    private int transactionType;

    
    
    
    public Transaction(Account account, @NotNull BigDecimal amount, @NotNull int transactionType) {
        super();
        this.account = account;
        this.amount = amount;
        this.transactionType = transactionType;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Account getAccount() {
        return account;
    }

    public void setAccount(Account account) {
        this.account = account;
    }

    public BigDecimal getAmount() {
        return amount;
    }

    public void setAmount(BigDecimal amount) {
        this.amount = amount;
    }

    public int getTransactionType() {
        return transactionType;
    }

    public void setTransactionType(int transactionType) {
        this.transactionType = transactionType;
    }

    
}

审计模型

package com.mybank.accountmanagement.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(value = { "createdAt", "updatedAt" }, allowGetters = true)
public abstract class AuditModel implements Serializable {
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "created_at", nullable = false, updatable = false)
    @CreatedDate
    private Date createdAt;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "updated_at", nullable = false)
    @LastModifiedDate
    private Date updatedAt;

    public Date getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Date createdAt) {
        this.createdAt = createdAt;
    }

    public Date getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(Date updatedAt) {
        this.updatedAt = updatedAt;
    }
}

使用 "fromDate":"2020-08-02", "toDate":"2020-08-02" 搜索时我期望返回的结果

但它根本没有返回任何记录

***已编辑

由于某些未知的原因,我的记录与 fromDate 和 toDate 完全匹配的日期与结果无关

  1. 用更长的 fromDate 和 toDate 搜索

    curl --location --request GET 'localhost:2000/transaction/1/bankstatement' --header 'Content-Type: application/json' --data-raw '{ "fromDate":"2020-08-01", “迄今为止”:“2020-08-03” }'

带来两条记录:

[
    {
        "createdAt": "2020-08-02T16:29:08.085+00:00",
        "updatedAt": "2020-08-02T16:29:08.085+00:00",
        "id": 1,
        "amount": 1.00,
        "transactionType": 2,
        "accoout_id": 1
    },
    {
        "createdAt": "2020-08-02T16:29:11.185+00:00",
        "updatedAt": "2020-08-02T16:29:11.185+00:00",
        "id": 2,
        "amount": 2.00,
        "transactionType": 1,
        "accoout_id": 1
    }
]

到目前为止一切顺利

  1. 现在我在搜索确切日期时遇到问题。我希望上述查询的结果相同

    curl --location --request GET 'localhost:2000/transaction/1/bankstatement' --header 'Content-Type: application/json' --data-raw '{ "fromDate":"2020-08-02", “迄今为止”:“2020-08-02” }'

完全没有结果

*** 已编辑。感谢 Kavithakaran Kanapathippillai 提供的示例,我注意到它适用于新日期或 2020-08-02T00:00:00.000+00:00 但它在 2020-08-02 时失败。我仍然不知道为什么它会在 2020-08-02 失败,因为我不在乎时间,而且如果尝试少一天它也会起作用:2020-08-01

控制器

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.mybank.accountmanagement.BankStatementFilter;

import com.mybank.accountmanagement.model.Transaction;
import com.mybank.accountmanagement.repository.TransactionRepository;
import com.mybank.accountmanagement.service.TransactionService;

@RestController
public class TransactionController {

    @Autowired
    TransactionService transactionService;

    @Autowired
    TransactionRepository transactionRepository;

    @GetMapping("/transaction/{accountId}/bankstatement")
    public List<Transaction> bankStatement(@PathVariable(value = "accountId") Long accountId,
            @Valid @RequestBody BankStatementFilter bankStatementFilter) {

        return transactionRepository.findByAccountIdWithCreationDateBetween(accountId,
                bankStatementFilter.getFromDate(), bankStatementFilter.getToDate());
    }

}

一个简单的 Pojo 仅用于从客户端发送的字符串中获取新的日期(例如 Postman 或 curl)

import java.util.Date;

public class BankStatementFilter {
    
    private Date fromDate;
    private Date toDate;
    public Date getFromDate() {
        return fromDate;
    }
    public void setFromDate(Date fromDate) {
        this.fromDate = fromDate;
    }
    public Date getToDate() {
        return toDate;
    }
    public void setToDate(Date toDate) {
        this.toDate = toDate;
    }

}

我用这个 POJO BankStatementFilter 做了一些愚蠢的事情

这是我在比较 new Date() 和 2020-08-02 时注意到的。既然我不在乎时间,那对吗?

*** 最后评论

它现在正在工作。我将我的 POJO 更改为波纹管。如果我在做一些愚蠢的事情,我会感谢你的建议。顺便说一句,我最初的问题得到了 100% 的回答。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class BankStatementFilter {

    private Date fromDate;
    private Date toDate;

    public Date getFromDate() {
        return fromDate;
    }

    public void setFromDate(String fromDate) {

        String pattern = "yyyy-MM-dd";
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);

        try {
            this.fromDate = simpleDateFormat.parse(fromDate);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

    public Date getToDate() {
        return toDate;
    }

    public void setToDate(String toDate) {
        String pattern = "yyyy-MM-dd";
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);

        try {
            this.toDate = simpleDateFormat.parse(toDate);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

}

【问题讨论】:

  • 您现在面临的问题是什么?您的查询是否没有给您正确的输出?您能否添加您现在得到的输出?您的查询对我来说似乎很好。希望您会尝试更清楚地解释您的问题。
  • 谢谢。我想有两个问题:(1)我误用了@Temporal。 (2) 不包括fromDate和toDate中日期准确的记录。我现在将在上面添加更多信息
  • 您检查数据库和应用程序是否使用相同的时区?因为数据库中 created_at 的数据和响应不同

标签: java hibernate jpa spring-data-jpa spring-data


【解决方案1】:
  • @Temporal.Date 对您的 spring 数据存储库方法没有任何影响。只有在实体中注解时才有影响。

  • 改为对cast您的参数执行以下操作:

    @Query(value = "SELECT * FROM transactions WHERE account_id=:idAccount " +
            "AND CAST(created_at AS date) " +
            "BETWEEN CAST(:fromDate AS date) AND CAST(:toDate AS date)"
            , nativeQuery = true)
    List<Transaction> findBy(@Param("idAccount") Long idAccount,
                             @Param("fromDate") Date fromDate,
                             @Param("toDate") Date toDate);
  • 这是我与您的实体一起设置的示例项目,account 除外,因为如果您想比较,这不是问题。您可以将项目作为 maven 导入 IntelliJ 并运行主应用程序,它将插入数据并将其带回

    https://github.com/kavi-kanap/stackoverflow-63212441

【讨论】:

  • 首先,谢谢。我不知道“@Temporal.Date 对您的 spring 数据存储库方法没有任何影响”。据我记得,我在 Spring Data Repository 中也使用了 @Temporal 示例。看来我失去了一些点。其次,我必须做些什么才能包括与日期匹配的记录。我的意思是,假设有日期为 2020-08-02 的记录,并且在我的过滤器中 fromDate 是 2020-08-02 并且 toDate 也是 2020-08-02。使用您的答案,它没有带来确切日期的记录,我将添加两个打印件以显示我的意思
  • 我实际上使用您的代码设置了一个示例项目,并在今天的某个时间插入到记录中并运行查询并获取了记录。
  • 您的示例项目对我带来了很大的帮助。当我的控制器从客户端(PostMan/curl)接收到“2020-02-08”时,您正在使用 new Date()。现在我想我用日期序列化而不是 Spring Data 搞砸了一些东西。我刚刚尝试从 PostMan 发送到控制器端点 "fromDate":"2020-08-02T00:00:00.000+00:00", "toDate":"2020-08-02T23:59:59.062+00:00"它工作得很好。好吧,还不清楚有什么问题,因为我想忽略时间而只使用一天。这就是我对 BETWEEN CAST(:fromDate AS date) AND CAST(:toDate AS date) 的期望
  • 即使发送2020-08-08,也需要创建一个java日期对象。最好的选择是打印出控制器内部收到的日期对象
【解决方案2】:

您是否尝试过在 SQL 查询中使用 >= 和

另外我的建议是不要使用本机查询并尝试使用模型类来快速正确地反序列化,因为您已经定义了模型类。

【讨论】:

  • 谢谢。请您多扩展您的语句“不使用本机查询并尝试在您的情况下使用模型类进行快速和正确的反序列化,因为您定义了模型类”,看来您正在使用与上述不同的方法原因。如果我只是用“nativeQuery = false”替换“nativeQuery = true”,我会听从你的建议吗(我猜不是)
  • 将您的查询替换为:@Query(value = "SELECT TRANSACTION ts FROM TRANSACTIONS ts WHERE ts.accountId=:idAccount AND CAST(created_at AS date) BETWEEN :fromDate AND :toDate ", nativeQuery = false )从这里阅读差异和实现link如果它对你有用,请投票并接受anwser
  • 我相信我的问题不在于 Spring Data。相反,它在某种程度上与日期转换有关。我还将添加调用存储库的控制器
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-23
  • 1970-01-01
  • 2021-01-07
  • 2015-01-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多