教授告诉我们将序列化的 Java 对象存储为 blob,而不是定义关系表

Tyl*_*vis 21 database-design

我的教授告诉我们,我们可以像这样将对象映射到 id,而不是实际定义具有正确属性的表:

id (int)  |   Serialized Object (blob)
   1               10010110110
Run Code Online (Sandbox Code Playgroud)

我可以看到很多问题;数据冗余,必须单独跟踪 id,必须将整个表拉入内存以搜索任何内容,**如果我想在 Java 代码中更改我的模型,我将不再能够反序列化存储在数据库到该模型。

要么我永远坚持那个模型,要么我必须做一些其他非常丑陋的事情来改变我的模型。**这整件事对我来说似乎是糟糕的形式。我有理由不同意我的教授吗?这样做有什么我没有想到的好处吗?如果我是对的,我应该对我的教授说些什么吗?他向我全班宣讲了这一点,甚至说他是用这种方式建造项目的。第二个意见会很棒。

该课程名为软件设计

我的教授并没有说这是最好的方法,但他确实说这是定义关系表的合理替代方法。

该模型在任何方面都不是动态的。

fdr*_*ger 35

  1. 这本身并不是一件坏事——完全不是。在没有适当背景(=确切要求)的情况下争论“哪个更好”是徒劳的。

  2. 粗体部分是错误的。您可以轻松扩展已序列化的对象以添加新字段并实现与旧对象的完全二进制兼容性。您也可以简单地创建新类而不是更改原始类。

你与教授的讨论应该集中在不同场景中“关系”与“键值存储”的优缺点,而不是抽象的“更好”。或者你也可以讨论圣诞节是否优于感恩节。

- 阅读其他答案后进行编辑。

其他答案之一甚至指出“很难想象利大于弊的情况”。

因为整个讨论必须是具体问题(否则我们甚至无法定义“更好”和“更坏”),让我举一个具体的例子。它完全是编造的,但我试图充实尽可能多的细节。

假设您有一个在线游戏站点,其中有一个数据库存储不同在线游戏(在浏览器中玩,用 GWT 编写并交叉编译为 javascript)中玩家的统计数据。有些游戏是战略游戏,有些是动作游戏,有些是平台游戏。该数据库是关系型的,存储球员和比赛历史以及得分。

有一天你会收到一个额外的要求:让玩家在游戏过程中将游戏状态保存到云端,以便他们稍后在同一时间重新开始游戏。不用说,存储这个临时状态的唯一原因是回到游戏中,状态本身永远不会被反省。

现在您有两个基本选择:

  • 由于游戏是用 Java 编写的,因此您可以轻松获取模型,将其发送到服务器,在一行代码中将其序列化并存储为 blob。该表将被称为“saved_games”,它将具有玩家的外键等等。从数据库的角度来看,“保存游戏”是一个不透明的、不可分割的 blob。

  • 您可以为 100 场比赛中的每场创建一个单独的关系模型(每场比赛将有数十张桌子)。例如,对于吃豆人来说,你必须有一个表格来存储所有未吃的药丸的位置、奖金、位置和幽灵的当前状态。如果有一天有人修改了游戏,即使是轻微的修改,您也必须更新关系模型。此外,对于每种类型的游戏,您都必须实现一个逻辑来将 Java 模型写入数据库,然后将其读回。

Justin Cave 的回答说,你应该选择第二个选项。我认为这将是一个巨大的错误。

此外,我有一种预感,贾斯汀·凯夫 (Justin Cave) 的看法是,我上面介绍的是“边缘”或“罕见”案例。我相信除非他能提供某种硬数据(基于世界上所有 IT 项目的代表性样本,而不仅仅是美国的企业应用程序),否则我会认为这种观点是典型的预测案例偏见。

实际上,在关系数据库中序列化 Java 对象的问题比看起来要深得多。它触及了 1NF 的核心,即 属性的域是什么?. 如果您真的对这个主题感兴趣,那么 CJ Date 在他的Date on Database: Writings 2000-2006 中有一篇很棒的文章。


Jus*_*ave 22

人们能否(并且确实)成功交付执行此类事情的项目?不幸的是,是的,他们经常这样做。

这是一个好方法吗?不,这不对。您基本上是将相对昂贵的数据库变成一个相对较慢的文件系统。如果您真的想构建一个通过序列化和反序列化对象来保存其状态的系统,您最好使用文件系统而不是使用数据库。

如果您通过将对象序列化到数据库中来构建存储数据的系统,那么您将不会与 DBA 成为朋友。您最终将存储冗余数据。您最终会得到非常不一致的数据——任何时候共享数据被更新,一些对象将以新值结束,而一些对象将以旧值结束。您将无法对数据进行任何形式的报告——任何人想要对数据进行的所有操作都需要有人编写额外的代码。对于大多数企业来说,这是一个巨大的问题,因为他们想要做一些事情,比如从一个系统中提取数据以加载到另一个系统中,或者拥有一个可以从多个前端应用程序提供报告的报告系统。另外,正如你所指出的,当你“

这种方法有优势吗?我想您可能会争辩说,实现该应用程序的第一个版本非常容易。它让开发人员完全忽略与正确与数据库交互相关的任何事情。我很难想象在许多情况下,这些优点超过了该方法的众多缺点。

至于您应该如何与这位特定教授打交道,这是一个单独的问题(并且可能超出了本论坛的范围)。如果你的教授在现实世界中积极开发项目,他可能不会非常接受学生提出的任何关于他的方法根本错误的论点(即使方法真的从根本上是错误的)。你可能会更好地按照教授想要的方式做你的项目,并学习自己(或在不同的课程中)保存数据的正确方法。

  • 你说的,加上我的两分钱。可重用性是关于模块化和共享。对象模型侧重于共享对象和重用代码。数据库模型侧重于共享和重用数据。这两个模型都不是完全愚蠢的。两种模式都不是完美的。调和这两者非常非常困难。 (2认同)

Lie*_*yan 10

在某些情况下,这种设计是明智的,如果没有你描述你的项目是关于什么以及它是如何使用的,很难说这是否合适。

如果您存储 BLOB,您的 DBA 可能会讨厌您,但在许多情况下,唯一的其他选择是将表转换为 Entity-attribute-value,这让 DBA 更加讨厌。另一种选择是使用非关系型数据库,通常是基于对象或基于字典的数据库或面向文档的数据库,一些 DBA,尤其是那些只了解关系型的 DBA,会更加讨厌这些数据库。非关系数据库有自己的问题需要处理,但使用对象数据库存储对象肯定会暴露其他问题,而这些问题在关系系统中可以轻松解决。

这样做有什么我没有想到的好处吗?

存储序列化对象意味着您可以存储无模式数据(请注意,尽管名称如此,无模式通常并不意味着实际上根本没有模式,而是只有隐式模式)。有许多问题域,您不可能在开发时提前定义架构,并且遵循传统的关系数据库设计意味着您必须每隔一周更改一次数据库的架构,或者您最终会得到一个表80% 时间未使用的 80% 的列,或数百个不同的表来存储真正相同的数据,这些都表明设计不是很好。此问题的根源通常是因为您强行将非关系问题域拟合到关系数据库中。

当然,在很多项目中,人们认为他们需要使用 EAV、无模式或 Blob 存储,结果证明这会不必要地造成本可以避免的痛苦。你绝对应该和你的教授讨论他的推理是什么,并提供你自己的论点;聆听争论,并准备好您最终可能会同意他的观点,或者不同意,也许他是错的。


Jus*_*tin 7

我以前做过这个 - 它在某些情况下是一种有用的技术,但是取决于所使用的序列化格式。如果我这样做,我确保我使用序列化格式,允许我反序列化我的模型的旧版本(例如 XML)。

我通常会在数据格式会导致复杂的关系模型没有任何优势的情况下使用它(例如,当业务需求不需要任何过滤等时......)并且我已经在使用数据库(对于其他关系数据)。一个这样的例子是一个有用户查询的应用程序 - 关系模型有一些表来存储条件、嵌套条件(OR / AND 等...)、排序选项等...它非常复杂,所以当我们需要添加一个需要对数据库进行更改的新功能,我将整个内容替换为一个查询表,其中包含一个表示所有其他选项的序列化 blob。

另一个案例是处理各种“作业”的系统。有几种不同类型的工作,每个工作都有不同的参数,没有业务要求能够根据这些参数搜索/过滤工作。将其存储为关系数据库将需要每个作业类型至少 1 个新表,因此很难添加新的作业类型。相反,参数作为 blob 存储在数据库中 - 每个作业类型负责序列化和反序列化自己的参数。

您不会经常遇到这样的情况,但是时不时会出现上述情况,其中序列化 blob 数据可以节省您的工作量,使您的应用程序更易于维护并且没有真正的缺点。


小智 6

Justin Cave 是正确的,这会导致冗余数据,但这实际上取决于您如何设计数据库。

将整个对象序列化为 blob 的方法并不像这里的大多数人认为的那样离谱。事实上,对于某些应用程序,这可能是您能做的最佳设计,正如我在此处解释的:https : //stackoverflow.com/a/12644223/1121352

事实上,序列化一个对象至少有两个好处:

1-减少阻抗不匹配:某些 Java 类型在 SQL 中不可用,特别是如果您使用大量类和自定义类型,因此从 Java 对象来回转换为 SQL 可能会非常麻烦,甚至会导致歧义。

2-在您的架构中具有更大的灵活性。确实,关系模式非常适合共享相同结构的数据,但是如果单个类中的某些对象根据运行时的条件可以具有不同的属性,那么关系模式可能会严重阻碍您的工作流程。

因此,这种方法肯定有好处(至少是这两个,但肯定还有其他我没有引用的方法),但当然要付出的巨大代价是你失去了几乎所有关系模式的好处。

但是,如果您仔细设计数据库,则可以两全其美:您仍然可以通过使用每个对象唯一的属性来设置关系模式(即:唯一键列),然后将对象存储在 blob 中. 这样,您仍然可以确保在给定由对象属性定义的某些唯一标识符的情况下快速检索对象,同时减少冗余,同时消除阻抗不匹配并保持 Java 对象的完全灵活性。

作为旁注,一些数据库制造商尝试将关系模型和对象模型混合在一起,例如PostSQL和 PostgreSQL 中的 JSON 数据类型,以便您可以像处理任何关系列一样直接处理 JSON,以及 SQL3 和 OQL(对象查询语言)将(有限的)对象支持添加到 SQL 中。

归根结底,这完全是关系模型和对象模型之间的设计和折衷问题。

/EDIT阅读评论后:当然,如果您的数据必须可搜索(“可查询”),则不应将数据存储为 blob。但是,如果您的数据的某些部分不是可搜索的,而是某种元数据,那么将此数据部分作为对象存储在 blob 中可能是一个很好的解决方案,特别是如果此元数据具有灵活的结构并且可以从一个对象改变到另一个对象。


Ian*_*ose 5

让我们举一个实际的例子,说明我过去何时这样做过。

我们有一个包含多用户应用程序所有数据的数据库;该数据库还有一个用户表及其访问权限。所有这些数据都按预期进行了标准化。

然后我们有一个请求,要求应用程序记住用户打开了哪些窗口以及他们在做什么,以便在用户第二天早上开始工作时恢复状态。

  • 首先,如果这有时会失败,这不是无礼的吗?

    • 例如,如果有人第一次使用新版本的应用程序,它会忘记他们打开的窗口,那怎么办……
  • 因此,如果对象发生变化,则有 100% 的回退,因此我们无法读取该块。

  • 我们已经有一个带有访问控制、备份等功能的集中式数据库。
  • 将数据存储在文件中的成本很高,因为文件必须放在所有用户机器都可以访问的某种文件服务器上,或者必须编写 API 来读取这些文件。

还有一次,我们有一个应用程序进行了大量长时间运行的计算,如果停电等情况,用户希望能够从最后一个已知的好点重新开始计算。应用程序可能会重新开始计算,并且由于需要保存大量对象,因此对数据进行规范化的成本会很高。

由于数据库已经就位并用于定义明确的规范化应用程序数据,并且没有真正的理由不使用它来存储博客,我们采取了明智而快速的选择。