我们正在使用jOOQ与包含该表的MySQL数据库通信:
CREATE TABLE daily_sessions
(
session_id INT AUTO_INCREMENT NOT NULL,
user_id VARCHAR(45) NULL,
day date NULL,
CONSTRAINT PK_DAILY_SESSIONS PRIMARY KEY (session_id)
);
我们已经启用了对JSR-310类型的支持,因此我们在Java / Kotlin端使用LocalDate
来映射它。
我们看到的是day
字段以一天的偏移量被检索。 jOOQ记录的插入和select语句似乎表明它在绑定SQL参数时正在做正确的事情,但是当结果返回时它显示前一天:
2019-04-05 09:32:08 [Gax-20 ] DEBUG o.j.i.DefaultConnectionProvider - setting auto commit : false
2019-04-05 09:32:08 [Gax-20 ] DEBUG o.j.tools.LoggerListener - Executing query : select `daily_sessions`.`session_id`, `daily_sessions`.`user_id`, `daily_sessions`.`day` from `daily_sessions` where (`daily_sessions`.`user_id` = ? and `daily_sessions`.`day` = ?)
2019-04-05 09:32:08 [Gax-20 ] DEBUG o.j.tools.LoggerListener - -> with bind values : select `daily_sessions`.`session_id`, `daily_sessions`.`user_id`, `daily_sessions`.`day` from `daily_sessions` where (`daily_sessions`.`user_id` = '87a09702-0d6b-485c-895c-986f238e1d30' and `daily_sessions`.`day` = {d '2011-11-11'})
2019-04-05 09:32:08 [Gax-20 ] DEBUG o.j.tools.LoggerListener - Fetched result : +----------+------------------------------------+----------+
2019-04-05 09:32:08 [Gax-20 ] DEBUG o.j.tools.LoggerListener - : |session_id|user_id |day |
2019-04-05 09:32:08 [Gax-20 ] DEBUG o.j.tools.LoggerListener - : +----------+------------------------------------+----------+
2019-04-05 09:32:08 [Gax-20 ] DEBUG o.j.tools.LoggerListener - : | 13|87a09702-0d6b-485c-895c-986f238e1d30|2011-11-10|
2019-04-05 09:32:08 [Gax-20 ] DEBUG o.j.tools.LoggerListener - : +----------+------------------------------------+----------+
2019-04-05 09:32:08 [Gax-20 ] DEBUG o.j.tools.LoggerListener - Fetched row(s) : 1
2019-04-05 09:32:08 [Gax-20 ] DEBUG o.j.i.DefaultConnectionProvider - commit
2019-04-05 09:32:08 [Gax-20 ] DEBUG o.j.i.DefaultConnectionProvider - setting auto commit : true
2019-04-05 09:32:08 [Gax-20 ] DEBUG c.z.hikari.pool.PoolBase - HikariPool-1 - Reset (isolation) on connection com.mysql.cj.jdbc.ConnectionImpl@4af95547
注意2011-11-11
上的选择过滤器,但结果表显示2011-11-10
。
这是来自我本地机器(UTC + 10)上运行的测试,也是针对本地运行的标准mysql
Docker镜像。
尽管使用DATE
,但我认为我们遇到了一些时区问题,但我不能通过直接讨论JDBC来重现这个问题。我尝试在其他测试运行的相同设置中运行它:
@Test
fun testDateColumn() {
DriverManager.getConnection("jdbc:mysql://localhost:8890/rewards-test", "root", "").use { con ->
con.createStatement().use { stmt ->
stmt.execute("insert into `daily_sessions` (`user_id`, `day`) values ('a20add98-5a93-417f-a771-848757b2b1f8', {d '2011-11-11'})")
}
con.createStatement().use { stmt ->
stmt.executeQuery("select `daily_sessions`.`session_id`, `daily_sessions`.`user_id`, `daily_sessions`.`day` from `daily_sessions` where (`daily_sessions`.`user_id` = 'a20add98-5a93-417f-a771-848757b2b1f8' and `daily_sessions`.`day` = {d '2011-11-11'})").use { rs ->
while (rs.next()) {
println("${rs.getString(3)} - ${rs.getDate(3)}")
}
}
}
}
}
此代码生成预期输出。 SQL语句是jOOQ日志的直接副本。 jOOQ必须有其他东西,我不明白。
我是否需要以某种方式在jOOQ中配置时区?或者我错过了什么?
更新
正如Lukas在评论中提出的那样,我尝试将JDBC测试更改为使用预准备语句:
@Test
fun testDateColumn() {
DriverManager.getConnection("jdbc:mysql://localhost:8890/rewards-test", "root", "").use { con ->
con.prepareStatement("insert into `daily_sessions` (`user_id`, `day`) values (?, ?)").use { ps ->
ps.setString(1, "a20add98-5a93-417f-a771-848757b2b1f8")
ps.setDate(2, Date.valueOf(LocalDate.of(2011, 11, 11)))
ps.execute()
}
con.prepareStatement("select `daily_sessions`.`session_id`, `daily_sessions`.`user_id`, `daily_sessions`.`day` from `daily_sessions` where (`daily_sessions`.`user_id` = ? and `daily_sessions`.`day` = ?)").use { ps ->
ps.setString(1, "a20add98-5a93-417f-a771-848757b2b1f8")
ps.setDate(2, Date.valueOf(LocalDate.of(2011, 11, 11)))
ps.executeQuery().use { rs ->
while (rs.next()) {
println("${rs.getString(3)} - ${rs.getDate(3)}")
}
}
}
}
}
这确实产生了错误的结果,字符串和日期变体的输出是2011-11-10
。在JDBC中似乎有一些我不理解的东西。
更新2
上面的代码可以通过将默认的java.util.Calendar
实例作为第三个参数传递给setDate
方法来修复,即将上述两种情况替换为:
ps.setDate(2, Date.valueOf(LocalDate.of(2011, 11, 11)), Calendar.getInstance())
使用它我们看到预期的输出,而没有第三个参数的普通版本则没有。
JavaDoc of the setDate method说没有Calendar对象会导致VM的时区被使用,这似乎与the definition of Calendar.getInstance() specifies完全相同,这似乎表明什么都不应该改变。
原来是a known bug in the MySQL JDBC driver。我的修复方法是恢复到早于问题的旧版本。