更新时间相差8个小时

下面的java代码在updateFill方法里面生成的modifiedTime时间是当前时间是正确的,为什么到service层testCommonFieldAutoUpdate方法里面去更新的时候modifiedTime就差8个小时呢?代码如下所示:

@Slf4j
@Component
public class MpMetaObjectHandler implements MetaObjectHandler {
    
    @Override
    public void insertFill(MetaObject metaObject) {
        //通用填充
        this.setFieldValByName("createdTime", LocalDateTime.now(), metaObject);
        //严格模式:根据更新策略填充
        this.strictInsertFill(metaObject,"modifiedTime",LocalDateTime::now,LocalDateTime.class);
        this.strictInsertFill(metaObject,"tenantNo",()-> HttpHeaderUtil.getHeaderValue("tenant-no"),String.class);

    }
    /**
     *  使用mp的更新类api时会回调
     *  update user set address=?,modified_time=? where id=?
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "modifiedTime", () ->
                Date.from(ZonedDateTime.now(ZoneId.of("Asia/Shanghai")).toInstant()), Date.class);
    }

    
    @Override
    public MetaObjectHandler strictFillStrategy(MetaObject metaObject, String fieldName, Supplier<?> fieldVal) {
        Object obj = fieldVal.get();
        if (Objects.nonNull(obj)) {
            metaObject.setValue(fieldName, obj);
        }
        return this;
    }
}

service层代码如下所示:
@Service
@Slf4j
public class UserRoadServiceImpl extends ServiceImpl<UserRoadMapper, UserDO> implements UserRoadService {
import cn.hutool.core.convert.Convert;
import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.project.mapper.UserRoadMapper;
import org.project.roadjava.ramble.test.bean.entry.UserDO;
import org.project.roadjava.ramble.test.bean.req.user.AddUserRequest;
import org.project.roadjava.ramble.test.bean.req.user.UpdateUserRequest;
import org.project.roadjava.ramble.test.ex.BizEx;
import org.project.roadjava.ramble.test.service.UserRoadService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
@Override
    @Transactional(rollbackFor = Exception.class)
    public void testCommonFieldAutoUpdate(UpdateUserRequest updateReq) {
        UserDO existedEntity = getById(updateReq.getId());
       
        if (existedEntity == null) {
            throw new BizEx("数据不存在");
        }
        existedEntity.setAddress(updateReq.getAddress());
        existedEntity.setGender(updateReq.getGender());
        updateById(existedEntity);
    }
}

你这个问题非常经典,也具有一定的代表性 —— 在 MetaObjectHandler 中设置的时间是正确的(东八区时间),但在 Service 层通过 updateById() 更新时,数据库中记录的 modifiedTime 却少了 8 小时。

我们来一步步分析问题的本质和根本原因。

✅ 现象总结
场景    时间值    是否正确
MetaObjectHandler.updateFill() 中生成时间    Date.from(ZonedDateTime.now(ZoneId.of("Asia/Shanghai")).toInstant())    ✅ 正确
数据库中的 modified_time 字段    存入的是 UTC 时间(比北京时间晚 8 小时)    ❌ 错误
Java 实体类字段类型    Date    ⚠️ 潜在问题
🧩 核心问题:java.util.Date 是一个“无时区”的包装器,但内部保存的是 UTC 时间戳
关键点:
new Date() 或 Date.from(...) 创建的对象,其本质是一个从 1970-01-01T00:00:00Z 开始的毫秒数(即 Unix 时间戳),它本身不包含任何时区信息。
当你在代码中打印 Date.toString() 时,它会根据 JVM 的默认时区格式化输出,所以看起来像是本地时间。
但是当这个 Date 被写入数据库时,JDBC 驱动、MyBatis、数据库连接池等组件可能会以不同的方式处理这个时间值,导致出现时区偏差。
🔍 为什么 MetaObjectHandler 中设置的是正确时间?
你的这段代码:

Java
深色版本
this.strictUpdateFill(metaObject, "modifiedTime", () ->
        Date.from(ZonedDateTime.now(ZoneId.of("Asia/Shanghai")).toInstant()), Date.class);
实际上是这样做的:

获取了当前东八区时间(ZonedDateTime.now(ZoneId.of("Asia/Shanghai")))
转换为 Instant(这是标准的 UTC 时间)
再转换为 Date(仍然是基于 UTC 时间戳)
此时,Date 对象表示的是一个 UTC 时间点。但由于你在日志或调试中看到的是类似 Tue Jun 04 15:30:00 CST 2025 这样的字符串,这其实是 Date.toString() 使用了 JVM 默认时区(可能是 Asia/Shanghai)进行展示,因此看起来是正确的。

💥 为什么 Service 层更新的时候却差了 8 小时?
当你执行如下代码时:

Java
深色版本
UserDO existedEntity = getById(updateReq.getId());
existedEntity.setAddress(updateReq.getAddress());
updateById(existedEntity);
此时:

MyBatis Plus 会将 existedEntity 中的 modifiedTime 字段(类型是 Date)直接传给 JDBC 驱动;
如果数据库字段是 DATETIME 类型,则 JDBC 驱动通常会将 Date 直接当作 UTC 时间 来处理(特别是 MySQL 8+ 的行为);
所以即使你存入的是 Date.from(ZonedDateTime.now(...).toInstant()),数据库最终可能认为这是一个 UTC 时间,而不是东八区时间;
最终显示出来就少了 8 小时。
✅ 解决方案
✅ 推荐做法一:统一使用 LocalDateTime + TypeHandler(推荐)
1. 修改实体类字段为 LocalDateTime
Java
深色版本
private LocalDateTime modifiedTime;
2. 在自动填充中使用 LocalDateTime::now
Java
深色版本
@Override
public void updateFill(MetaObject metaObject) {
    this.strictUpdateFill(metaObject, "modifiedTime", LocalDateTime::now, LocalDateTime.class);
}
3. 添加 TypeHandler 支持
确保你的项目中引入了以下依赖(用于支持 LocalDateTime 到数据库的映射):

Xml
深色版本
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-typehandlers-jsr310</artifactId>
    <version>3.5.3</version> <!-- 请根据你的 MP 版本调整 -->
</dependency>
MyBatis Plus 会自动识别并注册这些处理器。

4. 数据库字段类型应为 DATETIME 或 TIMESTAMP
MySQL 推荐使用 TIMESTAMP,它会自动存储为 UTC 并在查询时根据连接参数转为本地时间。

✅ 替代做法二:继续使用 Date,但修改 JDBC 连接配置指定时区
如果你坚持使用 Date 类型,可以在数据库连接字符串中显式指定时区。

示例(MySQL):
Properties
深色版本
jdbc:mysql://localhost:3306/your_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false
serverTimezone=GMT%2B8 表示服务器时间偏移量为 UTC+8,这样 JDBC 驱动就知道如何正确地把 Date 映射到数据库时间。
📌 总结对比
方案    类型    优点    缺点
✅ 使用 LocalDateTime + TypeHandler    推荐    语义清晰,避免时区混乱,Java 8 原生支持    需要引入额外依赖
✅ 使用 Date + JDBC 设置时区    可行    不改动现有代码结构    容易遗漏或配置错误
❌ 继续用 Date 且不改配置    ❌    无需改代码    极容易出现时区偏差
✅ 建议操作顺序
把 modifiedTime 字段改为 LocalDateTime 类型;
修改自动填充逻辑为 LocalDateTime::now;
引入 mybatis-plus-typehandlers-jsr310;
数据库字段保持为 DATETIME 或改为 TIMESTAMP;
(可选)数据库连接串加上 serverTimezone=GMT%2B8。
🧪 如何验证是否修复成功?
在 Debug 模式下查看 modifiedTime 字段是否是预期时间;
查看数据库记录是否与代码中一致;
日志中打印出 modifiedTime,确认输出格式和时区;
多台不同系统时区的机器测试,确认一致性。
如你能提供:

数据库字段定义(DATETIME / TIMESTAMP)
使用的数据库类型及版本(MySQL 8?PostgreSQL?)
MyBatis Plus 版本
数据库连接 URL 和驱动版本
我可以进一步帮你做更精准的建议。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/bicheng/82715.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows逆向工程提升之IMAGE_TLS_DIRECTORY

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​链接点击跳转博客主页 目录 TLS的作用 TLS的实现 静态 TLS​​ 动态 TLS​​ 内部实现 回调机制 TLS Directory 的结构 TLS的作用 TLS (Thread Local Storage) 是一种用于为多线程应用程序提供线程独立存储空…

云效流水线Flow使用记录

概述 最近在频繁使用阿里云云效的几款产品&#xff0c;如流水线。之前写过一篇&#xff0c;参考云效流水线缓存问题。 这篇文章来记录更多问题。 环境变量 不管是云效流水线Flow还是应用交付AppStack&#xff08;基于流水线&#xff0c;后文不再赘述&#xff09;&#xff0…

Android中获取控件尺寸进阶方案

在Android开发中,很多场景都需要获取控件(View)的宽高信息,比如动态布局调整、动画效果实现等。然而,直接在Activity的onCreate()中调用控件的getWidth()或getHeight()`方法,得到结果却是0,因为控件还没完成布局测量。 本文总结了几种获取控件大小的实用方法,并对各方…

android 输入系统

一、输入系统的核心角色与分层架构 Android 输入系统的本质是桥梁&#xff1a;一端连接硬件驱动产生的原始事件&#xff0c;另一端将事件精准派发给应用窗口。整个过程涉及三层架构和多个关键组件&#xff0c;可类比为 “快递分拣系统”&#xff1a; 1. 硬件与内核层&#xf…

ubuntu中,c和c+程序,预编译、编译、链接和运行命令

目录 安装编译器一.c编译运行&#xff08;粗暴简单&#xff09;1.编写 C 程序&#xff1a;2. 预处理&#xff1a;3.编译&#xff1a;4. 汇编&#xff1a;5. 链接&#xff1a;6.运行 二.c编译运行&#xff08;粗暴简单&#xff09;1.编写 C 程序&#xff1a;2.预处理&#xff1a…

【FastAPI】--2.进阶教程(一)

【FastAPI】--基础教程-CSDN博客 app.get和post的参数&#xff1a; 参数类型说明pathstr路由路径&#xff08;如 "/marks"&#xff09;&#xff0c;必填。response_modelType[T]定义响应数据的模型&#xff08;如 percent&#xff09;&#xff0c;会自动校验和序列…

KT6368A通过蓝牙芯片获取手机时间详细说明,对应串口指令举例

一、功能简介 KT6368A双模蓝牙芯片支持连接手机&#xff0c;获取手机的日期、时间信息&#xff0c;可以同步RTC时钟 1、无需安装任何app&#xff0c;直接使用系统蓝牙即可实现 2、同时它不影响音频蓝牙&#xff0c;还支持一些简单的AT指令进行操作 3、实现的方式&#xff1…

【平面波导外腔激光器专题系列】用于光纤传感的低噪声PLC外腔窄线宽激光器

----翻译自Mazin Alalusi等人的文章 摘要 高性价比的 1550 nm DWDM平面外腔 &#xff08;PLANEX&#xff09; 激光器是干涉测量、布里渊、LIDAR 和其他光传感应用的最佳选择。其线宽<3kHz、低相位/频率噪声和极低的RIN。 简介 高性能光纤分布式传感技术是在过去几年中开发…

企业微信内部网页开发流程笔记

背景 基于ai实现企微侧边栏和工作台快速问答小助&#xff0c;需要h5开发&#xff0c;因为流程不清楚摸索半天&#xff0c;所以记录一下 一、网页授权登录 1. 配置步骤 1.1 设置可信域名 登录企业微信管理后台 进入"应用管理" > 选择开发的具体应用 > “网…

WORD 转 PDF 工具:排版 / 图片 / 表格批量转换提升办公效率

各位办公小能手们&#xff0c;今天来聊聊文档工具里的WORD转PDF工具&#xff01;这玩意儿到底是干啥的呢&#xff1f;咱来好好说道说道。 先说核心功能。第一个就是格式转换&#xff0c;能把Word文档转换成PDF&#xff0c;不管是格式、排版&#xff0c;还是图片、表格啥的&…

从逆流监测到智慧用电:ADL200N-CT系列单相导轨表赋能家庭绿色能源

在新能源浪潮席卷全球的当下&#xff0c;阳台光伏以及家庭储能&#xff08;户储&#xff09;系统逐渐成为众多追求绿色生活、渴望实现能源自主家庭的新选择。它不仅能有效利用太阳能等清洁能源&#xff0c;还能在用电高峰时段为家庭提供稳定电力支持&#xff0c;降低用电成本。…

std::thread的说明与示例

源于通义千问 在 C 中&#xff0c;std::thread 支持传递多种类型的函数作为线程入口点。你可以传递普通函数、类的成员函数、Lambda 表达式、函数对象&#xff08;仿函数&#xff09;等。以下是详细的说明和示例。 1. 传递普通函数 普通函数是最简单的用法。 示例 #include…

【Day38】

DAY 38 Dataset和Dataloader类 对应5. 27作业 知识点回顾&#xff1a; Dataset类的__getitem__和__len__方法&#xff08;本质是python的特殊方法&#xff09;Dataloader类minist手写数据集的了解 作业&#xff1a;了解下cifar数据集&#xff0c;尝试获取其中一张图片 import …

RabbitMQ 集群与高可用方案设计(三)

五、高可用方案设计与实现 &#xff08;一&#xff09;负载均衡与代理 1. HAProxy 配置 HAProxy 是一款广泛应用的开源负载均衡器和代理服务器&#xff0c;它能够实现对 RabbitMQ 集群节点的负载均衡和健康检查&#xff0c;有效提高系统的可用性和性能。以下是使用 HAProxy …

机器学习第二十四讲:scikit-learn → 机器学习界的瑞士军刀

机器学习第二十四讲&#xff1a;scikit-learn → 机器学习界的瑞士军刀 资料取自《零基础学机器学习》。 查看总目录&#xff1a;学习大纲 关于DeepSeek本地部署指南可以看下我之前写的文章&#xff1a;DeepSeek R1本地与线上满血版部署&#xff1a;超详细手把手指南 Scikit-…

百度ocr的简单封装

百度ocr地址 以下代码为对百度ocr的简单封装,实际使用时推荐使用baidu-aip 百度通用ocr import base64 from enum import Enum, unique import requests import logging as logunique class OcrType(Enum):# 标准版STANDARD_BASIC "https://aip.baidubce.com/rest/2.0…

Ubuntu20.04 gr-gsm完整安装教程

gr-gsm完整安装教程 安装gnuradio3.8安装依赖项指定gnuradio源安装gnuradio 安装gr-gsm安装依赖项安装gr-gsm修改环境变量 安装成功 安装gnuradio3.8 安装依赖项 sudo apt install git cmake g libboost-all-dev libgmp-dev swig python3-numpy python3-mako python3-sphinx …

(自用)Java学习-5.15(模糊搜索,收藏,购物车)

1. 模糊搜索商品功能 前端实现&#xff1a; 通过解析URL参数&#xff08;如search联想&#xff09;获取搜索关键字&#xff0c;发送AJAX GET请求到后端接口/product/searchGoodsMessage。 动态渲染搜索结果&#xff1a;若结果非空&#xff0c;循环遍历返回的商品数据&#xff…

STM32 TIM 定时器深度剖析:结构、时基、中断与应用开发(超形象详解)

文章目录 定时器&#xff08;TIM&#xff09;定时器种类与分布定时器的基本结构时基单元时基单元基本结构计数器计数方向时基单元时钟来源计算寄存器预加载机制 自制延时函数获取单片机当前时间实现延迟函数初始化定时器3的时基单元配置中断编写中断响应函数测试延迟函数 定时器…

Java使用minio上传整个目录下的所有内容

目录 1、添加相关配置 2、添加依赖 3、实现方法 1️⃣基础版&#xff1a; 2️⃣优化版&#xff08;推荐使用&#xff09;&#xff1a; 3️⃣上传远程主机上的目录内容&#xff1a; 4️⃣直接上传远程主机中的目录内容 业务背景&#xff1a;需要需要minio进行上传指定目录下所有…