接着来看下如何使用 Scan 方法实现用户详情的自定义输出,由于 Scan 不支持预加载,需要手动做些处理,代码如下:
var user User var profile Profile var userOutput CustomUser // 将不带关联查询的数据直接按 userOutput 结构扫描赋值 err := DB.Debug(). Model(&user). Where("id = ?", 1). Scan(&userOutput). Error // 这里要判断查询是否出错,可能查询本身出错,也可能是查询不到对应数据 if err != nil { return } // 只有正常查询到 User 数据,才能继续查询其关联的 Profile 数据, // 可以简单构造一个对应的 User 数据用于下面的关联查询, // 这里简单构造一个 ID = 1 的 User 数据用于演示,并不严谨,实际应用需要根据需要进行调整 user.ID = 1 // 获取 Profile 关联数据,并赋值给变量 profile, // 注意,分步查询中,Model方法中不能传 &User{},而要传递同一个实例,否则无法保证两次查询数据的关联性 DB.Debug(). Model(&user). Related(&profile, "UserID") // 手动赋值 userOutput.Profile = &CustomProfile{ Nickname: profile.Nickname, Phone: profile.Phone, }然后将 userOutput 序列化输出即可。
小结本篇介绍了如何自定义输出结构体,并使用“自定义序列化方法”、“Scan 方法”两种数据映射方式,实现自定义结构的数据输出。
在关键的数据映射方式的选择上,两种方式各有优劣,个人认为:
简单应用场景下,使用 Scan 方法方便快捷,代码量也少,但是不支持预加载,需自行处理;
复杂应用场景下,推荐使用自定义序列化方法这种方式,虽然代码量多了,但这种方式更灵活,低耦合,便于理解和维护,代码的可读性和可维护性更重要。
顺带抛出一个疑问,在 Restful API 盛行的今天,关联查询是否还那么重要?欢迎一起探讨。