如何优雅的操作数据库(分层)
一、 Dao层操作数据库
DAO层:dao层叫数据访问层,全称为data access object,属于一种比较底层,比较基础的操作,具体到对于某个表、某个实体的增删改查。DAO层一定是和数据库的某几张表一一对应的,其中封装了增删改查基本操作,只做原子操作,增删改查。
实现: 一般在dao层声明一个数据表的结构体,然后在dao层中实现对数据表的增删改查的操作接口。
示例:
//user_dao.go
type ID struct {
ID string `gorm:"column: id"`
}
type NAME struct {
NAME string `gorm:"column: name"`
}
type ADDRESS struct {
PHONE string `gorm:"column: phone"`
}
type USER struct {
ID
NAME
ADDRESS
}
func (u *USER) addUser() error {
DB.Create(&user)....
}
func (u *USER) deleteUser() error {
...
}
func (u *USER) updateUser() error {
...
}
func (u *USER) getUser() error {
...
}
二、 Service层处理业务逻辑
Service层: 业务层,Service层是建立在DAO层之上的服务层,粗略的理解就是对一个或多个DAOj接口进行的再次封装,封装成一个服务,所以这里也就不会是一个原子操作了,需要事务控制。具体要调用已经定义的多个dao层接口,封装service层业务逻辑有利于通用的业务逻辑的独立性和重复利用性。程序显得非常简洁。
//user_service.go
func (u *USER) UserAdd(req []byte) (interface{}, int, error) {
//1.处理验证req请求的逻辑
....
//2.与其它表或模块进行交互
....
//3. 调用dao层的接口处理数据
....
}
func (u *USER) Userupdate(req []byte) (interface{}, int, error) {
//1.处理验证req请求的逻辑
....
//2.与其它表或模块进行交互
....
//3. 调用dao层的接口处理数据
....
}
...
如何优雅的把service层的处理逻辑与restful请求绑定
一、service层实现了业务逻辑处理,此外对每一个业务逻辑接口额外创建一个结构体struct,实现HandleFunc方法,即struct为一个handler接口, 可用于绑定url。
1、首先确定业务接口与url的绑定逻辑
// handle.go
var handlers = make(map[string]Handler)
type Handler interface {
HandleFunc(req string) (interface{}, int, error)
}
//RegisterHandler
func RegisterHandler(url string, h Handler) error {
_, ok := handlers[url]
if ok {
return errors.New("Url handle func alread registed")
}
handlers[url] = h
return nil
}
2、在service层中声明一个空的结构体AddUser ,实现HandleFunc的方法,该接口的逻辑是把req请求转到UserAdd接口中。
//user_service.go
func (u *USER) UserAdd(req []byte) (interface{}, int, error) {
//1.处理验证req请求的逻辑
....
//2.与其它表或模块进行交互
....
//3. 调用dao层的接口处理数据
....
}
// 为 UserAdd 接口新建一个结构体 AddUser
type AddUser struct {
}
// AddUser结构体实现 HandleFunc 方法,为 Handler接口类型
func (A *AddUser) HandleFunc(req string) (rsp interface{}, err error) {
var input USER
err := json.Unmarshal([]byte(req), &input)
if err != nil {
SLOG.Warnf("Json unmarshal req fail: %v", req)
return nil, err
}
err=(&input).UserAdd(req)
}
var A = AddUser{}
RegisterHandler("/add_user", &A )
分析:在上述方法中每个业务逻辑接口都对应的需要新建一个结构体,并且新建的结构体都需要实现handler接口的HandleFunc方法,可以再进一步的进行抽象。
二、service层的逻辑处理抽象为一个公共接口形式IService,此外对所有的业务逻辑接口创建一个公共的结构体Common,Common结构体含有IService匿名接口成员,即继承IService。Common结构体还实现HandleFunc方法,在HandleFunc中调用IService接口的方法,实现为所有service层的业务接口提供公共的HandleFunc方法逻辑。即Common结构体也为一个handler接口, 可用于绑定url。
1、service层的逻辑处理的公共接口形式IService
//
type IService interface {
Init(req []byte, c *IConfig) error //初始化请求, 把请求数据配置到对应IService接口的实现结构体中
ToUserData() *USER //根据请求数据,配置对应IService接口的实现结构体为相应数据表的结构体形式
Handle(d *USER) (interface{}, int, error) //根据请求方法,实现service层的业务逻辑处理,利用上一步生成对应数据表的结构体调用Dao层的数据库接口。
}
2、service层的每个业务处理都对应的新建一个结构体,结构体按照以上接口形式实现接口IService。
type AddUser struct {
Name string `json:"name" validate:"required,min=1,max=64"`
}
//初始化请求,把请求数据配置到对应IService接口结构体中
func (req *AddUser) Init(req []byte, input *IService) error {
*input = &AddUser{}
json.Unmarshal(req, input)
}
//根据请求数据,配置对应IService接口的实现结构体为相应数据表的结构体形式
func (inputC *AddUser) ToUserData() *USER {
user:=USER{}
user.NAME = input.Name
return user
}
//根据请求方法,实现service层的业务逻辑处理,会调用Dao层的数据库接口。
func (inputC *AddUser) Handle(u *USER) (interface{}, int, error) {
u.addUser()
}
//注册新建的AddUser结构体即IService接口类型和其对应处理的url。
registerIConfig("/add_user", &AddUser{})
3、对所有的业务逻辑接口创建一个公共的结构体IServiceHandler,IServiceHandler结构体含有IService匿名接口成员,即继承IService。IServiceHandler结构体还实现HandleFunc方法,在HandleFunc中调用IService接口的方法,实现为所有service层的业务接口提供公共的HandleFunc方法逻辑。即IServiceHandler结构体也为一个handler接口, 可用于注册绑定url到handles的map中。
var handlers = make(map[string]IHandler)
type IHandler interface {
HandleFunc(url string, req []byte) (interface{}, int, error)
}
func RegisterHandler(name string, handler IHandler) {
handlers[name] = handler
}
func registerIConfig(name string, i IService) {
h := IServiceHandler{i}
model.RegisterHandler(name, &h)
}
type IServiceHandler struct {
IService
}
func (c *IServiceHandler) HandleFunc(url string, req []byte) (interface{}, int, error) {
var service IService
...
c.Init(req, &service); err != nil {
...
d:=service.ToUserData()
...
return service.Handle(d)
}
三、上述第二种方法抽象为不用为service层的每个业务逻辑接口对应的结构体实现HandleFunc方法,全局有一个公共的HandleFunc的处理逻辑。但是这种方法仍有一定的缺点,就是仍需要为每个service层的逻辑接口新建一个结构体。接下来我们采用函数类型匹配的方法解决这个问题。
1、数据库操作分层—Dao层直接数据库, 文章开始介绍Dao层操作数据表的方法是用结构体指针的形式调用dao层接口。还有一种方法是定义一个Dao接口IUser,IUser接口内定义了Dao层的所有crud的方法,把对应数据表的结构体参数放到接口方法的入参当中。在Dao层新建一个公共结构体CommonDao并实现这个IUser接口。即在Service层调用CommonDao结构体即可调用Dao层的所有crud的方法。
//user_dao.go
type IUser interface {
addUser(user User) error
deleteUser(user string) error
updateUser(user User) error
listUser(user User) error
//其它表和user表的操作相同,添加方法即可
//addOther
//deleteOther
//updateOther
//listOther
}
type CommonDao struct{}
func (u *CommonDao) addUser(user User) error {...}
func (u *CommonDao) deleteUser(user User) error {...}
func (u *CommonDao) updateUser(user User) error {...}
func (u *CommonDao) listUser(user User) error {...}
//其它表和user表的操作相同
//func (u *CommonDao) addOther
2、service层按规定的函数签名func(req []byte) (interface{}, int, error)
直接实现业务逻辑,并直接把业务函数绑定到url路径上。
//user_service.go
func UserAdd(req []byte) (interface{}, int, error) {
//1.把请求的数据转成相应的结构体
var input User
err := json.Unmarshal([]byte(req), &input)
//2.与其它表或模块进行交互
....
//3. 业务逻辑并调用dao层的接口处理数据,即获取CommonDao结构体指针,用来调用Dao层接口。
//获取CommonDao结构体可单独实现为一个接口,用sync.Once为一个全局IUser变量赋予一个&CommonDao{}结构体。
//每次调用GetCommonDao获取这个系统全局变量的值
....
}
//把此上面的函数UserAdd直接注册绑定一个url路径
RegisterHandle("/add_user", UserAdd)
3、因为UserAdd的函数签名和HandleFunction一致。利用函数类型匹配的技巧,可先UserAdd函数转成一个函数类型HandlerFunc,而HandlerFunc类型实现HandleFunction方法,即UserAdd函数最终变为IHandle接口,可用于真正的注册。
var handlers = make(map[string]IHanle)
type IHandle interface {
HandleFunc(req []byte) (interface{}, int, error)
}
// HandlerFunc adapter
type HandlerFunc func(req []byte) (interface{}, int, error)
// HandleFunc calls f(url, req)
func (f HandlerFunc) HandleFunc(req []byte) (interface{}, int, error) {
return f(req)
}
//第2步中是调用的此RegisterHandle接口。
func RegisterHandle(name string, handle func(req []byte) (interface{}, int, error)) {
RegisterHandler(name, HandlerFunc(handle))
}
//RegisterHandler
func RegisterHandler(name string, handle IHandle) error {
handlers[name] = handle
return nil
}