抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

本文面向熟悉Java + SpringBoot技术栈的后端开发工程师,通过一个用户登录的Demo项目,快速掌握Golang语法基础、Gin Web框架和GORM ORM框架的使用。

技术栈对照

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──────────────────────────────────────────────────────────────────┐
│ Java vs Golang 技术栈对照 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ Java技术栈 Golang技术栈 │
│ ───────────────────────────────────────────────────────────── │
│ JDK Go SDK │
│ Maven/Gradle Go Modules │
│ SpringBoot Gin / Echo / Fiber │
│ Spring MVC Gin Router │
│ MyBatis/JPA GORM / sqlx │
│ Lombok 不需要(语法简洁) │
│ Spring IoC Wire / 手动依赖注入 │
│ application.yml Viper / 环境变量 │
│ │
└──────────────────────────────────────────────────────────────────┘

Golang基础语法

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
┌──────────────────────────────────────────────────────────────────┐
│ 项目目录结构 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ Java SpringBoot项目 Golang项目 │
│ ──────────────────── ──────────────────── │
│ src/ user-login-demo/ │
│ ├── main/ ├── main.go (入口) │
│ │ ├── java/ ├── go.mod (依赖管理) │
│ │ │ └── com/example/ ├── go.sum │
│ │ │ ├── controller/ ├── config/ │
│ │ │ ├── service/ │ └── database.go │
│ │ │ ├── mapper/ ├── controller/ │
│ │ │ ├── entity/ │ └── user_controller.go │
│ │ │ └── Application.java├── service/ │
│ │ └── resources/ │ └── user_service.go │
│ │ └── application.yml ├── repository/ │
│ └── test/ │ └── user_repository.go │
│ └── java/ ├── model/ │
│ │ └── user.go │
│ pom.xml └── dto/ │
│ ├── request.go │
│ └── response.go │
│ │
└──────────────────────────────────────────────────────────────────┘

变量与类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ============ Java vs Golang 变量声明 ============

// Java
// String name = "张三";
// int age = 25;
// final String CONSTANT = "常量";

// Golang
var name string = "张三" // 完整声明
var age = 25 // 类型推断
count := 100 // 短变量声明(最常用,只能在函数内使用)
const CONSTANT = "常量" // 常量

// 多变量声明
var (
host string = "localhost"
port int = 8080
debug bool = true
)

基本类型对照

Java Golang 说明
int int, int32, int64 Go有明确位数类型
long int64
float float32
double float64
boolean bool
String string 小写,值类型
byte[] []byte 切片
Object interface{} / any any是interface{}的别名

函数定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// ============ Java vs Golang 函数定义 ============

// Java
// public String sayHello(String name) {
// return "Hello, " + name;
// }

// Golang - 返回值在参数后面
func sayHello(name string) string {
return "Hello, " + name
}

// 多返回值(Go的特色,用于返回结果+错误)
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("除数不能为0")
}
return a / b, nil
}

// 调用多返回值函数
result, err := divide(10, 2)
if err != nil {
// 处理错误
log.Fatal(err)
}
fmt.Println(result) // 5

// 命名返回值
func getUser() (name string, age int) {
name = "张三"
age = 25
return // 裸返回
}

结构体(替代Class)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// ============ Java vs Golang 类/结构体 ============

// Java
// @Data
// public class User {
// private Long id;
// private String username;
// private String password;
// }

// Golang - 使用struct
type User struct {
ID int64 `json:"id" gorm:"primaryKey"`
Username string `json:"username" gorm:"unique;not null"`
Password string `json:"-" gorm:"not null"` // json:"-" 表示JSON序列化时忽略
}

// 创建结构体实例
user1 := User{ID: 1, Username: "admin", Password: "123456"}
user2 := User{Username: "test"} // 其他字段为零值
user3 := &User{} // 指针类型

// 访问字段
fmt.Println(user1.Username)

结构体标签(Tag)说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌──────────────────────────────────────────────────────────────────┐
│ 结构体标签(Tag) │
├──────────────────────────────────────────────────────────────────┤
│ │
│ type User struct { │
│ ID int64 `json:"id" gorm:"primaryKey"` │
│ Username string `json:"username" gorm:"unique;not null"` │
│ Password string `json:"-" gorm:"not null"` │
│ } ↑ │
│ │ │
│ ┌───────────────────┴───────────────────────────────────┐ │
│ │ 标签用途: │ │
│ │ • json:"xxx" - JSON序列化时的字段名 │ │
│ │ • json:"-" - JSON序列化时忽略该字段 │ │
│ │ • gorm:"xxx" - GORM ORM的数据库映射配置 │ │
│ │ • binding:"xxx" - Gin参数绑定验证规则 │ │
│ │ • form:"xxx" - 表单字段名 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────┘

方法(给结构体添加行为)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// ============ Java vs Golang 方法 ============

// Java - 方法定义在类内部
// public class User {
// public String getInfo() {
// return "User: " + this.username;
// }
// }

// Golang - 方法定义在结构体外部,通过接收者关联
type User struct {
ID int64
Username string
}

// 值接收者(类似Java的普通方法)
func (u User) GetInfo() string {
return "User: " + u.Username
}

// 指针接收者(可以修改结构体字段,类似Java的setter)
func (u *User) SetUsername(name string) {
u.Username = name
}

// 调用
user := User{ID: 1, Username: "admin"}
fmt.Println(user.GetInfo()) // User: admin
user.SetUsername("newname")
fmt.Println(user.Username) // newname

值接收者 vs 指针接收者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──────────────────────────────────────────────────────────────────┐
│ 值接收者 vs 指针接收者 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ 值接收者 func (u User) Method() │
│ • 方法内操作的是结构体的副本 │
│ • 不能修改原结构体的字段 │
│ • 适用于只读操作 │
│ │
│ 指针接收者 func (u *User) Method() │
│ • 方法内操作的是结构体本身 │
│ • 可以修改结构体的字段 │
│ • 避免大结构体的复制开销 │
│ • 推荐:统一使用指针接收者 │
│ │
└──────────────────────────────────────────────────────────────────┘

接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// ============ Java vs Golang 接口 ============

// Java - 显式实现
// public interface UserService {
// User findById(Long id);
// }
// public class UserServiceImpl implements UserService {
// @Override
// public User findById(Long id) { ... }
// }

// Golang - 隐式实现(鸭子类型)
type UserService interface {
FindByID(id int64) (*User, error)
Create(user *User) error
}

// 只要实现了接口的所有方法,就自动实现了该接口,无需显式声明
type UserServiceImpl struct {
repo UserRepository
}

func (s *UserServiceImpl) FindByID(id int64) (*User, error) {
return s.repo.FindByID(id)
}

func (s *UserServiceImpl) Create(user *User) error {
return s.repo.Create(user)
}

// UserServiceImpl 自动实现了 UserService 接口
var _ UserService = (*UserServiceImpl)(nil) // 编译期检查

错误处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// ============ Java vs Golang 错误处理 ============

// Java - 异常
// try {
// User user = userService.findById(id);
// } catch (UserNotFoundException e) {
// log.error("用户不存在", e);
// }

// Golang - 错误返回值(没有try-catch)
user, err := userService.FindByID(id)
if err != nil {
// 处理错误
log.Printf("查询用户失败: %v", err)
return nil, err
}
// 正常处理
fmt.Println(user.Username)

// 自定义错误
var ErrUserNotFound = errors.New("用户不存在")

func (s *UserServiceImpl) FindByID(id int64) (*User, error) {
user, err := s.repo.FindByID(id)
if err != nil {
return nil, ErrUserNotFound
}
return user, nil
}

// 错误判断
if errors.Is(err, ErrUserNotFound) {
// 处理用户不存在的情况
}

切片和Map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// ============ Java vs Golang 集合 ============

// Java
// List<String> list = new ArrayList<>();
// Map<String, Integer> map = new HashMap<>();

// Golang - 切片(动态数组)
var slice []string // 声明
slice = make([]string, 0) // 初始化
slice = []string{"a", "b", "c"} // 字面量初始化
slice = append(slice, "d") // 追加元素
fmt.Println(len(slice)) // 长度: 4
fmt.Println(slice[0]) // 访问: a

// 遍历切片
for index, value := range slice {
fmt.Printf("index: %d, value: %s\n", index, value)
}

// Golang - Map
var m map[string]int // 声明
m = make(map[string]int) // 初始化
m = map[string]int{"age": 25} // 字面量初始化
m["score"] = 100 // 添加/修改
value, exists := m["age"] // 获取(推荐用两个返回值判断是否存在)
delete(m, "age") // 删除

// 遍历Map
for key, value := range m {
fmt.Printf("key: %s, value: %d\n", key, value)
}

包管理与导入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// ============ Java vs Golang 包管理 ============

// Java - Maven pom.xml
// <dependency>
// <groupId>org.springframework.boot</groupId>
// <artifactId>spring-boot-starter-web</artifactId>
// </dependency>

// Golang - go.mod
// module user-login-demo
// go 1.21
// require (
// github.com/gin-gonic/gin v1.9.1
// gorm.io/gorm v1.25.5
// )

// 导入包
import (
"fmt" // 标准库
"errors"

"github.com/gin-gonic/gin" // 第三方库
"gorm.io/gorm"

"user-login-demo/model" // 项目内部包
"user-login-demo/service"
)

// 别名导入
import (
gormMysql "gorm.io/driver/mysql" // 给包起别名
)

// 空白导入(只执行init函数,不直接使用)
import (
_ "github.com/go-sql-driver/mysql"
)

Gin框架基础

什么是Gin

Gin是Go语言最流行的Web框架,类似于Java的SpringMVC,特点是高性能、轻量级。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──────────────────────────────────────────────────────────────────┐
│ Gin框架核心概念 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ Spring MVC Gin │
│ ───────────────────────────────────────────────────────────── │
│ @RestController gin.Engine │
│ @RequestMapping router.GET/POST/PUT/DELETE │
│ @RequestBody c.ShouldBindJSON() │
│ @RequestParam c.Query() │
│ @PathVariable c.Param() │
│ ResponseEntity c.JSON() │
│ @ControllerAdvice gin.Recovery() 中间件 │
│ Filter/Interceptor 中间件 Middleware │
│ │
└──────────────────────────────────────────────────────────────────┘

创建项目

1
2
3
4
5
6
7
8
9
10
# 创建项目目录
mkdir user-login-demo && cd user-login-demo

# 初始化Go模块
go mod init user-login-demo

# 安装依赖
go get github.com/gin-gonic/gin
go get gorm.io/gorm
go get gorm.io/driver/mysql

Hello World

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// main.go
package main

import (
"github.com/gin-gonic/gin"
"net/http"
)

func main() {
// 创建Gin引擎(类似于new SpringApplication())
r := gin.Default() // 包含Logger和Recovery中间件

// 注册路由(类似于@GetMapping)
r.GET("/hello", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "Hello, World!",
})
})

// 启动服务(类似于SpringApplication.run())
r.Run(":8080") // 监听 0.0.0.0:8080
}

路由与请求处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// ============ 路由注册 ============

r := gin.Default()

// RESTful API
r.GET("/users", listUsers) // 查询列表
r.GET("/users/:id", getUser) // 查询单个 :id是路径参数
r.POST("/users", createUser) // 创建
r.PUT("/users/:id", updateUser) // 更新
r.DELETE("/users/:id", deleteUser) // 删除

// 路由分组(类似于@RequestMapping在类上)
api := r.Group("/api/v1")
{
api.GET("/users", listUsers)
api.POST("/users", createUser)

// 需要认证的路由
auth := api.Group("/")
auth.Use(AuthMiddleware()) // 添加认证中间件
{
auth.GET("/profile", getProfile)
}
}

获取请求参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// ============ 获取各类请求参数 ============

// 1. 路径参数 /users/:id
// Java: @PathVariable Long id
func getUser(c *gin.Context) {
id := c.Param("id") // 返回string
// 转换为int64
idInt, err := strconv.ParseInt(id, 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
return
}
// ...
}

// 2. Query参数 /users?page=1&size=10
// Java: @RequestParam Integer page
func listUsers(c *gin.Context) {
page := c.DefaultQuery("page", "1") // 带默认值
size := c.Query("size") // 无默认值,不存在返回""
keyword := c.QueryArray("keyword") // 数组参数
}

// 3. JSON Body
// Java: @RequestBody LoginRequest request
type LoginRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required,min=6"`
}

func login(c *gin.Context) {
var req LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// req.Username, req.Password 可用
}

// 4. 表单参数
func formHandler(c *gin.Context) {
username := c.PostForm("username")
password := c.DefaultPostForm("password", "")
}

// 5. Header
func headerHandler(c *gin.Context) {
token := c.GetHeader("Authorization")
}

响应处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// ============ 响应处理 ============

// 1. JSON响应
c.JSON(http.StatusOK, gin.H{
"code": 200,
"message": "success",
"data": user,
})

// 2. 使用结构体响应
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}

c.JSON(http.StatusOK, Response{
Code: 200,
Message: "success",
Data: user,
})

// 3. 错误响应
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"message": "参数错误",
})

// 4. 重定向
c.Redirect(http.StatusMovedPermanently, "https://example.com")

// 5. 设置Header
c.Header("X-Custom-Header", "value")

// 6. 设置Cookie
c.SetCookie("token", "xxx", 3600, "/", "localhost", false, true)

中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// ============ 中间件 ============
// 类似于Java的Filter/Interceptor

// 定义中间件
func LoggerMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 请求前
startTime := time.Now()
path := c.Request.URL.Path

c.Next() // 继续处理请求

// 请求后
latency := time.Since(startTime)
statusCode := c.Writer.Status()
log.Printf("[%d] %s %v", statusCode, path, latency)
}
}

// 认证中间件
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "未登录"})
c.Abort() // 终止后续处理
return
}

// 解析token,获取用户信息
userID, err := parseToken(token)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "token无效"})
c.Abort()
return
}

// 将用户信息存入Context,后续Handler可以获取
c.Set("userID", userID)
c.Next()
}
}

// 在Handler中获取中间件设置的值
func getProfile(c *gin.Context) {
userID, _ := c.Get("userID")
// 或者使用类型断言
userID := c.MustGet("userID").(int64)
}

// 使用中间件
r := gin.Default()
r.Use(LoggerMiddleware()) // 全局中间件

api := r.Group("/api")
api.Use(AuthMiddleware()) // 分组中间件

GORM框架基础

什么是GORM

GORM是Go语言最流行的ORM框架,类似于Java的MyBatis-Plus或JPA。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──────────────────────────────────────────────────────────────────┐
│ GORM核心概念 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ MyBatis/JPA GORM │
│ ───────────────────────────────────────────────────────────── │
│ @Entity struct + gorm tag │
│ @Table TableName() 方法 │
│ @Id @GeneratedValue gorm:"primaryKey" │
│ @Column gorm:"column:xxx" │
│ EntityManager *gorm.DB │
│ JpaRepository 自定义Repository │
│ @Query db.Raw() / db.Where() │
│ @Transactional db.Transaction() │
│ │
└──────────────────────────────────────────────────────────────────┘

数据库连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// config/database.go
package config

import (
"fmt"
"log"

"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)

var DB *gorm.DB

func InitDB() {
dsn := "root:password@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info), // 打印SQL日志
})
if err != nil {
log.Fatal("数据库连接失败:", err)
}

// 获取底层sql.DB,设置连接池
sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(10) // 最大空闲连接数
sqlDB.SetMaxOpenConns(100) // 最大打开连接数
sqlDB.SetConnMaxLifetime(time.Hour) // 连接最大存活时间

DB = db
log.Println("数据库连接成功")
}

定义Model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// model/user.go
package model

import (
"time"
"gorm.io/gorm"
)

// User 用户模型
// 类似于Java的@Entity
type User struct {
ID int64 `json:"id" gorm:"primaryKey;autoIncrement"`
Username string `json:"username" gorm:"type:varchar(50);uniqueIndex;not null"`
Password string `json:"-" gorm:"type:varchar(255);not null"` // json:"-" 不返回给前端
Nickname string `json:"nickname" gorm:"type:varchar(50)"`
Email string `json:"email" gorm:"type:varchar(100)"`
Status int `json:"status" gorm:"default:1"` // 1:正常 0:禁用
CreatedAt time.Time `json:"createdAt" gorm:"autoCreateTime"`
UpdatedAt time.Time `json:"updatedAt" gorm:"autoUpdateTime"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"` // 软删除
}

// TableName 指定表名(默认是结构体名的蛇形复数:users)
func (User) TableName() string {
return "t_user"
}

GORM Tag说明

Tag 说明 示例
primaryKey 主键 gorm:"primaryKey"
autoIncrement 自增 gorm:"autoIncrement"
type 列类型 gorm:"type:varchar(100)"
not null 非空 gorm:"not null"
default 默认值 gorm:"default:1"
uniqueIndex 唯一索引 gorm:"uniqueIndex"
index 普通索引 gorm:"index"
column 指定列名 gorm:"column:user_name"
autoCreateTime 自动创建时间 gorm:"autoCreateTime"
autoUpdateTime 自动更新时间 gorm:"autoUpdateTime"

自动迁移

1
2
3
4
5
6
7
// 自动建表(类似于JPA的ddl-auto)
func AutoMigrate() {
err := config.DB.AutoMigrate(&User{})
if err != nil {
log.Fatal("自动迁移失败:", err)
}
}

CRUD操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// repository/user_repository.go
package repository

import (
"user-login-demo/config"
"user-login-demo/model"
"gorm.io/gorm"
)

type UserRepository struct {
db *gorm.DB
}

func NewUserRepository() *UserRepository {
return &UserRepository{db: config.DB}
}

// Create 创建用户
// Java: userMapper.insert(user)
func (r *UserRepository) Create(user *model.User) error {
return r.db.Create(user).Error
}

// FindByID 根据ID查询
// Java: userMapper.selectById(id)
func (r *UserRepository) FindByID(id int64) (*model.User, error) {
var user model.User
err := r.db.First(&user, id).Error
if err != nil {
return nil, err
}
return &user, nil
}

// FindByUsername 根据用户名查询
// Java: userMapper.selectOne(new QueryWrapper<User>().eq("username", username))
func (r *UserRepository) FindByUsername(username string) (*model.User, error) {
var user model.User
err := r.db.Where("username = ?", username).First(&user).Error
if err != nil {
return nil, err
}
return &user, nil
}

// FindAll 查询所有
// Java: userMapper.selectList(null)
func (r *UserRepository) FindAll() ([]model.User, error) {
var users []model.User
err := r.db.Find(&users).Error
return users, err
}

// FindByPage 分页查询
// Java: userMapper.selectPage(page, queryWrapper)
func (r *UserRepository) FindByPage(page, pageSize int) ([]model.User, int64, error) {
var users []model.User
var total int64

offset := (page - 1) * pageSize

// 查询总数
r.db.Model(&model.User{}).Count(&total)

// 分页查询
err := r.db.Offset(offset).Limit(pageSize).Find(&users).Error

return users, total, err
}

// Update 更新用户
// Java: userMapper.updateById(user)
func (r *UserRepository) Update(user *model.User) error {
return r.db.Save(user).Error
}

// UpdateFields 更新指定字段
// Java: userMapper.update(null, new UpdateWrapper<User>().eq("id", id).set("nickname", nickname))
func (r *UserRepository) UpdateFields(id int64, fields map[string]interface{}) error {
return r.db.Model(&model.User{}).Where("id = ?", id).Updates(fields).Error
}

// Delete 删除用户(软删除)
// Java: userMapper.deleteById(id)
func (r *UserRepository) Delete(id int64) error {
return r.db.Delete(&model.User{}, id).Error
}

// 真正删除
func (r *UserRepository) HardDelete(id int64) error {
return r.db.Unscoped().Delete(&model.User{}, id).Error
}

复杂查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 条件查询
db.Where("status = ?", 1).
Where("created_at > ?", time.Now().AddDate(0, 0, -7)).
Find(&users)

// OR条件
db.Where("status = ?", 1).Or("role = ?", "admin").Find(&users)

// IN查询
db.Where("id IN ?", []int64{1, 2, 3}).Find(&users)

// LIKE查询
db.Where("username LIKE ?", "%admin%").Find(&users)

// 排序
db.Order("created_at DESC").Find(&users)

// 选择字段
db.Select("id", "username", "nickname").Find(&users)

// 原生SQL
db.Raw("SELECT * FROM t_user WHERE status = ?", 1).Scan(&users)
db.Exec("UPDATE t_user SET status = ? WHERE id = ?", 0, 1)

// 链式调用
db.Model(&User{}).
Select("id, username").
Where("status = ?", 1).
Order("id DESC").
Offset(0).
Limit(10).
Find(&users)

事务处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// ============ 事务处理 ============

// 方式1:手动事务
tx := db.Begin()
if err := tx.Create(&user1).Error; err != nil {
tx.Rollback()
return err
}
if err := tx.Create(&user2).Error; err != nil {
tx.Rollback()
return err
}
tx.Commit()

// 方式2:自动事务(推荐)
// 类似于Java的@Transactional
err := db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&user1).Error; err != nil {
return err // 返回错误会自动回滚
}
if err := tx.Create(&user2).Error; err != nil {
return err
}
return nil // 返回nil会自动提交
})

用户登录Demo完整示例

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
user-login-demo/
├── main.go
├── go.mod
├── config/
│ └── database.go
├── model/
│ └── user.go
├── dto/
│ ├── request.go
│ └── response.go
├── repository/
│ └── user_repository.go
├── service/
│ └── user_service.go
├── controller/
│ └── user_controller.go
└── middleware/
└── auth.go

DTO定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// dto/request.go
package dto

// 注册请求
type RegisterRequest struct {
Username string `json:"username" binding:"required,min=3,max=20"`
Password string `json:"password" binding:"required,min=6,max=20"`
Nickname string `json:"nickname" binding:"max=50"`
Email string `json:"email" binding:"omitempty,email"`
}

// 登录请求
type LoginRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}

// dto/response.go
package dto

// 统一响应结构
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}

// 登录响应
type LoginResponse struct {
Token string `json:"token"`
UserInfo UserVO `json:"userInfo"`
}

// 用户VO(不包含敏感信息)
type UserVO struct {
ID int64 `json:"id"`
Username string `json:"username"`
Nickname string `json:"nickname"`
Email string `json:"email"`
}

// 成功响应
func Success(data interface{}) Response {
return Response{Code: 200, Message: "success", Data: data}
}

// 失败响应
func Error(code int, message string) Response {
return Response{Code: code, Message: message}
}

Service层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// service/user_service.go
package service

import (
"errors"
"time"

"golang.org/x/crypto/bcrypt"
"github.com/golang-jwt/jwt/v5"

"user-login-demo/dto"
"user-login-demo/model"
"user-login-demo/repository"
)

var jwtSecret = []byte("your-secret-key")

type UserService struct {
repo *repository.UserRepository
}

func NewUserService() *UserService {
return &UserService{repo: repository.NewUserRepository()}
}

// Register 用户注册
func (s *UserService) Register(req *dto.RegisterRequest) error {
// 检查用户名是否已存在
existUser, _ := s.repo.FindByUsername(req.Username)
if existUser != nil {
return errors.New("用户名已存在")
}

// 密码加密
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
if err != nil {
return errors.New("密码加密失败")
}

// 创建用户
user := &model.User{
Username: req.Username,
Password: string(hashedPassword),
Nickname: req.Nickname,
Email: req.Email,
Status: 1,
}

return s.repo.Create(user)
}

// Login 用户登录
func (s *UserService) Login(req *dto.LoginRequest) (*dto.LoginResponse, error) {
// 查询用户
user, err := s.repo.FindByUsername(req.Username)
if err != nil {
return nil, errors.New("用户名或密码错误")
}

// 校验密码
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
if err != nil {
return nil, errors.New("用户名或密码错误")
}

// 检查状态
if user.Status != 1 {
return nil, errors.New("账号已被禁用")
}

// 生成JWT Token
token, err := s.generateToken(user.ID)
if err != nil {
return nil, errors.New("生成Token失败")
}

return &dto.LoginResponse{
Token: token,
UserInfo: dto.UserVO{
ID: user.ID,
Username: user.Username,
Nickname: user.Nickname,
Email: user.Email,
},
}, nil
}

// generateToken 生成JWT Token
func (s *UserService) generateToken(userID int64) (string, error) {
claims := jwt.MapClaims{
"user_id": userID,
"exp": time.Now().Add(24 * time.Hour).Unix(),
"iat": time.Now().Unix(),
}

token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(jwtSecret)
}

// ParseToken 解析JWT Token
func (s *UserService) ParseToken(tokenString string) (int64, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return jwtSecret, nil
})

if err != nil || !token.Valid {
return 0, errors.New("token无效")
}

claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return 0, errors.New("token解析失败")
}

userID := int64(claims["user_id"].(float64))
return userID, nil
}

// GetUserByID 根据ID获取用户信息
func (s *UserService) GetUserByID(id int64) (*dto.UserVO, error) {
user, err := s.repo.FindByID(id)
if err != nil {
return nil, errors.New("用户不存在")
}

return &dto.UserVO{
ID: user.ID,
Username: user.Username,
Nickname: user.Nickname,
Email: user.Email,
}, nil
}

Controller层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// controller/user_controller.go
package controller

import (
"net/http"

"github.com/gin-gonic/gin"

"user-login-demo/dto"
"user-login-demo/service"
)

type UserController struct {
service *service.UserService
}

func NewUserController() *UserController {
return &UserController{service: service.NewUserService()}
}

// Register 用户注册
// POST /api/register
func (ctrl *UserController) Register(c *gin.Context) {
var req dto.RegisterRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, dto.Error(400, "参数错误: "+err.Error()))
return
}

err := ctrl.service.Register(&req)
if err != nil {
c.JSON(http.StatusBadRequest, dto.Error(400, err.Error()))
return
}

c.JSON(http.StatusOK, dto.Success(nil))
}

// Login 用户登录
// POST /api/login
func (ctrl *UserController) Login(c *gin.Context) {
var req dto.LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, dto.Error(400, "参数错误"))
return
}

resp, err := ctrl.service.Login(&req)
if err != nil {
c.JSON(http.StatusBadRequest, dto.Error(400, err.Error()))
return
}

c.JSON(http.StatusOK, dto.Success(resp))
}

// GetProfile 获取当前用户信息(需要登录)
// GET /api/profile
func (ctrl *UserController) GetProfile(c *gin.Context) {
// 从Context中获取中间件设置的用户ID
userID := c.MustGet("userID").(int64)

user, err := ctrl.service.GetUserByID(userID)
if err != nil {
c.JSON(http.StatusNotFound, dto.Error(404, err.Error()))
return
}

c.JSON(http.StatusOK, dto.Success(user))
}

认证中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// middleware/auth.go
package middleware

import (
"net/http"
"strings"

"github.com/gin-gonic/gin"

"user-login-demo/dto"
"user-login-demo/service"
)

func AuthMiddleware() gin.HandlerFunc {
userService := service.NewUserService()

return func(c *gin.Context) {
// 从Header获取Token
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.JSON(http.StatusUnauthorized, dto.Error(401, "请先登录"))
c.Abort()
return
}

// 解析Bearer Token
parts := strings.SplitN(authHeader, " ", 2)
if len(parts) != 2 || parts[0] != "Bearer" {
c.JSON(http.StatusUnauthorized, dto.Error(401, "Token格式错误"))
c.Abort()
return
}

// 解析Token获取用户ID
userID, err := userService.ParseToken(parts[1])
if err != nil {
c.JSON(http.StatusUnauthorized, dto.Error(401, "Token无效或已过期"))
c.Abort()
return
}

// 将用户ID存入Context
c.Set("userID", userID)
c.Next()
}
}

主入口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// main.go
package main

import (
"log"

"github.com/gin-gonic/gin"

"user-login-demo/config"
"user-login-demo/controller"
"user-login-demo/middleware"
"user-login-demo/model"
)

func main() {
// 初始化数据库
config.InitDB()

// 自动迁移
config.DB.AutoMigrate(&model.User{})

// 创建Gin引擎
r := gin.Default()

// 创建Controller
userCtrl := controller.NewUserController()

// 注册路由
api := r.Group("/api")
{
// 公开接口
api.POST("/register", userCtrl.Register)
api.POST("/login", userCtrl.Login)

// 需要认证的接口
auth := api.Group("/")
auth.Use(middleware.AuthMiddleware())
{
auth.GET("/profile", userCtrl.GetProfile)
}
}

// 启动服务
log.Println("服务启动在 :8080")
r.Run(":8080")
}

go.mod

1
2
3
4
5
6
7
8
9
10
11
module user-login-demo

go 1.21

require (
github.com/gin-gonic/gin v1.9.1
github.com/golang-jwt/jwt/v5 v5.2.0
golang.org/x/crypto v0.18.0
gorm.io/driver/mysql v1.5.2
gorm.io/gorm v1.25.5
)

运行与测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 安装依赖
go mod tidy

# 运行
go run main.go

# 测试注册
curl -X POST http://localhost:8080/api/register \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"123456","nickname":"管理员"}'

# 测试登录
curl -X POST http://localhost:8080/api/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"123456"}'

# 测试获取用户信息(带Token)
curl http://localhost:8080/api/profile \
-H "Authorization: Bearer <your-token>"

进阶学习路线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
┌──────────────────────────────────────────────────────────────────┐
│ Golang进阶学习路线 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ 第一阶段:语言深入 │
│ ─────────────────────────────────────────────────────────────── │
│ • 并发编程:goroutine、channel、select、sync包 │
│ • 内存管理:逃逸分析、GC原理、内存对齐 │
│ • 反射机制:reflect包的使用 │
│ • 泛型(Go 1.18+):类型参数、约束 │
│ • Context:超时控制、取消传播、值传递 │
│ │
│ 第二阶段:Web开发进阶 │
│ ─────────────────────────────────────────────────────────────── │
│ • Gin高级特性:自定义验证器、优雅关机、限流 │
│ • GORM高级:关联查询、Hooks、Scopes、原生SQL优化 │
│ • 缓存:go-redis操作Redis │
│ • 消息队列:RabbitMQ/Kafka客户端使用 │
│ • 配置管理:Viper库读取yaml/env配置 │
│ • 日志:zap/logrus结构化日志 │
│ • API文档:Swagger/OpenAPI集成 │
│ │
│ 第三阶段:微服务架构 │
│ ─────────────────────────────────────────────────────────────── │
│ • gRPC:Protocol Buffers、gRPC服务开发 │
│ • 服务注册与发现:Consul/Etcd/Nacos │
│ • 网关:Kong/Traefik/自研 │
│ • 链路追踪:OpenTelemetry/Jaeger │
│ • 熔断限流:sentinel-go │
│ • 微服务框架:go-micro、go-zero、kratos │
│ │
│ 第四阶段:工程实践 │
│ ─────────────────────────────────────────────────────────────── │
│ • 项目规范:项目布局、代码规范、错误处理规范 │
│ • 单元测试:testing包、testify、mock │
│ • 依赖注入:wire、fx │
│ • CI/CD:GitHub Actions、Docker镜像构建 │
│ • 性能优化:pprof、benchmark、sync.Pool │
│ • 部署:Docker、Kubernetes │
│ │
│ 推荐学习资源 │
│ ─────────────────────────────────────────────────────────────── │
│ • 官方文档:https://go.dev/doc/ │
│ • Go语言圣经:https://gopl-zh.github.io/ │
│ • Go语言高级编程:https://chai2010.cn/advanced-go-programming-book│
│ • Effective Go:https://go.dev/doc/effective_go │
│ • Go项目标准布局:https://github.com/golang-standards/project-layout│
│ │
└──────────────────────────────────────────────────────────────────┘

Java与Go对比速查表

场景 Java Go
项目初始化 mvn archetype:generate go mod init
包管理 Maven/Gradle Go Modules
程序入口 public static void main func main()
定义类 class User {} type User struct {}
构造函数 new User() User{}&User{}
继承 extends 组合 + 接口
实现接口 implements 隐式实现
异常处理 try-catch 多返回值 err
空值 null nil
字符串拼接 "a" + "b" "a" + "b"fmt.Sprintf
集合遍历 for-each for range
Lambda (x) -> x*2 func(x int) int { return x*2 }
注解 @Annotation 结构体标签 `tag:"value"`
依赖注入 Spring IoC 手动 / Wire
Web框架 Spring MVC Gin / Echo
ORM MyBatis / JPA GORM / sqlx
日志 SLF4J + Logback zap / logrus
配置文件 application.yml Viper
单元测试 JUnit testing包
并发 Thread/CompletableFuture goroutine/channel

总结

从Java转向Go的核心变化:

  1. 语法更简洁:没有class、没有public/private关键字(首字母大小写决定)、没有try-catch
  2. 错误处理不同:Go使用多返回值而非异常,需要频繁检查if err != nil
  3. 面向对象不同:Go没有继承,使用组合+接口实现多态
  4. 并发模型不同:Go使用goroutine和channel,而非线程
  5. 包管理更简单:Go Modules开箱即用,无需配置Maven仓库
  6. 编译部署简单:直接编译成单个二进制文件,无需JVM

建议的学习路径:

  1. 先跑通本文的Demo,理解基本工作流程
  2. 深入学习Go语法,特别是接口、指针、切片
  3. 学习并发编程(goroutine、channel)
  4. 根据项目需求学习微服务相关技术栈