1 思路
H2數據遷移到mysql,網上查找了一圈,沒有現成的工具。
有幾種方法,不過都可能出現各種bug,需要調試:
- H2導出為csv文件然后導入mysql.
- H2導出為sql文件然后導入mysql的過程
- 使用轉換工具軟件,比如
Spectral Core
(https://www.fullconvert.com/howto/h2-to-mysql 沒試過) ,比如IDEA。
在使用第二種方法遷移數據成功后,我嘗試了idea,我覺得非常好用,只是測試了導出數據到.sql文件,比使用h2內置方法導出的.sql文件人性化多了,稍微修改就能兼容和導入到mysql。當然我也沒嘗試完整的導出導入,因為已經遷移完成了,沒必要了。
方法:選中要導出的表,右擊,選擇Dump Data to File(s) -- SQL Inserts
,參考如下截圖:
下一次我會使用IDEA來進行不同數據庫之間的數據遷移,原因如下:
- 兼容10多種數據庫。idea不僅可以連接mysql和h2,還能連接oracle等10多種數據庫。
- 導出的sql文件中的sql語法比較通用,和其他數據庫兼容性好。
- 一次可以導出一張表、多張表、所有表。
- 可以導出為一個文件和可以導出為多個文件,一張表導出成一個文件。
- 導出時可以添加建表語句,也可以不加,如果被導入數據的數據庫中已存在表結構,則不需要建表語句。
我選擇了第二種方法。這種方法遇到了很多坑,原因有多種,比如h2的導出sql文件完全是為了用於h2的導入,h2和mysql語法不同,需要轉換,又比如hibernate執行原生的insert語句有bug。下面慢慢來說。
注:mysql中創建數據庫的語法:create database halo default character set utf8mb4 default collate utf8mb4_unicode_ci;
2 導出h2數據為.sql文件
java -cp $H2DRIVERS org.h2.tools.Script -url jdbc:h2:/root/.halo/db/halo -user root -password root -script halo_h2_db_exp_original20200613.sql
# 或者如下的語法
java -cp ./h2-1.4.200.jar org.h2.tools.Script -url jdbc:h2:/root/.halo/db/halo -user root -password root -script halo_h2_db_exp_original20200613.sql
# 本地url的格式:jdbc:h2:/root/.halo/db/halo
# 遠程url的格式:jdbc:h2:tcp://192.168.6.16:8082/root/.halo/db/halo
- 坑1:
STRINGDECODE
導出sql文件中,所有的中文都是\u0011這種unicode字符,h2打算在導入數據時,使用內置函數STRINGDECODE
解碼,比如:
INSERT INTO SYSTEM_LOB_STREAM VALUES(278, 0, STRINGDECODE('<h1 id=\"\\u0011\\u2390\\u0013aaabbbccc\">****</h1>')
解決方法:寫java程序將unicode字符全部轉換成中文。然后用正則表達式將STRINGDECODE
全部去掉。
IDEA中的操作:
STRINGDECODE\('(.*?)'\), NULL\); 替換為 '$1', NULL);
GEDIT和notepadqq中的操作:
STRINGDECODE\('(.*?)'\), 替換為 '\1',
- 坑2:建表的sql語法不同。
解決方法:
(1)在建表語句最后添加默認字符集和默認校驗方式:ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC
另外,AUTO_INCREMENT=40
,設置自增長到哪個數字了。這個數字還要到sql文件的序列中去找:
-- CREATE SEQUENCE `halo`."SYSTEM_SEQUENCE_07030136_5506_425A_9226_CC6A332680DA" START WITH 39 BELONGS_TO_TABLE;
-- CREATE SEQUENCE `halo`."SYSTEM_SEQUENCE_85A72195_448B_47C7_91E3_8EDCBF47C0B5" START WITH 38 BELONGS_TO_TABLE;
-- CREATE SEQUENCE `halo`."SYSTEM_SEQUENCE_28935B3E_1674_4941_9D8B_E107ADBFE655" START WITH 2 BELONGS_TO_TABLE;
-- CREATE SEQUENCE `halo`."SYSTEM_SEQUENCE_8A63B720_7E60_4B97_B457_77B9D788B782" START WITH 100 BELONGS_TO_TABLE;
-- CREATE SEQUENCE `halo`."SYSTEM_SEQUENCE_BA607316_C0CA_4AC9_93B7_4EDD398DCE5E" START WITH 242 BELONGS_TO_TABLE;
-- CREATE SEQUENCE `halo`."SYSTEM_SEQUENCE_C77F783A_7DED_4E69_9C04_901BA9342C28" START WITH 9 BELONGS_TO_TABLE;
-- CREATE SEQUENCE `halo`."SYSTEM_SEQUENCE_69675EBF_E100_4418_A967_F88A376A4AEF" START WITH 5775 BELONGS_TO_TABLE;
-- CREATE SEQUENCE `halo`."SYSTEM_SEQUENCE_95A9EFBB_A2DE_4C87_8D19_A805E62C0173" START WITH 1 BELONGS_TO_TABLE;
-- CREATE SEQUENCE `halo`."SYSTEM_SEQUENCE_7137CFFF_AD51_432D_AD29_C22596D7D471" START WITH 73 BELONGS_TO_TABLE;
-- CREATE SEQUENCE `halo`."SYSTEM_SEQUENCE_56A6F5AE_2FC4_437F_A7BD_3B12F355782E" START WITH 20 BELONGS_TO_TABLE;
-- CREATE SEQUENCE `halo`."SYSTEM_SEQUENCE_3FA77B52_1B71_4B7C_B75F_7CD707111A1C" START WITH 73 BELONGS_TO_TABLE;
-- CREATE SEQUENCE `halo`."SYSTEM_SEQUENCE_5749AACE_18C3_42AE_A57F_F776A4CEB70B" START WITH 35 BELONGS_TO_TABLE;
-- CREATE SEQUENCE `halo`."SYSTEM_SEQUENCE_7C9D7387_4BD6_4031_ACB0_EBF15B600A93" START WITH 6666 BELONGS_TO_TABLE;
-- CREATE SEQUENCE `halo`."SYSTEM_SEQUENCE_7437AF47_F79E_47AE_B97A_73C915A54E73" START WITH 1155 BELONGS_TO_TABLE;
-- CREATE SEQUENCE `halo`."SYSTEM_SEQUENCE_558AED66_5DC3_46BF_8E83_C8FF7AF1F02F" START WITH 1257 BELONGS_TO_TABLE;
-- CREATE SEQUENCE `halo`."SYSTEM_SEQUENCE_42A96F72_A81E_4741_AF00_3EA740EB24E7" START WITH 39 BELONGS_TO_TABLE;
(2)修改建表語句中的字段類型為mysql語法的。
(3)每張表都有一個主鍵字段ID
,原來是設置自增長的,使用序列來實現,mysql中沒有序列,但是也可設置自增長,所以這么修改:ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT
(4)H2的字段是用雙引號"
包裹起來的,而mysql是用飄號`,需要用正則來替換。
eg. `$1` VARCHAR(200) NOT NULL,
"([A-Z0-9]+_?[A-Z0-9]*?)" VARCHAR\( 替換為 `\1` VARCHAR( 或者 `$1` VARCHAR(,notpad++使用 \1,idea使用 $1
"([A-Z0-9]+_?[A-Z0-9]*?)" BIGINT\( 替換為 `$1` BIGINT(
"([A-Z0-9]+_?[A-Z0-9]*?)" TINYINT\( 替換為 `$1` TINYINT(
- java代碼報錯:
Caused by: org.hibernate.QueryException: Space is not allowed after parameter prefix ':'
原因:參數前綴:
(即引用占位符)的前后都不允許有空格。(其實不是這個原因)
舉例如下:
# error
...AND apply.startTime >=: startTime1 AND apply.endTime <=: endTime1
# correct
...AND apply.startTime >=:startTime1 AND apply.endTime <=:endTime1
解決方法:拼接時給concat參數加上英文單引號。
-
java代碼報錯:
java.lang.IllegalArgumentException: Illegal group reference
String test = "abc ";
test = test.replaceAll("abc", "1111111111$$");
//以上語句會報異常java.lang.IllegalArgumentException: Illegal group reference,解決方法為改寫以下形式:
test = test.replaceAll("abc", java.util.regex.Matcher.quoteReplacement("dXNlcjM1NDk2NQ$$")); -
java代碼報錯:
data too long for column
FORMAT_CONTENT``
解決方法:將FORMAT_CONTENT字段類型由text(最長4K)改為mediumtext(最長16M) -
java代碼報錯:
java.lang.IllegalArgumentException: org.hibernate.QueryException: Unmatched braces(大括號) for alias path
原因:我使用的原生sql語句,拼接后直接執行。此時,因為insert語句中有{括號所以會報錯,解決方案就是去掉{
或者加上}
,使{}
不出現或成對出現。
總之,當保存的字段內容有其他特殊符號,或者比較復雜的內容時,容易出現該錯誤,容易使程序解析不了SQL語句。其實也就是因為insert into
語句完全是用手動拼字符串而成的SQL語句。
解決方法:- 所有
{
后添加}
。
eg. id=549的posts記錄是這么處理的。 - 改為
字段內容
提取出來成為參數,使用query.setParameter(1,strParam)來設置參數。這樣也是防止了SQL注入。
- 所有
-
java代碼報錯:
IllegalArgumentException: Could not locate ordinal(順序的) parameter [1], expecting one of []
原因:list中沒有值可以被調用。這個不講了,很簡單。
全是坑,不用hibernate了;最終使用了java原生的jdbc方法來執行insert語句,非常香!(原來使用query = em.createNativeQuery(sql)生成Query對象,再執行query.executeUpdate())
3 小結
- 下次使用IDEA來遷移試試。
- 遷移之前,保存草稿時,halo系統經常報錯“網絡異常”,遷移之后,現在不報錯了。