Go入门背景
这里作者讲述一下自己入门go的背景,首先本人一直从事Java应用程序的开发。公司项目统一采用云平台部署,在微服务时代,不同的业务拆分成不同的微服务,也就造成了微服务的数量增加。而Java的应用程序必须运行在jvm虚拟机当中,虚拟机也要占用系统资源。对于一些只负责计算、计算量大或者并发计算高的服务,可以考虑用其他语言编写。
众所周知,Java是JVM平台的语言,一处编译处处运行,这个是Java引以为傲的优点,但它的运行时环境必须在JVM上,这就导致了Java运行时的臃肿,浪费了一部分资源。而Go通过编译成可执行文件巧妙的解决了这个问题,虽然它不是跨平台性语言,但它在编码器层面依然可以实现一套代码编译出不同平台的执行文件,可以直接在各个平台上运行,而不用拖着一个想JVM一样的运行时环境。Go运行时环境就是系统环境,而Java还需要一个虚拟机环境(JVM)。
Go 作为一种为现代多核计算机设计的语言,简单优雅的并发,并且具有强大的并发模型,其设计基于 两级线程模型改进的GMP模型 ,这样能大大减少并发切换线程的性能开销,而且这些模型统一封装到语言级别的调度层,只需通过关键字 go 就可以开启协成,提高了易用性。
Go基础语法学习
首先了解的是Go的语法,对于编程人员来说,学过任意一门语言再学其他语言其实是有不少相似之处的。
Go的变量定义
// go语言的变量申明用var关键字[var] [变量名] [类型]
// 申明一个字符串变量
var str string = ""
// 申明一个数字类型变量
var number int = 0
// 申明一个常量
const name string = "123"
Go的函数定义
// go语言定义函数规则, go函数支持多个返回值
func 函数名(形式参数列表)(返回值列表){
函数体
}
// 以下是笔者在学习的时候写的两个函数,排序算法,
// 带*参数的表示传递的是指针也就是引用传递,默认情况下,
// Go 语言使用的是值传递,即在调用过程中不会影响到实际参数。
func sortArray2(array [10]int) [10]int {
fmt.Println("================")
var length int = len(array)
for i := 0; i < length; i++ {
for j := 0; j < length-1-i; j++ {
if array[j] < array[j+1] {
array[j], array[j+1] = array[j+1], array[j]
}
fmt.Println(array)
}
}
fmt.Println("================")
return array
}
func sortArray(array *[10]int) {
fmt.Println("================")
var length int = len(array)
for i := 0; i < length; i++ {
for j := 0; j < length-1-i; j++ {
if array[j] < array[j+1] {
array[j], array[j+1] = array[j+1], array[j]
}
fmt.Println(array)
}
}
fmt.Println("================")
fmt.Println(array)
}
Go的结构体
// type [名称] struct {}
// 以下定义了一个StudentDemo结构体,含有name,age,birthday三个属性
// 注意结构体属性后面尽量首字母大写,不然会出现访问不到属性的坑
type StudentDemo struct {
Name string
Age int
Birthday time.Time
}
// go的结构体就类似于java中的实体类,当然结构体也可以拥有自己的函数
// 下面是一个StudentDemo结构体的函数,必须用StudentDemo变量调用,
// 可以和此结构体外的函数重名,当然go的函数和java方法不一样,go的函数不能重载
func (s StudentDemo) getStudentAge() int {
return s.age + 1
}
了解以上后,基本就可以写一个简单的crud方法了,对于一些常规的字符串操作,数字操作等,对于有其他语言经验的人来说应该不难。下面开始我们的CRUD接口编写,采用gorm框架 + gin web框架
Go入门之CRUD
// 首先,需要加载配置文件,本次只加载数据库配置
// 新建config.go文件,编写Config结构体,用于存放读取yaml文件的配置
// 定义一个Config结构体全局变量,用于存放配置参数
var Instance *Config
type Config struct {
// mysql
MysqlConfig struct {
Url string `yaml:"url"`
MaxIdleConns int `yaml:"maxIdleConns"`
MaxOpenConns int `yaml:"maxOpenConns"`
ConnMaxIdleTimeSeconds int `yaml:"connMaxIdleTimeSeconds"`
ConnMaxLifetimeSeconds int `yaml:"connMaxLifetimeSeconds"`
} `yaml:"DB"`
// redis
RedisConfig struct {
} `yaml:"Redis"`
}
// 初始化配置文件数据到结构体
func Init(filename string) *Config {
Instance = &Config{}
file, err := os.ReadFile(filename)
if err != nil {
logrus.Error(err)
} else if err = yaml.Unmarshal(file, Instance); err != nil {
logrus.Error(err)
}
return Instance
}
// 编写CRUD的结构体
type BaseModel struct {
FdId string `gorm:"column:fd_id" gorm:"primaryKey" json:"fdId"`
}
type Student struct {
BaseModel
FdAge int `gorm:"column:fd_age" json:"fdAge"`
FdAilas string `gorm:"column:fd_ailas" json:"fdAilas"`
FdClass string `gorm:"column:fd_class" json:"fdClass"`
FdCost float32 `gorm:"column:fd_cost" json:"fdCost"`
FdHight float32 `gorm:"column:fd_hight" json:"fdHight"`
FdName string `gorm:"column:fd_name" json:"fdName"`
FdNo string `gorm:"column:fd_no" json:"fdNo"`
FdSex string `gorm:"column:fd_sex" json:"fdSex"`
FdStudyTime time.Time `gorm:"column:fd_study_time" json:"fdStudyTime"`
}
func (Student) TableName() string {
return "student"
}
// 新增一个student_dao.go文件,编写操作数据库的代码
// 定义一个StudentDao的变量
var StudentDao = newStudentDao()
// 创建studentDao对象的指针函数
func newStudentDao() *studentDao {
return &studentDao{}
}
// 定义一个结构体,下面的函数只能通过这个结构体对象调用
type studentDao struct {
}
func (s *studentDao) Insert(db *gorm.DB, t *model.Student) (err error) {
err = db.Transaction(func(tx *gorm.DB) error {
err = tx.Create(t).Error
if err != nil {
return err
}
// 返回空提交事物,任务错误都会回滚
return nil
})
//tx := db.Begin()
//err = tx.Create(t).Error
//if err != nil {
// tx.Rollback()
// return err
//}
//tx.Commit()
//return nil
//err = db.Create(t).Error
if err != nil {
return err
}
return nil
}
func (s *studentDao) Update(db *gorm.DB, t *model.Student) (err error) {
err = db.Save(t).Error
if err != nil {
return err
}
return nil
}
func (s *studentDao) Delete(db *gorm.DB, fdId string) (err error) {
err = db.Delete(&model.Student{}, "fd_id = ?", fdId).Error
if err != nil {
return err
}
return nil
}
func (s *studentDao) FindByPrimaryKey(db *gorm.DB, fdId string) *model.Student {
ret := &model.Student{}
err := db.First(ret, "fd_id = ?", fdId).Error
if err != nil {
return nil
}
return ret
}
// 新增一个student_service.go文件,这里不多说了,其实就是直接调用dao的函数
var StudentService = newStudentService()
func newStudentService() *studentService {
return &studentService{}
}
type studentService struct {
}
func (s *studentService) Insert(t *model.Student) (err error) {
return dao.StudentDao.Insert(sqls.DB(), t)
}
func (s *studentService) Update(t *model.Student) (err error) {
return dao.StudentDao.Update(sqls.DB(), t)
}
func (s *studentService) Delete(fdId string) (err error) {
return dao.StudentDao.Delete(sqls.DB(), fdId)
}
func (s *studentService) FindByPrimaryKey(fdId string) *model.Student {
return dao.StudentDao.FindByPrimaryKey(sqls.DB(), fdId)
}
// 新增一个student_controller.go文件,这里函数入参统一为(c *gin.Context)
var StudentController = studentController{}
type studentController struct {
}
func (studentController) Add(c *gin.Context) {
var student *model.Student = &model.Student{}
// 把请求体的参数绑定到student中
c.BindJSON(student)
student.FdId = common.IDGenerator.GenerateID()
student.FdStudyTime = time.Now()
err := service.StudentService.Insert(student)
if err != nil {
c.JSON(http.StatusInternalServerError, common.AjaxResult.Fail(""))
}
c.JSON(http.StatusOK, common.AjaxResult.Success(nil))
}
func (studentController) Update(c *gin.Context) {
var student *model.Student = &model.Student{}
c.BindJSON(student)
err := service.StudentService.Update(student)
if err != nil {
c.JSON(http.StatusInternalServerError, common.AjaxResult.Fail(""))
}
c.JSON(http.StatusOK, common.AjaxResult.Success(nil))
}
func (studentController) View(c *gin.Context) {
fdId := c.Param("fdId")
student := service.StudentService.FindByPrimaryKey(fdId)
c.JSON(http.StatusOK, common.AjaxResult.Success(student))
}
func (studentController) Delete(c *gin.Context) {
fdId := c.Param("fdId")
err := service.StudentService.Delete(fdId)
if err != nil {
c.JSON(http.StatusInternalServerError, common.AjaxResult.Fail(""))
}
c.JSON(http.StatusOK, common.AjaxResult.Success(nil))
}
// 新增一个router.go文件,在controller中说到函数入参统一,
// 原因是在注册路由的时候需要满足函数规则
func Router() {
router := gin.Default()
// 使用自定义的全局异常处理
router.Use(common.Recover)
// 分组处理路由
student := router.Group("/student")
{
student.POST("/add", StudentController.Add)
student.POST("/update", StudentController.Update)
// :fdId是路径参数
student.GET("/view/:fdId", StudentController.View)
student.DELETE("/delete/:fdId", StudentController.Delete)
}
teacher := router.Group("/teacher")
{
teacher.POST("/add", TeacherController.Add)
teacher.POST("/update", TeacherController.Update)
teacher.GET("/view/:fdId", TeacherController.View)
teacher.DELETE("/delete/:fdId", TeacherController.Delete)
}
// 监听8080端口
router.Run(":8080")
}
//附加统一返回结构体
var AjaxResult = defaultResult()
type ajaxResult struct {
Code int `json:"code,omitempty"`
Data interface{} `json:"data,omitempty"`
Message string `json:"message,omitempty"`
ErrMsg string `json:"errMsg,omitempty"`
}
// Success 全局统一返回结果/*
func (ajaxResult) Success(data interface{}) *ajaxResult {
result := defaultResult()
result.ErrMsg = ""
result.Message = SUCCESS.Name()
result.Data = data
result.Code = SUCCESS.Value()
return result
}
func (ajaxResult) Fail(message string) *ajaxResult {
result := defaultResult()
result.ErrMsg = ""
result.Data = nil
result.Code = FAIL.Value()
if len(message) > 0 {
result.Message = message
} else {
result.Message = FAIL.Name()
}
return result
}
func (ajaxResult) Error(errMsg string, message string) *ajaxResult {
result := defaultResult()
result.ErrMsg = errMsg
result.Data = nil
result.Code = ERROR.Value()
if len(message) > 0 {
result.Message = message
} else {
result.Message = ERROR.Name()
}
return result
}
func defaultResult() *ajaxResult {
return new(ajaxResult)
}
// Recover 全局异常返回/*
func Recover(c *gin.Context) {
defer func() {
if r := recover(); r != nil {
log.Printf("panic: %v\n", r)
c.JSON(http.StatusOK, AjaxResult.Error(errorToString(r), ""))
// 终止调用
c.Abort()
}
}()
// 继续调用
c.Next()
}
func errorToString(r interface{}) string {
switch v := r.(type) {
case error:
return v.Error()
default:
return r.(string)
}
}
// main.go文件
var configFile = flag.String("config", "./code/study-go.yaml", "配置文件路径")
func main() {
flag.Parse()
// 读取配置文件
conf := config.Init(*configFile)
// orm
gormConf := &gorm.Config{
Logger: logger.New(logrus.StandardLogger(), logger.Config{
SlowThreshold: time.Second,
Colorful: true,
LogLevel: logger.Warn,
IgnoreRecordNotFoundError: true,
}),
// 禁用全局默认的事物
//SkipDefaultTransaction: true,
}
// 数据库
dbConfig := sqls.DbConfig{
Url: conf.MysqlConfig.Url,
MaxIdleConns: conf.MysqlConfig.MaxIdleConns,
MaxOpenConns: conf.MysqlConfig.MaxOpenConns,
ConnMaxIdleTimeSeconds: conf.MysqlConfig.ConnMaxIdleTimeSeconds,
ConnMaxLifetimeSeconds: conf.MysqlConfig.ConnMaxLifetimeSeconds,
}
// 开启数据库连接
err := sqls.Open(dbConfig, gormConf)
if err != nil {
logrus.Error(err)
}
// 监听路由
controller.Router()
}
// yaml文件
# 数据库连接
DB:
url: dbname:password@tcp(127.0.0.1:3302)/databasename?charset=utf8mb4&parseTime=True&loc=Local
maxIdleConns: 50
maxOpenConns: 200
connMaxIdleTimeSeconds: 30000
connMaxLifetimeSeconds: 60000
至此,使用go语言 + gorm + gin 编写的简单CRUD已经结束,此过程笔者花了四天的空余时间,从学习go基础语法到编写CRUD,当然go远不止这些,这只是一个简单的入门。如下测试截图