从MySQL 5.6.6开始,时间戳列的某些默认行为会发生变化:
因为在MySQL 5.7中这种行为是默认的,所以我现在尝试整合它并遇到一些问题。在我的情况下,在NULL
字段中插入具有旧行为的TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
导致将时间戳字段的值设置为CURRENT_TIMESTAMP
非常重要,但在新行为中,这是不可能的。为什么呢?
考虑下一个设置:
MySQL:5.6.34-79.1 Percona Server
# /etc/my.cnf
[mysqld]
sql-mode="TRADITIONAL"
explicit_defaults_for_timestamp=1
在MySQL重启后检查此设置:
SHOW VARIABLES LIKE 'explicit_defaults_for_timestamp';
SELECT @@GLOBAL.sql_mode;
登录MySQL服务器并设置数据库和表:
DROP TABLE IF EXISTS `testTable`;
CREATE TABLE IF NOT EXISTS `testTable` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
)
ENGINE = InnoDB
DEFAULT CHARSET = `utf8`;
一些解释会发生什么的问题:
mysql> INSERT INTO `testTable` (`id`, `ts`) VALUES (NULL, NOW());
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM `testTable`;
+----+---------------------+
| id | ts |
+----+---------------------+
| 1 | 2017-04-24 06:49:45 |
+----+---------------------+
1 row in set (0.00 sec)
mysql> INSERT INTO `testTable` (`id`, `ts`) VALUES (NULL, NULL);
ERROR 1048 (23000): Column 'ts' cannot be null
mysql> SELECT * FROM `testTable`;
+----+---------------------+
| id | ts |
+----+---------------------+
| 1 | 2017-04-24 06:49:45 |
+----+---------------------+
1 row in set (0.00 sec)
mysql> INSERT INTO `testTable` (`id`) VALUES (NULL);
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM `testTable`;
+----+---------------------+
| id | ts |
+----+---------------------+
| 1 | 2017-04-24 06:49:45 |
| 2 | 2017-04-24 06:49:45 |
+----+---------------------+
2 rows in set (0.00 sec)
主要问题是第二个查询:INSERT INTO `testTable` (`id`, `ts`) VALUES (NULL, NULL);
。在旧行为中,此请求会导致将CURRENT_TIMESTAMP
值插入ts
字段。如果我将列定义从TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
更改为TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP
,则ts
字段的新行为值将按预期设置为NULL
而不是CURRENT_TIMESTAMP
。< / p>
要模拟旧行为,我应该使用TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP
列定义和第三个查询INSERT INTO `testTable` (`id`) VALUES (NULL);
,而不使用ts
字段的显式值。但是,在这种情况下,Doctrine使用null
作为ts
字段的值,如果它没有通过setter或其他方式设置明确,而Doctrine查询在这里看起来像第二个查询,但应该像第三个。
因此,问题是告诉Doctrine为timestamp列插入正确的默认值。这可以通过以下方式完成:
__construct
添加到每个实体,并将new \DateTime()
实例设置为该属性。这种方式导致了样板代码的增长,并且在构造函数中生成的日期和实际插入的行时间戳由于繁重的查询,事务或侦听器而在几秒钟内会有所不同的情况下不太好。Column
密码使用default
annodation选项。看起来,设置CURRENT_TIMESTAMP
或NOW()
之类的东西是不可能的,因为这个默认值不是作为表达式计算的,而是作为普通值:https://github.com/doctrine/doctrine2/issues/6346 setTsField()
这样的显式setter调用,但这是我想摆脱的东西。所有这些方法都不理想。而且,在我看来,最好是告诉Doctrine生成正确的查询而不添加一些字段。