MrRobot5 生也有涯,知也无涯

Mysql driver zeroDateTimeBehavior 实现原理

2022-11-10

使用 Mysql driver 遇到的问题

java.sql.SQLException: Value '0000-00-00 00:00:00' can not be represented as java.sql.Timestamp
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:861)
    at com.mysql.jdbc.ResultSetRow.getTimestampFast(ResultSetRow.java:947)
    at com.mysql.jdbc.ResultSetImpl.getTimestampInternal(ResultSetImpl.java:5921)

问题解决

jdbcUrl 声明 zeroDateTimeBehavior=convertToNull即可。

jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=true&zeroDateTimeBehavior=convertToNull

Code Insight

根据上述的异常日志,可以直接找到异常的出处。

/**
 * @see com.mysql.jdbc.ResultSetRow#getTimestampFast
 */
protected Timestamp getTimestampFast(...) throws SQLException {

    try {
        // 标识是否 '0000-00-00 00:00:00'
        boolean allZeroTimestamp = true;
        // 标识是否为时间格式, 时间是允许 '00:00:00'
        boolean onlyTimePresent = false;
        // check 数据格式
        for (int i = 0; i < length; i++) {
            byte b = timestampAsBytes[offset + i];

            if (b == ' ' || b == '-' || b == '/') {
                onlyTimePresent = false;
            }

            if (b != '0' && b != ' ' && b != ':' && b != '-' && b != '/' && b != '.') {
                allZeroTimestamp = false;

                break;
            }
        }

        if (!onlyTimePresent && allZeroTimestamp) {
            if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL.equals(conn.getZeroDateTimeBehavior())) {
                // 如果连接指定 convertToNull,遇到 allZeroTimestamp 直接返回 null
                return null;
            } else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION.equals(conn.getZeroDateTimeBehavior())) {
                // 默认配置 exception, 遇到 allZeroTimestamp 就会抛出上述的异常
                throw SQLError.createSQLException("Value '" + StringUtils.toString(timestampAsBytes) + "' can not be represented as java.sql.Timestamp",
                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor);
            }

            // We're left with the case of 'round' to a date Java _can_ represent, which is '0001-01-01'.
            return rs.fastTimestampCreate(null, 1, 1, 1, 0, 0, 0, 0);
        }
    }
}

Mysql JDBC Connection 配置

连接所有的配置是以 com.mysql.jdbc.ConnectionPropertiesImpl.ConnectionProperty 体现的。包括 allowableValues defaultValue sinceVersion 关键信息。

private StringConnectionProperty zeroDateTimeBehavior = new StringConnectionProperty("zeroDateTimeBehavior", ZERO_DATETIME_BEHAVIOR_EXCEPTION,
            new String[] { ZERO_DATETIME_BEHAVIOR_EXCEPTION, ZERO_DATETIME_BEHAVIOR_ROUND, ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL },
            Messages.getString("ConnectionProperties.zeroDateTimeBehavior",
                    new Object[] { ZERO_DATETIME_BEHAVIOR_EXCEPTION, ZERO_DATETIME_BEHAVIOR_ROUND, ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL }),
            "3.1.4", MISC_CATEGORY, Integer.MIN_VALUE);

总结

mysql driver 对于异常日期数据的处理,可以借鉴到应用程序的开发中。

首先,需要考虑到异常数据对程序的影响,如果无法继续,需要抛出异常;

然后,针对异常数据,提供声明容错的的方案;提升系统稳定性。


Content