版本信息:
go版本:go1.17.4 windows/amd64
gorm版本:v1.22.4
mysql版本:8.0
go官网:go.dev
1.windows下交叉编译Linux环境下的可执行文件。
不管是用powershell还是cmd,都需要使用管理员打开,才能正常编译。如果你是使用visual studio code,vs code终端默认使用的是powershell,如下:
需要使用下面的命令先设置环境变量:
// 要带上$
$Env:GOOS = "linux"
$Env:GOARCH = "amd64"
然后在项目目录下执行:
go build
就会自动生成一个不带扩展名的可执行文件,将该文件放到Linux上,直接执行就可以了,可能会报一个错误:Permission Denied,这个是因为权限不够导致的,给一个权限就行了,比如:
chmod 777 ./gotest
当然权限你可以自己更细地去控制。
如果是cmd,那么执行下面的命令:
set GOOS = linux
set GOARCH = amd64
然后再执行build命令就可以了。
2.使用gorm连接数据库。gorm是中国开发者开发的一个插件,在go官方的文档里有推荐。这里以mysql为例介绍下它的使用方法。引入gorm:
import (
"gorm.io/gorm"
)
然后在项目根目录下使用命令下载依赖:
go get .
依赖下载好之后,创建一个struct:
type Product struct {
gorm.Model
Code string
Price uint
}
继承自gorm.Model,其中Model的定义查看go源码可以发现如下:
type Model struct {
ID uint `gorm:"primarykey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt DeletedAt `gorm:"index"`
}
这里的命名方式一律采用大写开头的驼峰式命名。创建好结构体之后,使用下面的方法连接mysql数据库:
dsn := "root:123@tcp(127.0.0.1:3306)/my_schema?
charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: dsn, // DSN data source name
DefaultStringSize: 256, // string 类型字段的默认长度
DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
}), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
连接好数据库之后,如果是代码先的方式,那么需要迁移数据库,方法如下:
// 迁移 schema
db.AutoMigrate(&Product{})
执行完该句代码后,查看数据库,会发现多了一张products表,这里gorm给表名自动加了s,同时列名也以大写字母位置使用下划线隔开的风格为标准,这是符合mysql的命名规范的,如下:
如果该数据库已经存在,则是根据结构体的情况决定是否更新数据表,如果结构体未发生变化,则不需要更新,如果发生了变化,且table表结构未及时更新,那么调用AutoMigrate方法就会触发数据库更新。创建好表之后,则是增删改查数据,具体操作如下:
// Create
db.Create(&Product{Code: "D42", Price: 100})
// Read
var product Product
db.First(&product, 1) // 根据整形主键查找
db.First(&product, "code = ?", "D42") // 查找 code 字段值为 D42 的记录
// Update - 将 product 的 price 更新为 200
db.Model(&product).Update("Price", 200)
// Update - 更新多个字段
db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // 仅更新非零值字段
db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})
// Delete - 删除 product
// 这里的删除是将delete_at字段的值设置为当前的值
db.Delete(&product, 1)
3.gorm数据库先操作数据库。前面我们用gorm操作数据库,是采用代码先的方式,所谓代码先就是先有代码,后有数据库结构,实际上大多数场景下,这个方式都是不合适的,为了更好的管理数据库,我们一般都是自己设计数据库结构,然后再用orm连接数据库进行操作。
创建一张user_table表,如下:
创建struct同时继承接口TableName:
type User struct {
Uid uint `gorm:"primarykey;column:id"`
UserName string
Telephone string
Nickname string
PW string `gorm:"column:password"`
}
func (User) TableName() string {
return "user_table"
}
id在数据库中是自增的,因此在插入数据的时候,不需要给id的值,插入数据方法如下:
data := User{UserName: "test", Telephone: "12345678912",
Nickname: "测试", PW: "sdfjklsjfalk"}
db.Create(&data)
插入数据后,生成的自增id会自动填充User的uid,此时struct的uid就是数据库返回的自增id
如果想在指定的列插入,如下,只插入UserName和Telephone两个值:
data := User{UserName: "test", Telephone: "12345678912",
Nickname: "测试", PW: "sdfjklsjfalk"}
db.Select("UserName", "Telephone").Create(&data)
4.引用其他项目的模块。创建两个目录,一个叫go-lib,一个叫go-test。
在go-lib目录下执行:
go mod init go-lib
在go-test目录下执行:
go mod init go-test
再分别在两个目录下创建子目录help,在go-test目录下创建文件main.go,如下:
此时的go-test和go-lib相当于两个项目。
分别在两个两个help目录下创建help.go文件,其中go-lib里面的内容如下:
package help
import "fmt"
func SayHello() {
fmt.Println("hello from go-lib")
}
go-test里面的内容如下:
package help
import "fmt"
func SayHello() {
fmt.Println("hello from go-test")
}
目录结构如下:
现在我们希望在go-test项目里调用go-lib项目的某个方法,比如在go-test的main方法里面调用go-lib的help包下的SayHello方法,代码如下:
package main
import (
"go-lib/help"
)
func main() {
help.SayHello()
}
这个时候如果编译go-test会报:main.go:4:2: package go-lib/help is not in GOROOT的错误,这是因为找不到go-lib/help这个包。在go-test的go.mod文件里面添加如下代码:
module go-test
go 1.17
// 添加如下内容
// 描述依赖的package名,v0.0.0用于网上版本的版本号,本地模式也要有
require go-lib v0.0.0
// 描述依赖的package所在的目录,../go-lib就是路径
replace go-lib => ../go-lib
再使用
go run .
就可以正常编译并运行了,运行结果如下:
这样我们就实现了调用不同项目的包里面的函数。
这里是如何区分两个help包的呢,关键在于引用代码使用的module名,也就是下面的go-lib:
import (
"go-lib/help"
)
如果把go-lib改成go-test,那么引用的就是当前项目的help包,比如下面这样:
package main
import (
"go-test/help"
)
func main() {
help.SayHello()
}
运行结果如下:
所以我们最初使用的命令:
go mod init go-lib
go mod init go-test
其实定义的是模块名,以方便其他的项目引用。这个命令并不会创建任何额外的目录,只会在当前目录下创建一个go.mod文件。go.mod文件规定了go的版本,和当前项目的module的名称。
5.上例中,我们是在本地引用电脑里其他项目的包,这里说一下如何将go-lib发布到github并通过远程下载的方式在本地项目里面引用。
在github创建一个public的项目,名字叫go-lib。将本地的go-lib上传到刚创建的仓库,这里上传方法不再介绍,创建好仓库后,github会有上传步骤。上传前先做一下修改,将go.mod文件里面的module名字改为你的github仓库所在的路径,比如:
module github.com/DaiAnpeng/go-lib
go 1.17
让引用稍微复杂一些,这里多添加一个模块叫common,help最终引用的是common的sayhello方法,如下:
common.go的代码如下:
package common
import "fmt"
func SayHello() {
fmt.Println("hello from go-lib")
}
help.go的代码改为:
package help
import "github.com/DaiAnpeng/go-lib/common"
func SayHello() {
common.SayHello()
}
修改好之后,将项目推送到github,这里是我推送的代码示例:https://github.com/DaiAnpeng/go-lib
推送好之后,就可以在任意的项目里面通过:
import (
"github.com/DaiAnpeng/go-lib/help"
)
引用该项目里面的包了。
随便新建一个项目叫go-publish-study,使用go mod初始化,然后添加main函数,内容如下:
package main
import "github.com/DaiAnpeng/go-lib/help"
func main() {
help.SayHello()
}
项目的目录如下:
这里的go.sum是执行下面的命令自动创建的:
go get .
然后执行:
go run .
结果如下:
这样,我们就成功发布了自己的开源项目。
6.go发布包的版本控制。
go的版本控制是遵循Semantic Versioning协议的。