【问题标题】:Getting records from MySQL based on a DateTime column ignoring the time portion using JPA along with Joda-Time基于 DateTime 列从 MySQL 获取记录,忽略使用 JPA 和 Joda-Time 的时间部分
【发布时间】:2013-12-10 21:12:54
【问题描述】:

如何使用 JPA 过滤 MySQL 数据库中的行,忽略 MySQL 中给定 DateTime 字段的时间部分?

例如,以下代码段计算数据库表中位于 MySQL 中 DateTime 类型列中给定的两个日期之间的行数。

CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<Long>criteriaQuery=criteriaBuilder.createQuery(Long.class);
Root<Discount> root = criteriaQuery.from(entityManager.getMetamodel().entity(Discount.class));
criteriaQuery.select(criteriaBuilder.countDistinct(root));

DateTimeFormatter dateTimeFormatter=DateTimeFormat.forPattern("dd-MMM-yyyy hh:mm:ss aa");
DateTime firstDate = dateTimeFormatter.parseDateTime("01-Oct-2013 11:34:26 AM").withZone(DateTimeZone.UTC);
DateTime secondDate = dateTimeFormatter.parseDateTime("31-Oct-2013 09:22:23 PM").withZone(DateTimeZone.UTC);

criteriaQuery.where(criteriaBuilder.between(root.get(Discount_.discountStartDate), firstDate, secondDate));
Long rowCount = entityManager.createQuery(criteriaQuery).getSingleResult();

firstDatesecondDate 这两个参数将依次是动态的。

如何重写此查询,以便比较不包括 SQL 查询中要委托给 MySQL 的时间部分。

实体Discount中的discount_start_date列指定如下。

@Column(name = "discount_start_date")
@Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
private DateTime discountStartDate;

【问题讨论】:

  • 真的需要委托给mysql吗?如果没有,您可以在 DateTime.withMillisOfDay(0)DateTime.withMillisOfDay(0).plusDays(1).plusMillis(-1) 之间使用
  • @samlewis 比withMillisOfDay 更好的是withTimeAtStartOfDay。并非每个时区的每一天都有午夜,因此最好避免以午夜为导向的数学。 “withTimeAtStartOfDay”被添加到Joda-Time 以处理夏令时或其他可能改变一天开始的问题。
  • withMillisOfDay(0)withTimeAtStartOfDay() 都生成类似 [2013-10-02 05:30:00.0, 2013-11-01 05:29:59.999] 的日期。实际上,应该完全避免时间部分。这可能吗?它应该对应于这个 MySQL 查询 - select count(*) as cnt from discount where date(discount_start_date) between '2013-10-02' and '2013-11-01'

标签: mysql datetime jpa criteria jodatime


【解决方案1】:

看来你工作太辛苦了。

(a) 显然,MySQL 提供了一个DATE() 函数来提取日期的日期部分—— 时间场。 (我是Postgres 的人,不了解 MySQL。)您可以使用该函数调用作为查询的一部分来寻求一种方法。但是我猜如果您在执行 SQL 查询之前先通过在 Java 中使用 Joda-Time 计算来获得开始和停止时间,那么性能会更快,如下所示。

(b) 为什么不用一个简单的 SQL 查询,一个两个条件 SELECT 来做到这一点?

在伪代码中:

Find Discount records that go into effect from the moment this month starts up until the moment the next month starts.

使用 Java 和 Joda-Time 为您提供开始和停止值。

org.joda.time.DateTime startOfThisMonth = new org.joda.time.DateTime().dayOfMonth().withMinimumValue().withTimeAtStartOfDay();
org.joda.time.DateTime startofNextMonth = startOfThisMonth.plusMonths( 1 ).dayOfMonth().withMinimumValue().withTimeAtStartOfDay();

注意:以上代码使用默认时区。您应该在构造函数中指定一个时区。

MySql 似乎缺乏对时区等的复杂时间-日期处理。所以我想你会将那些带时区的 DateTime 对象转换为 UTC。

org.joda.time.DateTime startOfThisMonthInUtc = startOfThisMonth.toDateTime( org.joda.time.DateTimeZone.UTC );
org.joda.time.DateTime startofNextMonthInUtc = startofNextMonth.toDateTime( org.joda.time.DateTimeZone.UTC );

然后按照您的操作获取 MySQL 的日期时间值。

然后形成一个看起来像这样的查询......(注意使用&gt;= 与不带等号的&lt;。)

SELECT title_, amount_, start_date_ 
FROM discount_
WHERE discount_.start_datetime_ >= startOfThisMonthFromJodaTime
AND discount_.start_datetime_ < startOfNextMonthFromJodaTime
;

使用日期和时间时,通常最好使用一天中的第一刻、每月第一天的第一刻等,而不是尝试找到最后一刻或结束时间。所以我的查询是基于查找值上升到但不包括我感兴趣的时间范围之后的那一刻的行的想法。

【讨论】:

    猜你喜欢
    • 2015-07-11
    • 2011-05-23
    • 1970-01-01
    • 2010-12-23
    • 1970-01-01
    • 2013-10-20
    • 2011-01-23
    • 1970-01-01
    • 2014-03-24
    相关资源
    最近更新 更多