Mysql datetime在mysql 5.6版中变成了00:00:00

时间:2014-08-04 13:08:10

标签: java mysql

我们有一个基于java的程序,其中包含数十万行代码,这些代码在过去8到9年之前完美地运行在mysql 5.5之前。 客户安装了mysql 5.6.17,现在我们遇到了一个大问题:日期时间值变为00:00:00 以下是其中一个表格:如您所见,默认值为Null:

+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| Id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| Key1  | varchar(10) | NO   | MUL | NULL    |                |
| Key2  | varchar(25) | NO   |     | NULL    |                |
| Date  | datetime    | YES  |     | NULL    |                |
| Value | text        | NO   |     | NULL    |                |

我们在java-code中通过mysql-connector插入这样的内容:

String sql = "INSERT INTO DATA_SUNDRYMATRIX (Key1, Key2, Date, Value) VALUES(?,?,?,?)";
PreparedStatement p = c.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
p.setString(1, i.getKey1());
p.setString(2, i.getKey2());
p.setTimestamp(3, i.getDate()==null?null:new Timestamp(i.getDate().getTime()));
p.setString(4, i.getValue());
p.executeUpdate();

我可以在Preparedstatement上使用println系统在executeupdate之前查看sql,它看起来像这样:

com.mysql.jdbc.ServerPreparedStatement[1] - INSERT INTO DATA_SUNDRYMATRIX (Key1, Key2, Date, Value) VALUES('TEST2','','2014-08-04 14:46:15','')

但结果仍然是表格中的00:00:00。这是为什么?? 真正奇怪的是,如果我将插入查询复制/粘贴到mysql控制台,则正确设置日期时间值。 我试过没有运气升级到mysql-connector-java-5.1.31-bin.jar。 SELECT @@ sql_mode;给出NO_ENGINE_SUBSTITUTION 唉,由于存在大量遗留表和代码,我们无法将其设置为严格模式。 我不认为?zeroDateTimeBehavior = convertToNull可以提供帮助,因为我们不想要空值,我们想要实际值,并且它们以某种方式转换为0000-00-00,即使NULL是默认值表...(顺便说一句,更新也是如此)这个mysql-setup中究竟出了什么问题?

编辑: 我有更多信息。我现在用以下代码扩展了我的代码:

System.out.println(p);
p.executeUpdate();
SQLWarning warning = p.getWarnings();
while (warning != null){
    System.out.println("ToString(): "+warning.toString());
    System.out.println("ErrorCode: "+warning.getErrorCode());
    System.out.println("LocalizedMessage: "+warning.getLocalizedMessage());
    System.out.println("State: "+warning.getSQLState());
    warning = warning.getNextWarning();
}

输出结果为:

com.mysql.jdbc.ServerPreparedStatement[1] - INSERT INTO DATA_SUNDRYMATRIX (Key1, Key2, Date, Value) VALUES('TEST2','','2014-08-04 15:35:02','')
ToString(): java.sql.SQLWarning: Data truncated for column 'Date' at row 1
ErrorCode: 1265
LocalizedMessage: Data truncated for column 'Date' at row 1
State: 01000

所以我实际上得到了一个数据截断警告,即使我正确使用了准备好的setTimeStamp(就像我们尝试过的所有其他mysql版本一样),并且在执行它之前打印PreparedStatement对象时sql似乎没问题。为什么然后数据截断的原因?

EDIT#2 我被告知尝试使用setDate而不是setTimestamp来查看是否会出现相同的错误。它没有,但我不能使用setDate,因为我没有得到DATETIME的时间组件,只有日期。 正如我所说,准备好的PreparedStatement的System.out.println给出了" INSERT INTO DATA_SUNDRYMATRIX(Key1,Key2,Date,Value)VALUES(' TEST2','' ,' 2014-08-04 15:35:02','')"这是有效的SQL,并在将其复制到mysql控制台时无需警告和截断。我现在也通过以下代码直接尝试了这个sql:

String sql = "INSERT INTO DATA_SUNDRYMATRIX (Key1, Key2, Date, Value) VALUES('TEST2','','2014-08-04 15:35:02','')";
PreparedStatement p = c.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
p.executeUpdate();

这也给出了正确的结果,没有警告也没有截断。只有当我在PreparedStatement上使用setTimestamp时才会发生截断。我尝试过jconnector的旧版本和新版本3.1.10,5.1.18和5.1.31。所有版本都可以使用我的旧mysql版本,并在mysql 5.6.17中产生这个特殊的错误。

EDIT#3 我注意到setTimestamp(1,new Timestamp(0));没有截断,但1970年1月的正确日期。因此我做了一些小测试代码:

//CREATE TABLE `test` (Id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, `Test` DATETIME NULL DEFAULT NULL);
String sql = "INSERT INTO Test (Id, Test) VALUES (?,?)";
PreparedStatement p = c.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
long current = System.currentTimeMillis();
for (long t = 0; t < current; t += (long)100000000){
    p.setLong(1,t);
    p.setTimestamp(2, new Timestamp(t));
    p.executeUpdate();
}
p.close();
p = c.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
p.setLong(1,current);
p.setTimestamp(2, new Timestamp(current));
p.executeUpdate();
p.close();

这只会给出一个不正确的结果,即最后一个。 SELECT * FROM test给出了以下最后一行:

| Id            | Test                |
+---------------+---------------------+
....
| 1407000000000 | 2014-08-02 19:20:00 |
| 1407100000000 | 2014-08-03 23:06:40 |
| 1407200000000 | 2014-08-05 02:53:20 |
| 1407224107714 | 0000-00-00 00:00:00 |

EDIT#4 似乎有一个错误,如果在设置setTimestamp时时间戳中存在亚秒级精度,那么整个日期时间将被破坏,而不是截断纳秒。如果我执行以下操作似乎可以正常工作:

p.setTimestamp(2, new Timestamp(current/1000*1000));

然后我会得到结果

| 1407225888000 | 2014-08-05 10:04:48 |

我很快就会把它写成一个解决方案。但我必须在代码中修复很多setTimestamp值。在5.6.17之后,任何人都可以确认这是否仍然是mysql数据库中的错误?

编辑#5(解决方案) theConstructors建议useServerPrepStmts = false解决了这个问题!并且是一个更好的解决方案,我自己的/ 1000 * 1000解决方案,我发现,因为我只需要更改连接字符串而不是每个setTimestamp调用(好吧,我已经在最后一小时完成了这个,但很高兴有“真实的”解决方案;-))

1 个答案:

答案 0 :(得分:1)

由于服务器端预备语句来自disabled by default Connector/J 5.0.5 on,因此很有可能与引入fractional second support in MySQL 5.6.4相关联而导致您的问题。

尝试将useServerPrepStmts=false添加到您的连接字符串(这也应该确保System.out.println确实打印执行的SQL而不仅仅是近似值。