热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Go语言高效率Web开发:什么是对象关系映射ORM?只是单纯的不手写SQL?

需求场景:文章表articles:id(自增)user_id(用户id)title(文章标题)content(文章内容)created_at(创建时

需求场景:

文章表articles:
id(自增) user_id(用户id) title(文章标题) content(文章内容) created_at(创建时间)帖子表posts:
id(自增) user_id(用户id) title(帖子标题) content(帖子内容) created_at(创建时间)评论表comments:
id(自增) user_id(用户id) article_id(文章id) content(帖子内容) created_at(创建时间)用户表users:
id(自增) name(用户名称) avatar(用户头像) created_at(创建时间)

文章列表接口: /articles
需求:文章id、文章标题、发表时间、用户名、用户头像
实现(两表关联查询):SELECT a.id, a.user_id, a.title, a.created_at, u.name, u.avatar FROM articles AS a, users AS u WHERE a.user_id=u.id ORDER BY a.created_at DESC

[{"id": 1,"userId": 1,"title": "文章标题1","createdAt": "2020-02-03 11:00:00","name": "用户1","avatar": "头像地址1"},...
]

帖子列表接口 /posts
需求:帖子id、帖子标题、发表时间、用户名、用户头像
实现(两表关联查询):SELECT p.id, p.user_id, p.title, p.created_at, u.name, u.avatar FROM posts AS p, users AS u WHERE p.user_id=u.id ORDER BY p.created_at DESC

[{"id": 1,"userId": 1,"title": "帖子标题1","createdAt": "2020-02-03 11:00:00","name": "用户1","avatar": "头像地址1"},...
]

最新文章评论列表接口 /latestComments

需求:按评论时间排序,评论内容、评论时间、评论者名称,评论者头像、文章标题
实现(三表关联查询):SELECT c.id, c.article_id, c.user_id, c.content, c.created_at, u.name, u.avatar, a.title FROM comments AS c, users AS u, articles AS a WHERE c.user_id=u.id AND c.article_id=a.id ORDER BY c.created_at DESC

[{"id": 1,"artileId": 1,"userId": 1,"content": "评论内容1","createdAt": "2020-02-03 11:00:00","name": "用户1","avatar": "头像地址1",'title': "新闻标题1"},...
]

客户端对接服务器接口过程(Android举例)

// 文章列表接口:新建Acticle模型
public class Article {public Integer id; public Integer userId; public String title; public String name; public String avatar;public String createdAt;
}// 帖子列表接口:新建Post模型
public class Post {public Integer id; public Integer userId; public String title; public String name; public String avatar;public String createdAt;
}// 最新评论接口:新建Comment模型
public class Comment {public Integer id; public Integer articleId;public Integer userId;public String content; public String createdAt;public String name; public String avatar;public String title;
}

新增需求


增加用户等级功能,以上3个接口中增加用户等级字段


服务端程序开发过程:
步骤一:用户表增加level字段
步骤二:3条sql增加用户level字段
SELECT a.id, a.user_id, a.title, a.created_at, u.name, u.avatar, u.level FROM articles AS a, users AS u WHERE a.user_id=u.id ORDER BY a.created_at DESC
SELECT p.id, p.user_id, p.title, p.created_at, u.name, u.avatar, u.level FROM posts AS p, users AS u WHERE p.user_id=u.id ORDER BY p.created_at DESC
SELECT c.id, c.article_id, c.user_id, c.content, c.created_at, u.name, u.avatar, u.level, a.title FROM comments AS c, users AS u, articles AS a WHERE c.user_id=u.id AND c.article_id=a.id ORDER BY c.created_at DESC

客户端开发过程: 找到Article、Post、Comment模型,增加level属性;


思考:users表增加1个level字段导致服务端程序要修改3条sql并且连带客户端要修改3个模型?


正确做法<伪代码>

文章列表接口&#xff1a; /articles
$articles &#61; SELECT * FROM articles ORDER BY created_at DESC;
// 获取文章的用户id
$userIds &#61; [];
for $acticle in $articles {$userIds[] &#61; $acticle[&#39;user_id&#39;]
}
$userIdsIn &#61; implode(&#39;,&#39;, $userIds)
// 通过用户id查用户表
$users &#61; SELECT * FROM users WHERE &#96;id&#96; IN ($userIdsIn)$usersMap &#61; new map;
for $user in $users {$usersMap[$user[&#39;id&#39;]] &#61; $user;
}
// 把用户信息合到对应的文章中
for $acticle in $articles {$article[&#39;user&#39;] &#61; $usersMap[$acticle[&#39;user_id&#39;]]
}
这种写法是为了避免嵌套查询
疑惑&#xff1a;费了这么大劲这么多行代码才实现1条联表sql的功能&#xff0c;得不偿失。
// 返回格式
[{"id":1,"userId":1,"title":"文章标题1","content":"文章内容1","createdAt":"2020-02-03 11:00:00","user":{"id":1,"name":"用户1","avatar":"头像地址1","createdAt":"2020-02-03 11:00:00",},},...
]帖子列表接口&#xff1a; /posts
[{"id":1,"userId":1,"title":"帖子标题1","content":"帖子内容1","createdAt":"2020-02-03 11:00:00","user":{"id":1,"name":"用户1","avatar":"头像地址1","createdAt":"2020-02-03 11:00:00",},},...
]最新文章评论列表接口&#xff1a; /latestComments
[{"id":1,"userId":1,"articleId":1,"content":"评论1","createdAt":"2020-02-03 11:00:00","user":{"id":1,"name":"用户1","avatar":"头像地址1","createdAt":"2020-02-03 11:00:00",},"article":{"id":1,"userId":1,"title":"文章标题1","content":"文章内容1","user":null,"createdAt":"2020-02-03 11:00:00",},},...
]

客户端模型&#xff08;Android举例&#xff09;

// User模型
public class User {public Integer id; public String name; public String avatar;
}
// Acticle模型
public class Article {public Integer id; public String title; public String content;public String createdAt;public User user; // 用户模型
}
// Post模型
public class Post {public Integer id; public String title; public String content;public String createdAt;public User user; // 用户模型
}
// Comment模型
public class Comment {public Integer id; public Integer userId;public String content; public String createdAt;public User user; // 用户模型public Article article; // 文章模型
}

实现增加用户等级功能

服务端程序开发过程&#xff1a;
用户表增加level字段&#xff0c;找到User模型&#xff0c;增加level对象属性&#xff1b;客户端开发过程&#xff1a;
找到User模型&#xff0c;增加level对象属性;

什么是ORM?

对象关系映射&#xff08;Object Relational Mapping&#xff0c;简称ORM或O/R mapping&#xff09;&#xff0c;是一种程序技术&#xff0c;用于实现面向对象编程里不同类型系统的数据之间的转换。

ORM是一种程序技术用于实现面向对象编程里在不同类型系统的数据转换时保持对象之间的映射关系。


关键词&#xff1a;不同类型系统&#xff1b;数据转换&#xff1b;保持对象之间的映射关系&#xff1b;


很多同学认为ORM就是比如用PHP的EloquentORM、GoLand的GROM、Java的Hibernate等框架实现数据的增删改查操作&#xff1b;根据ORM的定义&#xff0c;ORM是强调在不同类型的系统数据转换时要保持正确的对象映射关系&#xff0c;不同类型的系统并不仅仅是数据库系统的数据和应用程序的数据相互转换&#xff0c;也包括应用程序中的数据和客户端的数据相互转换&#xff1b;
大部分人对ORM的理解就是不用自己再手写SQL&#xff0c;通过上面的例子说明&#xff0c;不用ORM框架也能写出符合ORM规范的接口&#xff1b;


思考&#xff1a;为什么非要保持这种对象映射关系&#xff1f;


什么是对象关系&#xff1f;

解释&#xff1a;一对一、一对多、多对多
分析&#xff1a;acticle跟user就是一对一关系&#xff0c;一篇文章对应一个用户&#xff1b;user跟acticle是一对多关系&#xff0c;一个用户可以写多篇文章&#xff0c;网上很多文章解释的很清楚&#xff0c;这里不累述

// 用户文章列表接口
{"id":1,"name":"用户1","avatar":"头像地址1","createdAt":"2020-02-03 11:00:00","articles":[{"id":1,"userId":1,"title":"文章标题1","content":"文章内容1","user":null,"createdAt":"2020-02-03 11:00:00",}...],
}客户端模型
// User模型
public class User {public Integer id; public String name; public String avatar;public ArrayList

articles; // 文章模型数组
}

为什么要使用ORM框架

实现1条联表sql的功能用了十几行代码&#xff0c;最新评论接口3张表关联&#xff0c;写起来更麻烦了&#xff0c;ORM框架可以简化这个过程

// gorm举例
// 文章列表接口 /articles
var articles []model.Article
db.Preload("User").Find(&articles)// 执行过程 跟刚才手写的过程是一样的&#xff0c;只是封装好了
SELECT * FROM &#96;articles&#96; WHERE &#96;articles&#96;.&#96;deleted_at&#96; IS NULL
SELECT * FROM &#96;users&#96; WHERE &#96;users&#96;.&#96;deleted_at&#96; IS NULL AND ((&#96;id&#96; IN (1,2)))最新文章评论列表接口 /latestComments
var comments []model.Comment
db.Preload("User").Preload("Article").Find(&comments)
// 执行过程
SELECT * FROM &#96;comments&#96; WHERE &#96;comments&#96;.&#96;deleted_at&#96; IS NULL
SELECT * FROM &#96;users&#96; WHERE &#96;users&#96;.&#96;deleted_at&#96; IS NULL AND ((&#96;id&#96; IN (1,2)))
SELECT * FROM &#96;articles&#96; WHERE &#96;articles&#96;.&#96;deleted_at&#96; IS NULL AND ((&#96;id&#96; IN (1,2)))

增加一个稍微复杂的例子加深理解&#xff1a;

// 文章详情接口 /article/:id
var article model.Article
db.Preload("User").Preload("Comments").Preload("Comments.User").Find(&article, 1)
// 执行过程
SELECT * FROM &#96;articles&#96; WHERE &#96;articles&#96;.&#96;deleted_at&#96; IS NULL AND ((&#96;articles&#96;.&#96;id&#96; &#61; 1))
SELECT * FROM &#96;users&#96; WHERE &#96;users&#96;.&#96;deleted_at&#96; IS NULL AND ((&#96;id&#96; IN (1)))
SELECT * FROM &#96;comments&#96; WHERE &#96;comments&#96;.&#96;deleted_at&#96; IS NULL AND ((&#96;article_id&#96; IN (1)))
SELECT * FROM &#96;users&#96; WHERE &#96;users&#96;.&#96;deleted_at&#96; IS NULL AND ((&#96;id&#96; IN (1))) 返回格式&#xff1a;
{"id":1,"userId":1,"title":"文章标题1","content":"文章内容1","createdAt":"2020-02-03 11:00:00","user":{"id":1,"name":"用户1","avatar":"头像地址1","articles":null,"createdAt":"2020-02-03 11:00:00",},"comments":[{"id":1,"userId":1,"articleId":1,"content":"评论1","article":null,"createdAt":"2020-02-03 11:00:00","user":{"id":1,"name":"用户1","avatar":"头像地址1","articles":null,"createdAt":"2020-02-03 11:00:00",},}...],
}客户端模型
// Acticle模型
public class Article {public Integer id; public String title; public String content;public String createdAt;public ArrayList comments; // 评论模型数组public User user; // 用户模型
}

代码符合ORM规范的优势

一、提高服务器开发效率&#xff0c;使用ORM框架不用手写效率低下SQL语句&#xff1b;
二、提高客户端的对接效率&#xff0c;客户端模型与服务器模型一一对应&#xff0c;不同业务就是不同模型的组合&#xff1b;
三、降低客户端与服务器端人员的沟通成本&#xff0c;都在相同的模型上讨论业务&#xff0c;不容易产生歧义&#xff1b;
四、可以通过服务器模型生成对应的客户端模型文件&#xff0c;参照Apache Thrift和gRPC&#xff1b;
五、使用ORM框架&#xff0c;避免了关联查询&#xff0c;针对单表sql的慢查询优化比多表关联sql要简单&#xff1b;
六、ORM框架都实现了SQL预编译&#xff0c;从而避免了SQL注入&#xff1b;
七、避免关联查询会带来很多好处&#xff0c;参照<<高性能MySQL第3版 6.3.3章节&#xff1a;分解关联查询>>&#xff1a;


后记

SELECT * FROM 带来的问题
问题1&#xff1a;不需要大字段的时候也被查出来&#xff0c;比如文章列表接口&#xff0c;content字段为text类型&#xff0c;手写sql的可以不写content&#xff0c;但是ORM框架每次都是全部查出来&#xff1b;
分析&#xff1a;sql规范没有某种语法糖&#xff0c;比如SELECT NOT(content) FROM articles可以把排除content字段的剩余所有字段全部查询出来&#xff1b;
解决&#xff1a;增加articlesContent表&#xff0c;把content放在这张表中&#xff0c;通过article_id关联articles&#xff0c;需要content的时候再load进来&#xff1b;

问题2&#xff1a;某些接口不想暴露某张表中的全部字段&#xff0c;但是SELECT * FROM没法过滤&#xff1b;
解决&#xff1a;应用程序增加接口数据过滤功能&#xff0c;通过字段参数配置过滤不想暴露的字段&#xff1b;

问题3&#xff1a;有次讨论有人问统计业务怎么用ORM&#xff0c;统计业务要我做我也手写sql&#xff0c;ORM说的是对象映射关系&#xff0c;跟统计业务没关系&#xff1b;


 

 


推荐阅读
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 标题: ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
author-avatar
手机用户2502922161
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有