为什么在使用JOOQ时我要回溯一次日期?

时间:2019-04-05 01:10:14

标签: mysql jooq

我们正在使用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记录的insert和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完全相同,这表明没有任何更改。

1 个答案:

答案 0 :(得分:1)

原来是a known bug in the MySQL JDBC driver。我的解决方法是还原到早于该问题的旧版本。