欢迎投稿

今日深度:

ORM规范API通用格式及禁止联表查询方案实现ORM,

ORM规范API通用格式及禁止联表查询方案实现ORM,apiorm


什么是ORM?


大部分程序员对ORM的理解就是不用写SQL,通过对象的方式来增删改查数据,这种对ORM的理解可以说是偏差很大,如果通读百度百科对于ORM的解释,没有任何描述是关于写SQL的,我们先看一下百度百科的解释:

对象关系映射(Object Relational Mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换

着重看标红部分,比如说PHP写API接口,Android调用此接口,这就是不同类型系统的数据之间的转换。


如何写出符合ORM的接口?


实例演示:

文章表:articles   
字段:id user_id title content created_at updated_at

评论表:comments  
字段:id user_id article_id content created_at updated_at 

用户表:users
字段:id name avatar created_at updated_at 


文章列表接口 /articles:
需求:文章id 文章标题 用户名 用户头像
常用联表实现:
SELECT a.id, a.title, u.name, u.avatar FROM articles AS a, users AS u WHERE a.user_id=u.id

返回结果:
[
    {
        "id": 1,
        "title": "文章标题1",
        "name": "用户1",
        "avatar": "头像地址1"
    },
    {
        "id": 2,
        "title": "文章标题2",
        "name": "用户2",
        "avatar": "头像地址2"
    },
]

这样写会有什么问题???

我们先看Android如何解析这个文章列表接口:

先建个bean,跟接口字段一一对应,如下

public class ArticleBean {
    public int id;    
    public String title;    
    public String name;    
    public String avatar;

}

如果Android端要做缓存,在sqlite里建了一张articles表,表里字段id、title、name、avatar;

现在问题来了,本来name、avatar是服务器users表中的字段,到了Android端变成了articles中的字段,这样就不符合ORM规范。



文章列表接口符合ORM到底要长什么样?先看正确格式:
[
    {
        "id": 1,
        "user_id" => 1,
        "title": "文章标题1",

        "user": {

            "id" => 1,

            "name": "用户1",
            "avatar": "头像地址1"
        }
    },
    {
        "id": 2,
        "user_id" => 2,
        "title": "文章标题2",
        "user": {
            "id" => 2,
            "name": "用户2",
            "avatar": "头像地址2"
        }
    }
]

再看看这样的格式Android要如何解析?

建一个ArticleBean和一个UserBean,如下

public class ArticleBean {

    public int id;    

    public int user_id;    

    public String title;    
    public UserBean user;    // 注意这里
}

public class UserBean { 
    public int id;    
    public String name;    
    public String avatar;
}

这个时候Android如果要做缓存,会建articles和users表,这样就保证了客户端跟服务器数据表是一一对应,这样才符合ORM规范。


练习一下:最新文章评论列表接口怎么写?

[
    {
        "id": 1,
        "artile_id": 1,
        "user_id": 1,
        "content": "评论内容1",
        "artile": {
            "id": 1,
            "title": "文章标题1"
        },
        "user": {
            "id": 1,
            "name": "用户1",
            "avatar": "头像地址1"
        }
    }
]

符合ORM规范的API接口的优势:
1、移动端model与服务器model一一对应,不同业务就是不同model的组合。
2、移动端使用第三方json框架非常容易解析,不用写一行代码,比如 Android:Gson  Objective-C:MJExtension。
3、数据表增加属性服务器端和移动端都很简单,比如增加一个用户等级字段level,如果按以前的方式客户端要在所有bean里找到需要用到level的bean,再一一添加,现在只需要在userBean添加level属性就OK了。
 
PHP如何写出如何符合ORM的API?
文章列表接口:

$articles = 'SELECT id, user_id, title FROM `articles`';


// 把文章表中的user_id字段归入一个数组中

$userIdArr = [];
forearh($articles as $article) {
    $userIdArr[] = $article['user_id'];
}


// 通过userId数组查询出所需要的user

$users = 'SELECT id, name, avatar FROM `users` WHERE id IN ' . '(' . implode(',', $userIdArr) . ')';

// 用用户id做key,创建出一个新的用户数组

$usersNew = [];
forearh($users as $user) {
    $usersNew[$user['id']] = $user;
}


// 循环文章数组,通过用户id获取到对应的用户

forearh($articles as $article) {
    $articles['user'] => $usersNew[$article['user_id']];

}


大家看起来感觉肯定很费劲,本来联表查询两三行的代码变成了十几行,这还只是两张表,如果是最新文章评论接口,涉及到三张表更麻烦,如果以这种方式要求程序员写出符合ORM规范的接口肯定有抵触情绪;

如果让大家开心快乐又省心又省力的写出符合ORM规范的接口呢?

后文会有解决方案,先不急,我们先看看一种不常见的优化MySQL的方案:禁止联表方案。



MySQL优化方案:禁止联表


禁止联表的好处:
高性能MySQL_第3版 6.3.3章节:分解关联查询


补充:
优化慢查询:针对单表SQL的优化肯定比多表联合SQL优化要简单。


如何方便的把禁止联表方案和ORM结合一起?

Eloquent ORM的使用:


class User extends Model { }


class Article extends Model { 
    public function user() { 
        return $this->belongsTo('App\User'); 
    }

}


class Comment extends Model { 
    public function user() { 
        return $this->belongsTo('App\User'); 
    } 
    public function article() { 
        return $this->belongsTo('App\Article'); 
    } 
}

文章列表:

$articles = Article::with('user')->get();  // 一行代码代替了上面的十几行代码


最新文章评论列表:

$comments = Comment::with('user')->with('article')->get();  // 涉及到三张表的查询,也只是一行代码

Eloquent ORM跟禁止联表有毛关系?


我们把查询文章列表的SQL打印出来:
DB::enableQueryLog(); 
$articles = Article::with('user')->get(); 
print_r(DB::getQueryLog());

Array(    
    [0] => Array        (            
        [query] => select * from `articles`           
        [bindings] => Array (  )            
        [time] => 0        
    )    
    [1] => Array  (            
        [query] => select * from `users` where `users`.`id` in (?, ?, ?, ?)   
        [bindings] => Array (                    
            [0] => 1                    
            [1] => 2                    
            [2] => 3                    
            [3] => 4                
        )           
        [time] => 1        
     )
)

这里可以看到,Eloquent ORM跟我们刚才十几行代码查询的方式一样,也是先取出文章表中的用户id,然后通过用户id查询到所需要的用户,再合到文章数组中。


再看看最新文章评论列表:
DB::enableQueryLog();    
$comments = Comment::with('user')->with('article')->get();    
print_r(DB::getQueryLog());

Array(    
    [0] => Array  (            
        [query] => select * from `comments`           
        [bindings] => Array  (  )           
        [time] => 0       
    )    
    [1] => Array  (            
        [query] => select * from `users` where `users`.`id` in (?)          
        [bindings] => Array (                    
            [0] => 1               
        )           
        [time] => 0        
    )    
    [2] => Array   (            
        [query] => select * from `articles` where `articles`.`id` in (?)          
        [bindings] => Array   (                   
            [0] => 1                
        )            
        [time] => 0       
     )
)


很方便是吧,当时看到Eloquent ORM是这种分表查询的方式,很激动,刚好跟禁止联表查询方案思路很符合,推广起来大家也愿意接受。


总结:

1、符合ORM规范的API可以大大提高客户端的对接效率;

2、使用Eloquent ORM,可以节省程序员20-30%的开发时间;

3、Eloquent ORM非常好用,个人觉得也是Laravel在PHP框架中排行第一的很重要的一个原因;

4、Laravel开发后台 + Lumen开发接口,完美搭配。


曾经跟一个携程的哥们和一个腾讯的哥们聊这个方案,他们认为这个方案适合服务器跟客户端打交道,如果只是服务器之间的API调用,或者微服务之间的API调用,这方案还适合吗,比如PHP对接文章列表接口,对接项目还需要建ArticleModel、UserModel、CommentModel,需要这么麻烦吗?其实如果使用过Apache Thrift(远程服务调用框架 )或者Go语言中使用很广泛的Protobuf数据交换格式,这两种RPC框架都会自动生成各个语言的扩展包,扩展包包含类似的Model集合,然后由对接项目引入这个扩展包进行对接,所以ORM在这些RPC框架里都是广泛存在的,只不过PHP语言里应用的很少。

ORM不仅仅可以作用于服务器与移动端的交互,也可以作用于前后端分离、RPC调用,前后端分离跟上述的方式差不多,我们再看看ORM在RPC中的使用。


www.htsjk.Com true http://www.htsjk.com/shujukunews/10061.html NewsArticle ORM规范API通用格式及禁止联表查询方案实现ORM,apiorm 什么是ORM? 大部分程序员对ORM的理解就是不用写SQL,通过对象的方式来增删改查数据,这种对ORM的理解可以说是偏差很大,如果通读...
评论暂时关闭