Doctrine,MySQL sql_mode =“TRADITIONAL”和时间戳字段

时间:2017-04-24 07:35:56

标签: php mysql symfony doctrine-orm doctrine

从MySQL 5.6.6开始,时间戳列的某些默认行为会发生变化:

https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_explicit_defaults_for_timestamp

因为在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()实例设置为该属性。这种方式导致了样板代码的增长,并且在构造函数中生成的日期和实际插入的行时间戳由于繁重的查询,事务或侦听器而在几秒钟内会有所不同的情况下不太好。
  • 在实体中使用Lifecycle回调。与以前相同,但增加了更多的复杂性。
  • 使用Column密码使用default annodation选项。看起来,设置CURRENT_TIMESTAMPNOW()之类的东西是不可能的,因为这个默认值不是作为表达式计算的,而是作为普通值:https://github.com/doctrine/doctrine2/issues/6346
  • 使用https://github.com/Atlantic18/DoctrineExtensions但这只是添加了处理每个实体注释的侦听器。由于性能原因不太好。
  • 使用像setTsField()这样的显式setter调用,但这是我想摆脱的东西。

所有这些方法都不理想。而且,在我看来,最好是告诉Doctrine生成正确的查询而不添加一些字段。

0 个答案:

没有答案