首页 > 数据库 >sql数据库连接

sql数据库连接

时间:2023-04-26 11:01:11浏览次数:43  
标签:err 数据库 mysql db 连接 sql Println fmt

前言

作为数据存储的数据库,近年来发展飞快,在早期的程序自我存储到后面的独立出中间件服务,再到集群,不可谓不快了。早期的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数据库,下载对应驱动就行,也验证了上面的一个说法。

标签:err,数据库,mysql,db,连接,sql,Println,fmt
From: https://www.cnblogs.com/Jack-artical/p/17351588.html

相关文章

  • MySQL主从复制Slave_IO_Running为No
    主要记录解决问题的过程,为以后发现类似问题提供解决方法的参考。首先查看从机的mysql日志文件:tail/var/log/mysqld.log日志从上往下看,可以很快看到在中间位置上有一个ERROR的标志,后面写得很清楚,我的主机UUID和从机UUID重复了,而这两个UUID在这里要求必须要不相等的,所以我上面......
  • sqlmap挂代理走burp
    参考:sqlmap的两种挂代理方式_sqlmap--proxy_杰尼龟会呲水的博客-CSDN博客......
  • SQL查询:优化注意事项
    DISTINCT:过滤掉多余的重复记录只保留一条,效率低;和ORDERBY结合使用可以提高效率;可以用GROUPBY替代。IN与EXISTS:IN适合指定常量列表,EXISTS适合用于判断在另一张表中是否存在指定值。IN适合内外表都很大的情况,EXISTS适合外表结果集很小的情况;NOTIN用(外连接+判断为空)方案......
  • [SQL Server 2008R2] 有关于判断表、字段、存过等元素是否存在相关SQL写法
    表相关普通表查询普通表是否存在可以使用object_id函数,下面的例子是查询表“t_test”是否存在之后从而进行其他的DLL操作:ifobject_id('t_test')isnotnullbegin--如果表存在这段里面写相关逻辑select1end 临时表临时表同样可以用object_id但......
  • MySQL数据类型
     DB哥MySQL高级教程-系统学习MySQL共149课时关注微信公众号免费学:【DB哥】文末有MySQL高级课程目录1、MySQL数据类型MySQL支持多种类型,大致可以分为三类:数值、日期/时间和字符串(字符)类型。 1.2、mysql中编码和字符在mysql中,一个中文汉字所占的字节数与编码格式有......
  • MySQL索引详解
     DB哥MySQL高级教程-系统学习MySQL共149课时加我微信公众号免费学:DB哥文末有MySQL高级课程目录前言因为现在使用的mysql默认存储引擎是Innodb,所以本篇文章重点讲述Innodb下的索引,顺带简单讲述其他引擎。希望小伙伴们能通过这片文章对mysql的索引有更加清晰的认识,废话不多......
  • mybatis where标签动态sql问题
    使用where标签注意事项:where标签只会去掉第一个多出来的and和or,使用where标签时要把and放到前面这种情况下生成的SQL更干净,更贴切,不会在任何情况下都有where1=1这样的条件。<selectid="search"resultType="com.example.springweb2.pojo.Member"> selectid,name,a......
  • 权限模型与建表及SQL语句编写
    权限模型RBAC权限模型​RBAC权限模型(Role-BasedAccessControl)即:基于角色的权限控制。这是目前最常被开发者使用也是相对易用、通用权限模型。 准备工作      菜单表实体类}  建表及SQL语句编......
  • SQL Server实现group_concat功能的详细实例
    目录一、实现二、原理分析2.1、FOR XML PATH的作用2.2、STUFF函数2.2.1、STUFF函数在本SQL的作用2.2.2、STUFF函数语法2.3、sql语分分析2.3.1、一个简单的group by2.3.2、在select语句后面加上子查询2.3.3、去掉子查询结果集的第一个分隔符总结一、实......
  • Kali系统 连接 Xshell
    1>进入kali系统,修改ssh配置文件:vi/etc/ssh/sshd_config2>将PermitRootLoginwithout-password修改为:PermitRootLoginyes快速查找::setnumber/PermitRootLogin3>找到#PasswordAuthenticationyes把#的注释去掉快速查找::setnumber/PasswordAuthentication......