近期在使用gorm的过程中遇到个问题: 我使用了gorm来进行mysql数据库初始化(建表/插入初始化数据/创建函数/添加触发器等),建表和插入初始化数据很容易实现,但是添加函数和触发器时,无法直接使用gorm实现(可能是我没找到方法),每次执行的时候都会报错。
我的mysql脚本如下:
-- 函数;
DELIMITER $$
DROP FUNCTION IF EXISTS get_ver$$
CREATE FUNCTION `get_ver` ( ver_name VARCHAR ( 50 )) RETURNS INT ( 11 )
DETERMINISTIC
BEGIN
DECLARE val INTEGER;
SET val = 0;
SELECT current_val INTO val
FROM sys_sequence
WHERE seq_name = ver_name
FOR UPDATE;
UPDATE sys_sequence SET current_val = current_val + increment_val
WHERE seq_name = ver_name;
RETURN val;
END $$
DELIMITER ;
-- 触发器;
DELIMITER $$
DROP TRIGGER IF EXISTS `TRI_user_info_before_insert`$$
CREATE TRIGGER `TRI_user_info_before_insert` BEFORE INSERT ON `user_info` FOR EACH ROW BEGIN
set NEW.auto_ver = get_ver('user_info_ver');
END $$
DROP TRIGGER IF EXISTS `TRI_user_info_before_update`$$
CREATE TRIGGER `TRI_user_info_before_update` BEFORE UPDATE ON `user_info` FOR EACH ROW BEGIN
set NEW.auto_ver = get_ver('user_info_ver');
END $$
DELIMITER ;
COMMIT;
最初尝试的是使用gorm的Exec方法,但是执行报错,说有语法错误“You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DELIMITER $$”
然后我又试了一种方式,将脚本写到单独的文件里,然后使用os.exec命令去执行,这样倒是执行成功了,但是这有个限制(运行的电脑上必须安装了mysql或mysql客户端)。
想问下大家有没有其他的好方法实现golang执行mysql原生脚本,如使用gorm等,不用电脑上必须安装mysql。
参考GPT和自己的思路:
针对你的问题,我们可以使用GORM的Raw
方法来执行原生的SQL语句,这样就能够执行你的MySQL脚本了。你可以把你的MySQL脚本先写到一个字符串变量中,然后通过Raw
方法执行它。
以下是一个示例代码,供你参考:
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
// 连接 MySQL 数据库
dsn := "user:password@tcp(127.0.0.1:3306)/database?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
// 定义 MySQL 脚本
sqlScript := `
-- 函数;
DELIMITER $$
DROP FUNCTION IF EXISTS get\_ver$$
CREATE FUNCTION ` + "`get_ver` (ver_name VARCHAR ( 50 )) RETURNS INT ( 11 )" + `
DETERMINISTIC
BEGIN
DECLARE val INTEGER;
SET val = 0;
SELECT current_val INTO val
FROM sys_sequence
WHERE seq_name = ver_name
FOR UPDATE;
UPDATE sys_sequence SET current_val = current_val + increment_val
WHERE seq_name = ver_name;
RETURN val;
END $$
DELIMITER ;
-- 触发器;
DELIMITER $$
DROP TRIGGER IF EXISTS ` + "`TRI_user_info_before_insert`" + `$$
CREATE TRIGGER ` + "`TRI_user_info_before_insert`" + ` BEFORE INSERT ON ` + "`user_info`" + ` FOR EACH ROW BEGIN
set NEW.auto_ver = get_ver('user_info_ver');
END $$
DROP TRIGGER IF EXISTS ` + "`TRI_user_info_before_update`" + `$$
CREATE TRIGGER ` + "`TRI_user_info_before_update`" + ` BEFORE UPDATE ON ` + "`user_info`" + ` FOR EACH ROW BEGIN
set NEW.auto_ver = get_ver('user_info_ver');
END $$
DELIMITER ;
COMMIT;
`
// 执行 MySQL 脚本
if err := db.Exec(sqlScript).Error; err != nil {
fmt.Println(err)
} else {
fmt.Println("执行成功!")
}
}
这个示例代码中,我们直接使用db.Exec
方法执行MySQL脚本。因为我们使用的是GORM的MySQL驱动,所以是会自动将脚本中的;
替换成\n
,这样就可以解决语法错误的问题了。
关于这个问题,我没有找到比较好的执行整个脚本的方法,貌似golang的mysql驱动不支持DELIMITER ,最后用笨办法解决了。
我用的笨办法就是将各个语句拆开,然后用分别用Exec执行,并且整体加到一个事务里。
这个方法虽然能解决,但是个人认为并不好,等有空了试试写一个按$$分隔,循环执行的方法。