动态分配和静态分配

    技术2024-04-06  72

    关于本系列

    大约十年来,信息存储和检索几乎已经成为RDBMS的同义词,但是最近情况已经开始改变。 尤其是Java开发人员对所谓的对象关系阻抗不匹配感到沮丧,并且对尝试解决它的解决方案不耐烦。 这以及可行的替代方案的出现,引起了对对象持久性和检索兴趣的复兴。 繁忙的Java开发人员db4o指南介绍了db4o,这是一个开放源码数据库,它利用了当今的面向对象的语言,系统和思维方式。 请参见db4o主页以立即下载db4o。 您将需要它来遵循示例。

    在本系列中,我介绍了使用db4o进行面向对象的数据管理的要点。 但是,我尚未做的一件事就是解决OODBMS如何在Web应用程序中使用以及它与在Swing或SWT应用程序中的使用可能有何不同。 您可以说我已经忽略了Java(或.NET)开发人员无法忽视的所有问题。

    在某种程度上,我一直希望专注于有关OODBMS的最吸引人的方面,即面向对象的数据存储,操纵和检索。 同样,OODBMS供应商倾向于实现类似于交易管理和安全性之类的核心功能,类似于各种RDBMS处理它们的方式,并且具有类似的广泛选择。

    在繁忙的Java开发人员db4o指南的最后一部分中,我将介绍您期望从任何数据存储系统中获得的三个特性,无论它们是面向对象的,关系的或其他的。 准备学习db4o如何支持应用程序安全性,分发和事务。

    多个客户端连接

    到目前为止,我为该系列编写的代码都假定数据库只有一个客户端。 也就是说,仅将一个逻辑连接创建到数据库,并且所有交互都将通过该逻辑连接进行。 对于Swing或SWT应用程序访问配置数据库或本地存储系统,这是一个完全合理的假设。 但是对于Web应用程序,即使是其中所有存储都在Web表示层中完成的应用程序,也不太现实。

    在db4o系统中,即使数据库位于本地磁盘上,也很难打开与数据库的第二个逻辑连接。 我只需要添加一个调用来首先创建一个ObjectServer ,然后从该ObjectServer获取ObjectContainer对象。 让ObjectServer监听端口0会告诉它以“嵌入式”模式运行,并且在进行下一个探索测试时不会打开(或损坏)实际的TCP / IP端口。

    清单1.嵌入式连接
    @Test public void letsTryMultipleEmbeddedClientConnections() { ObjectServer server = Db4o.openServer("persons.data", 0); try { ObjectContainer client1 = server.openClient(); Employee ted1 = (Employee) client1.get( new Employee("Ted", "Neward", null, null, 0, null)) .next(); System.out.println("client1 found ted: " + ted1); ObjectContainer client2 = server.openClient(); Employee ted2 = (Employee) client2.get( new Employee("Ted", "Neward", null, null, 0, null)) .next(); System.out.println("client2 found ted: " + ted2); ted1.setTitle("Lord and Most High Guru"); client1.set(ted1); System.out.println("set(ted1)"); System.out.println("client1 found ted1: " + client1.get( new Employee("Ted", "Neward", null, null, 0, null)) .next()); System.out.println("client2 found ted2: " + client2.get( new Employee("Ted", "Neward", null, null, 0, null)) .next()); client1.commit(); System.out.println("client1.commit()"); System.out.println("client1 found ted1: " + client1.get( new Employee("Ted", "Neward", null, null, 0, null)) .next()); System.out.println("client2 found ted2: " + client2.get( new Employee("Ted", "Neward", null, null, 0, null)) .next()); client2.ext().refresh(ted2, 1); System.out.println("After client2.refresh()"); System.out.println("client2 found ted2: " + client2.get( new Employee("Ted", "Neward", null, null, 0, null)) .next()); client1.close(); client2.close(); } finally { server.close(); } }

    刷新对象视图

    请注意,在运行测试时,请注意在测试中间放置几条输出线。 跟踪正在发生的事情很重要,因为您知道db4o系统会保留对在此过程中已打开的对象的引用。 我想确保我知道何时和何处将内存中对象的更新“传递”给第二个客户端。

    set(ted1) :当我执行set(ted1)调用时,db4o系统修改其内部状态以知道ted1对象是脏的并且需要更新。 在commit()隐式事务之前,它不会进行实际的更新,但是,使用ObjectContainer上的commit()方法。 此时,数据已写入磁盘,但是与磁盘上的内容相比, client2在内存中的对象视图仍然过时。 (浏览一下探索测试的输出以进行演示。阅读本系列文章时,您已经在浏览器旁边的控制台窗口中运行了代码,对吗?)

    修复很简单: client2使用extension对象(由ext()返回refresh()上的refresh()方法刷新其在内存中的对象图视图。 请注意,激活深度的问题在这里成为一个因素:您希望db4o在刷新对象时下降到对象图有多深? 在这种情况下,只需执行一次下移就可以检索修改后的Employee ,但是显然,应该根据具体情况重新考虑此决策。

    刷新其对象视图后, client2将看到更改。 快速查询即可找到该公司(显然是自负的)领导者的新头衔。

    它将以层级结束

    在大多数情况下,您不会有多个客户端生活在一个流程中,而是跨多个流程。 例如,您可能会在Servlet容器内以经典的客户端-服务器样式与一台服务器通信的客户端集群。 在db4o中实现这一点几乎与清单1中看到的相同。 唯一的不同是,您需要一个非零的端口号来打开服务器。 端口号表示服务器正在侦听的TCP / IP端口。 与所有基于TCP / IP的通信一样,客户端在连接时必须指定主机名和端口。

    巩固堡垒

    自然,一旦涉及端口,安全性就成为问题,因为您不能允许“任何人”连接到服务器并开始发出查询。 在传统的RDBMS实现中,供应商提供了丰富而强大的安全模型,它基于打开连接时发送到数据库的凭据(用户名和密码)来授予对数据库实例的某些或所有部分的访问权限。

    db4o实现没有什么不同,至少没有效果。 db4o数据库实例创建者设置授予的安全策略的方式与通常的RDBMS场景相比是一个惊人的变化(如清单2所示):

    清单2.与其他人很好地沟通
    @Test public void letsTryMultipleNetworkClientConnections() { ObjectServer server = Db4o.openServer("persons.data", 2771); server.grantAccess("client1", "password"); server.grantAccess("client2", "password"); // Yes, "password" is a bad password. Don't do this in production // code. I get to do it only because I have a special Pedagogical // Code License from Sun Microsystems. And you don't. So don't do // this. Don't make me come over there. I'm serious. // Fuggedaboutit. Never. Not ever. Capice? try { ObjectContainer client1 = server.openClient("localhost", 2771, "client1", "password"); ObjectContainer client2 = server.openClient("localhost", 2771, "client1", "password"); Employee ted1 = (Employee) client1.get( new Employee("Ted", "Neward", null, null, 0, null)) .next(); System.out.println("client1 found ted: " + ted1); Employee ted2 = (Employee) client2.get( new Employee("Ted", "Neward", null, null, 0, null)) .next(); System.out.println("client2 found ted: " + ted2); ted1.setTitle("Lord and Most High Guru"); client1.set(ted1); System.out.println("set(ted1)"); System.out.println("client1 found ted1: " + client1.get( new Employee("Ted", "Neward", null, null, 0, null)) .next()); System.out.println("client2 found ted2: " + client2.get( new Employee("Ted", "Neward", null, null, 0, null)) .next()); client1.commit(); System.out.println("client1.commit()"); System.out.println("client1 found ted1: " + client1.get( new Employee("Ted", "Neward", null, null, 0, null)) .next()); System.out.println("client2 found ted2: " + client2.get( new Employee("Ted", "Neward", null, null, 0, null)) .next()); client2.ext().refresh(ted2, 1); System.out.println("After client2.refresh()"); System.out.println("client2 found ted2: " + client2.get( new Employee("Ted", "Neward", null, null, 0, null)) .next()); client1.close(); client2.close(); } finally { server.close(); } }

    在db4o中定义访问控制仅由grantAccess()方法组成,它授予对整个数据库实例的访问权限。 这既是一件好事,也有一件坏事,因为它简化了安全设置方案,但又使您无法实践“最小特权原则” 。

    最小特权原则

    像许多安全性想法一样,“最小特权原则”在理论上是一个简单的想法,但在实践中有时很难实现。 原则指出,给定的用户或代码体应仅被赋予执行其分配任务所必需的特权,仅此而已(显然也同样如此)。 因此,例如,在RDBMS方案中,访问RDBMS的代码应仅对它访问的那些表具有基本的SELECT/INSERT/UPDATE/DELETE权限,而仅此而已。 这样,如果代码成为SQL注入攻击的牺牲品,则由于数据库访问受到限制,该代码将无法执行注入攻击。

    尽管其他OODBMS系统可能更灵活,但在db4o中目前无法在更细粒度的级别上解决此问题。 目前,您应该确保仅使用最少数量的安全凭证。 如果您不能限制登录使用的资源,那么您至少可以限制访问系统的主体数量。

    加密格式

    分布式系统中的安全问题(最著名的是企业计算的第四种谬论)(请参阅参考资料中的 有效企业Java )指出,您不能相信侦听网络流量的唯一个人是受信任的个人或代码。 这意味着您必须确保通过网络传输的数据不会以明文或二进制形式发送(即使是以二进制形式发送,即使格式众所周知,也与明文相同)。

    关注安全性的开发人员还将关注db4o文件中存储的数据,因为该文件只是“文件”,因此可以通过打开文件并读取内容来进行攻击。 (关系数据库也存在同样的问题!)

    解决方案是对文件进行加密,这在db4o中相对简单。 在大多数情况下,db4o的默认加密方案(扩展的Tiny加密算法(XTEA))可以很好地将数据混淆给偶然的攻击者。 对于其他实例,db4o提供了一个定制的加密“挂钩”,使您能够使用第三方加密提供程序。 (除非定义您自己的加密格式,否则除非您可以写一篇挑战既定标准的论文,由世界上最好的密码学家和数学家合集并在一次大型安全会议上进行口头审查为自己辩护,否则不要定义自己的加密格式。 '已经做了所有这些,您可能会考虑使用它;毕竟,仅仅因为以上所有内容都没有揭示出一种流程,并不意味着一个流程根本就不存在。)

    您看不到我-或我的数据!

    保护有线格式的数据传输绝对是一件棘手的事情,因为db4o到6.3一直没有通过安全传输线(例如通过SecureSocket SSL)通信的功能。 这意味着任何敏感数据都必须以加密方式传输,这意味着对象本身内部将进行某种形式的加密。 (如果db4o系统直接实现它会更好;在撰写本文时,db4o 6.4发行版已计划支持将SocketFactory传递给openServer调用,因此您可以使用SecureSocket连接而不是全开Socket连接。)

    请注意,您可以使用自定义封送程序以“跨领域”方式跨传输保护数据。 顾名思义,这使您能够控制数据跨线传输的打包方式。 这样做看起来与我们通过Externalizable接口在自定义序列化中看到的非常相似:编写一个实现ObjectMarshaller接口的类,实现一个readFields()和writeFields()方法,然后告诉db4o系统将自定义编组器用于特定的通过在所需类的ObjectClass上调用marshallWith()来实现对象的类。 整个过程如下:

    Db4o.configure().objectClass(Item.class).marshallWith(customMarshaller);

    这样做将无法确保整个线路的安全-攻击者仍然可以看到所保留的对象的种类-但是,当数据在网络中从一个节点到另一个节点穿越时,它将防止看到数据。

    对于db4o选择“嵌入式还是客户机/服务器”的方案不足以满足您的需求,例如将数据存储为特定的文件格式或非传统的数据存储资源,db4o库允许您创建以下类型的子类: IoAdaptor ,这是db4o在存储对象时向其写入数据的关键抽象。 这允许在大多数RDBMS系统中看不到的存储灵活性。

    一切美好的事物 ...

    关于OODBM和db4o还有很多要说和探索的地方,但是我已经完成了我打算要做的事情,并决定现在结束本系列。 我相信我为db4o和面向对象的数据管理提供了很好的案例,并且从Java开发人员的角度介绍了使OODBMS与RDBMS不同的所有功能。 我已经展示了db4o如何轻松跟踪关联,如何将继承作为数据库中的一流概念捕获以及如何使用定义那些对象的本机编程语言简化了对这些对象的检索。

    我还试图指出OODBMS的缺点,以及db4o遇到RDBMS的相同问题,例如与处理跨客户端-服务器网络的“往返”相关的性能挑战。

    如果您一直在跟踪示例并尝试本系列中的代码,那么您已经建立了使用任何OODBMS系统(而不仅仅是db4o)所必需的基本技能。 尝试将您学到的知识应用于Cache'或Versant,可能是一个不错的练习。 大多数OODBMS遵循相同的基本编码约定和惯用表达式,实际上db4o对本机查询的支持催生了将该功能标准化为所有OODBMS的工作。

    希望您在本系列中找到了您想要的东西,并且已经准备好在自己的项目中开始使用OODBMS。 这可以是一种非常自由的体验,而不必担心关系模式及其相关的所有内容。 因此,放松一点:尝试,实施,在玩的同时获得乐趣,别忘了给我一张明信片,上面写着您的经历。 (哦,很好,那样,电子邮件也可以。)


    翻译自: https://www.ibm.com/developerworks/java/library/j-db4o7/index.html

    Processed: 0.010, SQL: 9