ZStack的SQL相關(guān)內(nèi)容踩坑一覽

1. org.zstack.core.db.SQL存在的問題

String sql = "SELECT snapshot.uuid" +
                " FROM BackupDBSnapshotVO AS snapshot" +
                " LEFT JOIN" +
                " (SELECT volumeSnapshotUuid, COUNT(volumeUuid) AS copyCount" +
                " FROM VolumeSnapshotCloneVO" +
                " GROUP BY volumeSnapshotUuid) AS clone" +
                " ON snapshot.volumeSnapshotUuid = clone.volumeSnapshotUuid" +
                " WHERE snapshot.importance = :importance" +
                " AND snapshot.backupDBUuid = :backupDBUuid" +
                " AND clone.copyCount is null" +
                " ORDER BY snapshot.backupDate LIMIT 1";
        List<String> auschwitz = SQL.New(sql)
                .param("importance", BackupDBSnapshotImportance.Normal.toString())
                .param("backupDBUuid", this.getTargetResourceUuid())
                .list();

以上是利用SQL類來執(zhí)行一個比較復(fù)雜的SQL語句, 該語句包含了函數(shù), 子查詢, 分組, 排序, 左外連接. 實際執(zhí)行的時候會報錯. 大抵意思是"我Hibernate就是餓死, 死外邊, 從這里跳下去, 也不認(rèn)你這個語法!"
平時寫寫SELECT FROM這樣簡單的SQL語句的時候, SQL類君還是比較正常的, 那么這次抽風(fēng)的問題是在哪呢?
直接將報錯原因丟去百度, 看到一句"JPQL語句和SQL原生語句有些不同, 復(fù)雜的語法會導(dǎo)致Hibernate無法解析", 根本原因get.
下一步查看源代碼, 在SQL類的構(gòu)造函數(shù)中看到這玩意:

    private SQL(String sql) {
        this.sql = sql;
        query = dbf.getEntityManager().createQuery(this.sql);
    }

點擊createQuery進(jìn)去一探究竟.

/**
     * Create an instance of <code>Query</code> for executing a
     * Java Persistence query language statement.
     * @param qlString a Java Persistence query string
     * @return the new query instance
     * @throws IllegalArgumentException if the query string is
     * found to be invalid
     */
    public Query createQuery(String qlString);

根據(jù)介紹可以得知該方法是創(chuàng)建一個JPQL語句的查詢實例, 那么不難想到這周圍肯定會有創(chuàng)建原生SQL語句的查詢實例, 果然:

    /**
     * Create an instance of <code>Query</code> for executing
     * a native SQL statement, e.g., for update or delete.
     * @param sqlString a native SQL query string
     * @return the new query instance
     */
    public Query createNativeQuery(String sqlString);

由此, 根據(jù)此方法對一開始的查詢語句進(jìn)行改造:

List<String> auschwitz = dbf.getEntityManager().createNativeQuery(sql)
                .setParameter("importance", BackupDBSnapshotImportance.Normal.toString())
                .setParameter("backupDBUuid", this.getTargetResourceUuid())
                .getResultList();

Hibernate: "真香".

2. *.sql文件中Timestamp類型隱藏的坑點

由于ZStack所應(yīng)用的數(shù)據(jù)庫依然是5.5.56版本的MariaDB(當(dāng)前穩(wěn)定版本是10.3.9), 會有很多潛在的問題.

2.1 建表報錯

CREATE TABLE `zstack`.`CornHubVO` (
 `uuid` varchar(32) NOT NULL UNIQUE COMMENT 'uuid',
 `oldestTime` timestamp,
 `latestTime` timestamp,
 `lastOpDate` timestamp ON UPDATE CURRENT_TIMESTAMP,
 `createDate` timestamp,
  PRIMARY KEY (`uuid`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

上表是一個虛擬的VO, 只留有必要的字段. 這種表創(chuàng)建語句乍看沒有問題, 實際在進(jìn)行測試的時候:

SQL State : HY000
Error Code : 1293
Message : Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause
Location : /root/zstack/conf/tools/flyway-3.2.1/sql/V2.3.1.1.1__schema.sql (/root/zstack/conf/tools/flyway-3.2.1/sql/V2.3.1.1.1__schema.sql)
Line : 1

Surprise? 為什么我們只有一個Timestamp設(shè)定了CURRENT_TIMESTAMP, 卻依然報錯? 這其實是Mysql 5.7版本前的一個Bug.
當(dāng)同時滿足:

  1. 表中有1個以上的Timestamp字段,
  2. 其中一個Timestamp字段X設(shè)定了DEFAULT CURRENT_TIMESTAMP或ON UPDATE CURRENT_TIMESTAMP
  3. X字段之前有別的Timestamp

三種條件時, 就會觸發(fā)該Bug.
由于MariaDB 10 才對應(yīng)到Mysql 5.6, 因此在該版本中, 這個Bug顯然是存在的.
介于此, 在不升級版本的情況下, 解決辦法有兩種:

  1. 給X以外的所有Timestamp設(shè)定默認(rèn)值, 例如:
CREATE TABLE `zstack`.`CornHubVO` (
 `uuid` varchar(32) NOT NULL UNIQUE COMMENT 'uuid',
 `oldestTime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
 `latestTime` timestamp NOT NULL DEFAULT  '0000-00-00 00:00:00',
 `lastOpDate` timestamp ON UPDATE CURRENT_TIMESTAMP,
 `createDate` timestamp,
  PRIMARY KEY (`uuid`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  1. 將X以外所有的Timestamp置于X的后面:
 `uuid` varchar(32) NOT NULL UNIQUE COMMENT 'uuid',
 `lastOpDate` timestamp ON UPDATE CURRENT_TIMESTAMP,
 `createDate` timestamp,
 `oldestArchiveTime` timestamp,
 `latestArchiveTime` timestamp,
  PRIMARY KEY (`uuid`),

2.2 Timestamp的DEFAULT及(NOT) NULL關(guān)鍵字的特殊行為

CREATE TABLE `zstack`.`CornHubVO` (
 `uuid` varchar(32) NOT NULL UNIQUE COMMENT 'uuid',
 `oldestTime` timestamp NULL DEFAULT '0000-00-00 00:00:00',
 `latestTime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
 `lastOpDate` timestamp ON UPDATE CURRENT_TIMESTAMP,
 `createDate` timestamp,
  PRIMARY KEY (`uuid`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

oldestTime設(shè)置為NULL, latestTime設(shè)置為NOT NULL, 兩個字段都設(shè)置了默認(rèn)值
實際插入數(shù)據(jù)(所有的時間戳字段均未主動賦值)后表中數(shù)據(jù)如下:

uuid: 746a876edb5f45b3baa3bdf615061393
oldestTime: NULL
latestTime: 2018-08-23 21:04:37
lastOpDate: 2018-08-23 21:04:38
createDate: 2018-08-23 21:04:37
  1. 可以看出, oldestTime和latestTime默認(rèn)值本來應(yīng)該是1970-1-1 08:00:00這樣的時間, 實際上沒有卵用
  2. 設(shè)定為NULL的oldestTime沒有被賦值
  3. lastOpDate, createDate和設(shè)定為NOT NULL的latestTime一樣都被賦值為當(dāng)前時間了(雖然這個賦值行為也在意料之外)

總結(jié):

  1. Timestamp的Default關(guān)鍵字實際上沒有卵用
  2. 當(dāng)Timestamp設(shè)定為NULL時, 插入數(shù)據(jù)不賦值的情況下此列值為NULL
  3. 當(dāng)Timestamp設(shè)定為NOT NULL或不設(shè)定的時候, 插入數(shù)據(jù)不賦值的情況下, 此列會賦值為當(dāng)前的時間
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末爆阶,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子金句,更是在濱河造成了極大的恐慌瓶蚂,老刑警劉巖慈鸠,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狂窑,死亡現(xiàn)場離奇詭異走越,居然都是意外死亡杏糙,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進(jìn)店門衷快,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宙橱,“玉大人,你說我怎么就攤上這事蘸拔∈χ#” “怎么了?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵调窍,是天一觀的道長宝冕。 經(jīng)常有香客問我,道長邓萨,這世上最難降的妖魔是什么地梨? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮缔恳,結(jié)果婚禮上宝剖,老公的妹妹穿的比我還像新娘。我一直安慰自己歉甚,他們只是感情好万细,可當(dāng)我...
    茶點故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著纸泄,像睡著了一般赖钞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上聘裁,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天雪营,我揣著相機(jī)與錄音,去河邊找鬼衡便。 笑死献起,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的镣陕。 我是一名探鬼主播征唬,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼茁彭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起扶歪,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤理肺,失蹤者是張志新(化名)和其女友劉穎摄闸,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體妹萨,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡年枕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了乎完。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片熏兄。...
    茶點故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖树姨,靈堂內(nèi)的尸體忽然破棺而出摩桶,到底是詐尸還是另有隱情,我是刑警寧澤帽揪,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布硝清,位于F島的核電站,受9級特大地震影響转晰,放射性物質(zhì)發(fā)生泄漏芦拿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一查邢、第九天 我趴在偏房一處隱蔽的房頂上張望蔗崎。 院中可真熱鬧,春花似錦扰藕、人聲如沸缓苛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽他嫡。三九已至,卻和暖如春庐完,著一層夾襖步出監(jiān)牢的瞬間钢属,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工门躯, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留淆党,地道東北人。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓讶凉,卻偏偏與公主長得像染乌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子懂讯,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,689評論 2 354

推薦閱讀更多精彩內(nèi)容