只是让自己了解Gin和GORM的一个demo

Go
package main
import (
"net/http"
"github.com/gin-gonic/gin"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type User struct {
ID uint `json:"id" gorm:"primaryKey"`
Name string `json:"name"`
Age int `json:"age"`
}
var db *gorm.DB
func init() {
// 2. 初始化 GORM 连接 MySQL 数据库
var err error
// ⚠️ 请务必将下方 'root:你的密码' 替换成你本地 MySQL 的真实账号密码
dsn := "root:root@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("数据库连接失败:" + err.Error())
}
// 自动迁移表结构(程序启动时会自动在 test 库里创建 user_infos 表)
err = db.AutoMigrate(&User{})
if err != nil {
return
}
}
func main() {
r := gin.Default()
// 允许所有跨域请求的中间件
r.Use(func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*") // 允许任意来源
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") // 允许的请求方法
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") // 允许的请求头
c.Writer.Header().Set("Access-Control-Expose-Headers", "Content-Length") // 暴露给前端的响应头
// 专门处理浏览器发出的预检请求(OPTIONS),直接返回 204 状态码并终止后续逻辑
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
})
r.GET("/users", GetUsers) // 查询所有用户
r.GET("/users/:id", GetUserByID) // 查询单个用户
r.POST("/users", CreateUser) // 新增用户
r.PUT("/users/:id", UpdateUser) // 更新用户
r.DELETE("/users/:id", DeleteUser) // 删除用户
// 启动服务,监听 8080 端口
err := r.Run(":8080")
if err != nil {
return
}
}
// --- 下面是具体的增删改查逻辑实现 ---
// 【查】获取所有用户
func GetUsers(c *gin.Context) {
var users []User
db.Find(&users)
c.JSON(http.StatusOK, gin.H{"data": users})
}
// 【查】根据 ID 获取单个用户
func GetUserByID(c *gin.Context) {
id := c.Param("id")
var user User
if err := db.First(&user, id).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "用户未找到"})
return
}
c.JSON(http.StatusOK, gin.H{"data": user})
}
// 【增】创建新用户
func CreateUser(c *gin.Context) {
var user User
// 将前端传来的 JSON 数据绑定到 user 结构体
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "参数格式错误"})
return
}
db.Create(&user)
c.JSON(http.StatusCreated, gin.H{"message": "创建成功", "data": user})
}
// 【改】更新用户信息
func UpdateUser(c *gin.Context) {
id := c.Param("id")
var user User
// 先查找用户是否存在
if err := db.First(&user, id).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "用户未找到"})
return
}
var input User
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "参数格式错误"})
return
}
// 更新字段并保存
user.Name = input.Name
user.Age = input.Age
db.Save(&user)
c.JSON(http.StatusOK, gin.H{"message": "更新成功", "data": user})
}
// 【删】删除用户
func DeleteUser(c *gin.Context) {
id := c.Param("id")
result := db.Delete(&User{}, id)
if result.RowsAffected == 0 {
c.JSON(http.StatusNotFound, gin.H{"error": "用户未找到"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "删除成功"})
}
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Gin 接口测试工具</title>
<style>
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; max-width: 900px; margin: 30px auto; padding: 20px; background-color: #f5f7fa; }
.container { display: flex; gap: 20px; }
.panel { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 12px rgba(0,0,0,0.1); flex: 1; }
h2 { margin-top: 0; color: #333; border-bottom: 2px solid #42b983; padding-bottom: 10px; }
input, button { width: 100%; padding: 10px; margin: 8px 0; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box; }
button { background-color: #42b983; color: white; border: none; cursor: pointer; font-weight: bold; transition: 0.3s; }
button:hover { background-color: #369c6e; }
button.delete-btn { background-color: #ff4d4f; }
button.delete-btn:hover { background-color: #d9363e; }
pre { background: #282c34; color: #abb2bf; padding: 15px; border-radius: 4px; overflow-x: auto; min-height: 100px; }
table { width: 100%; border-collapse: collapse; margin-top: 10px; }
th, td { border: 1px solid #eee; padding: 10px; text-align: left; }
th { background-color: #f9fafc; }
</style>
</head>
<body>
<h1 style="text-align: center; color: #42b983;">Gin + GORM 接口可视化测试</h1>
<div class="container">
<!-- 左侧:操作区 -->
<div class="panel">
<h2>🚀 发送请求</h2>
<label>用户 ID (用于查询/修改/删除):</label>
<input type="number" id="userId" placeholder="例如: 1">
<label>姓名:</label>
<input type="text" id="userName" placeholder="例如: 张三">
<label>年龄:</label>
<input type="number" id="userAge" placeholder="例如: 25">
<button onclick="createUser()">➕ 新增用户 (POST)</button>
<button onclick="getUser()">🔍 查询单个用户 (GET)</button>
<button onclick="updateUser()">✏️ 更新用户 (PUT)</button>
<button class="delete-btn" onclick="deleteUser()">❌ 删除用户 (DELETE)</button>
<hr>
<button onclick="getAllUsers()" style="background-color: #007bff;">📋 刷新并获取所有用户列表</button>
</div>
<!-- 右侧:结果展示区 -->
<div class="panel">
<h2>📊 返回结果</h2>
<pre id="responseResult">// 接口返回的 JSON 数据会显示在这里...</pre>
<h3>当前数据库用户列表:</h3>
<table>
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>年龄</th>
</tr>
</thead>
<tbody id="userTableBody">
<!-- 动态生成的表格行 -->
</tbody>
</table>
</div>
</div>
<script>
const API_BASE = 'http://localhost:8080/users';
// 辅助函数:显示返回结果
function showResult(data) {
document.getElementById('responseResult').textContent = JSON.stringify(data, null, 4);
}
// 1. 获取所有用户
async function getAllUsers() {
try {
const res = await fetch(API_BASE);
const data = await res.json();
showResult(data);
// 渲染到表格
const tbody = document.getElementById('userTableBody');
tbody.innerHTML = '';
if (data.data) {
data.data.forEach(user => {
tbody.innerHTML += `<tr><td>${user.id}</td><td>${user.name}</td><td>${user.age}</td></tr>`;
});
}
} catch (error) {
showResult({ error: "请求失败,请检查后端是否启动!" });
}
}
// 2. 新增用户
async function createUser() {
const name = document.getElementById('userName').value;
const age = Number(document.getElementById('userAge').value); if (!name || !age) return alert("请输入姓名和年龄!");
try {
const res = await fetch(API_BASE, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, age })
});
const data = await res.json();
showResult(data);
getAllUsers(); // 自动刷新列表
} catch (error) {
showResult({ error: "请求失败" });
}
}
// 3. 查询单个用户
async function getUser() {
const id = document.getElementById('userId').value;
if (!id) return alert("请输入用户ID!");
try {
const res = await fetch(`${API_BASE}/${id}`);
const data = await res.json();
showResult(data);
} catch (error) {
showResult({ error: "请求失败" });
}
}
// 4. 更新用户
async function updateUser() {
const id = document.getElementById('userId').value;
const name = document.getElementById('userName').value;
const age = Number(document.getElementById('userAge').value); if (!name || !age) return alert("请输入姓名和年龄!");
if (!id || !name || !age) return alert("请输入完整的ID、姓名和年龄!");
try {
const res = await fetch(`${API_BASE}/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, age })
});
const data = await res.json();
showResult(data);
getAllUsers();
} catch (error) {
showResult({ error: "请求失败" });
}
}
// 5. 删除用户
async function deleteUser() {
const id = document.getElementById('userId').value;
if (!id) return alert("请输入要删除的用户ID!");
if (!confirm("确定要删除吗?")) return;
try {
const res = await fetch(`${API_BASE}/${id}`, { method: 'DELETE' });
const data = await res.json();
showResult(data);
getAllUsers();
} catch (error) {
showResult({ error: "请求失败" });
}
}
// 页面加载时自动获取一次列表
window.onload = getAllUsers;
</script>
</body>
</html>
&(取地址符):作用是"我要这个变量的地址/指针 "。所以&user的意思就是"把user这个变量在内存中的地址交出来"。*(解引用符):作用才是"我要这个指针指向的值"。
为了修改原来的数据(最主要的原因)
Go 语言的函数默认是"值传递"的。如果你只传
user(不带&),Go 会把user里的数据完整复制一份 传给函数。那么,当
ShouldBindJSON辛苦地把前端传来的 JSON 数据解析好并填进去时,它填的是那份**"复印件"** 。等函数运行结束,"复印件"被扔掉,你原本定义的user变量里依然空空如也。而当你传入
&user(指针/地址)时,就相当于告诉函数:"原件放在这个地址上,你直接去那个地址把数据填进我的原件里!"这样函数内部的操作才能真实地改变你的user变量。为了避免大数据量的复制(性能优化)
如果结构体非常大,每次调用函数都复制一份会非常消耗内存和 CPU。直接传一个轻量级的指针(地址),效率会高得多。
html
// 必须加 *User 接收指针,调用时必须传 &user
func fillData(user *User) {
user.Name = "张三"
}
var u User
fillData(&u) // 必须显式取地址!
java
package org.example;
public class Main {
// 直接接收 User 对象,不用管什么指针不指针
public static void fillData(User user) {
user.setName("张三"); // 这里修改的也是原本的那个对象
}
public static void main(String[] args) {
User u = new User();
fillData(u); // 直接传 u 就行!Java 内部自动传递的是引用
System.out.println(u.getName()); // 输出: 张三
}
}
class User{
String Name;
public void setName(String user)
{
this.Name = user;
}
public String getName()
{
return Name;
}
}
我还是更倾向与go的特性