freedom new-po
是 Freedom 框架提供的一个强大的代码生成工具,用于自动生成持久化对象(Persistent Object,PO)和相关的 CRUD 操作代码。本工具可以显著提高开发效率,确保代码质量的一致性。
- 自动生成与数据库表结构对应的 Go 结构体
- 生成标准的 CRUD 操作方法
- 支持多种数据源(MySQL 数据库和 JSON Schema)
- 自动处理字段命名转换(下划线到驼峰)
- 内置智能类型映射
- 支持主键和索引识别
- 生成 Gorm 标签
- 自动生成字段更新方法
freedom new-po --dsn "username:password@tcp(host:port)/database_name?charset=utf8"
参数说明:
- username: 数据库用户名
- password: 数据库密码
- host: 数据库主机地址
- port: 数据库端口
- database_name: 数据库名称
示例:
freedom new-po --dsn "root:123456@tcp(127.0.0.1:3306)/myapp?charset=utf8"
freedom new-po --json ./path/to/schema.json
JSON Schema 文件格式示例:
[
{
"tableName": "users",
"primaryKey": "id",
"columns:int": ["id", "age", "status"],
"columns:string": ["name", "email", "phone"],
"columns:time.Time": ["created_at", "updated_at"]
},
{
"tableName": "orders",
"primaryKey": "order_id",
"columns:int": ["order_id", "user_id", "amount"],
"columns:string": ["order_no", "status"],
"columns:time.Time": ["created_at"]
}
]
freedom new-po -h
常用选项:
--prefix
: 设置表名前缀,生成代码时会自动去除--dir
: 指定生成文件的目录--package
: 指定生成代码的包名
执行命令后,将会生成如下文件:
├── po/
│ ├── user.go # 用户表对应的 PO 结构体
│ ├── order.go # 订单表对应的 PO 结构体
│ └── ...
└── repository/
├── user_repo.go # 用户表的仓储实现
├── order_repo.go # 订单表的仓储实现
├── generate.go # 生成的仓储方法
└── ...
每个生成的 PO 对象都包含以下特性:
// 以 User 表为例
type User struct {
// 1. 数据库字段映射
ID int `gorm:"primaryKey;column:id"`
Name string `gorm:"column:name"`
Email string `gorm:"column:email"`
CreatedAt time.Time `gorm:"column:created_at"`
UpdatedAt time.Time `gorm:"column:updated_at"`
// 2. 变更跟踪字段
changes map[string]interface{}
}
// 3. 表名方法
func (obj *User) TableName() string {
return "users"
}
// 4. 获取变更内容
func (obj *User) GetChanges() map[string]interface{} {
if obj.changes == nil {
return nil
}
result := make(map[string]interface{})
for k, v := range obj.changes {
result[k] = v
}
obj.changes = nil
return result
}
// 5. 更新字段方法
func (obj *User) Update(name string, value interface{}) {
if obj.changes == nil {
obj.changes = make(map[string]interface{})
}
obj.changes[name] = value
}
每个字段都会生成对应的 Set 和 Add 方法(数值类型):
// Set 方法 - 适用于所有类型
func (obj *User) SetName(name string) {
obj.Name = name
obj.Update("name", name)
}
func (obj *User) SetEmail(email string) {
obj.Email = email
obj.Update("email", email)
}
// Add 方法 - 仅适用于数值类型(如 int, float64)
func (obj *User) AddAge(age int) {
obj.Age += age
obj.Update("age", gorm.Expr("age + ?", age))
}
func (obj *User) AddBalance(amount float64) {
obj.Balance += amount
obj.Update("balance", gorm.Expr("balance + ?", amount))
}
生成的仓储方法包含丰富的查询选项:
// 1. 基础查询方法
func findUser(repo GORMRepository, result *po.User, builders ...Builder) error
func findUserByID(repo GORMRepository, id int) (po.User, error)
func findUserListByID(repo GORMRepository, ids ...int) ([]*po.User, error)
// 2. 条件查询方法
func findUserByWhere(repo GORMRepository, query string, args []interface{}, builders ...Builder) (po.User, error)
func findUserByMap(repo GORMRepository, query map[string]interface{}, builders ...Builder) (po.User, error)
// 3. 列表查询方法
func findUserList(repo GORMRepository, query po.User, builders ...Builder) ([]*po.User, error)
func findUserListByWhere(repo GORMRepository, query string, args []interface{}, builders ...Builder) ([]*po.User, error)
func findUserListByMap(repo GORMRepository, query map[string]interface{}, builders ...Builder) ([]*po.User, error)
// 4. 创建和保存方法
func createUser(repo GORMRepository, object *po.User) (rowsAffected int64, error)
func saveUser(repo GORMRepository, object saveObject) (rowsAffected int64, error)
// 5. 辅助方法
func UserListToMap(list []*po.User, inErr error) (map[int]*po.User, error)
func UserToPoint(object po.User, inErr error) (*po.User, error)
框架提供了两种内置的 Builder 实现,用于分页和限制查询结果:
// Pager - 分页查询
pager := NewDescPager("created_at") // 按创建时间降序
pager.SetPage(1, 10) // 第1页,每页10条
users, err := findUserList(repo, po.User{}, pager)
// Limiter - 限制结果数量
limiter := NewDescLimiter("created_at", 5) // 获取最新5条记录
users, err := findUserList(repo, po.User{}, limiter)
生成的方法都在 repository 包中,需要在 repository 层中使用。以下是完整的使用示例:
// repository/user_repository.go
type UserRepository struct {
Worker freedom.Worker //依赖倒置
}
func NewUserRepository(worker freedom.Worker) *UserRepository {
return &UserRepository{worker: worker}
}
// 实现 GORMRepository 接口
func (repo *UserRepository) db() *gorm.DB {
return repo.worker.DB()
}
func (repo *UserRepository) Worker() freedom.Worker {
return repo.worker
}
// 1. 创建用户
func (repo *UserRepository) Create(user *po.User) error {
affected, err := createUser(repo, user)
if err != nil {
return err
}
if affected == 0 {
return errors.New("创建用户失败")
}
return nil
}
// 2. 更新用户信息
func (repo *UserRepository) Update(user *po.User) error {
affected, err := saveUser(repo, user)
if err != nil {
return err
}
if affected == 0 {
return errors.New("更新用户失败")
}
return nil
}
// 3. 根据ID查询用户
func (repo *UserRepository) FindByID(id int) (*po.User, error) {
user, err := findUserByID(repo, id)
if err != nil {
return nil, err
}
return &user, nil
}
// 4. 条件查询
func (repo *UserRepository) FindByEmail(email string) (*po.User, error) {
user, err := findUserByMap(repo, map[string]interface{}{
"email": email,
})
if err != nil {
return nil, err
}
return &user, nil
}
// 5. 分页查询活跃用户
func (repo *UserRepository) FindActiveUsers(page, pageSize int) ([]*po.User, int, error) {
pager := NewDescPager("created_at")
pager.SetPage(page, pageSize)
users, err := findUserListByWhere(repo, "status = ?", []interface{}{1}, pager)
if err != nil {
return nil, 0, err
}
return users, pager.TotalPage(), nil
}
// 6. 批量查询
func (repo *UserRepository) FindByIDs(ids ...int) (map[int]*po.User, error) {
users, err := findUserListByID(repo, ids...)
if err != nil {
return nil, err
}
return UserListToMap(users, nil)
}
// service/user_service.go
type UserService struct {
Worker freedom.Worker
UserRepo *repository.UserRepository
}
func NewUserService(worker freedom.Worker) *UserService {
return &UserService{
worker: worker,
userRepo: repository.NewUserRepository(worker),
}
}
// 创建用户
func (s *UserService) CreateUser(name, email string) error {
user := &po.User{
Name: name,
Email: email,
}
return s.userRepo.Create(user)
}
// 更新用户信息
func (s *UserService) UpdateUserProfile(userID int, newName, newEmail string) error {
// 1. 先获取 PO
user, err := s.userRepo.FindByID(userID)
if err != nil {
return fmt.Errorf("查询用户失败: %v", err)
}
// 2. 使用 Set 方法修改字段
user.SetName(newName)
user.SetEmail(newEmail)
// 3. 保存修改
return s.userRepo.Update(user)
}
// 用户充值示例
func (s *UserService) RechargeBalance(userID int, amount float64) error {
if amount <= 0 {
return errors.New("充值金额必须大于0")
}
// 1. 获取用户 PO
user, err := s.userRepo.FindByID(userID)
if err != nil {
return fmt.Errorf("查询用户失败: %v", err)
}
// 2. 使用 Add 方法修改数值字段
user.AddBalance(amount)
// 3. 保存修改
return s.userRepo.Update(user)
}
// 更新用户状态示例
func (s *UserService) UpdateUserStatus(userID int, newStatus int) error {
// 1. 获取用户 PO
user, err := s.userRepo.FindByID(userID)
if err != nil {
return fmt.Errorf("查询用户失败: %v", err)
}
// 2. 使用 Set 方法更新状态
user.SetStatus(newStatus)
// 3. 可以同时更新多个字段
user.SetUpdateTime(time.Now())
user.SetUpdater("system")
// 4. 保存所有修改
return s.userRepo.Update(user)
}
// 分页获取用户列表
func (s *UserService) GetActiveUsers(page int) ([]*po.User, int, error) {
return s.userRepo.FindActiveUsers(page, 10)
}
type User struct {
ID int `gorm:"primaryKey;column:id"`
Name string `gorm:"column:name"`
Email string `gorm:"column:email"`
CreatedAt time.Time `gorm:"column:created_at"`
UpdatedAt time.Time `gorm:"column:updated_at"`
changes map[string]interface{}
}
func (obj *User) TableName() string {
return "users"
}
生成的仓储方法包括:
findUser
: 查找单个用户findUserByID
: 根据 ID 查找用户findUserList
: 查找用户列表findUserListByWhere
: 条件查询用户列表createUser
: 创建用户saveUser
: 保存用户更改
-
表结构规范
- 建议每个表都包含主键
- 使用统一的命名规范(建议使用下划线命名)
- 合理设置字段类型
-
代码组织
- PO 对象应只包含与数据库表结构对应的字段
- 业务逻辑应在领域层实现,而不是在 PO 层
-
性能考虑
- 合理使用索引
- 避免生成不必要的字段
- 适当使用批量操作方法
-
生成的字段类型不正确
- 检查数据库字段类型是否标准
- 使用 JSON Schema 时确保类型定义正确
-
表名前缀处理
- 使用
--prefix
参数指定要去除的前缀
- 使用
-
自定义类型映射
- 可以通过修改模板来自定义类型映射规则
- 生成代码前请确保已经备份现有代码
- 确保数据库连接信息正确
- 建议在开发环境中使用此工具
- 生成的代码可能需要根据实际需求进行调整