MySQL日期更改为JPA保存后的昨天日期
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL日期更改为JPA保存后的昨天日期相关的知识,希望对你有一定的参考价值。
I have encountered a problem with a date field/database column while using Spring's JpaRepository to save an entity to mysql.
该实体具有LocalDate字段。使用LocalDate.now()进行测试时,遇到了返回Date字段的问题:
- 首先保存返回的对象的日期是正确的。
- 从MySQL数据库返回对象时,日期是前一天
例:
预计:2019-01-29
实际:2019-01-28
我昨天试过了,结果如下:
预计:2019-01-28
实际:2019-01-27
也许类似于这个JPA Saving wrong date in MySQL database
Code
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)
我希望保存并返回后日期字段不会改变,但是日期会一直变为昨天保存优惠券之后。
我认为这是因为时区问题。区域设置日期不考虑您已知的时区。但在数据库中,我猜日期与时区相关联。 JPA / Hibernate层将LocaleDate转换为TimeStamp(默认情况下,它将在转换期间使用JVM时区)。运行应用程序的时区与数据库时区不同,因为这是不匹配的。
要确认此操作,请将正在运行的计算机的时区设置为UTC。
我在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&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&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>
现在日期是正确的。
谢谢你的帮助。如上所述和回答,这是一个时区问题。
解:
- 在MySQL
SET GLOBAL time_zone = '+02:00';";
中设置 - 从
?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。
Workaround
如上所述,在pom.xml中更改Spring Boot默认的mysql-connector-java
:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</>
</dependency>
并且属性文件中spring.datasource.driverClassName=com.mysql.jdbc.Driver
的驱动程序也返回了正确的日期。
以上是关于MySQL日期更改为JPA保存后的昨天日期的主要内容,如果未能解决你的问题,请参考以下文章
MYSQL 8 JPA 2.0 mysql 在 JPA 保存后将 1 秒添加到当前日期
带有 MySql DB 的 Spring Boot JPA - 映射日期以错误的日期结束(休息 1 天)