随笔 - 6,  文章 - 3,  评论 - 0,  阅读 - 5419

背景

问题描述

使用了“rw.persist(newInstance);”方法,保存实体对象到数据库中,发现有8小时的时差

项目相关框架

  • Spring Boot 2.2.4
  • HikariCp 3.4.2
  • joda-time 2.10.5
  • mysql-connector-java 8.0.19
  • usertype.core 6.0.1.GA
  • mysql 5.7.22

相关配置

数据库配置:
image

application.properties配置:

# 时区
spring.jackson.time-zone=Asia/Shanghai

实体字段:
image

原因探究

项目启动阶段

找到ConnectionImpl类源码:
1.从数据库中获取相关的配置,包含时区等信息
image

// 查询数据库相关配置
this.session.loadServerVariables(this.getConnectionMutex(), 
this.dbmd.getDriverVersion());
// 初始化时区等信息
this.session.getProtocol().initServerSession();
  1. 存入session中
    NativeSession类中loadServerVariables方法
    image

  2. 初始化默认时区
    NativeProtocol类中的configureTimezone方法
    image
    image

这里debug看到拿到的是正确的:
image

说明不是数据库时区的问题,继续往下找
4. 我们打断点跟代码发现,初始化默认值的时候,调用了无参构造方法,默认给赋值了DateTimeZone.UTC
image

调用接口阶段

  1. 跟踪发现,前面启动阶段,给TimestampColumnDateTimeMapper对象给了默认值
    看到getHibernateType()方法中databaseZone不为空,故初始化了一个Calendar对象
    image

  2. 最终调用了setTimesTamp方法,由图可以看出当targetCalendar为空的情况,才使用数据库连时区
    image

TimeUtil.java的getSimpleDateFormat方法实现如下:
image

解决方案

方案一

给Bean添加注解 parameters = {       
@Parameter(name = "databaseZone", value = "jvm"),       
@Parameter(name = "javaZone", value = "jvm")}
例如:

/** * 过期时间 */
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
@Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime", parameters = {        
        @Parameter(name = "databaseZone", value = "jvm"),        
        @Parameter(name = "javaZone", value = "jvm")})
@Column(name = "expired_date")
@DateTimeFormat(pattern = "yyyy-MM-dd") 
private DateTime expiredDate;

方案二

原理

生成abstractParameterizedUserType对象的时候,指定了databaseZone="jvm",则置空databaseZone
image

这样在最终设置时区的时候,就会默认使用数据库提供的时区,否则则会使用默认时区,是不对的。

类似问题其他解决方案

可能受限于版本不同,会有不同的表现

数据库时区使用的默认System

mysql>show variables like '%time_zone%';
+------------------+--------+ 
| Variable_name | Value | 
+------------------+--------+ 
| system_time_zone | CST | 
| time_zone | SYSTEM | 
+------------------+--------+ 
2 rows in set (0.00 sec)

这种情况会导致拿到的时区存在问题,但不同版本情况不一样,有的版本会有问题,有的不会,建议统一修改成合适的时区

例如:

mysql> set global time_zone = 'Asia/Shanghai'; 
Query OK, 0 rows affected (0.00 sec) 

# 针对当前session
mysql> set time_zone = 'Asia/Shanghai; 
Query OK, 0 rows affected (0.00 sec)

# 刷新权限表,立即生效
mysql> flush privileges;
Query OK, 0 rows affected (0.36 sec)

mysql> show variables like '%time_zone%';
+------------------+---------------+
| Variable_name    | Value         |
+------------------+---------------+
| system_time_zone | CST           |
| time_zone        | Asia/Shanghai |
+------------------+---------------+
2 rows in set (0.30 sec)


数据库datetime时间,在前台展示不正常

解决方法一

aplication.propeties中添加

# 时区
spring.jackson.time-zone=Asia/Shanghai

解决方法二

数据库连接url后添加:

serverTimezone=Asia/Shanghai

例如:

rw.datasource.jdbc-url=jdbc:mysql://xxxxxx:3306/dbname?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai

解决方法三

@PostConstructvoid setDefaultTimezone() {
 TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
} 

用Jackson对Json中的日期类型进行反序列化时,出现了日期错误的问题

参考

方法一

加注解:timezone="GMT+8"

  @NotNull
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
  @JsonProperty("start_time")
  private Date startTime;

方法二

配置个bean

  @Bean
  public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() {
        return jacksonObjectMapperBuilder ->
                jacksonObjectMapperBuilder.timeZone(TimeZone.getTimeZone("GMT+8"));
    }
posted on   名泉  阅读(97)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示