背景
问题描述
使用了“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
相关配置
数据库配置:
application.properties配置:
# 时区
spring.jackson.time-zone=Asia/Shanghai
实体字段:
原因探究
项目启动阶段
找到ConnectionImpl类源码:
1.从数据库中获取相关的配置,包含时区等信息
// 查询数据库相关配置
this.session.loadServerVariables(this.getConnectionMutex(),
this.dbmd.getDriverVersion());
// 初始化时区等信息
this.session.getProtocol().initServerSession();
-
存入session中
NativeSession类中loadServerVariables方法
-
初始化默认时区
NativeProtocol类中的configureTimezone方法
这里debug看到拿到的是正确的:
说明不是数据库时区的问题,继续往下找
4. 我们打断点跟代码发现,初始化默认值的时候,调用了无参构造方法,默认给赋值了DateTimeZone.UTC
调用接口阶段
-
跟踪发现,前面启动阶段,给TimestampColumnDateTimeMapper对象给了默认值
看到getHibernateType()方法中databaseZone不为空,故初始化了一个Calendar对象
-
最终调用了setTimesTamp方法,由图可以看出当targetCalendar为空的情况,才使用数据库连时区
TimeUtil.java的getSimpleDateFormat方法实现如下:
解决方案
方案一
给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
这样在最终设置时区的时候,就会默认使用数据库提供的时区,否则则会使用默认时区,是不对的。
类似问题其他解决方案
可能受限于版本不同,会有不同的表现
数据库时区使用的默认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"));
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 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)