GORM 基础 -- CRUD 接口
Posted chinusyan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GORM 基础 -- CRUD 接口相关的知识,希望对你有一定的参考价值。
1、Create
1.1 创建纪录
user := UserName: "Jinzhu", Age: 18, Birthday: time.Now()
result := db.Create(&user) // pass pointer of data to Create
user.ID // 回填插入数据的主键
result.Error // 返回的 error 信息
result.RowsAffected // 返回插入记录的个数
1.2 用选定的字段创建记录
创建一个记录并为指定的字段分配值
db.Select("Name", "Age", "CreatedAt").Create(&user)
// INSERT INTO `users` (`name`,`age`,`created_at`) VALUES ("jinzhu", 18, "2020-07-04 11:05:21.775")
创建一个记录并忽略传递给要忽略的字段的值。
db.Omit("Name", "Age", "CreatedAt").Create(&user)
// INSERT INTO `users` (`birthday`,`updated_at`) VALUES ("2020-01-01 00:00:00.000", "2020-07-04 11:05:21.775")
1.3 Batch Insert
为了有效地插入大量的记录,将一个切片传递给Create
方法。GORM将生成一条SQL语句来插入所有数据并回填主键值,钩子方法(hook methods
)也将被调用。
var users = []UserName: "jinzhu1", Name: "jinzhu2", Name: "jinzhu3"
db.Create(&users)
for _, user := range users
user.ID // 1,2,3
你可以在创建CreateInBatches
时指定批大小,例如:
var users = []UserName: "jinzhu_1", ...., Name: "jinzhu_10000"
// batch size 100
db.CreateInBatches(users, 100)
批量插入也支持 Upsert和Create With Associations时,
用
CreateBatchSize
选项初始化GORM,所有INSERT
在创建记录和关联时都会遵守这个选项
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config
CreateBatchSize: 1000,
)
db := db.Session(&gorm.SessionCreateBatchSize: 1000)
users = [5000]UserName: "jinzhu", Pets: []Petpet1, pet2, pet3...
db.Create(&users)
// INSERT INTO users xxx (5 batches)
// INSERT INTO pets xxx (15 batches)
1.4 创建钩子(Hooks)
GORM允许用户定义钩子实现BeforeSave
, BeforeCreate
, AfterSave
, AfterCreate
。这些钩子方法将在创建记录时被调用,有关生命周期的详细信息请参阅钩子
func (u *User) BeforeCreate(tx *gorm.DB) (err error)
u.UUID = uuid.New()
if u.Role == "admin"
return errors.New("invalid role")
return
如果你想跳过Hooks
方法,你可以使用SkipHooks
会话模式(Session mode),例如:
DB.Session(&gorm.SessionSkipHooks: true).Create(&user)
DB.Session(&gorm.SessionSkipHooks: true).Create(&users)
DB.Session(&gorm.SessionSkipHooks: true).CreateInBatches(users, 100)
1.5 Create From Map
GORM支持从map[string]interface
和[]map[string]interface
创建,例如:
db.Model(&User).Create(map[string]interface
"Name": "jinzhu", "Age": 18,
)
// batch insert from `[]map[string]interface`
db.Model(&User).Create([]map[string]interface
"Name": "jinzhu_1", "Age": 18,
"Name": "jinzhu_2", "Age": 20,
)
当从map创建时,钩子不会被调用,关联不会被保存,主键值也不会被返回填充
1.6 从 SQL Expression/Context Valuer 创建
GORM允许用SQL表达式插入数据,有两种方法来实现这个目标,从map[string]interface
或自定义数据类型创建,例如:
// Create from map
db.Model(User).Create(map[string]interface
"Name": "jinzhu",
"Location": clause.ExprSQL: "ST_PointFromText(?)", Vars: []interface"POINT(100 100)",
)
// INSERT INTO `users` (`name`,`location`) VALUES ("jinzhu",ST_PointFromText("POINT(100 100)"));
// Create from customized data type
type Location struct
X, Y int
// Scan implements the sql.Scanner interface
func (loc *Location) Scan(v interface) error
// Scan a value into struct from database driver
func (loc Location) GormDataType() string
return "geometry"
func (loc Location) GormValue(ctx context.Context, db *gorm.DB) clause.Expr
return clause.Expr
SQL: "ST_PointFromText(?)",
Vars: []interfacefmt.Sprintf("POINT(%d %d)", loc.X, loc.Y),
type User struct
Name string
Location Location
db.Create(&User
Name: "jinzhu",
Location: LocationX: 100, Y: 100,
)
// INSERT INTO `users` (`name`,`location`) VALUES ("jinzhu",ST_PointFromText("POINT(100 100)"))
1.7 进阶
1.7.1 Create With Associations
在创建带有关联的数据时,如果其关联值不是零,则这些关联将被插入,并且将调用其Hooks
方法。
type CreditCard struct
gorm.Model
Number string
UserID uint
type User struct
gorm.Model
Name string
CreditCard CreditCard
db.Create(&User
Name: "jinzhu",
CreditCard: CreditCardNumber: "411111111111"
)
// INSERT INTO `users` ...
// INSERT INTO `credit_cards` ...
您可以使用Select
、Omit
跳过保存关联,例如:
db.Omit("CreditCard").Create(&user)
// skip all associations
db.Omit(clause.Associations).Create(&user)
1.7.2 默认值
你可以为使用default
标签为字段定义默认值,例如:
type User struct
ID int64
Name string `gorm:"default:galeone"`
Age int64 `gorm:"default:18"`
然后,将零值字段插入数据库时将使用默认值
任何零值,如
0
,"
,false
将不会保存到数据库中那些字段定义的默认值,你可能想使用指针类型或Scanner/Valuer
来避免这种情况,例如:
type User struct
gorm.Model
Name string
Age *int `gorm:"default:18"`
Active sql.NullBool `gorm:"default:true"`
你必须为数据库中有默认值或虚拟/生成值的字段设置默认标记,如果你想在迁移时跳过默认值定义,你可以使用
default:(-)
,例如:
type User struct
ID string `gorm:"default:uuid_generate_v3()"` // db func
FirstName string
LastName string
Age uint8
FullName string `gorm:"->;type:GENERATED ALWAYS AS (concat(firstname,' ',lastname));default:(-);"`
当使用虚拟/生成(virtual/generated)值时,你可能需要禁用它的创建/更新权限,检查字段级权限
1.8 Upsert / On Conflict
GORM为不同的数据库提供了兼容的Upsert
支持
import "gorm.io/gorm/clause"
// Do nothing on conflict
db.Clauses(clause.OnConflictDoNothing: true).Create(&user)
// Update columns to default value on `id` conflict
db.Clauses(clause.OnConflict
Columns: []clause.ColumnName: "id",
DoUpdates: clause.Assignments(map[string]interface"role": "user"),
).Create(&users)
// MERGE INTO "users" USING *** WHEN NOT MATCHED THEN INSERT *** WHEN MATCHED THEN UPDATE SET ***; SQL Server
// INSERT INTO `users` *** ON DUPLICATE KEY UPDATE ***; mysql
// Use SQL expression
db.Clauses(clause.OnConflict
Columns: []clause.ColumnName: "id",
DoUpdates: clause.Assignments(map[string]interface"count": gorm.Expr("GREATEST(count, VALUES(count))")),
).Create(&users)
// INSERT INTO `users` *** ON DUPLICATE KEY UPDATE `count`=GREATEST(count, VALUES(count));
// Update columns to new value on `id` conflict
db.Clauses(clause.OnConflict
Columns: []clause.ColumnName: "id",
DoUpdates: clause.AssignmentColumns([]string"name", "age"),
).Create(&users)
// MERGE INTO "users" USING *** WHEN NOT MATCHED THEN INSERT *** WHEN MATCHED THEN UPDATE SET "name"="excluded"."name"; SQL Server
// INSERT INTO "users" *** ON CONFLICT ("id") DO UPDATE SET "name"="excluded"."name", "age"="excluded"."age"; PostgreSQL
// INSERT INTO `users` *** ON DUPLICATE KEY UPDATE `name`=VALUES(name),`age`=VALUES(age); MySQL
// Update all columns to new value on conflict except primary keys and those columns having default values from sql func
db.Clauses(clause.OnConflict
UpdateAll: true,
).Create(&users)
// INSERT INTO "users" *** ON CONFLICT ("id") DO UPDATE SET "name"="excluded"."name", "age"="excluded"."age", ...;
// INSERT INTO `users` *** ON DUPLICATE KEY UPDATE `name`=VALUES(name),`age`=VALUES(age), ...; MySQL
在高级查询中查看FirstOrInit
, FirstOrCreate
更多细节请检出原始SQL和SQL Builder
2、Query
2.1 检索单个对象
GORM提供了First
、Take
、Last
方法来从数据库中检索单个对象,它在查询数据库时添加了LIMIT 1
条件,如果没有找到记录,它将返回错误ErrRecordNotFound
。
// Get the first record ordered by primary key
db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1;
// Get one record, no specified order
db.Take(&user)
// SELECT * FROM users LIMIT 1;
// Get last record, ordered by primary key desc
db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;
result := db.First(&user)
result.RowsAffected // returns count of records found
result.Error // returns error or nil
// check error ErrRecordNotFound
errors.Is(result.Error, gorm.ErrRecordNotFound)
如果你想避免
ErrRecordNotFound
错误,你可以像db.Limit(1).Find(&user)
一样使用Find
,Find
方法同时接受结构数据和切片数据
使用 Find
而不限制单个对象db.Find(&user)
将查询整个表并只返回第一个非性能和不确定性的对象
First
和Last
方法将根据主键顺序分别查找第一个和最后一个记录。只有当指向目标结构的指针作为参数传递给方法时,或者当使用db.Model()
指定模型时,它们才有效。此外,如果相关模型没有定义主键,则模型将按第一个字段排序。例如:
var user User
var users []User
// works because destination struct is passed in
db.First(&user)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1
// works because model is specified using `db.Model()`
result := map[string]interface
db.Model(&User).First(&result)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1
// doesn't work
result := map[string]interface
db.Table("users").First(&result)
// works with Take
result := map[string]interface
db.Table("users").Take(&result)
// no primary key defined, results will be ordered by first field (i.e., `Code`)
type Language struct
Code string
Name string
db.First(&Language)
// SELECT * FROM `languages` ORDER BY `languages`.`code` LIMIT 1
用主键检索多个对象
如果主键是数字,则可以通过内联条件使用主键检索对象。当使用字符串时,需要特别注意避免SQL注入;详细信息请查看安全部分。
db.First(&user, 10)
// SELECT * FROM users WHERE id = 10;
db.First(&user, "10")
// SELECT * FROM users WHERE id = 10;
db.Find(&users, []int1,2,3)
// SELECT * FROM users WHERE id IN (1,2,3);
如果主键是一个字符串(例如,像uuid),查询将被写成如下:
db.First(&user, "id = ?", "1b74413f-f3b8-409f-ac47-e8c062e3472a")
// SELECT * FROM users WHERE id = "1b74413f-f3b8-409f-ac47-e8c062e3472a";
当目标对象有一个主键时,主键将被用于构建条件,例如:
var user = UserID: 10
db.First(&user)
// SELECT * FROM users WHERE id = 10;
var result User
db.Model(UserID: 10).First(&result)
// SELECT * FROM users WHERE id = 10;
2.2 检索所有对象
// Get all records
result := db.Find(&users)
// SELECT * FROM users;
result.RowsAffected // returns found records count, equals `len(users)`
result.Error // returns error
2.3 条件(Conditions)
2.3.1 String Conditions
// Get first matched record
db.Where("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;
// Get all matched records
db.Where("name <> ?", "jinzhu").Find(&users)
// SELECT * FROM users WHERE name <> 'jinzhu';
// IN
db.Where("name IN ?", []string"jinzhu", "jinzhu 2").Find(&users)
// SELECT * FROM users WHERE name IN ('jinzhu','jinzhu 2');
// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
// SELECT * FROM users WHERE name LIKE '%jin%';
// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;
// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
// SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';
// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
// SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';
如果已经设置了对象的主键,则条件查询不会覆盖主键的值,而是将其用作’ and '条件。例如:
var user = UserID: 10
db.Where("id = ?", 20).First(&user)
// SELECT * FROM users WHERE id = 10 and id = 20 ORDER BY id ASC LIMIT 1
查询将给出record not found
错误。因此,在你想使用变量(如user
)从数据库获取新值之前,将主键属性(如id
)设置为nil
。
2.3.2 Struct & Map Conditions
// Struct
db.Where(&UserName: "jinzhu", Age: 20).First(&user)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 ORDER BY id LIMIT 1;
// Map
db.Where(map[string]interface"name": "jinzhu", "age": 20).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;
// Slice of primary keys
db.Where([]int6420, 21, 22).Find(&users)
// SELECT * FROM users WHERE id IN (20, 21, 22
当使用struct
查询时,GORM只会查询非零字段,这意味着如果你的字段值为0
,'
',false
或其他零值,它将不会被用来构建查询条件,例如:
db.Where(&UserName: "jinzhu", Age: 0).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu";
要在查询条件中包含零值,您可以使用map
,它将包括所有键值作为查询条件,例如:
db.Where(map[string]interface"Name": "jinzhu", "Age": 0).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 0;
有关详细信息,请参见Specify Struct search fields。
2.3.3 指定Struct搜索字段
当使用struct
进行搜索时,你可以在查询条件中通过指定使用struct
中的特定值来将相关的字段名或dbname传递给Where()
,例如:
db.Where(&UserName: "jinzhu", "name", "Age").Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 0;
db.Where(&UserName: "jinzhu", "Age").Find(&users)
// SELECT * FROM users WHERE age = 0;
kratos学习从零使用kratos创建一个CRUD的demo,是gorm做数据库操作,经过几天研究,调试代码开源放到github上,还是非常方便的。服务也实现了CRUD的http接口。
目录
前言
本文的原文连接是:
https://blog.csdn.net/freewebsys/article/details/124262158
未经博主允许不得转载。
博主地址是:http://blog.csdn.net/freewebsys
1,关于kratos
Kratos 一套轻量级 Go 微服务框架,包含大量微服务相关框架及工具。
网络上面的demo并不全面,从头来一点点进行研究学习。
学到老活到老。
2,使用
golang 需要使用1.18 的最新版本进行开发吧。
然后安装kratos 工具:
国内下载:
https://golang.google.cn/dl/
语法中文学习:
https://www.runoob.com/go/go-tutorial.html
golang install 加速:
#kratos 基础工具命令
go install github.com/go-kratos/kratos/cmd/kratos/v2@latest
#golang grpc 特别好的工具,可以直接调用grpc 服务
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
kratos new kratos-crud
cd kratos-crud
然后使用 make 工具,提供几个常用的功能,先把环境创建好。
make
Usage:
make [target]
Targets:
init init env
config generate internal proto
api generate api proto
build build
generate generate
all generate all
help show help
protobuffer 文档:
https://developers.google.cn/protocol-buffers/docs/proto3
在 ubuntu系统上安装protoc 工具,默认的工具是3.0.0的版本太低了。
否则报错误找不到
conf/conf.proto:22:5: "google.protobuf.Duration" is not defined.
重新安装 prototc 解决问题,估计是新的库中才有google的proto文件定义。
源码下载地址:
git clone https://github.com/protocolbuffers/protobuf.git
安装依赖的库:
sudo install -y autoconf automake libtool curl make g++ unzip
安装:
./autogen.sh
./configure
make && sudo make install
# 然后使用这个版本就可以了。
protoc --version
libprotoc 3.20.1-rc1
然后在执行 make all 就可以了:
go mod vendor
make all
有的时候 wire 不好用,可以直接修改成 wire 命令:
# makefile 的部分代码修改下命令:
.PHONY: generate
# generate
generate:
go mod tidy
go mod vendor
cd cmd/kratos-crud/ && wire
项目启动特别简单:使用kratos 命令就行
$ kratos run
INFO msg=config loaded: config.yaml format: yaml
INFO msg=[gRPC] server listening on: [::]:9000
INFO msg=[HTTP] server listening on: [::]:8000
可以看到grpc 服务在9000 端口,http 在8000 端口
4,使用 grpcul进行服务查看调用
可以使用上面的工具命令进行安装:
#golang grpc 特别好的工具,可以直接调用grpc 服务
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
然后就可以使用了。进行grpc 服务接口的查看,调用。
#查看改地址端口服务
$ grpcurl -plaintext 127.0.0.1:9000 list
grpc.health.v1.Health
grpc.reflection.v1alpha.ServerReflection
helloworld.v1.Greeter
kratos.api.Metadata
#查看函数
$ grpcurl -plaintext 127.0.0.1:9000 list helloworld.v1.Greeter
helloworld.v1.Greeter.SayHello
#查看函数描述
$ grpcurl -plaintext 127.0.0.1:9000 describe helloworld.v1.Greeter.SayHello
helloworld.v1.Greeter.SayHello is a method:
rpc SayHello ( .helloworld.v1.HelloRequest ) returns ( .helloworld.v1.HelloReply )
option (.google.api.http) = get:"/helloworld/name" ;
#查看入参
$ grpcurl -plaintext 127.0.0.1:9000 describe .helloworld.v1.HelloRequest
helloworld.v1.HelloRequest is a message:
message HelloRequest
string name = 1;
#调用函数,并返回
$ grpcurl -d '"name": "zhangsan"' -plaintext 127.0.0.1:9000 helloworld.v1.Greeter.SayHello
"message": "Hello zhangsan"
5,创建数据CRUD使用gorm
https://gorm.io/zh_CN/docs/index.html
创建数据库,使用 gorm 操作数据库CRUD
使用root账号创建数据库和用户使用 demo demo 账号登录。
CREATE DATABASE IF NOT EXISTS demo DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
grant all privileges on demo.* to demo@'%' identified by 'demo';
# 创建 userInfo 的用户表:
CREATE TABLE `user_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_name` varchar(225) NOT NULL,
`password` varchar(225) DEFAULT NULL,
`age` tinyint(4) DEFAULT NULL,
`phone` varchar(20) DEFAULT NULL,
`address` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
发现在插入数据的时候偶尔比较慢,使用连接池解决:
2022/04/24 21:23:12 /media/test/NewDisk1/go/src/kratos-crud/internal/data/user_info_repo.go:24 SLOW SQL >= 200ms
[489.453ms] [rows:1] INSERT INTO `user_info` (`user_name`,`password`,`age`,`phone`,`address`) VALUES ('','',0,'','')
设置连接池,解决链接问题,但在更新的时候偶尔还是会有,已经比之前好多了。
sqlDB.SetMaxIdleConns(int(c.Database.MaxIdleConns))
// SetMaxIdleConns sets the maximum number of connections in the idle connection pool.
sqlDB.SetMaxOpenConns(int(c.Database.MaxOpenConns))
// SetMaxOpenConns sets the maximum number of open connections to the database.
sqlDB.SetConnMaxLifetime(time.Second * time.Duration(c.Database.ConnMaxLifetime))
// SetConnMaxLifetime sets the maximum amount of time a connection may be reused.
// 配置10 100 600 加大链接数量。
首先定义 biz 的接口:
package biz
import (
"context"
v1 "kratos-crud/api/helloworld/v1"
"time"
"github.com/go-kratos/kratos/v2/errors"
"github.com/go-kratos/kratos/v2/log"
)
var (
// ErrUserNotFound is user not found.
ErrUserNotFound = errors.NotFound(v1.ErrorReason_USER_NOT_FOUND.String(), "user not found")
)
/**
CREATE TABLE `user_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_name` varchar(225) NOT NULL,
`password` varchar(225) DEFAULT NULL,
`age` tinyint(4) DEFAULT NULL,
`phone` varchar(20) DEFAULT NULL,
`address` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
*/
// UserInfo model.
type UserInfo struct
Id int64
UserName string
Password string
Age uint32
Phone string
Address string
CreatedAt time.Time `gorm:"not null"`
UpdatedAt time.Time `gorm:"not null"`
func (UserInfo) TableName() string
return "user_info"
// UserInfoRepo is a Greater repo.
type UserInfoRepoIf interface
Save(context.Context, *UserInfo) (*UserInfo, error) // save or update
Delete(context.Context, int64) error
FindByID(context.Context, int64) (*UserInfo, error)
FindAll(context.Context) ([]*UserInfo, error)
// UserInfoUsecase is a UserInfo usecase.
type UserInfoUsecase struct
repo UserInfoRepoIf
log *log.Helper
// NewUserInfoUsecase new a UserInfo usecase.
func NewUserInfoUsecase(repo UserInfoRepoIf, logger log.Logger) *UserInfoUsecase
return &UserInfoUsecaserepo: repo, log: log.NewHelper(logger)
// SaveUserInfo
func (uc *UserInfoUsecase) SaveUserInfo(ctx context.Context, user *UserInfo) (*UserInfo, error)
uc.log.WithContext(ctx).Infof("CreateUserInfo: %v", user.UserName)
return uc.repo.Save(ctx, user)
// DeleteUserInfo
func (uc *UserInfoUsecase) DeleteUserInfo(ctx context.Context, id int64) error
uc.log.WithContext(ctx).Infof("DeleteUserInfo: %v", id)
return uc.repo.Delete(ctx, id)
// FindUserInfoByID
func (uc *UserInfoUsecase) FindUserInfoByID(ctx context.Context, id int64) (*UserInfo, error)
uc.log.WithContext(ctx).Infof("FindUserInfoByID: %v", id)
return uc.repo.FindByID(ctx, id)
// FindAllUserInfo
func (uc *UserInfoUsecase) FindAllUserInfo(ctx context.Context) ([]*UserInfo, error)
uc.log.WithContext(ctx).Infof("FindAllUserInfo ")
return uc.repo.FindAll(ctx)
然后对 user 表进行CRUD
package data
import (
"context"
"kratos-crud/internal/biz"
"github.com/go-kratos/kratos/v2/log"
)
type userInfoRepo struct
data *Data
log *log.Helper
// NewUserInfoRepo .
func NewUserInfoRepo(data *Data, logger log.Logger) biz.UserInfoRepoIf
return &userInfoRepo
data: data,
log: log.NewHelper(logger),
func (repo *userInfoRepo) Save(ctx context.Context, userInfo *biz.UserInfo) (*biz.UserInfo, error)
repo.log.Debug(" Save result :", userInfo) // 返回 error
if userInfo.Id > 0 //更新 userInfo := biz.UserInfo
var oldUserInfo biz.UserInfo
result1 := repo.data.db.First(&oldUserInfo, "id = ? ", userInfo.Id) // 通过id进行数据查询
repo.log.Debug("result.Error :", result1.Error) // 返回 error
repo.log.Debug("save userInfo :", userInfo)
if result1.Error != nil // 有错误返回
return userInfo, result1.Error
else
oldUserInfo.UserName = userInfo.UserName
oldUserInfo.Password = userInfo.Password
oldUserInfo.Age = userInfo.Age
oldUserInfo.Phone = userInfo.Phone
oldUserInfo.Address = userInfo.Address
result := repo.data.db.Save(&oldUserInfo) // 通过数据的指针来创建
repo.log.Debug("result.Error :", result.Error) // 返回 error
repo.log.Debug("save userInfo :", userInfo)
return userInfo, result1.Error
else // 创建
result := repo.data.db.Create(userInfo) // 通过数据的指针来创建
repo.log.Debug("result.Error :", result.Error) // 返回 error
repo.log.Debug("save userInfo :", userInfo)
return userInfo, result.Error
func (repo *userInfoRepo) Update(ctx context.Context, userInfo *biz.UserInfo) error
repo.log.Debug("Update :", userInfo) // 返回
return nil
func (repo *userInfoRepo) Delete(ctx context.Context, id int64) error
repo.log.Debug("Delete By Id :", id) // 返回
result := repo.data.db.Delete(&biz.UserInfoId: id) // 通过id删除数据
repo.log.Debug("result.Error :", result.Error) // 返回 error
return result.Error
func (repo *userInfoRepo) FindByID(ctx context.Context, id int64) (*biz.UserInfo, error)
repo.log.Debug("FindByID :", id) // 返回
userInfo := biz.UserInfo
result := repo.data.db.First(&userInfo, "id = ? ", id) // 通过id进行数据查询
repo.log.Debug("result.Error :", result.Error) // 返回 error
return &userInfo, result.Error
func (repo *userInfoRepo) FindAll(ctx context.Context) ([]*biz.UserInfo, error)
repo.log.Debug("FindAll :") //
var userInfoList []*biz.UserInfo
result := repo.data.db.Find(&userInfoList) // 通过数据查询
repo.log.Debug("result.Error :", result.Error) // 返回 error
return userInfoList, result.Error
可以使用GORM 对数据进行CRUD,其中save 方法比较特殊。判断id,然后执行create 或者save 执行更新操作。
6,做CRUD接口
首先定义protoc 接口:
syntax = "proto3";
package demo.v1;
import "google/api/annotations.proto";
option go_package = "kratos-crud/api/demo/v1;v1";
option java_multiple_files = true;
option java_package = "api.demo.v1";
option java_outer_classname = "UserInfoV1";
// The UserInfo service definition.
service UserInfoService
rpc Save(UserInfo) returns (CommReply)
option (google.api.http) =
post: "/userInfo/save",
body: "*"
;
rpc Delete(IdRequest) returns (CommReply)
option (google.api.http) =
post: "/userInfo/delete",
body: "*"
;
rpc Get(IdRequest) returns (UserInfoReply)
option (google.api.http) =
get: "/userInfo/get/id"
;
rpc List(ListRequest) returns (ListUserInfoReply)
option (google.api.http) =
post: "/userInfo/list",
body: "*"
;
// https://developers.google.cn/protocol-buffers/docs/proto3
// 定义一个公用类型
message UserInfo
int64 id = 1;
string userName = 2;
string password = 3;
uint32 age = 4;
string phone = 5;
string address = 6;
message IdRequest
int64 id = 1;
message ListRequest
string name = 1;
// return replay
message CommReply
int64 code = 1;
string message = 2;
message UserInfoReply
int64 code = 1;
string message = 2;
UserInfo userInfo = 3;
message ListUserInfoReply
int64 code = 1;
string message = 2;
repeated UserInfo userInfoList = 3;
其中保存,删除,查询使用POST方法,查询单个使用GET方法。当然删除也可以使用delete方法。
然后是service实现如下:
package service
import (
"context"
v1 "kratos-crud/api/helloworld/v1"
"kratos-crud/internal/biz"
"strconv"
"github.com/go-kratos/kratos/v2/log"
)
// UserInfoService is a UserInfo service.
type UserInfoService struct
v1.UnimplementedUserInfoServiceServer
uc *biz.UserInfoUsecase
log *log.Helper
// NewUserInfoService new a UserInfo service.
func NewUserInfoService(uc *biz.UserInfoUsecase, logger log.Logger) *UserInfoService
log := log.NewHelper(logger)
return &UserInfoServiceuc: uc, log: log
// Save
func (s *UserInfoService) Save(ctx context.Context, in *v1.UserInfo) (*v1.CommReply, error)
s.log.Info(in.GetUserName())
userInfo := biz.UserInfo
userInfo.Id = in.GetId()
userInfo.UserName = in.GetUserName()
userInfo.Password = in.GetPassword()
userInfo.Age = in.GetAge()
userInfo.Phone = in.GetPhone()
userInfo.Address = in.GetAddress()
g, err := s.uc.SaveUserInfo(ctx, &userInfo)
if err != nil
return nil, err
return &v1.CommReplyMessage: "Hello " + g.UserName, nil
// Delete
func (s *UserInfoService) Delete(ctx context.Context, in *v1.IdRequest) (*v1.CommReply, error)
err := s.uc.DeleteUserInfo(ctx, in.GetId())
if err != nil
return nil, err
return &v1.CommReplyMessage: "Delete " + strconv.FormatInt(in.GetId(), 10), nil
// Get
func (s *UserInfoService) Get(ctx context.Context, in *v1.IdRequest) (*v1.UserInfoReply, error)
userInfo, err := s.uc.FindUserInfoByID(ctx, in.GetId())
if err != nil
return nil, err
// 对象转换
userInfoReply := &v1.UserInfo
Id: userInfo.Id,
UserName: userInfo.UserName,
Password: userInfo.Password,
Age: userInfo.Age,
Phone: userInfo.Phone,
Address: userInfo.Address,
return &v1.UserInfoReplyMessage: "Get " + userInfo.UserName, UserInfo: userInfoReply, nil
// List
func (s *UserInfoService) List(ctx context.Context, in *v1.ListRequest) (*v1.ListUserInfoReply, error)
userInfoList, err := s.uc.FindAllUserInfo(ctx)
if err != nil
return nil, err
// log.Info(userList)
var userInfoReplyList []*v1.UserInfo
// 循环转换对象。
for _, userInfo := range userInfoList
userInfoReplyList = append(userInfoReplyList,
&v1.UserInfo
Id: userInfo.Id,
UserName: userInfo.UserName,
Password: userInfo.Password,
Age: userInfo.Age,
Phone: userInfo.Phone,
Address: userInfo.Address,
)
return &v1.ListUserInfoReplyMessage: "List UserInfo", UserInfoList: userInfoReplyList, nil
这里使用了一个特殊方法进行测试,就是vscode的 rest api 插件,保存一个 .rest 文件,然后直接执行就可以了:
### 增加数据
POST http://localhost:8000/userInfo/save HTTP/1.1
content-type: application/json
"userName":"Hendry",
"password":"pwd123456",
"age":25,
"phone":"13811223344",
"address":"北京王府井1号"
### 删除数据
POST http://localhost:8000/userInfo/delete HTTP/1.1
content-type: application/json
"id":1
### 更新
POST http://localhost:8000/userInfo/save HTTP/1.1
content-type: application/json
"id":2,
"userName":"HendryNew",
"password":"pwd123456New",
"age":35,
"phone":"13811223344New",
"address":"北京王府井1号New"
### 按Id查询
GET http://localhost:8000/userInfo/get/2 HTTP/1.1
content-type: application/json
### 查询全部
POST http://localhost:8000/userInfo/list HTTP/1.1
content-type: application/json
"id":200
使用情况:
7,总结
经过几天研究,终于完成了kratos的CRUD操作,解决若干问题。并上传了代码到github上。
发现网上的代码都是很简单的CRUD,并不全面。
完善了CURD的demo。
本文的原文连接是:
https://blog.csdn.net/freewebsys/article/details/124262158
博主地址是:https://blog.csdn.net/freewebsys
以上是关于GORM 基础 -- CRUD 接口的主要内容,如果未能解决你的问题,请参考以下文章
kratos学习从零使用kratos创建一个CRUD的demo,是gorm做数据库操作,经过几天研究,调试代码开源放到github上,还是非常方便的。服务也实现了CRUD的http接口。
kratos学习从零使用kratos创建一个CRUD的demo,是gorm做数据库操作,经过几天研究,调试代码开源放到github上,还是非常方便的。服务也实现了CRUD的http接口。