JPA保存后,MySQL日期更改为昨天的日期

时间:2019-01-29 10:23:55

标签: java mysql hibernate spring-data-jpa

在使用Spring的JpaRepository将实体保存到MySQL时,日期字段/数据库列遇到问题。

该实体具有LocalDate字段。使用LocalDate.now()进行测试时,返回的Date字段遇到问题:

  1. 首先保存返回对象的日期是正确的。
  2. 从MySQL数据库返回对象时,日期是前一天

示例:

预计:2019-01-29

实际时间:2019-01-28

我昨天尝试过,结果是:

预计:2019-01-28

实际时间:2019-01-27

可能与此JPA Saving wrong date in MySQL database

类似

代码

application-mysql-test-connection.properties

spring.jpa.hibernate.ddl-auto=create

# Database url
spring.datasource.url=jdbc:mysql://localhost:3306/test_coupon_system?serverTimezone=UTC

spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver

# Test Database credentials
spring.datasource.username=springuser
spring.datasource.password=springuser

### showing values - for development
spring.jpa.show-sql=true

优惠券为简洁起见删除了构造函数和getter / setter

@Entity
public class Coupon {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id", length = 45)
    private long id;


    @Column(name = "name", unique = true, nullable = false, length = 45)
    private String name;

    @Column(name = "description", length = 100)
    private String description;

    @Column(name = "imageLocation")
    private String imageLocation;

    @Column(name = "startDate", length = 45)
    private LocalDate startDate;

    @Column(name = "endDate", length = 45)
    private LocalDate endDate;

    @ManyToOne(cascade = CascadeType.PERSIST)
    @JoinColumn(name = "company_id")
    private Company company;

    @ManyToMany(cascade = CascadeType.PERSIST,fetch = FetchType.EAGER)
    @JoinTable(
            name = "customer_coupon",
            joinColumns = @JoinColumn(name = "coupon_id"),
            inverseJoinColumns = @JoinColumn(name = "customer_id")
    )
    private List<Customer> customers;

CouponRepository

@Repository
public interface CouponRepository extends JpaRepository<Coupon, Long> {

    Coupon findByName(String name);
}

测试类(带有打印输出)

@SpringBootTest
@TestPropertySource(locations = {
        "classpath:application-mysql-test-connection.properties",
})
public class CouponDateIT {

    @Autowired
    private CouponRepository repository;

    @BeforeEach
    void setUp() {
        repository.deleteAll();
    }

    @Test
    void returnsLocalDate() {
        LocalDate testDate = LocalDate.now();

        DateTime.now();
        Coupon coupon = new Coupon();
        String couponName = "test1";
        coupon.setName(couponName);
        coupon.setStartDate(LocalDate.now());
        coupon.setEndDate(LocalDate.now());
        coupon = repository.saveAndFlush(coupon);

        System.out.println("===> test date " + testDate);
        System.out.println("===> coupon from db " + coupon);

        assertEquals(testDate, coupon.getStartDate());
        assertEquals(testDate, coupon.getEndDate());

        Coupon returned = repository.findByName(couponName);
        System.out.println("===> after save " + returned);
        assertEquals(testDate, returned.getStartDate());
        assertEquals(testDate, returned.getEndDate());
    }

测试失败

Hibernate: select coupon0_.id as id1_1_, coupon0_.company_id as company_7_1_, coupon0_.description as descript2_1_, coupon0_.end_date as end_date3_1_, coupon0_.image_location as image_lo4_1_, coupon0_.name as name5_1_, coupon0_.start_date as start_da6_1_ from coupon coupon0_
Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into coupon (company_id, description, end_date, image_location, name, start_date, id) values (?, ?, ?, ?, ?, ?, ?)
===> test date 2019-01-29
===> coupon from db Coupon{id=1, name='test1', description='null', imageLocation='null', startDate=2019-01-29, endDate=2019-01-29}
Hibernate: select coupon0_.id as id1_1_, coupon0_.company_id as company_7_1_, coupon0_.description as descript2_1_, coupon0_.end_date as end_date3_1_, coupon0_.image_location as image_lo4_1_, coupon0_.name as name5_1_, coupon0_.start_date as start_da6_1_ from coupon coupon0_ where coupon0_.name=?
Hibernate: select customers0_.coupon_id as coupon_i2_3_0_, customers0_.customer_id as customer1_3_0_, customer1_.id as id1_2_1_, customer1_.email as email2_2_1_, customer1_.name as name3_2_1_ from customer_coupon customers0_ inner join customer customer1_ on customers0_.customer_id=customer1_.id where customers0_.coupon_id=?
===> after save Coupon{id=1, name='test1', description='null', imageLocation='null', startDate=2019-01-28, endDate=2019-01-28}

java.lang.AssertionError: expected:<2019-01-29> but was:<2019-01-28>
Expected :2019-01-29
Actual   :2019-01-28

使用测试前登录

2019-01-29 11:26:07.986  INFO 6576 --- [           main] g.f.d.s.database.CouponDateIT            : No active profile set, falling back to default profiles: default
2019-01-29 11:26:09.235  INFO 6576 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data repositories in DEFAULT mode.
2019-01-29 11:26:09.308  INFO 6576 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 73ms. Found 3 repository interfaces.
2019-01-29 11:26:09.762  INFO 6576 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$$EnhancerBySpringCGLIB$$70fe81c1] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2019-01-29 11:26:09.981  INFO 6576 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2019-01-29 11:26:10.894  INFO 6576 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2019-01-29 11:26:10.957  INFO 6576 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [
    name: default
    ...]
2019-01-29 11:26:11.019  INFO 6576 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate Core {5.3.7.Final}
2019-01-29 11:26:11.019  INFO 6576 --- [           main] org.hibernate.cfg.Environment            : HHH000206: hibernate.properties not found
2019-01-29 11:26:11.191  INFO 6576 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.0.4.Final}
2019-01-29 11:26:11.341  INFO 6576 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
2019-01-29 11:26:11.591  WARN 6576 --- [           main] org.hibernate.mapping.RootClass          : HHH000038: Composite-id class does not override equals(): .entity.Customer_Coupon
2019-01-29 11:26:11.591  WARN 6576 --- [           main] org.hibernate.mapping.RootClass          : HHH000039: Composite-id class does not override hashCode(): .entity.Customer_Coupon
Hibernate: drop table if exists company
Hibernate: drop table if exists coupon
Hibernate: drop table if exists customer
Hibernate: drop table if exists customer_coupon
Hibernate: drop table if exists hibernate_sequence
Hibernate: create table company (id bigint not null, email varchar(45) not null, name varchar(45) not null, password varchar(45) not null, primary key (id)) engine=MyISAM
Hibernate: alter table company add constraint UK_bma9lv19ba3yjwf12a34xord3 unique (email)
Hibernate: alter table company add constraint UK_niu8sfil2gxywcru9ah3r4ec5 unique (name)
Hibernate: create table coupon (id bigint not null, description varchar(100), end_date date, image_location varchar(255), name varchar(45) not null, start_date date, company_id bigint, primary key (id)) engine=MyISAM
Hibernate: create table customer (id integer not null, email varchar(45) not null, name varchar(45) not null, primary key (id)) engine=MyISAM
Hibernate: create table customer_coupon (customer_id bigint not null, coupon_id bigint not null, primary key (customer_id, coupon_id)) engine=MyISAM
Hibernate: create table hibernate_sequence (next_val bigint) engine=MyISAM
Hibernate: insert into hibernate_sequence values ( 1 )
Hibernate: insert into hibernate_sequence values ( 1 )
Hibernate: insert into hibernate_sequence values ( 1 )
Hibernate: insert into hibernate_sequence values ( 1 )
Hibernate: insert into hibernate_sequence values ( 1 )
Hibernate: alter table coupon add constraint UK_dfikvnp7dxdfishfvpnlc0xc1 unique (name)
Hibernate: alter table customer add constraint UK_dwk6cx0afu8bs9o4t536v1j5v unique (email)
Hibernate: alter table customer add constraint UK_crkjmjk1oj8gb6j6t5kt7gcxm unique (name)
Hibernate: alter table coupon add constraint FKe2v6qnb3w90rekqrae28iiqhm foreign key (company_id) references company (id)
Hibernate: alter table customer_coupon add constraint FKppndqdpydmsumc9yqslm5hss4 foreign key (coupon_id) references coupon (id)
Hibernate: alter table customer_coupon add constraint FKi755t5tde9sf6nrp4rm2rnnmn foreign key (customer_id) references customer (id)
2019-01-29 11:26:12.515  INFO 6576 --- [           main] o.h.t.schema.internal.SchemaCreatorImpl  : HHH000476: Executing import script 'ScriptSourceInputFromUrl(/import.sql)'
2019-01-29 11:26:12.515  INFO 6576 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2019-01-29 11:26:12.812  INFO 6576 --- [           main] o.h.h.i.QueryTranslatorFactoryInitiator  : HHH000397: Using ASTQueryTranslatorFactory
2019-01-29 11:26:13.747  INFO 6576 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2019-01-29 11:26:13.793  WARN 6576 --- [           main] aWebConfiguration$JpaWebMvcConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2019-01-29 11:26:14.592  INFO 6576 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 2 endpoint(s) beneath base path '/actuator'
2019-01-29 11:26:14.655  INFO 6576 --- [           main] g.f.d.s.database.CouponDateIT            : Started CouponDateIT in 6.985 seconds (JVM running for 8.059)

我希望保存并返回日期后日期字段不会更改,但是保存优惠券后日期会一直更改为昨天的日期。

9 个答案:

答案 0 :(得分:4)

如果您的时区为

Europe/Warsaw

您可以设置:

spring.datasource.url=jdbc:mysql://localhost:3306/database?serverTimezone=Europe/Warsaw

代替此:

spring.datasource.url=jdbc:mysql://localhost:3306/database?serverTimezone=UTC

,但是您还可以在application.properties中添加一个条目:

spring.jpa.properties.hibernate.jdbc.time_zone=Europe/Warsaw

答案 1 :(得分:3)

此问题在Spring Boot 2.2.7中再次发生-根本原因似乎是mysql连接器版本8.0.20中的错误。

我将此报告给了oracle:https://bugs.mysql.com/bug.php?id=99487

暂时,我将mysql连接器降级为8.0.19,它可以正常工作:

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
        <version>8.0.19</version>
    </dependency>

答案 2 :(得分:1)

我在Wildfly 13中有同样的问题,这是我的数据源:

       <datasource jndi-name="java:/comp/env/isobit" pool-name="isobit-dev" enabled="true" use-java-context="true">
                <connection-url>jdbc:mysql://localhost:3306/isobit?zeroDateTimeBehavior=convertToNull&amp;useUnicode=true&amp;useJDBCCompliantTimezoneShift=true&amp;useLegacyDatetimeCode=false&amp;serverTimezone=UTC</connection-url>
                <driver>mysql-connector-java-8.0.13.jar</driver>

然后将驱动程序更改为mysql-connector-java-5.1.23-bin.jar并在我的standalone.xml中

             <datasource jndi-name="java:/comp/env/isobit" pool-name="isobit-dev" enabled="true" use-java-context="true">
                <connection-url>jdbc:mysql://localhost:3306/isobit?zeroDateTimeBehavior=convertToNull</connection-url>
                <driver>mysql-connector-java-5.1.23-bin.jar</driver>
                <security>
                    <user-name>root</user-name>
                    <password>root</password>
                </security>
            </datasource>

现在日期正确了。

答案 3 :(得分:1)

在我的国家/地区,时间是格林尼治标准时间+ 1,我遇到了同样的问题。

我首先要做的是serverTimezone=GMT+1,这给了我错误。

但是JDBC使用了所谓的“连接URL”,因此可以通过“%2B”转义“ +”,即

db.url=jdbc:mysql://localhost:3306/MyDB?useSSL=false&serverTimezone=GMT%2B1&useLegacyDatetimeCode=false

P.S:检查此Why useLegacyDatetimeCode=false以了解更多信息。

希望它可以帮助某人。如果这样的话,别忘了投票。

答案 4 :(得分:0)

我认为是因为时区问题。如您所知,“区域设置日期”不考虑时区。但是在数据库中,我想日期与时区相关。 JPA /休眠层会将LocaleDate转换为TimeStamp(默认情况下,转换期间将使用JVM时区)。由于存在不匹配,因此运行应用程序的时区与数据库时区不同。

要对此进行确认,请将正在运行的计算机的时区设置为UTC。

答案 5 :(得分:0)

感谢您的帮助。如上所述,这是一个时区问题

解决方案:

  1. 在MySQL SET GLOBAL time_zone = '+02:00';";中设置
  2. ?serverTimezone=UTC移除spring.datasource.url=jdbc:mysql://localhost:3306/schema_name

发现这些MySQL命令很有用: SELECT @@GLOBAL.time_zone, @@session.time_zone, @@system_time_zone;SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP) as GMT_TIME_DIFF;

Should MySQL have its timezone set to UTC?MySQL Documentaion中的其他信息。

解决方法

如上所述,在 pom.xml 中更改Spring Boot默认值mysql-connector-java

<dependency>
 <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</>
</dependency>

properties 文件中spring.datasource.driverClassName=com.mysql.jdbc.Driver的驱动程序也返回了正确的日期。

答案 6 :(得分:0)

将“日期”字段转换为TimeStamp将解决此问题,并确保通过Spring属性文件传递时区

spring.jpa.properties.hibernate.jdbc.time_zone = GMT + 10

答案 7 :(得分:0)

要解决此问题,我们只需将服务器时区设置为UTC,因为每个地方的时区都不同。

在application.properties文件中添加或更新以下行:

spring.datasource.url = jdbc:mysql:// localhost:3306 / test_coupon_system?serverTimezone = UTC

答案 8 :(得分:0)

时区不匹配在本地时区上运行的 Java 代码与在 UTC 时区中运行的 MySQL 之间。

根据您所在的时区,这一天差异仅发生在您的一天与 UTC 不同的特定时间。

将 MySQL 中的连接字符串更改为与您的计算机相同的时区。