• 技术文章 >后端开发 >Golang

    go语言可以写数据库么

    青灯夜游青灯夜游2023-01-06 10:35:28原创123

    go语言可以写数据库。Go语言和其他语言不同的地方是,Go官方没有提供数据库驱动,而是编写了开发数据库驱动的标准接口,开发者可以根据定义的接口来开发相应的数据库驱动;这样做的好处在于,只要是按照标准接口开发的代码,以后迁移数据库时,不需要做任何修改,极大方便了后期的架构调整。

    本教程操作环境:windows7系统、GO 1.18版本、Dell G3电脑。

    数据库(database)是按照数据结构来组织、存储和管理数据的仓库。相对于其他存储方式,存储只是数据库的其中一个功能,数据的组织和管理才是数据库的核心。

    相较于数据库,文件保存数据的缺点:

    数据库的使用水平是衡量一个程序员能力的重要指标。

    1、MySQL简介

    MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发,目前属于甲骨文公司(Oracle)旗下产品。MySQL是最流行的关系型数据库管理系统之一,而在Web应用方面,MySQL是最好的RDBMS(Relational Database Management System,关系数据库管理系统)应用软件之一。

    1.1 安装MySQL

    1.png

    1.2 MySQL常见命令

    > mysql -h主机地址 -u用户名 -p用户密码

    2.png

    假设远程主机的IP为:192.168.1.1,用户名为root,密码为root。

    > mysql -h192.168.1.1 -uroot -proot
    mysql> exit/quit;

    2、database/sql

    Go操作数据库,是通过database/sql包以及第三方的实现了database/sql/driver接口的数据库驱动包来共同完成的。

    其中database/sql/driver中的接口Conn和Stmt,官方交给第三方实现驱动,并且是协程不安全的。官方实现的database/sql包中的DB和Stmt是协程安全的,因为内部实现是连接池。

    Go语言和其他语言不同的地方是,Go官方没有提供数据库驱动,而是编写了开发数据库驱动的标准接口,开发者可以根据定义的接口来开发相应的数据库驱动。这样做的好处在于,只要是按照标准接口开发的代码,以后迁移数据库时,不需要做任何修改,极大方便了后期的架构调整。

    在每一个由第三方开发者编写的数据库驱动中,都会实现一个init函数,在init函数内会调用一个叫Register的方法来完成数据库驱动的注册。

    func Register(name string, driver driver.Driver)

    Register注册并命名一个数据库,可以在Open函数中使用该命名启用该驱动。如果Register注册同一名称两次,或者driver参数为nil,会导致panic。

    func init() {
        sql.Register("mysql", &MySQLDriver{})
    }

    包在引入的过程中会自动调用包中的init函数,因此,注册数据库驱动时只需要使用匿名导入的方式引用该包即可,这样代码就可以直接使用这个数据库驱动。

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

    3、数据库基本操作

    数据库的最基本操作便是增(create)、查(read)、改(update)、删(delete),简称CRUD。数据库中有八类对象,分别是数据库、数据表、记录、字段、索引、查询、过滤器、视
    图。

    3.1 连接数据库

    Go语言中,sql包提供了一个Open方法来创建一个数据库连接。

    func Open(driverName, dataSourceName string) (*DB, error)

    Open打开一个dirverName指定的数据库,dataSourceName指定数据源,一般至少包括数据库文件名和(可能的)连接信息。

    Open函数只是验证其参数,而不创建与数据库的连接。如果要检查数据源的名称是否合法,应调用返回值的Ping方法。

    func (db *DB) Ping() error

    Ping检查与数据库的连接是否仍有效,如果需要会创建连接。

    package main
    
    import (
       "database/sql"
       _ "github.com/go-sql-driver/mysql"
       "log"
    )
    
    func main() {
       db, err := sql.Open("mysql", "root:200039@tcp(127.0.0.1:3306)/gostudy")
       if err != nil {
          log.Fatal(err)
       }
       defer db.Close()
       //验证连接的可用性
       err = db.Ping()
       if err != nil {
          log.Fatal("数据库连接失败:", err)
       }
       log.Println("数据库连接成功!")
    }

    3.png

    3.2 创建数据表

    创建MySQL数据表需要定义表名、表字段名、字段类型及约束。创建数据表语法结构为:

    CREATE TABLE 表名 ( 
     	字段名1 数据类型 [列级别约束条件] [默认值],
     	字段名2 数据类型 [列级别约束条件] [默认值],
     	字段名3 数据类型 [列级别约束条件] [默认值],
     	... [表级别约束条件] 
    );

    创建一张数据表

    CREATE TABLE `user`(
    		`uid` INT(10) NOT NULL AUTO_INCREMENT,
    		`username` VARCHAR(64) NULL DEFAULT 1,
    		`gender` TINYINT(1) NULL DEFAULT NULL,
    		`password` VARCHAR(64) NULL DEFAULT NULL,
    		`created` DATE NULL DEFAULT NULL,
    		PRIMARY KEY (`uid`)
    );

    4.png

    使用Go语言创建数据表需要使用Exec函数。

    func (db *DB) Exec(query string, args ...interface{}) (Result, error)

    Exec执行一次命令(包括查询、删除、更新、插入等),不返回任何执行结果。参数args表示query中的占位参数。

    Exec的返回值为Result接口,Result的定义如下:

    type Result interface {
     	LastInsertId() (int64, error)
     	RowsAffected() (int64, error)
    }

    Result主要有两个方法。LastInsertId返回一个数据库生成的回应命令的整数,当插入新行时,返回由数据库执行插入操作得到的自增ID号。RowsAffected返回被update、insert或delete命令影响的行数。

    3.3 插入数据

    MySQL中使用INSERT INTO语句来插入数据,插入的语法结构为:

    INSERT INTO table_name ( field1, field2,...fieldN ) VALUES ( value1, value2,...valueN );

    如果需要同时插入多条数据,可以使用如下方式:

    INSERT INTO table_name (field1, field2,...fieldN) VALUES (valueA1,valueA2,...valueAN),(valueB1,valueB2,...value BN)......;
    package main
    
    import (
       "database/sql"
       _ "github.com/go-sql-driver/mysql"
       "log"
       "time"
    )
    
    func checkErr(err error) {
       if err != nil {
          log.Fatal(err)
       }
    }
    
    func main() {
       db, err := sql.Open("mysql", "root:200039@tcp(127.0.0.1:3306)/gostudy")
       checkErr(err)
       defer db.Close()
    
       //验证连接可用性
       err = db.Ping()
       checkErr(err)
       log.Println("数据库连接成功")
    
       rs, err := db.Exec("insert into `user`(username,gender,password,created) values (?,?,?,?)", "tom", 1, "123456", time.Now())
       checkErr(err)
    
       rowCount, err := rs.RowsAffected()
       checkErr(err)
       log.Printf("插入了 % d行", rowCount)
    }

    5.png

    SQL注入(SQLi)是一种注入攻击,可以执行恶意SQL语句。它通过将任意SQL代码插入数据库查询,使攻击者能够完全控制Web应用程序后面的数据库服务器。攻击者可以使用SQL注入漏洞绕过应用程序安全措施;可以绕过网页或Web应用程序的身份验证和授权,并检索整个SQL数据库的内容;还可以使用SQL注入来添加、修改和删除数据库中的记录。

    sql包还提供一种预编译的方式来执行SQL语句,通常在处理批量SQL语句时会用到这种方式,这种方式比手动拼接字符串SQL语句高效,还可以防止SQL注入攻击。

    func (db *DB) Prepare(query string) (*Stmt, error)

    Prepare创建一个准备好的状态用于之后的查询和命令。返回值可以同时执行多个查询和命令。

    package main
    
    import (
       "database/sql"
       _ "github.com/go-sql-driver/mysql"
       "log"
       "time"
    )
    
    func checkErr(err error) {
       if err != nil {
          log.Fatal(err)
       }
    }
    
    func main() {
       db, err := sql.Open("mysql", "root:200039@tcp(127.0.0.1:3306)/gostudy")
       checkErr(err)
       defer db.Close()
    
       //验证连接可用性
       err = db.Ping()
       checkErr(err)
       log.Println("数据库连接成功")
    
       //rs, err := db.Exec("insert into `user`(username,gender,password,created) values (?,?,?,?)", "tom", 1, "123456", time.Now())
       stmt, err := db.Prepare("INSERT INTO `user`(username,gender,password,created) VALUES (?,?,?,?)")
    
       defer stmt.Close()
       rs, err := stmt.Exec("Ailsa", 0, "111111", time.Now())
       checkErr(err)
       rowCount, err := rs.RowsAffected()
       checkErr(err)
       log.Printf("插入了 % d行", rowCount)
    }

    6.png

    3.4 查询数据

    MySQL数据库使用SELECT语句来查询数据。以下为在MySQL数据库中查询数据通用的SELECT语法:

    SELECT column_name,column_name
     FROM <表 1>, <表 2>...
     JOIN<表3>on...
     [WHERE <表达式>
     [GROUP BY <group by definition>
     [HAVING <expression> [{<operator> <expression>}...]]
     [ORDER BY <order by definition>]
     [LIMIT[<offset>,] <row count>]

    语法解释:

    在Go语言中,我们可以使用Query函数来查询数据:

    func (db *DB) Query(query string, args ...interface{}) (*Rows, error)

    Query执行一次查询,返回多行结果(即Rows),一般用于执行SELECT命令。参数args表示
    Query中的占位参数,Rows是查询的结果。它的游标指向结果集的第0行,使用Next方法来遍历各行结果。查询到数据后使用rows.Next获取一行结果,并使用Scan将查询到的结果赋值到目标变量中。

    func (r *Row) Scan(dest ...interface{}) error
    package main
    
    import (
       "database/sql"
       _ "github.com/go-sql-driver/mysql"
       "log"
    )
    
    type User struct {
       Uid      int
       Username string
       Gender   bool
       Password string
       Created  string
    }
    
    func checkErr(err error) {
       if err != nil {
          log.Fatal(err)
       }
    }
    
    func main() {
       db, err := sql.Open("mysql", "root:200039@tcp(127.0.0.1:3306)/gostudy")
       checkErr(err)
       defer db.Close()
    
       //验证连接的可用性
       err = db.Ping()
       checkErr(err)
       log.Println("数据库连接成功!")
    
       rows, err := db.Query("select * from `user` where username=?", "Tom")
       defer rows.Close()
    
       for rows.Next() {
          user := User{}
          err := rows.Scan(&user.Uid, &user.Username, &user.Gender, &user.Password, &user.Created)
          checkErr(err)
    
          log.Println(user)
       }
    
    }

    7.png

    3.5 更改数据

    如果需要修改或更新MySQL中的数据,我们可以使用UPDATE命令来操作。

    UPDATE <表名> 
     SET 字段 1=值 1 [,字段 2=值 2... ] 
     [WHERE 子句 ]
     [ORDER BY 子句] 
     [LIMIT 子句]

    语法解释:

    注意:修改一行数据的多个列值时,SET子句的每个值用逗号分开即可。

    rs, err := db.Exec("update `user` set password=? where sername=?","123123","john")
    checkErr(err)
    rowCount, err := rs.RowsAffected()
    checkErr(err)
    if rowCount > 0 {
      log.Println("更新成功!")
    }

    3.6 删除数据

    MySQL使用DELETE语句从单个表中删除数据,语法格式为:

    DELETE FROM <表名> 
     [WHERE 子句] 
     [ORDER BY 子句] 
     [LIMIT 子句]

    语法解释:

    注意:在不使用WHERE条件的时候,将删除所有数据;数据库一旦删除数据,数据就会永远
    消失。因此,在执行DELETE语句之前,应该先备份数据库,以防需要找回被删除的数据。

    rs, err := db.Exec("delete from `user` where uid=?",3)
    checkErr(err)
    rowCount, err := rs.RowsAffected()
    checkErr(err)
    if rowCount > 0 {
        log.Println("删除成功!")
    }

    3.7 MySQL事务

    MySQL数据库中的事务是用户一系列的数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位。

    事务具有四个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。这四个特性简称为ACID原则。

    事务的ACID原则保证了一个事务或者成功提交,或者失败回滚,二者必居其一。因此,它对事务的修改具有可恢复性,即当事务失败时,它对数据的修改都会恢复到该事务执行前的状态。

    简单来说,事务处理就两个过程,要么成功提交,要么失败回滚,在Go语言中使用Tx结构体来表示事务。

    type Tx interface{
     Commit() error
     Rollback() error
    }

    Tx代表一个进行中的数据库事务。一次事务必须以对Commit或Rollback的调用结束。调用Commit或Rollback后,所有对事务的操作都会失败并返回错误值ErrTxDone。

    package main
    
    import (
       "database/sql"
       _ "github.com/go-sql-driver/mysql"
       "log"
    )
    
    func checkErr(err error) {
       if err != nil {
          log.Fatal(err)
       }
    }
    
    func checkErrWithTx(err error, tx *sql.Tx) {
       if err != nil {
          tx.Rollback()
          log.Fatal(err)
       }
    }
    
    func main() {
       db, err := sql.Open("mysql", "root:200039@tcp(127.0.0.1:3306)/gostudy")
       checkErr(err)
       defer db.Close()
    
       //验证连接的可用性
       err = db.Ping()
       checkErr(err)
       log.Println("数据库连接成功!")
    
       var password string
       tx, err := db.Begin()
       checkErr(err)
    
       //查找Tom的密码,如果密码为123456就将密码改为111111,否则不执行任何操作
       err = tx.QueryRow("select password from `user` where username=?", "Tom").Scan(&password)
       checkErrWithTx(err, tx)
       if password == "123456" {
          rs, err := tx.Exec("update `user` set password=? where username=?", "111111", "Tom")
          checkErrWithTx(err, tx)
          rowCount, err := rs.RowsAffected()
          checkErrWithTx(err, tx)
          if rowCount > 0 {
             log.Println("密码更新完成!")
          }
       }
       tx.Commit()
       log.Println("事务处理完成!")
    }

    8.png

    4、知识拓展

    【相关推荐:Go视频教程编程教学

    以上就是go语言可以写数据库么的详细内容,更多请关注php中文网其它相关文章!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    专题推荐:go语言 Golang 数据库
    上一篇:golang是免费的吗 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • php PDO导入数据库失败怎么办• SQL Server跨服务器操作数据库的图文方法(LinkedServer)• thinkphp可以单独配置新数据库吗• go语言支不支持安卓开发• golang怎么将字符串转为整型• go语言能开发服务器端吗
    1/1

    PHP中文网