go-zero(三) 基于MySQL:实现数据库操作

go zero 基于MySQL:实现数据库操作

下面通过用户的注册和登录服务,来介绍如何在go zero中使用 MySQL 数据库.

一、Docker安装mysql

我们使用以下 Docker Compose 文件来部署 MySQL 服务,该文件包含了 MySQL 服务的各项配置信息,包括镜像版本、环境变量、端口映射、数据卷挂载、命令参数等。

账号root ,密码PXXDWCSeWDQ341 ,如果需要更改,请修改配置文件:

version: '3'

######## 项目依赖的环境,启动项目之前要先启动此环境 #######
services:
  mysql:
    image: mysql/mysql-server:8.0.28
    container_name: mysql
    environment:
      # 时区上海 - Time zone Shanghai (Change if needed)
      TZ: Asia/Shanghai
      # root 密码 - root password
      MYSQL_ROOT_PASSWORD: PXXDWCSeWDQ341 #请自定密码
    ports:
      - 33069:3306
    volumes:
      # 数据挂载 - Data mounting
      - ./data/mysql/data:/var/lib/mysql
      # 日志
    command:
      # 将mysql8.0默认密码策略 修改为 原先 策略 (mysql8.0对其默认策略做了更改 会导致密码无法匹配) 
      # Modify the Mysql 8.0 default password strategy to the original strategy (MySQL8.0 to change its default strategy will cause the password to be unable to match)
      --default-authentication-plugin=mysql_native_password
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
      --explicit_defaults_for_timestamp=true
      --lower_case_table_names=1
    privileged: true
    restart: always
    networks:
      - gozero_net
  

networks:
  gozero_net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.16.0.0/16

使用以下命令执行文件,启动 MySQL 服务:

docker compose up

为了方便本地工具连接 MySQL,我们需要进入容器,给 root 设置远程连接权限。以下是操作步骤:

docker exec -it mysql mysql -uroot -p
##输入密码:PXXDWCSeWDQ341 
##如果已经更改配置输入自定义密码
use mysql;
update user set host='%' where user='root';
FLUSH PRIVILEGES;

二、创建演示库和表

在 MySQL 中,我们将创建名为 test_zero 的数据库,并在其中创建 user 表,该表将用于存储用户的注册和登录信息。

create database test_zero;

use test_zero;

CREATE TABLE `users` (
	`id` BIGINT NOT NULL AUTO_INCREMENT,
	`username` VARCHAR(50) NOT NULL COLLATE 'utf8_general_ci',
	`password` VARCHAR(255) NOT NULL COLLATE 'utf8_general_ci',
	`created_at` TIMESTAMP NULL DEFAULT (CURRENT_TIMESTAMP),
	PRIMARY KEY (`id`) USING BTREE,
	UNIQUE INDEX `username` (`username`) USING BTREE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;

这里将 username 字段设置为唯一(UNIQUE),这样做的目的是确保用户名称的唯一性,避免出现多个用户使用相同用户名的情况。

当我们使用 goctl 工具生成模型代码时,它会根据这个唯一性设置自动生成通过 username 字段查找数据的方法,方便后续的用户查询和信息修改操作。

三、生成项目

在 Go Zero 框架中,使用 goctl 工具可以极大地简化开发过程,尤其是在生成模型代码和项目代码方面。

1.生成model代码

在go zero中可以使用goctl工具, 根据数据库,自动帮我们生成model代码,自动帮我实现数据库的基础crud方法。

goctl 生成model代码主要有2中方法,一种是通过sql文件,另一种是通过数据库连接字符串:

goctl model命令用于model层代码生成,目前暂时仅支持根据mysql指定ddl和datasource来生成。

使用sql文件生成

我们创建user\model目录,在model目录下创建user.sql文件:
user.sql:

CREATE TABLE `users` (
	`id` BIGINT NOT NULL AUTO_INCREMENT,
	`username` VARCHAR(50) NOT NULL COLLATE 'utf8_general_ci',
	`password` VARCHAR(255) NOT NULL COLLATE 'utf8_general_ci',
	`created_at` TIMESTAMP NULL DEFAULT (CURRENT_TIMESTAMP),
	PRIMARY KEY (`id`) USING BTREE,
	UNIQUE INDEX `username` (`username`) USING BTREE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;

接着使用 goctl 命令生成模型代码:

 goctl model mysql ddl --src user.sql --dir ./

或者使用datasource生成

当然也可以通过指定mysql连接地址来生成model代码,实现相同的效果:
goctl model mysql datasource -url="user:password@tcp(127.0.0.1:3306)/test_zero" -table="user" -dir ./model

  • url:指定数据库连接地址,如user:password@tcp(127.0.0.1:3306)/test_zero
  • table:指定表名,支持通配符匹配,即匹配gozero数据库中的表
  • dir:指定代码存放的目标文件夹

model文件介绍

当你看到 Done. 输出则代表生成成功了,帮我们生成了下面3个文件。

$ tree
.
├── usermodel.go
├── usermodel_gen.go
└── vars.go

  • usermodel.go: 定义数据库表的模型及其业务逻辑。
  • usermodel_gen.go: 自动生成的代码,包含数据库操作的实现。
  • vars.go: 全局变量和配置的定义,供各个模块使用。

3.查看model代码

现在我们来具体看下goctl帮我们生成的model代码,我们先看下usersmodel.go文件,

它帮我们定义了NewUsersModel 用来返回user表模型

// NewUsersModel returns a model for the database table.
//NewUsersModel 返回数据库表的模型
func NewUsersModel(conn sqlx.SqlConn) UsersModel {
	return &customUsersModel{
		defaultUsersModel: newUsersModel(conn),
	}
}

接着看下usersmodel_gen.go文件:

帮我们根据数据库自动生成了数据模型

 
	Users struct {
		Id        int64     `db:"id"`
		Username  string    `db:"username"`
		Password  string    `db:"password"`
		CreatedAt time.Time `db:"created_at"`
	}

给usersModel定义了基本的增删改查的接口

usersModel interface {
    Insert(ctx context.Context, data *Users) (sql.Result, error)
    FindOne(ctx context.Context, id int64) (*Users, error)
    FindOneByUsername(ctx context.Context, username string) (*Users, error)
    Update(ctx context.Context, data *Users) error
    Delete(ctx context.Context, id int64) error
}

2. 创建api并生成项目代码,

接下来,我们根据用户表的结构创建user.api文件,用于定义 API 服务的接口信息。

syntax = "v1"

type (
	// 定义注册接口的 json 请求体
	RegisterRequest {
		//请求体定义了 Username 和Password 字段, 并且都设置了不能为空
		Username string `json:"username" validate:"required"`   
		Password string `json:"password" validate:"required"`
	}
	// 定义注册接口的 json 响应体
	RegisterResponse {
		//响应体 定义类一个Message  用来返回结果
		Message string `json:"message"`
	}
)

type (
	// 定义登录接口的 json 请求体
	LoginRequest {
		Username string `json:"username" validate:"required"`
		Password string `json:"password" validate:"required"`
	}
	// 定义登录接口的 json 响应体
	LoginResponse {
		//正常的业务逻辑,用户登录后会产生一个token,用来记录登录信息
		Token string `json:"token"`
	}
)


@server (
	group:  user //  生成代码时都会被放到 user 目录下
	prefix: /v1 //定义路由前缀为 "/v1"
)


// 定义 HTTP 服务
// 微服务名称为 user-api,生成的代码目录和配置文件将和 user 值相关
service user-api {
	
	// 定义用户注册接口
    //定义 http.HandleFunc 转换的 go 文件名称及方法
	@handler RegisterHandler
	// 请求方法为 post
    // 路由为 /register 
    // 请求体为 RegisterRequest
    // 响应体为 RegisterResponse,响应体必须有 returns 关键字修饰
	post /register (RegisterRequest) returns (RegisterResponse)

	//用户登录
	@handler LoginHandler
	post /login (LoginRequest) returns (LoginResponse)
}

这个 API 的结构可以通过注释清晰地理解。在下面这段代码中,我们可以看到请求参数和响应参数之间通过 returns 进行修饰:

post /register (RegisterRequest) returns (RegisterResponse)

需要注意的是,请求参数响应参数并不总是必需的 。

例如,在后面的教程中我们会使用到获取用户信息和更新用户信息,在更新数据时,我们可以省略响应体,只保留请求参数:

post /update (UpdateUserInfoReq)

同样,当我们通过jwt 获取数据时,也可以省略请求体,而只定义响应参数:

get /getinfo returns (UserInfoResp)

生成项目代码

我们创建一个新的项目,目录设置为user-api ,使用goctl通过user.api生成项目代码:

goctl api go --api user.api --dir ./

四、项目演示

1.链接数据库

go zero 提供了一个强大的 sqlx 工具,用于操作数据库。 所有 SQL 相关操作的包在 github.com/zeromicro/go zero/core/stores/sqlx

在使用 go zero 框架与数据库交互时,通常会遵循一系列的步骤和逻辑,下面是调用数据库的典型顺序和逻辑:

增加数据库连接配置

打开 etc\user-api.yaml文件,增加 MySQL 连接字符串:

#定义了一个名为MysqlDB的结构体,并有一个名为DbSource的字符串。
MysqlDB:
	# 字符串请根据实际配置环境更改
  DbSource: "root:root@tcp(127.0.0.1:3306)/test_zero"	

设置结构体,用来解析配置文件

现在我们需要把这个MysqlDB这个字段映射到 go zero的结构体中,打开internal/config/config.go 文件,把代码修改为以下:

type Config struct {
	rest.RestConf
	
	MysqlDb struct{
		DbSource string `json:"DbSource"`
	}
}

我们之前提到过,Config 结构体中的字段与 YAML 文件中的字段可以不区分大小写,但必须保持一致,否则会导致解析错误。如果希望使用不同的名称,可以通过 json: 标签指定 YAML 文件中对应的字段名。

把数据库连接注册到服务上下文

go zero提供了一个快捷的方式可以创建 Mysql 链接,接着我们就可以使用这个连接进行各种数据库操作:

func NewMysql(datasource string, opts ...SqlOption) SqlConn

我们先使用sqlx.NewMysql(c.MysqlDb.DbSource) 创建数据库连接,然后传给NewUsersModel,初始化UserModel,打开internal/svc/servicecontext.go文件,把代码修改为:

//ServiceContext 结构体用户封装服务的上下文信息,相当环境初始化

type ServiceContext struct {
	Config config.Config  
	//UserModel: 类型为 model.UsersModel,表示与用户相关的数据库模型
	//用于处理与用户相关的数据操作(如用户的创建、读取、更新和删除等)
	UserModel model.UsersModel     
}
//NewServiceContext 是ServiceContext 的构造函数
//它接收配置参数并初始化 ServiceContext,确保服务可以访问所需的配置和数据模型
func NewServiceContext(c config.Config) *ServiceContext {
	return &ServiceContext{
		Config: c,  //把配置信息注册到服务上下文
		
		//通过调用 model.NewUsersModel 函数对UserModel 进行初始化
		//sqlx.NewMysql 是数据库连接,链接字符串为config中的MysqlDb.DbSource
		UserModel:   model.NewUsersModel(sqlx.NewMysql(c.MysqlDb.DbSource)),
	}
}

sqlx.NewMysql 是 sqlx 包中的一个函数,通常用于创建一个新的 MySQL 数据库连接,我们可以看下它在go zero中的代码, 就是传入datasource 字符串,然后返回SqlConn 数据库连接

func NewMysql(datasource string, opts ...SqlOption) SqlConn {
	opts = append([]SqlOption{withMysqlAcceptable()}, opts...)
	return NewSqlConn(mysqlDriverName, datasource, opts...)
}

2. 实现注册服务(查、赠)

现在我们开始实现注册逻辑
打开internal/logic/user/registerlogic.go文件,修改代码如下:

func (l *RegisterLogic) Register(req *types.RegisterRequest) (resp *types.RegisterResponse, err error) {
	// todo: add your logic here and delete this line

	userModel := l.svcCtx.UserModel  //从服务上下文获取UserModel 

	//调用FindOneByUsername查询用户数据,判断用户是否已经注册
	//req.Username 从请求信息中获取 Username
	user, _ := userModel.FindOneByUsername(l.ctx, req.Username)
	//如果username不为空说明已经注册
	if user != nil {
		//已经存在用户
		return nil, err
	}
	//插入新的数据
	_, err = userModel.Insert(l.ctx, &model.Users{
		Username:  req.Username,
		Password:  req.Password,
		CreatedAt: time.Now(),
	})
	if err != nil {
	//	注册失败
		return nil, err
	}
	//返回响应信息
	return &types.RegisterResponse{
		Message: "注册成功",
	}, nil
}

我们先运行程序,测试一下
在这里插入图片描述

3.实现登录服务 (查)

现在我们开始实现=登录逻辑
打开internal/logic/user/loginlogic.go文件,修改代码如下:

func (l *LoginLogic) Login(req *types.LoginRequest) (resp *types.LoginResponse, err error) {
	// todo: add your logic here and delete this line
	
	//因为我们目前还没涉及到jwt鉴权,所以先把token当面message使用
	userModel := l.svcCtx.UserModel
	user, _ := userModel.FindOneByUsername(l.ctx, req.Username)
	//查询username判断是否有数据
	if user != nil { 
		//如果有数据,密码是否和数据库匹配
		if req.Password == user.Password {
			return &types.LoginResponse{
				Token: "登录成功",
			}, nil
		} else {
			return &types.LoginResponse{
				Token: "密码错误",
			}, nil
		}
	} else {
		return &types.LoginResponse{
			Token: "用户未注册",
		}, nil
	}
	
}

运行项目
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值