问题描述:
数据库存储的时间字段类型为:timestamp
数据库为:mysql的5.7.31
现象:两个服务查询同一个字段,得出不同的结果。
例:字段值为:2020-11-16 09:46:25,查询结果为:2020-11-16 23:46:25
前提:使用的mysql驱动,一个为2.8.0.21,一个为2.5.1.37
排查思路:
首先怀疑是Calendar初始化的时候,Timezone不一致,追踪代码发现,Timezone初始化均使用jdk的路径判断,均为 Asia/Shanghai。
// get the time zone ID from the system properties
String zoneID = AccessController.doPrivileged(new GetPropertyAction("user.timezone"));
// if the time zone ID is not set (yet), perform the
// platform to Java time zone ID mapping.
if (zoneID == null || zoneID.isEmpty()) {String javaHome = AccessController.doPrivileged( new GetPropertyAction("java.home")); try { zoneID = getSystemTimeZoneID(javaHome); if (zoneID == null) { zoneID = GMT_ID; } } catch (NullPointerException e) { zoneID = GMT_ID; }
}
/**
- Gets the platform defined TimeZone ID.
**/
private static native String getSystemTimeZoneID(String javaHome);
- Gets the platform defined TimeZone ID.
于是怀疑是数据库的设置有问题,使用sql查询show global variables like'%time_zone%';发现数据库配置果然有误,但是不应该两个程序查到不同的结果。
mysql> show global variables like'%time_zone%'; Variable_name Value system_time_zone CST time_zone SYSTEM - rows in set (0.02 sec)
细细研究了下mysql驱动的源代码,发现了端倪。问题出现在ResultSetImpl.java类中。方法名为:getTimestap();
2.5:
public Timestamp getTimestamp(String columnName) throws SQLException {
return this.getTimestamp(this.findColumn(columnName));
}
public Timestamp getTimestamp(int columnIndex) throws SQLException {
return this.getTimestampInternal(columnIndex, (Calendar)null, this.getDefaultTimeZone(), false);
}
2.8:
public Timestamp getTimestamp(int columnIndex) throws SQLException {
try {this.checkRowPos(); this.checkColumnBounds(columnIndex); return (Timestamp)this.thisRow.getValue(columnIndex - 1, this.defaultTimestampValueFactory);
} catch (CJException var3) {
throw SQLExceptionsMapping.translateException(var3, this.getExceptionInterceptor());
}
}
this.defaultTimestampValueFactory = new SqlTimestampValueFactory(pset, (Calendar)null, this.session.getServerSession().getServerTimeZone());
mysql> SELECT @@GLOBAL.time_zone, @@SESSION.time_zone; | |
---|---|
@@GLOBAL.time_zone | @@SESSION.time_zone |
SYSTEM | SYSTEM |
1 row in set (0.01 sec)
这就很明显了,2.5的默认使用的是服务器的Timezone,而2.8则使用session的Timezone,从而导致查询结果不一致。
结论:
使用数据库使用timestamp字段时,需要注意规定timezone,避免使用系统默认,而造成不同驱动下取值不同的问题。
方式(二选一即可):
- (建议)在数据库连接url中增加:serverTimezone=Asia/Shanghai
- 修改数据库的session:set global time_zone="+8:00"