笔者在后端方面只是小白,没有受过专门的训练(比如看网上的培训视频),只是自己对着框架官网、项目代码、文档等瞎摸索。所以写下来的东西都有一定的阶段性和局限性。最近的体悟如下:
分层
- 一个好的项目最好能够有一定的分层,每个部分恪守职责,出了问题也可以方便定位。比如Java的Spring系列项目而言,单体项目从数据实体到接口分四层:entity、mapper、service、controller。接下来会自下而上来讲:
- 最下层的entity是数据实体类,一个类对应于数据库中的一个表;
- 当说起mapper的时候,它实际上对应了两个东西:一个是resource目录下的xml文件,一个是java目录下mapper包中的xxMapper,里面是一些增删查改的简单接口。两者中的内容可通过相同的id相对应(代码生成插件应该也是基于这样的规则吧)。
-
xml文件可以理解为是换了种方式写sql,把sql语句封装起来,一定程度上避免了SQL注入等等的问题,也比直接写原生的JDBC代码更方便。mybatis框架是一个orm(object relation mapping)框架。大家所说的SSM框架中的M指的就是mybatis。
-
为什么在java中要用mybatis呢?其实更早的时候大家用的是Hibernate(据说现在国外的Java开发者中用Hibernate的依然居多)。
-
Hibernate框架的特点是自上而下,即:写好了一个类实体,它能连接数据库,生成对应的表和sql语句;Mybatis则相反,是先在数据库中有了表,然后程序员再依据这个数据库表写mapper里面的xml。(若描述有误还请前辈指出哈)
-
为什么会有这样的差异呢?可能是因为那时候的Java程序员并不是很需要关心数据库的一些细节。
-
然而历史总是有轮回的,在现在很受推崇的Go语言编程中,笔者觉得Gorm的设计理念实际上和Hibernate很类似。(前者甚至提供了migrate功能,连数据库都不用预先创建,虽然框架开发者也不提倡盲目这样做哈)
-
-
service层写的是(可能稍复杂些的)增删查改业务逻辑。(笔者目前接触的系统只是最基础的xx管理系统,以下观点本身可能不具普遍性哈)。有时候,mapper写好的xml并不能完全满足要求,但是又不希望把xml写得过于复杂(或者说过于特例,导致不具有普遍性,每新添一个接口就得多写一次xml),这个时候就需要在service层进行一些筛选和封装。
-
这里又引申出一个问题:实际项目中应不应该使用硬删除、物理外键、join和子查询呢?笔者认为这因项目而异,没有绝对标准。(但是如果公司老板或者项目组长是这么要求的,无需理由,只需执行(手动狗头))
-
为什么规范里面会有这样的考虑呢?以硬删除为例,笔者认为有以下原因:
- 严格来讲,删除一条记录的数据库开销会比修改一条记录的值的开销大。当高并发、大容量的情况下这种差异尤其明显。
- 数据库分很多类,有的非关系型数据库其实对删除记录很不友好,所以不断往后追加新记录,并且每次以查询到的最新的记录为准是一种解决方案。
- 然而如果是写在本地练手的小demo,连的是装在本机的MySQL,个人觉得没必要折腾自己的电脑,表的数据太多太冗余了也不是一件好事。
-
关于物理外键:
- 物理外键很容易因为不恰当的删除操作引发数据异常(指有关联的表中一个表的数据删除了,另外一个表的还没有)。笔者认为这不应该完全把责任归咎于外键本身,毕竟存在即合理。
- 对于小的项目,其实花点心思编写service层,保证删除的时候有始有终、干干净净就可以了。
- 对于更大的项目也许确实可以考虑使用“逻辑外键”,也就是建表的时候不用外键的标识来关联,但是进行查询等等操作的时候使用join之类的方法将两个有关联的条件等同起来。
-
关于join和子查询:
- 想必学过数据库课程的同学都对这两者的概念比较熟悉了,这里不再赘述。为什么会有这样的问题呢?原因是据说在大型项目下使用数据库引擎的join和子查询会降低效率。所以建议在service层拼接数据。
- 其实笔者研究了一天之后还是不懂到底是怎么个拼接法(网上有段forforfor的代码,刷新了我对效率的认知),这种拼接是否真的能保证数据不丢失、不混乱,非大型项目中笔者手写的拼接又是否真的能比经过数据库引擎优化之后的原生语句好。所以还是默默地使用了join和子查询。
- 笔者对“在service层拼接数据”是持开放态度的,只是缺少了实战场景。如果有大佬了解这一块内容的话欢迎在评论区留言呀!不胜感激!
-
-
最后是controller层。其实就是对外的接口层,映射了url访问路径,和前端项目中的路由是对应的。
-
顺便提一句,笔者还没有接触过SpringCloud,但是接触过Go的微服务。个人认为Go中的微服务是比较简单的,在笔者接触过的项目中,与controller对应的是handler,从单体升级为微服务只需要改改handler的代码就行了。看似复杂的事情,往往只需要简单的实现方式。微服务并不神秘,不过可能某些框架把它搞得过于神秘了。
对人友好
写人能看懂的代码。写能让几个月后的自己、接手自己项目的小伙伴能看懂的代码。
对拓展开放
有些接口用不到,那就先不写了。但是整个项目架构要有规划:比如
- 一些通用的工具类就单独写好放在一处。
- 一些可能会经常用到的代码就封装成函数方便引用,不要总是复制粘贴到某处。
- 代码不要写死,要做好以后还会添加新功能、新方法、新语句的准备。
最后,这只是个人的一些碎碎念,也感谢你能看到这里,希望也能对你有帮助!如果你对文章提到的观点有什么想法的话,也欢迎提出,一起交流!