今天,在做黑马的秒杀项目时发现了一个关于MySQL连接配置信息serverTimezone的问题。
问题
秒杀项目为了保证商品信息加载的速度,会用到缓存减少数据库访问的压力。应该在秒杀开始前将数据从MySQL中取出并存入Redis中。
秒杀商品实体类:
public class SeckillGoods implements Serializable{
@Column(name = "status")
private String status;//审核状态,0未审核,1审核通过,2审核不通过
@Column(name = "start_time")
private Date startTime;//开始时间
@Column(name = "end_time")
private Date endTime;//结束时间
@Column(name = "stock_count")
private Integer stockCount;//剩余库存数
//Getter Setter及无关属性已删除
}
秒杀商品表结构:
create table changgou_seckill.tb_seckill_goods
( status char null comment '审核状态,0未审核,1审核通过,2审核不通过',
start_time datetime null comment '开始时间',
end_time datetime null comment '结束时间',
# 省略无关属性..
) charset = utf8;
在跟着视频做访问MySQL数据库操作时发现即使使用相同的代码也无法取出当前时间段符合秒杀条件的商品信息。经过一段时间的debug,发现,视频中的“当前时间”是2019年,而我的”当前时间“是2020年,能查出来才怪。于是我修改代码将“当前时间”固定为2019年。
可是事情发展总是没有那么顺利,即使是使用修改后的代码进行查询,tk-MyBatis返回的数据仍然是空的。
看到这,我首先想到判断是否为SQL语句有误:
SELECT id,status,start_time,end_time FROM tb_seckill_goods WHERE (
status = '1'
and stock_count > 0
and start_time <= '2019-05-23 16:14:23'
and end_time >= '2019-05-23 16:14:23')
没问题。
接着,我怀疑是否是tk-MyBatis生成的SQL语句存在问题。
所以,我开启debug模式,一步步跟踪tk-MyBatis的源代码,最后在org.apache.ibatis.executor.statement.PreparedStatementHandler#query找到了生成的完整的SQL语句:
我一看,这和我固定的时间不太一样:
我又一番debug,终于在com.mysql.cj.ClientPreparedQueryBindings#setTimestamp找到了原因:
可以看到它通过com.mysql.cj.protocol.a.NativeServerSession#getDefaultTimeZone获取到时区,然后使用SimpleDateFormat完成转换:
和下面的例子差不多:
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
Date date = new Date();
System.out.println(date);
String format = simpleDateFormat.format(date);
System.out.println(format);
测试结果:
Mon Nov 02 16:21:57 CST 2020
2020-11-02 08:21:57
正好,差8个小时,不就是UTC+8,时区问题,小意思。
解决
再次检查视频提供的配置文件,发现他们的MySQL的连接信息:
jdbc:mysql://192.168.29.140:3306/changgou_seckill?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
噢,太简单了,直接改:
jdbc:mysql://192.168.29.140:3306/changgou_seckill?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC%2B8
大功告成