feat(db): 添加数据库配置自动查找和缓存功能

- 实现配置文件自动查找功能,支持yaml、yml、toml、ini、json格式
- 添加查询缓存机制,提高重复查询性能
- 新增构建脚本build.sh和build.bat用于跨平台编译
- 添加完整的数据库连接配置和时间字段配置功能
- 实现DAO基类提供通用CRUD操作方法
- 添加配置文件示例和相关测试用例
This commit is contained in:
2026-04-03 07:39:18 +08:00
parent f50930ec74
commit b52c4aa3c7
48 changed files with 8888 additions and 1 deletions
+307
View File
@@ -0,0 +1,307 @@
# Magic-ORM 代码生成器使用指南
## 📚 什么是代码生成器?
代码生成器可以根据数据库表结构自动生成 Model 和 DAO 代码,大幅提高开发效率。
## 🚀 快速开始
### 1. 创建代码生成器
```go
package main
import (
"git.magicany.cc/black1552/gin-base/db/generator"
)
// 创建代码生成器实例
cg := generator.NewCodeGenerator("./generated")
```
### 2. 定义列信息
```go
columns := []generator.ColumnInfo{
{
ColumnName: "id", // 数据库列名
FieldName: "ID", // Go 字段名(驼峰)
FieldType: "int64", // Go 字段类型
JSONName: "id", // JSON 标签名
IsPrimary: true, // 是否主键
IsNullable: false, // 是否可为空
},
{
ColumnName: "username",
FieldName: "Username",
FieldType: "string",
JSONName: "username",
IsPrimary: false,
IsNullable: false,
},
{
ColumnName: "email",
FieldName: "Email",
FieldType: "string",
JSONName: "email",
IsPrimary: false,
IsNullable: true,
},
{
ColumnName: "created_at",
FieldName: "CreatedAt",
FieldType: "time.Time",
JSONName: "created_at",
},
}
```
### 3. 生成代码
#### 方式一:一键生成(推荐)
```go
// 同时生成 Model 和 DAO
err := cg.GenerateAll("user", columns)
if err != nil {
panic(err)
}
```
#### 方式二:分别生成
```go
// 只生成 Model
err := cg.GenerateModel("user", columns)
// 只生成 DAO
err := cg.GenerateDAO("user", "User")
```
## 📁 生成的文件结构
```
generated/
├── user.go # User Model
├── user_dao.go # User DAO
├── product.go # Product Model
└── product_dao.go # Product DAO
```
## 💡 使用生成的代码
### 导入包
```go
import (
"context"
"git.magicany.cc/black1552/gin-base/db/core"
"git.magicany.cc/black1552/gin-base/db/model"
"your-project/generated"
)
```
### 初始化数据库
```go
db, err := core.AutoConnect(false)
```
### 创建 DAO 实例
```go
userDAO := generated.NewUserDAO(db)
```
### CRUD 操作
```go
// 创建用户
user := &model.User{
Username: "john",
Email: "john@example.com",
}
err = userDAO.Create(context.Background(), user)
// 查询用户
user, err := userDAO.GetByID(context.Background(), 1)
// 更新用户
user.Email = "new@example.com"
err = userDAO.Update(context.Background(), user)
// 删除用户
err = userDAO.Delete(context.Background(), 1)
// 分页查询
users, err := userDAO.FindByPage(context.Background(), 1, 10)
```
## 🔧 API 参考
### NewCodeGenerator
```go
func NewCodeGenerator(outputDir string) *CodeGenerator
```
创建代码生成器实例。
**参数:**
- `outputDir` - 输出目录路径
### GenerateModel
```go
func (cg *CodeGenerator) GenerateModel(tableName string, columns []ColumnInfo) error
```
生成 Model 代码。
**参数:**
- `tableName` - 数据库表名
- `columns` - 列信息数组
### GenerateDAO
```go
func (cg *CodeGenerator) GenerateDAO(tableName string, modelName string) error
```
生成 DAO 代码。
**参数:**
- `tableName` - 数据库表名
- `modelName` - Model 名称(驼峰)
### GenerateAll
```go
func (cg *CodeGenerator) GenerateAll(tableName string, columns []ColumnInfo) error
```
一键生成 Model + DAO(推荐)。
**参数:**
- `tableName` - 数据库表名
- `columns` - 列信息数组
## 📋 ColumnInfo 结构
```go
type ColumnInfo struct {
ColumnName string // 数据库列名(下划线风格)
FieldName string // Go 字段名(驼峰风格)
FieldType string // Go 数据类型
JSONName string // JSON 标签名
IsPrimary bool // 是否主键
IsNullable bool // 是否可为空
}
```
## 🗺️ 类型映射表
| 数据库类型 | Go 类型 |
|-----------|---------|
| INT/BIGINT | int64 |
| VARCHAR/TEXT | string |
| DATETIME | time.Time |
| TIMESTAMP | time.Time |
| BOOLEAN | bool |
| FLOAT/DOUBLE | float64 |
| DECIMAL | string 或 float64 |
## 🎯 最佳实践
### 1. 从数据库读取真实表结构
```go
// 示例:从 MySQL INFORMATION_SCHEMA 获取列信息
query := `
SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_KEY
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'your_database'
AND TABLE_NAME = 'your_table'
`
```
### 2. 批量生成所有表
```go
tables := []string{"users", "products", "orders"}
for _, table := range tables {
columns := getColumnsFromDB(table) // 从数据库获取列信息
err := cg.GenerateAll(table, columns)
if err != nil {
log.Printf("生成 %s 失败:%v", table, err)
}
}
```
### 3. 自定义模板
修改 `generator.go` 中的模板字符串,添加自定义方法:
```go
tmpl := `package model
// {{.ModelName}} {{.TableName}} 模型
type {{.ModelName}} struct {
{{range .Columns}}
{{.FieldName}} {{.FieldType}} ` + "`" + `json:"{{.JSONName}}" db:"{{.ColumnName}}"` + "`" + `
{{end}}
}
// 自定义方法
func (m *{{.ModelName}}) Validate() error {
// 验证逻辑
return nil
}
`
```
### 4. 代码审查
- ✅ 检查生成的字段类型是否正确
- ✅ 验证主键和索引设置
- ✅ 添加业务逻辑方法
- ✅ 补充注释和文档
### 5. 版本控制
```bash
# 将生成的代码纳入 Git 管理
git add generated/
git commit -m "feat: 生成用户和产品模块代码"
```
## ⚠️ 注意事项
1. **不要频繁覆盖**: 手动修改的代码可能会被覆盖
2. **代码审查**: 生成的代码需要人工审查
3. **类型映射**: 特殊类型可能需要手动调整
4. **关联关系**: 复杂的模型关联需要手动实现
5. **验证逻辑**: 业务验证逻辑需要手动添加
## 🎉 总结
✅ **优势:**
- 大幅提高开发效率
- 代码规范统一
- 减少重复劳动
- 快速搭建项目骨架
✅ **适用场景:**
- 新项目快速启动
- 数据库表结构变更
- 批量生成基础代码
- 原型开发
✅ **推荐用法:**
- 使用 `GenerateAll` 一键生成
- 从真实数据库读取列信息
- 定期重新生成保持同步
- 配合版本控制管理代码
开始使用代码生成器,提升你的开发效率吧!🚀
+201
View File
@@ -0,0 +1,201 @@
package generator
import (
"fmt"
"os"
"path/filepath"
"strings"
"text/template"
)
// CodeGenerator 代码生成器 - 自动生成 Model 和 DAO 代码
type CodeGenerator struct {
outputDir string // 输出目录
}
// NewCodeGenerator 创建代码生成器实例
func NewCodeGenerator(outputDir string) *CodeGenerator {
return &CodeGenerator{
outputDir: outputDir,
}
}
// GenerateModel 生成 Model 代码
func (cg *CodeGenerator) GenerateModel(tableName string, columns []ColumnInfo) error {
// 生成文件名
fileName := strings.ToLower(tableName) + ".go"
filePath := filepath.Join(cg.outputDir, fileName)
// 创建输出目录
if err := os.MkdirAll(cg.outputDir, 0755); err != nil {
return fmt.Errorf("创建目录失败:%w", err)
}
// 生成代码
code := cg.generateModelCode(tableName, columns)
// 写入文件
if err := os.WriteFile(filePath, []byte(code), 0644); err != nil {
return fmt.Errorf("写入文件失败:%w", err)
}
fmt.Printf("[Model] Generated: %s\n", filePath)
return nil
}
// GenerateDAO 生成 DAO 代码
func (cg *CodeGenerator) GenerateDAO(tableName string, modelName string) error {
fileName := strings.ToLower(tableName) + "_dao.go"
// DAO 文件输出到 dao 子目录
daoDir := filepath.Join(cg.outputDir, "dao")
filePath := filepath.Join(daoDir, fileName)
// 创建输出目录(包括 dao 子目录)
if err := os.MkdirAll(daoDir, 0755); err != nil {
return fmt.Errorf("创建目录失败:%w", err)
}
// 生成代码
code := cg.generateDAOCode(tableName, modelName)
// 写入文件
if err := os.WriteFile(filePath, []byte(code), 0644); err != nil {
return fmt.Errorf("写入文件失败:%w", err)
}
fmt.Printf("[DAO] Generated: %s\n", filePath)
return nil
}
// GenerateAll 生成完整代码(Model + DAO)
func (cg *CodeGenerator) GenerateAll(tableName string, columns []ColumnInfo) error {
modelName := cg.toCamelCase(tableName)
// 生成 Model
if err := cg.GenerateModel(tableName, columns); err != nil {
return err
}
// 生成 DAO
if err := cg.GenerateDAO(tableName, modelName); err != nil {
return err
}
return nil
}
// generateModelCode 生成 Model 代码
func (cg *CodeGenerator) generateModelCode(tableName string, columns []ColumnInfo) string {
// 检查是否有时间字段
hasTime := false
for _, col := range columns {
if col.FieldType == "time.Time" {
hasTime = true
break
}
}
importStmt := ""
if hasTime {
importStmt = `import "time"`
} else {
importStmt = ``
}
tmpl := `package model
` + importStmt + `
// {{.ModelName}} {{.TableName}} 模型
type {{.ModelName}} struct {
{{range .Columns}} {{.FieldName}} {{.FieldType}} ` + "`" + `json:"{{.JSONName}}" db:"{{.ColumnName}}"` + "`" + `
{{end}}
}
// TableName 表名
func ({{.ModelName}}) TableName() string {
return "{{.TableName}}"
}
`
data := struct {
ModelName string
TableName string
Columns []ColumnInfo
}{
ModelName: cg.toCamelCase(tableName),
TableName: tableName,
Columns: columns,
}
return cg.executeTemplate(tmpl, data)
}
// generateDAOCode 生成 DAO 代码 - 简化版本,只定义结构体并继承 Database
func (cg *CodeGenerator) generateDAOCode(tableName string, modelName string) string {
tmpl := `package dao
import (
"git.magicany.cc/black1552/gin-base/db/core"
"git.magicany.cc/black1552/gin-base/db/model"
)
// {{.ModelName}}DAO {{.TableName}} 数据访问对象
// 嵌入 core.DAO,自动获得所有 CRUD 方法
type {{.ModelName}}DAO struct {
*core.DAO
}
// New{{.ModelName}}DAO 创建 {{.ModelName}}DAO 实例
func New{{.ModelName}}DAO(db *core.Database) *{{.ModelName}}DAO {
return &{{.ModelName}}DAO{
DAO: core.NewDAOWithModel(db, &model.{{.ModelName}}{}),
}
}
`
data := struct {
ModelName string
TableName string
}{
ModelName: cg.toCamelCase(tableName),
TableName: tableName,
}
return cg.executeTemplate(tmpl, data)
}
// executeTemplate 执行模板
func (cg *CodeGenerator) executeTemplate(tmpl string, data interface{}) string {
t := template.Must(template.New("code").Parse(tmpl))
var buf strings.Builder
if err := t.Execute(&buf, data); err != nil {
return fmt.Sprintf("// 模板执行错误:%v", err)
}
return buf.String()
}
// toCamelCase 转换为驼峰命名
func (cg *CodeGenerator) toCamelCase(str string) string {
parts := strings.Split(str, "_")
result := ""
for _, part := range parts {
if len(part) > 0 {
result += strings.ToUpper(string(part[0])) + part[1:]
}
}
return result
}
// ColumnInfo 列信息
type ColumnInfo struct {
ColumnName string // 列名
FieldName string // 字段名(驼峰)
FieldType string // 字段类型
JSONName string // JSON 标签名
IsPrimary bool // 是否主键
IsNullable bool // 是否可为空
}