在MySQL中存储为DATE的LocalDate返回不同​​的结果

时间:2018-02-08 13:41:24

标签: java mysql date timezone

我有一个Java类,其字段定义为:

  @Column
  @NotNull
  private LocalDate availabilityDate;

映射到定义为:

的列
availability_date DATE NOT NULL

当我通过Spring Data比较保存到MySQL数据库之前和之后的日期时,我在JUnit中获得了不同的结果:

Expecting:
  <[InventoryAvailability(size=000, availabilityDate=2018-02-07, inventoryContextId=3847, quantity=15, lastChange=2018-02-08T14:32:24.770+01:00[Europe/Berlin]),
    InventoryAvailability(size=001, availabilityDate=2018-02-07, inventoryContextId=3847, quantity=15, lastChange=2018-02-08T14:32:26.337+01:00[Europe/Berlin])]>
to contain exactly in any order:
  <[InventoryAvailability(size=001, availabilityDate=2018-02-08, inventoryContextId=3847, quantity=15, lastChange=2018-02-08T14:32:26.337+01:00[Europe/Berlin]),
    InventoryAvailability(size=000, availabilityDate=2018-02-08, inventoryContextId=3847, quantity=15, lastChange=2018-02-08T14:32:24.770+01:00[Europe/Berlin])]>

这怎么可能?我以为只有TIMESTAMPS有时区。我想把它存储为毫秒长或作为UTC ZonedDateTime作为解决方法,但我怀疑我不明白这里有重要的事情。

2 个答案:

答案 0 :(得分:1)

我的回答是基于您对字段availabilityDate使用java.time.LocalDate的假设。我编写了一个简单的Spring Boot应用程序,它使用Spring Data和MySQL连接器(数据库版本为5.7.20-log)来重现你的情况。

测试DateHolderRepositoryTest失败,因为它不知道如何将LocalDate转换为数据库中的Date。然后我添加了对hibernate-java8的依赖,测试变为绿色(如https://www.thoughts-on-java.org/hibernate-5-date-and-time/ 中所述)。

所以我的假设是你的JUnit测试有问题或可能是你的实体的equals和hashCode方法实现连接(coz Hamcrest将使用equals下面来比较类,并从数据库类加载并且创建的类只是不同的实例,默认的Object equals方法将返回false)。您的问题中的availabilityDate = 2018-02-08显示没有时区的日期。

为了重现我的结果,我添加了我的测试应用程序的代码。我这是我的DateHolder类,它包含id和availabilityDate,并包含基于字段id的hashCode和equals实现:

package hello;

import java.time.LocalDate;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;

@Entity
public class DateHolder {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Integer id;

    @Column
    @NotNull
    private LocalDate availabilityDate;

    protected DateHolder() {}

    public DateHolder(LocalDate availabilityDate)
    {
        this.availabilityDate = availabilityDate;
    }

    @Override
    public String toString()
    {
        return String.format("DateHolder[id=%d, availabilityDate='%s']", id, availabilityDate);
    }

    public Integer getId()
    {
        return id;
    }

    public LocalDate getAvailabilityDate()
    {
        return availabilityDate;
    }

    @Override
    public int hashCode()
    {
        return id;
    }

    @Override
    public boolean equals(Object other)
    {
        if (other instanceof DateHolder)
        {
            return this.id == ((DateHolder)other).id;
        }
        else
        {
            return false;
        }
    }
}

这是我的JPA存储库:

package hello;
import org.springframework.data.repository.CrudRepository;

public interface DateHolderRepository extends CrudRepository<DateHolder, Long>
{
}

这是主要的Spring Boot应用程序类:

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args)
    {
        SpringApplication.run(Application.class);
    }
}

这是test,它创建2个DateHolders,将它们保存在数据库中,并将它们与数据库中的任何内容进行比较(它不会对帐户进行排序)。它还测试从数据库加载的第一个实体的toString()方法的返回值。

package hello;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;

import java.time.LocalDate;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class DateHolderRepositoryTest
{
    @Autowired
    private DateHolderRepository repository;

    @Test
    public void testFindByLastName()
    {
        DateHolder dateHolder1 = new DateHolder(LocalDate.parse("2018-02-07"));
        DateHolder dateHolder2 = new DateHolder(LocalDate.parse("2018-02-08"));

        repository.save(dateHolder1);
        repository.save(dateHolder2);

        Iterable<DateHolder> dateHolders = repository.findAll();
        assertThat(dateHolders, containsInAnyOrder(dateHolder2, dateHolder1));

        assertThat(dateHolders.iterator().next().toString(), is("DateHolder[id=1, availabilityDate='2018-02-07']"));
    }
}

答案 1 :(得分:0)

只需为您的MySQL实例配置适当的时区即可。如果您在容器上运行MySQL,则可能会得到错误的时区,因此您会遇到这种奇怪的datetime值。

例如,如果您的JVM使用<script> $('#loginAlert').hide(); const urlParams = new URLSearchParams(window.location.search); const myParam = urlParams.get('error'); if(myParam == "Incorrect_Credential") { $('#loginAlert').show(); } </script> 时区,则可以将America/Sao_Paulo发送到MySQL配置。这样,您将确保JVM和MySQL处于同一时区,而无需接触Java应用程序。