前言

作为数据存储的数据库,近年来发展飞快,在早期的程序自我存储到后面的独立出中间件服务,再到集群,不可谓不快了。早期的sql数据库一枝独秀,到后面的Nosql,还有azure安全,五花八门,教人学不过来阿。

一、mysql数据库的golang操作

针对数据库操作,往往需要安装实体数据库和对应的数据库驱动,前者要实际安装配置,可参考菜鸟官网的mysql安装,后者则是下载安装第三方库即可。

jam@jam:~$ mysql -h127.0.0.1 -ujam -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 13
Server version: 8.0.33 MySQL Community Server - GPL Copyright (c) 2000, 2023, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| performance_schema |
| somedb |
+--------------------+
3 rows in set (0.00 sec) mysql> use somedb;
Database changed
mysql> show tables;
Empty set (0.01 sec) mysql>

这边已经准备好了本地数据库实体,也添加了somedb数据库和对应的jam用户,供golang的调用操作练习。

1.1 database/sql使用

用golang的database/sql接口来进行sql硬编码,后续得到的结果自己硬解码,不过golang需要下载数据库驱动go get github.com/go-sql-driver/mysql,下载好了以后,就是编写sql语句和定流程的事了。

大致程序流程就是连接数据库,调用exec类api来执行sql语句,在python用pymysql等第三方库操作的时候,还需要在插入和更新表数据的时候来个commit,golang倒是不用,就直接Exec,具体如下:

const (
USERNAME = "jam"
PWD = "abaaba"
NETWORK = "tcp"
SERVER = "127.0.0.1"
PORT = 3306
DATABASE = "somedb"
) type Student struct {
Id int
Name string
} func main() {
conn := fmt.Sprintf("%s:%s@%s(%s:%d)/%s", USERNAME, PWD, NETWORK, SERVER, PORT, DATABASE)
db, err := sql.Open("mysql", conn)
if err != nil {
fmt.Println("mysql数据库连接失败:", err)
return
}
//设置连接存活时间
db.SetConnMaxLifetime(60 * time.Second)
//设置最大连接数
db.SetMaxOpenConns(10) createTable(db)
insert(db, 1, "小昏")
insert(db, 2, "meiyi")
insert(db, 3, "明也")
query_single(db, 1)
//更新数据
update(db, 1, "baba")
all_query(db)
drop(db)
}

程序主体便是这样,几个执行sql语句根据语义包装成函数分开调用,另外因为用的是IDEA的goland,所以踩了不少的坑,关于数据库在ide中的配置出现了不少warning,有点烦。针对sql中的insert,update,delete操作还是使用db.Exec来对应调用:

// 插入,直接Exec一个sql也行
func insert(db *sql.DB, id int, name string) {
sql_state, err := db.Prepare("insert into students values (?, ?)")
res, err := sql_state.Exec(id, name)
if err != nil {
fmt.Println("插入失败:", err)
return
}
lastInsertID, _ := res.LastInsertId()
fmt.Println("插入id:", lastInsertID)
rowsaffected, _ := res.RowsAffected()
fmt.Println("affected rows:", rowsaffected)
} // 修改
func update(db *sql.DB, id int, name string) {
if res, err := db.Exec("update students set name=? where id=?", name, id); err != nil {
fmt.Println("更新表失败:", err)
} else {
fmt.Println("更新结果:", res)
}
} // 删除指定id对应的行
func delete(db *sql.DB, id int) {
if res, err := db.Exec("delete from students where id=?", id); err != nil {
fmt.Println("删除失败:", err)
return
} else {
fmt.Println("删除结果:", res)
}
}

然后针对查询的就用db.Query一类接口:

// 查询一行
func query_single(db *sql.DB, id int) {
student := new(Student)
row := db.QueryRow("select * from students where id=?", id)
if err := row.Scan(&student.Id, &student.Name); err != nil {
fmt.Println("scan失败:", err)
return
}
fmt.Println("第一行数据:", *student)
} func all_query(db *sql.DB) {
students := new(Student)
rows, err := db.Query("select * from students") defer func() {
if rows != nil {
rows.Close()
}
}()
if err != nil {
fmt.Println("查询失败:", err)
return
}
for rows.Next() {
err = rows.Scan(&students.Id, &students.Name)
if err != nil {
fmt.Println("scan失败:", err)
return
}
fmt.Println("scan successed:", *students)
}
}

另外还有创建表和删除表的操作,也还是用Exec,因为没返回嘛:

func createTable(db *sql.DB) {
sql_s := `create table if not exists students (
id int unsigned not null auto_increment primary key ,
name varchar(10) not null unique
)engine=InnoDB default charset=utf8;`
if _, err := db.Exec(sql_s); err != nil {
fmt.Println("创表失败:", err)
return
}
} func drop(db *sql.DB) {
if _, err := db.Exec("drop table students"); err != nil {
fmt.Println("删表失败:", err)
}
}

针对包引入部分,代码中是这样:

package main

import (
"database/sql"
"fmt"
"time" _ "github.com/go-sql-driver/mysql"
)

但实际中关于golang项目还有不少go modules的设置,这里只是实际记录,不做项目前准备的记录。走完一圈,可以发现database/sql提供的也就只是一个连接和对应的exec执行sql语句的接口,对于专门的coder来说,需要另外兼顾sql中的语法,未免有点麻烦,所以有了ORM技术--各玩各的。

二、gorm操作mysql

ORM技术,就是让高级语言的代码编辑专注于该语言的语法,而不用去另外理会sql的语义,具体实现就是,用类似c中的struct,cpp中的class,golang中的struct这类存储结构性信息来对接数据库中的关系型数据,中间的转换问题由专门的库负责,python中的是sqlalchemy,golang中目前接触的就是gorm了。

go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql

在项目中go get一下下载gorm库和对应mysql驱动即可,不同数据库对应不同驱动。

连接一下,然后进行设置:

const (
Host = "127.0.0.1"
User = "jam"
Pwd = "abaaba"
Port = 3306
Net = "tcp"
Database = "somedb"
) //空配置
func main() {
dst_uri := fmt.Sprintf("%s:%s@%s(%s:%d)/%s", User, Pwd, Net, Host, Port, Database)
//简单无配置
//db, err := gorm.Open(mysql.Open(dst_uri), &gorm.Config{}) //配置mysql驱动
//db, err := gorm.Open(mysql.New(mysql.Config{
// DSN: dst_uri,
// DefaultStringSize: 256, //string类型字符串默认长度
// DisableDatetimePrecision: true, //禁用datetime精度
// DontSupportRenameIndex: true, //重命名索引时使用删除并新建的方式
// DontSupportRenameColumn: true, //用change重命名列
// SkipInitializeWithVersion: false, //是否根据当前mysql版本自适应配置
//}), &gorm.Config{}) //gorm
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: dst_uri,
}), &gorm.Config{
SkipDefaultTransaction: false,
NamingStrategy: schema.NamingStrategy{
SingularTable: true, // 表名复数形式
TablePrefix: "t_", //表名前缀
},
DisableForeignKeyConstraintWhenMigrating: true, //设置逻辑外键,体现在代码上
}) //获取通用数据库对象sql.DB
sql_db, err := db.DB()
//设置连接池中空闲连接的最大数量
sql_db.SetMaxIdleConns(10)
//设置打开数据库连接的最大数
sql_db.SetConnMaxLifetime(time.Hour)
fmt.Println(sql_db, err)
}

建表

//通过自动迁移来建表,说白了就是提供好golang中的数据结构,然后调用接口给gorm就行,转换的问题交给gorm了
func auto_migrate(db *gorm.DB) {
type book struct {
id int
name string
price float32
} err := db.AutoMigrate(&book{})
if err != nil {
fmt.Println(err)
}
} //手动迁移的方式建表
func migrate(db *gorm.DB) {
type view struct {
Id int
Name string
} m := db.Migrator()
if !m.HasTable(&view{}) {
m.CreateTable(&view{})
} else {
fmt.Println("已存在同名表")
}
}

测试了一下,自动迁移的方式会针对同名表的创建有个判断,判断同名表是否存在的步骤,其实自动迁移的实现就是内部调用了Migrator接口的一个方法:

func (db *DB) AutoMigrate(dst ...interface{}) error {
return db.Migrator().AutoMigrate(dst...)
}
// Migrator migrator interface
type Migrator interface {
//下一小节
}

记,作为gorm的golang结构体的构造,内部成员名要大写开头

后续操作,也是基于Migrator这个接口的基础,因为它有许多方法:

        ...
//重命名
m := db.Migrator()
type view2 struct {
Id int
Name string
}
m.RenameTable(&view{}, &view2{})
fmt.Println("是否存在view2:", m.HasTable(&view2{}))
m.RenameTable(&view2{}, "view3") type view3 struct {
Id int
Name string
}
//删表
m.DropTable(&view3{})
fmt.Println("是否存在表view3:", m.HasTable(&view3{}))

插入,更新,删除,查询操作

        ...
type view struct {
Id int
Name string
}
type book struct {
Id int
Name string
Price float32
} //插入
views := []view{{1, "wind"}, {2, "rain"}}
db.Create(&views)
//查询, 获取第一条记录,并且更新
v := new(View)
db.First(&v)
fmt.Println(v.Id, v.Name)
//更新
v.Id = 4
v.Name = "river"
db.Save(&v)
//再查找然后删除表记录
db.Where("id=?", 3).Delete(&View{})

差不多就这样,更多api可以查看官方中文文档

另外,除了上面的mysql以外,还可以连接本地的sqlite数据库,下载对应驱动就行,也验证了上面的一个说法。

sql数据库连接的更多相关文章

  1. SQL数据库连接到服务器出错——无法连接到XXX

    问题:Windows或者SQL Server身份验证下,出现连接到服务器出错 解决方式: 1.检查实例名称是否正确,如下 根据自己电脑的实际名称修改修改如下: 1)Microsoft SQL Serv ...

  2. SQL数据库连接池与C#关键字return

    SQL数据库连接池: 先前做的一个Sharepoint项目,在上线后的不久,最近一直出现间歇性访问缓慢问题Sharepoint特性问题,并分析了其数据库服务器,发现所耗内存已经达到了97%. 所以断定 ...

  3. Java之MS SQL数据库连接

    一  1.首先,到微软官方下载jdbc驱动包 Microsoft JDBC Driver 4.0 for SQL Server 2.运行sqljdbc_4.0.2206.100_chs.exe,把文件 ...

  4. web第一节课 sql 数据库连接 查询

    1.数据库连接语句 <connectionStrings> <add name="yhotel" connectionString="Database= ...

  5. C# KTV 系统 SQL数据库连接 C# 应用窗体

    ---恢复内容开始--- 五道口 北大青鸟校区 KTV项目 指导老师: 袁玉明  SQL数据库关系图 第一步: private void DoubleClicklvContry() { ]!=null ...

  6. Go组件学习——database/sql数据库连接池你用对了吗

    1.案例 case1: maxOpenConns > 1 func fewConns() { db, _ := db.Open("mysql", "root:roo ...

  7. 远程sql数据库连接不上,provider: 命名管道提供程序, error: 40 - 无法打开到 SQL Server 的连接 错误解决

    错误信息: “ 标题: 连接到服务器------------------------------ 无法连接到 192.168.1.20. ------------------------------其 ...

  8. asp.net 可视化操作(二)——Sql数据库连接及简单查询功能的实现

    目录 连接数据库 利用repeater控件实现数据显示 查询功能 页面CSS美化 数据插入.更新-- 连接数据库 添加test.aspx 添加控件SqlDataSource,选择配置数据源 选择新建连 ...

  9. dedecms SQL数据库连接信息注解(借鉴)

    <?php $cfg_dbhost = 'localhost'; //数据库地址,这里的localhost指的是本机$cfg_dbname = 'hunuo'; //数据库名$cfg_dbuse ...

  10. SQL数据库连接语句

    一般的远程访问的写成这样: Data Source=IP ;Initial Catalog=数据库名 ;UserID= 用户名 ;Password=密码 本地访问的写成这样: Data Source= ...

随机推荐

  1. MySQL 中information_schema、mysql、performance_schema、sys 简介

    一.information_schema简介在MySQL中,把 information_schema 看作是一个数据库,确切说是信息数据库.其中保存着关于MySQL服务器所维护的所有其他数据库的信息. ...

  2. mybatis中的数据源和连接池

    1.核心配置文件中配置数据库相关属性 <?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE con ...

  3. shell脚本中的函数

    如果你学过开发,肯定知道函数的作用.如果你是刚刚接触到这个概念的话,也没有关系,其实很好理解的.函数就是把一段代码整理到了一个小单元中,并给这个小单元起一个名字,当用到这段代码时直接调用这个小单元的名 ...

  4. 【Java 温故而知新系列】基础知识-05 面向对象

    1.面向对象概述 面向对象(Object-Oriented,简称OO)是一种编程思想,核心思想是将现实世界中的事物抽象为程序中的"对象",通过对象之间的交互来解决问题. 对象 对象 ...

  5. JavaScript 中的组合继承 :ES5 与 ES6 中最近似的写法

    JavaScript 的继承写法较多,在此并不一一讨论,仅对最常用的组合式继承做一个说明: 组合式继承主要利用了原型链继承和构造函数继承. 一.ES5 中的写法 function Person(nam ...

  6. 使用天翼云云容器引擎CCE创建简单nginx服务

    本文分享自天翼云开发者社区<使用天翼云云容器引擎CCE创建简单nginx服务>,作者:b****n 一.创建一个nginx应用. 1.选择资源池,如[杭州2],进入云容器引擎CCE平台页面 ...

  7. Linux Bridge和Tap关系详解

    本文分享自天翼云开发者社区<Linux Bridge和Tap关系详解>,作者:x****n Linux Bridge介绍 Bridge(桥)是Linux上用来做TCP/IP二层协议交换的设 ...

  8. LeetCode 第2题:两数相加

    LeetCode 第2题:两数相加 题目描述 给你两个 非空 的链表,表示两个非负的整数.它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字. 请你将两个数相加,并以相同形式返 ...

  9. shell脚本sed命令

    Sed 是一个脚本型的编译器,全称StreamEDitor,即流编辑器是非交互式的编辑器 sed 原理简析sed 以行为处理单位,每次从标准输入/文本获取一行信息,存储到其" 模式空间 &q ...

  10. IDEA打开多个项目

    IDEA默认的情况下只能打开一个项目,即使添加了一个项目也会弹出一个窗口,将添加的项目显示在新的窗口中.通过下面操作可以,使IDEA打开过个项目. 1.1 打开项目结构 1.2 添加多个项目 点击&q ...