【REST2SQL】05 GO 操作 达梦 数据库
【REST2SQL】01RDB关系型数据库REST初设计
【REST2SQL】02 GO连接Oracle数据库
【REST2SQL】03 GO读取JSON文件
【REST2SQL】04 REST2SQL第一版Oracle版实现
信创要求用国产数据库,刚好有项目用的达梦,研究一下go如何操作达梦数据库
1 准备工作
1.1 安装达梦数据
登录 达梦 官网,有DM8开发版可以下载,我下载的是X86,Win64版的DM8开发版。下载成功后,安装配置等这里省略5217字,自己脑补。
创建测试表 guci
导入部分测试数据
1.2 达梦 go驱动安装
安装达梦后,在达梦的安装目录…\dmdbms\drivers\go下有go驱动包dm-go-driver.zip,解压到go开发环境dm目录即可,也可以在第三方下载。
达梦的go驱动还有安装如下两个依赖
github.com/golang/snappy v0.0.4 // indirect
golang.org/x/text v0.14.0 // indirect
众所周知的原因,可能同步失败,自己想办法翻墙或代理等一系列操作。
2 新建一个godm的项目
新建一下godm的项目用来测试go操作达梦数据库。这次也试试 go mod
2.1 初始化 go mod
go mod init godm
go mod tidy
自动创建了go.mod 和 go.sum
//go.mod
module godmgo 1.21.5require (github.com/golang/snappy v0.0.4 // indirectgolang.org/x/text v0.14.0 // indirect
)
// go.sum
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
2.2 go连接达梦数据库
1 引入相关包
import ("database/sql""database/sql/driver"_ "dm"
)
2 声明连接字符串
var ConnString string = "dm://BLMA:dameng5217@127.0.0.1:5236/BLMA"
3 连接数据库
// 连接dm数据库
func connDB(connStr string) *sql.DB {end := strings.Index(connStr, "://")if end < 0 {log.Println("连接字符串设置有误。")panic(nil)}driverName := connStr[:end] // dmDB, err := sql.Open(driverName, connStr)dieOnError("Can't open the driver:", err)if err = DB.Ping(); err != nil {return nil}// fmt.Printf("connect to \"%s\" succeed.\n", connStr)return DB
}
2.3 实现CRUD
CUD比较简单,都执行execSQL操作,只是sql语句不同。
代码如下:
/* 往表插入数据 */
func insertData(insertSql string) string {result, _ := execSQL(insertSql)rows, err := result.RowsAffected()dieOnError("Can't insert", err)ret := map[string]int{"Insert rowsAffected": int(rows),}jsonBytes, err := json.Marshal(ret)dieOnError("map 转 json失败:", err)return string(jsonBytes)
}/* 删除表数据 */
func deleteData(deleteSql string) string {result, _ := execSQL(deleteSql)rows, err := result.RowsAffected()dieOnError("Can't delete", err)ret := map[string]int{"Delete rowsAffected": int(rows),}jsonBytes, err := json.Marshal(ret)dieOnError("map 转 json失败:", err)return string(jsonBytes)
}/* 修改表数据 */
func updateData(updateSql string) string {result, _ := execSQL(updateSql)rows, err := result.RowsAffected()dieOnError("Can't update", err)ret := map[string]int{"Update rowsAffected": int(rows),}jsonBytes, err := json.Marshal(ret)dieOnError("map 转 json失败:", err)return string(jsonBytes)// var sql =// result, err := db.Exec(sql)// if err != nil {// return err// }// affectedRows, _ := result.RowsAffected()// fmt.Println("updateTable succeed Affected rows:", affectedRows)// return nil
}// 执行SQL, execute stmt (INSERT, UPDATE, DELETE, DML, PLSQL) and return driver.Result object
func execSQL(sqls string) (result driver.Result, err error) {//连接数据库DB := connDB(ConnString)//延迟关闭连接defer DB.Close()statement, err := DB.Prepare(sqls)if err != nil {fmt.Println("prepare statement failed:", err.Error())}defer statement.Close()//执行SQL, execute stmt (INSERT, UPDATE, DELETE, DML) and return driver.Result objectresult, err = statement.Exec()if err != nil {fmt.Println("Exec failed:", err.Error())}dieOnError("Can't execSql() ", err)return result, err
}
2.4 动态查询有点费劲
达梦提供了简单的查询驱动,貌似没有提供动态查询相关驱动,只好自己动手实现了,好在其它数据库也能用上。
总体思路是先查询获取 *sql.Rows对象,从这里通过rows.Columns()和 rows. ColumnTypes()再获取列名切片和列类型信息,第三步把列名和数据库数据类型组合在一个map[string]string里;第四步初始化列值接收变量;第五步 rows.Next() 逐行遍历返回结果集并根据数据库类型(目前只匹配的VARCHER2 和 NUMBER,遇到其它类型再匹配)转换为Go的数据类型,组成一个dataset数据集;第六步序列化并转json返回查询结果。
代码如下:
/* 查询表数据 */
func selectData(sqlSelect string) string {// 连接数据库DB := connDB(ConnString)//延迟关闭连接defer DB.Close()// 准备查询语句statement, err := DB.Prepare(sqlSelect)if err != nil {fmt.Println("prepare statement failed:", err.Error())return ""}defer statement.Close()
// 1查询rows, err := statement.Query()if err != nil {fmt.Println("query failed:", err.Error())}// 2查询的列名称切片columns, err := rows.Columns()if err != nil {fmt.Println(err.Error())return ""}//fmt.Println(columns)// 3数据库列类型cType, err := rows.ColumnTypes()if err != nil {log.Fatal(err)}//fmt.Println(cType[0].Name(), cType[0].DatabaseTypeName())// 4列名类型mapcoltyp := colType(cType)// 5初始化列值接收变量row := make([]sql.RawBytes, len(columns))scanArgs := make([]interface{}, len(row))for i := range row {scanArgs[i] = &row[i]}// 查询结果数据集var dataset []map[string]interface{} //元素为map的切片for rows.Next() {err := rows.Scan(scanArgs...)if err != nil {panic(err.Error())}// rowvar row1 map[string]interface{} = make(map[string]interface{})for pos, col := range row {//fmt.Println(columns[pos], ":", string(col))colname := columns[pos]svalue := string(col)//数据类型处理value := typeConv(colname, svalue, coltyp)row1[colname] = value}//fmt.Println("row1:", row1)dataset = append(dataset, row1)//fmt.Println()}if err != io.EOF {dieOnError("Can't Next", err)}//切片转jsonjsonBytes, err := json.Marshal(dataset)dieOnError("slice 转 json失败:", err)//fmt.Println(string(jsonBytes))return string(jsonBytes)
}// 生成列名和类型 map
func colType(cType []*sql.ColumnType) map[string]string {var colTyp map[string]string = make(map[string]string)for _, col := range cType {colTyp[col.Name()] = col.DatabaseTypeName()}//fmt.Println(colTyp)return colTyp
}// 字符根据数据库类型转为go数据类型
func typeConv(colname string, svalue string, ct map[string]string) interface{} {var ret interface{}switch ct[colname] {case "VARCHAR2":ret = svaluecase "NUMBER":if len(svalue) > 0 {flt, err := strconv.ParseFloat(svalue, 64)if err != nil {fmt.Println("转换失败:", colname, svalue, err)} else {ret = flt}} else { //空串处理ret = nil}default:ret = svalue}return ret
}
3 全部代码及运行结果截图
全部代码:
/*该例程实现了达梦数据库插入数据,修改数据,删除数据,数据查询等基本操作。*/
package main// 引入相关包
import ("database/sql""database/sql/driver"_ "dm""encoding/json""fmt""io""log""strconv""strings"
)var ConnString string = "dm://BLMA:dameng5217@127.0.0.1:5236/BLMA"// var ConnString string = config.Conf.ConnStringfunc main() {fmt.Println("\ngo 操作达梦数据库 dome")var (sqls string //sql语句result string //sql执行后返回的结果)// insert 插入一行数据sqls = `INSERT INTO guci(p_id,f_zh,f_gp,s_mc) VALUES(-109,'bailongma','005217','白龙马');`result = insertData(sqls)fmt.Println(result)// delete 删除数据sqls = "delete from guci where p_id = -108"result = deleteData(sqls)fmt.Println(result)// update 更新数据sqls = "UPDATE guci SET n_sul = 400 WHERE p_id = -100"result = updateData(sqls)fmt.Println(result)sqls = "select p_id,f_zh,f_gp,s_mc,n_sul from guci where rownum < 6"result = selectData(sqls)fmt.Println(result)}// 连接dm数据库
func connDB(connStr string) *sql.DB {end := strings.Index(connStr, "://")if end < 0 {log.Println("连接字符串设置有误。")panic(nil)}driverName := connStr[:end] // dmDB, err := sql.Open(driverName, connStr)dieOnError("Can't open the driver:", err)if err = DB.Ping(); err != nil {return nil}// fmt.Printf("connect to \"%s\" succeed.\n", connStr)return DB
}// 发生错误退出1
func dieOnError(msg string, err error) {if err != nil {log.Println(msg, err)//os.Exit(1)}
}/* 往表插入数据 */
func insertData(insertSql string) string {result, _ := execSQL(insertSql)rows, err := result.RowsAffected()dieOnError("Can't insert", err)ret := map[string]int{"Insert rowsAffected": int(rows),}jsonBytes, err := json.Marshal(ret)dieOnError("map 转 json失败:", err)return string(jsonBytes)
}/* 删除表数据 */
func deleteData(deleteSql string) string {result, _ := execSQL(deleteSql)rows, err := result.RowsAffected()dieOnError("Can't delete", err)ret := map[string]int{"Delete rowsAffected": int(rows),}jsonBytes, err := json.Marshal(ret)dieOnError("map 转 json失败:", err)return string(jsonBytes)
}/* 修改表数据 */
func updateData(updateSql string) string {result, _ := execSQL(updateSql)rows, err := result.RowsAffected()dieOnError("Can't update", err)ret := map[string]int{"Update rowsAffected": int(rows),}jsonBytes, err := json.Marshal(ret)dieOnError("map 转 json失败:", err)return string(jsonBytes)// var sql =// result, err := db.Exec(sql)// if err != nil {// return err// }// affectedRows, _ := result.RowsAffected()// fmt.Println("updateTable succeed Affected rows:", affectedRows)// return nil
}// 执行SQL, execute stmt (INSERT, UPDATE, DELETE, DML, PLSQL) and return driver.Result object
func execSQL(sqls string) (result driver.Result, err error) {//连接数据库DB := connDB(ConnString)//延迟关闭连接defer DB.Close()statement, err := DB.Prepare(sqls)if err != nil {fmt.Println("prepare statement failed:", err.Error())}defer statement.Close()//执行SQL, execute stmt (INSERT, UPDATE, DELETE, DML) and return driver.Result objectresult, err = statement.Exec()if err != nil {fmt.Println("Exec failed:", err.Error())}dieOnError("Can't execSql() ", err)return result, err
}/* 查询表数据 */
func selectData(sqlSelect string) string {// 连接数据库DB := connDB(ConnString)//延迟关闭连接defer DB.Close()// 准备查询语句statement, err := DB.Prepare(sqlSelect)if err != nil {fmt.Println("prepare statement failed:", err.Error())return ""}defer statement.Close()// 1查询rows, err := statement.Query()if err != nil {fmt.Println("query failed:", err.Error())}// 2查询的列名称切片columns, err := rows.Columns()if err != nil {fmt.Println(err.Error())return ""}//fmt.Println(columns)// 3数据库列类型cType, err := rows.ColumnTypes()if err != nil {log.Fatal(err)}//fmt.Println(cType[0].Name(), cType[0].DatabaseTypeName())// 4列名类型mapcoltyp := colType(cType)// 5初始化列值接收变量row := make([]sql.RawBytes, len(columns))scanArgs := make([]interface{}, len(row))for i := range row {scanArgs[i] = &row[i]}// 查询结果数据集var dataset []map[string]interface{} //元素为map的切片for rows.Next() {err := rows.Scan(scanArgs...)if err != nil {panic(err.Error())}// rowvar row1 map[string]interface{} = make(map[string]interface{})for pos, col := range row {//fmt.Println(columns[pos], ":", string(col))colname := columns[pos]svalue := string(col)//数据类型处理value := typeConv(colname, svalue, coltyp)row1[colname] = value}//fmt.Println("row1:", row1)dataset = append(dataset, row1)//fmt.Println()}if err != io.EOF {dieOnError("Can't Next", err)}//切片转jsonjsonBytes, err := json.Marshal(dataset)dieOnError("slice 转 json失败:", err)//fmt.Println(string(jsonBytes))return string(jsonBytes)
}// 生成列名和类型 map
func colType(cType []*sql.ColumnType) map[string]string {var colTyp map[string]string = make(map[string]string)for _, col := range cType {colTyp[col.Name()] = col.DatabaseTypeName()}//fmt.Println(colTyp)return colTyp
}// 字符根据数据库类型转为go数据类型
func typeConv(colname string, svalue string, ct map[string]string) interface{} {var ret interface{}switch ct[colname] {case "VARCHAR2":ret = svaluecase "NUMBER":flt, err := strconv.ParseFloat(svalue, 64)if err != nil {fmt.Println("转换失败")} else {ret = flt}default:ret = svalue}return ret
}
运行结果截图:

完
相关文章:
【REST2SQL】05 GO 操作 达梦 数据库
【REST2SQL】01RDB关系型数据库REST初设计 【REST2SQL】02 GO连接Oracle数据库 【REST2SQL】03 GO读取JSON文件 【REST2SQL】04 REST2SQL第一版Oracle版实现 信创要求用国产数据库,刚好有项目用的达梦,研究一下go如何操作达梦数据库 1 准备工作 1.1 安…...
GitLab 502 Whoops, GitLab is taking too much time to respond. 解决
1、先通过gitlab-ctl restart进行重启,2分钟后看是否可以正常访问,为什么要2分钟,因为gitlab启动会有很多配套的服务启动,包括postgresql等 2、如果上面不行,再看gitlab日志,通过gitlab-ctl tail命令查看&…...
vi ~/.bashrc 后如何编辑并退出
在使用 vi 编辑器打开 ~/.bashrc 文件后,可以按照以下步骤编辑并保存退出: vi ~/.bashrc 按 i 进入插入模式: 在 vi 编辑器中,按 i 键将进入插入模式。在插入模式中,您可以编辑文本。 编辑文件: 在插入模…...
KVM Vcpu概述
KVM Vcpu概述 Intel VTSMP系统CPU过载使用CPU模型CPU绑定和亲和性CPU优化 Intel VT Intel的硬件虚拟化技术大致分为3类: 1、VT-x技术:是指Intel处理器中的一些虚拟化技术支持,包括CPU中最基础的VMX技术,也包括内存虚拟化的硬件支…...
linux服务器ftp部署
1、ftp服务安装 # 检查是否安装 1、查询安装列表 sudo systemctl list-unit-files --typeservice | grep ftp 2、查询ftp服务状态 sudo service vsftpd status 或者 sudo systemctl status vsftpd # yum安装,一般yum仓库都有ftp安装包 sudo yum install vsftpd # 启…...
NSIS 安装windows 安装包(包括QT和MFC)
NSIS(Nullsoft Scriptable Install System)是一个开源的 Windows 系统下安装程序制作程序。它提供了安装、卸载、系统设置、文件解压缩等功能。 基本概念 区段 是对应某种安装/卸载选项的处理逻辑,该段代码仅当用户选择相应的选项才被执行…...
K8S----PVPVCSC
一、简介 1、PV(persistent volume)–持久卷 PV是集群中的一块存储,可以由管理员事先静态(static)制备, 也可以使用存储类(Storage Class)来动态(dynamic)制备。 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 Volume 一样, 也是使用卷插件(volume p…...
RSIC-V“一芯”学习笔记(一)——概述
考研的文章和资料之后想写的时候再写怕趴 文章目录 一、阶段设计二、环境、开发语言和工具三、最重要的两个观念四、处理器芯片设计五、处理器芯片设计包含很多软件问题六、处理器芯片的评价指标七、复杂系统的构建和维护八、专业世界观九,提问的艺术(提问模板)十、…...
MATLAB读取图片并转换为二进制数据格式
文章目录 前言一、MATLAB 文件读取方法1、文本文件读取2、二进制文件读取3、 图像文件读取4、其他文件读取 二、常用的图像处理标准图片链接三、MATLAB读取图片并转换为二进制数据格式1、matlab 源码2、运行结果 前言 本文记录使用 MATLAB 读取图片并转换为二进制数据格式的方…...
时序数据库
SELECT *,max(lp_index) FROM lp.tdm_lp_original_data where ts > 2023-12-28 18:11:33.521 and ts < 2023-12-29 19:03:12.148 INTERVAL(2s) FILL(PREV) 在时间序列数据库TDengine中,FILL函数与GROUP BY子句结合使用,提供了对于在指定间隔内…...
【第一次使用finalshell连接虚拟机内的centos】小白处理方式
第一次使用finalshell连接centos7的时候,因为都是新环境什么都没有配置,所以就需要安装finalshell和对新的centos7 进行一些配置。 安装finalshel,默认不安装d盘,就需要对安装路径做一下调整,其余都是下一步默认安装的…...
Pinia 踩坑记录
1、子store中如何使用router 以user.ts 这个store为例 错误写法 // 说明:不能使用插件实例化router,否则获取不到router的函数 // 错误写法如下:import { useRouter } from "vue-router"actions:{login(){const router useRout…...
在ASP.NET MVC中使用JQuery提供的弹出窗口(模态窗口)
在ASP.NET MVC中使用JQuery提供的弹出窗口(模态窗口) 原理 使用<div>图层灵活显示在浏览器的任何位置。默认情况下指定<div>不可见 引用 样式表 在JQuery的官方网站可以下载对应的css样式表。打开官网的样例页。 找到样式表引用路径 …...
基本工具配置
github加速 github.ur1.fun java # ubuntu20.04 安装 openjdk-17-jdk sudo apt install openjdk-17-jdk java -version javac -version which java参考 openjdk gradle换源 修改gradle-wrapper.properties distributionBaseGRADLE_USER_HOME distributionPathwrapper/dis…...
计算机网络——应用层(3)
计算机网络——应用层(3) 小程一言专栏链接: [link](http://t.csdnimg.cn/ZUTXU) 点对点(P2P)P2P网络一般用途优点缺点总结 套接字编程基本步骤UDP套接字TCP套接字基本步骤 二者对比 小程一言 我的计算机网络专栏,是自…...
配置ssh实现互相免密登陆
一、实验要求 通过两台linux主机 配置ssh 实现互相免密登陆 的操作 二、实验思路 免密登录我们可以理解为使用公钥登录,这里分别使用两台主机(client)和(server)作为实验主机。 首先让client免密登录server&#x…...
【UEFI基础】EDK网络框架(ARP)
ARP ARP协议说明 从这里开始涉及到的网络协议都是比较通用的了,在一般的TCP/IP四层模型中都能够看到这些内容,不过这里主要介绍的还是其在BIOS下的实现,但是在此之前还是需要先说明ARP的作用。 ARP的全称是Address Resolution Protocol&am…...
Linux进阶课:目录(文件夹)与文件操作
1、ls与cat的区别是是什么? 答:ls命令的含义是list,显示当前目录中内容。不加参数时它显示当前目录中除隐藏文件外的所有文件及目录的名字。 cat命令是linux下的一个文本输出命令,通常是用于查看某个文件的内容的。 2、[abc]这个…...
Flink自定义Source模拟数据流
maven依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.…...
[易语言]使用易语言部署工业级人脸检测模型
【框架地址】 https://github.com/ShiqiYu/libfacedetection 【算法介绍】 Libfacedetection是一个开源的计算机视觉库,主要用于实时的人脸检测。它利用深度学习技术,特别是卷积神经网络(CNN),实现了高精度的脸部定位…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
