数据库随笔
背景
目前软件开发行业中,无论是移动端开发还是后端开发,基本上都会碰到数据库的开发,这里就谈谈笔者对于数据库的感想
冲突
在移动端亦或是后端开发中,很多时候,我们会感觉到无论是 ORM 还是其他方案,都会存在着一些缺点,其实这来源于数据库本身和开发语言本身的冲突,现代化的语言基本上都是面向对象开发,面向对象是从软件工程基本原则(如耦合、聚合和封装)的基础上发展起来的,而关系数据库则是从数学理论发展而来的,两套理论存在显著的区别。
数据库最初的时候就是起源于文件,一个程序打开一个文件,然后将一些数据存入文件中,最后关闭文件,从这个需求开发,发展出了数据库。目前主流关系型数据库主要有两种,一种是基于网络传输的数据库,一种则是嵌入式数据库,这两种数据库区别就在于是否存在完善的权限管理和是否服务端代码嵌入到了程序中。
无论是哪种关系型数据库,开发者都是通过 SQL 语言和其打交道,主流的 ORM 技术实际上只是由数据库中查询数据,按条读取结果集中每一个字段,然后装配成对象,或者需要把对象的每一个属性拆出来,拼凑成SQL字符串,再提交数据库。
ORM 技术
广义上,ORM指的是面向对象的对象模型和关系型数据库的数据结构之间的相互转换。 狭义上,ORM可以被认为是,基于关系型数据库的数据存储,实现一个虚拟的面向对象的数据访问接口。这里指的 ORM 是指 Hibernate 这样的框架,至于 ORM 框架的好处就不说了,ORM 一般情况下,一个持久化类和一个表对应,类的每个实例对应表中的一条记录,类的每个属性对应表的每个字段。 但是,这本身 SQL 语言是存在冲突的,我们先来拆分一下要求
- 数据库需要做 Migration(比如建表,增加字段,删除字段,描述默认值,增加索引,增加约束等)
- 表和表通过关系产生联系
- SQL 语句可以让你只选择自己需要的列
- SQL 语句参数和结果不保证类型安全
- 结果可能是虚的,比如存储过程和视图
面向对象和上面的行为实际上是格格不入的,比如将表抽象为一个类,每个类的实例是一行数据,看起来确实很完美,但是其实存在一个隐藏的缺陷。
- 首先,主要的数据库操作,基本都是 CRUD 组成,一般来说,增加、删除、更新语法都是差不多的,很容易将其抽象,但是查询就不简单了,对数据的关注度不一样,所需要的查询语句就不同,在针对查询的方面,ORM 很难覆盖全部的情况。
- 其次,面向对象中,存在着继承,如果两个对象存在继承关系,那如何设计表结构,
目前的主流 ORM 技术有好多种,Hibernate 是一种比较典型的如上面所述的 ORM 技术,Mybatis 则是一种半自动化的 ORM 技术,这里讲一讲 Mybatis
- Mybatis 不存在 Migration 功能,必须要通过第三方功能支持
- Mybatis 只做了 SQL 到对象的映射,类本身不代表数据表,而转化出的对象都以 POJO 的形式提供。这实际上是一种字段绑定
也就是说,ORM 本身,需要做两件事
- 映射表的本身和迁移
- 属性和数据库的字段绑定
Mybatis 由于过于依赖 SQL,因此如果将底层数据库进行替换,则会导致很大的迁移工作量,Hibernate 就不存在这个问题,但是 Hibernate 确实不够灵活,刚才也讲到了 SQL 查询的问题,因此 Hibernate 如果要做到灵活,实在是代价太大了。两者各有优缺点。
ORM 的意义
ORM 主要有两个意义
- 防止注入,保证 SQL 安全
- 抽象 OOP,便于开发
除了上面这两种 ORM 以外,还有一种更加轻量级的技术方案,就是提供一整套类似于函数调用方式来写 SQL 查询,借助 IDE 的代码提示和编译器语法检查,来保证安全,这种方案是灵活度最高的。但是更加繁琐
在针对防注入方面,通常做法是使用绑定参数和预处理语句,避免字符串的拼接,或者采用手段,防止传入的单引号提前截断 SQL
针对查询的问题的想法
由于数据库查询存在这么大的问题,而且需要保证 SQL 安全,笔者有两条思路
- 封装常用的操作,覆盖大多数场景
- 提供安全的底层手段,解决特殊查询情况