MySQL 案例一则_JAVA

背景

对某业务数据库进行标准化,将其从a服务器迁移到b服务器并且纳入ZanDB系统,最大的变动是修改了my.cnf和目录配置。当前迁移完之后测试程序无异常,但是过了几天发现写入数据报错:

### The error occurred while setting parameters
### SQL: insert into XXX (cond_id, status, 
         update_time,create_time, template_id,
         name,bizline, bizcode, has_current,
         expression) 
    values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
### Cause: com.mysql.jdbc.exceptions.jdbc4.
           MySQLIntegrityConstraintViolationException: 
           Column 'update_time' cannot be null

而同样的sql在老的实例测试是无异常报错的。看到报错信息,拿着经验这个锤子,第一感觉是sql_mode配置的问题,但是对比发现两个实例的配置是一致的。于是一方面思考新老环境的其他配置参数,一方面谷歌 "MySQL timestamp NOT NULL插入NULL " ,发现该问题是由于参数 explicit_defaults_for_timestamp导致的。

了解explicit_defaults_for_timestamp

MySQL 5.6.6 引入该参数用于timestamp类型的字段处理null的方式。默认情况下该参数是关闭的。我们来看看该参数打开和关闭情况下对timestamp的影响 

该参数设置为OFF

  1. 在默认情况下,如果timestamp列没有显式的指明null属性,那么该列会被自动加上not null属性(而其他类型的列如果没有被显式的指定not null,那么是允许null值的),如果往这个列中插入null值,会自动的设置该列的值为current timestamp值

  2. 表中的第一个timestamp列,如果没有指定null属性或者没有指定默认值,也没有指定ON UPDATE语句。那么该列会自动被加上DEFAULT CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMESTAMP属性。

  3. 第一个timestamp列之后的其他的timestamp类型的列,如果既没有指定null属性,也没有显式指定默认值,那么该列会被自动加上not DEFAULT '0000-00-00 00:00:00'属性。如果insert语句中没有为该列指定值,那么该列中插入'0000-00-00 00:00:00',并且没有warning.

注意: 如果该参数在启动MySQL的时候设置为off,errorlog里面会有提示

[Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).

我们在explicit_defaults_for_timestamp=off的情况下,通过案例来理解上面的规则:

我们创建的表含有三个不带任何装饰的timestamp字段。

MySQL 案例一则_JAVA_02

从表结构来看,MySQL自动为第一个timestamp字段自动设置NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP属性,而后面的2个timestamp字段设置了 NOT NULL DEFAULT '0000-00-00 00:00:00'属性。我们继续测试插入null值的情形:

MySQL 案例一则_JAVA_03

从结果来看,如果向timestamp这个列中插入null值,系统会自动的设置该列的值为current timestamp值。也就是老的数据库实例可以插入null的值。接下来我们来了解explicit_defaults_for_timestamp=on 的情况。

该参数设置为ON

  1. 如果timestamp列没有显式的指定not null属性,那么默认的该列可以为null,此时向该列中插入null值时,会直接记录null,而不是current timestamp。

  2. 不会自动的为表中的第一个timestamp列加上DEFAULT CURRENT_TIMESTAMP 和ON UPDATE CURRENT_TIMESTAMP属性.

  3. 如果timestamp列被加上了not null属性,并且没有指定默认值。这时如果向表中插入记录,但是没有给该TIMESTAMP列指定值的时候,如果strict sql_mode被指定了,那么会直接报错。如果strict sql_mode没有被指定,那么会向该列中插入'0000-00-00 00:00:00'并且产生一个warning.

纸上来得终觉浅,据知此事要恭行,我们继续通过案例来理解explicit_defaults_for_timestamp=on对timestamp字段的影响。

案例一

我们创建的表含有三个不带任何属性的timestamp字段。

可以从表结构上看出,在参数开启的情况下MySQL默认会为参数添加 null default null属性,而且MySQL也没有为第一个timestamp字段设置该列为current timestamp值插入null值,测试如下:

MySQL 案例一则_JAVA_04

MySQL 案例一则_JAVA_05

可以发现向字段timestamp 字段写入null值,写入之后存储的就是null值,而不是当前的时间。

案例二

创建一个含有三个timestamp字段的表,其中一个字段显式声明为not null,然后初始化数据,发现报错 提示:t2 不能为null。也即本文开始提到的新的机器的报错情况。

MySQL 案例一则_JAVA_06

MySQL 案例一则_JAVA_05

总结

本文用生产案例解释了参数explicit_defaults_for_timestamp带来的影响。 从官方文档上的介绍来看timestamp为在将来的版本中废弃,本文介绍的explicit_defaults_for_timestamp也会被废弃。不过在这个之前大家还是要注意这个参数给timestamp字段带来的影响。