- 浏览: 289789 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
SpringJava:
摘过来的
小心使用ArrayList和LinkedList -
jingjing0907:
我要成为第一个赞的人!呵呵,
小心使用ArrayList和LinkedList -
SpringJava:
cilendeng 写道应该用ConcurrentHashMa ...
实现单用户登陆session先进先出(session踢出) -
lingxiajiudu:
不错,完美解决我了的问题,赞一个。
子窗体给父窗体传值 javascript opener -
cilendeng:
应该用ConcurrentHashMap
实现单用户登陆session先进先出(session踢出)
集合和数组为 面向 Java 开发人员的 db4o 指南: 超越简单对象 中首次讨论的结构化对象引入了新的复杂性。幸运的是,db4o 丝毫没有因为处理多样性关系而出现困难 —— 您应该也不会被它难倒。
在本系列的前一篇文章中,我开始谈到了 db4o 如何处理 结构化对象 ,或者包含非原始类型字段的对象。正如我所展示的那样,增加对象关系的复杂性对 db4o 持久模型有一些重大的影响。我谈到了在删除期间解决像激活深度(activation depth)、级联更新与删除和参照完整性等问题的重要性。我还介绍了一种叫做 探察测试 的开发人员测试策略,附带给出了使用 db4o API 的第一个练习。
在本文中,我继续介绍 db4o 中结构化对象的存储和操作,并首先介绍多样性关系(multiplicity
relationship)
,在多样性关系中,对象中含有对象集合形式的字段。(在此,集合
是指像 ArrayList
之类的 Collection
类和标准语言数组。)您将看到,db4o 可以轻松处理多样性。您还将进一步熟悉 db4o 对级联更新和激活深度的处理。
|
随着这个系列深入下去,之前的 Person
类肯定会变得更加复杂。在 关于结构化对象的上一次讨论
结束的时候,我在 Person
中添加了一个 spouse
字段和一些相应的业务逻辑。在那篇文章的最后我提到,舒适的家庭生活会导致一个或更多 “小人儿” 降临到这个家庭。但是,在增加小孩到家庭中之前,我想先确保我的 Person
真正有地方可住。我要给他们一个工作场所,或者还有一个很好的夏日度假屋。一个 Address
类型应该可以解决所有这三个地方。
清单 1. 添加一个 Address 类型到 Person 类中
package com.tedneward.model; public class Address { public Address() { } public Address(String street, String city, String state, String zip) { this.street = street; this.city = city; this.state = state; this.zip = zip; } public String toString() { return "[Address: " + "street=" + street + " " + "city=" + city + " " + "state=" + state + " " + "zip=" + zip + "]"; } public int hashCode() { return street.hashCode() & city.hashCode() & state.hashCode() & zip.hashCode(); } public boolean equals(Object obj) { if (obj == this) return this; if (obj instanceof Address) { Address rhs = (Address)obj; return (this.street.equals(rhs.street) && this.city.equals(rhs.city) && this.state.equals(rhs.state) && this.zip.equals(rhs.zip)); } else return false; } public String getStreet() { return this.street; } public void setStreet(String value) { this.street = value; } public String getCity() { return this.city; } public void setCity(String value) { this.city = value; } public String getState() { return this.state; } public void setState(String value) { this.state = value; } public String getZip() { return this.zip; } public void setZip(String value) { this.zip = value; } private String street; private String city; private String state; private String zip; } |
可以看到,Address
只是一个简单的数据对象。将它添加到 Person
类中意味着 Person
将有一个名为 addresses
的 Address
数组作为字段。第一个地址是家庭住址,第二个是工作地址,第三个(如果不为 null 的话)是度假屋地址。当然,这些都被设置为 protected,以便将来通过方法来封装。
完成这些设置后,现在可以增强 Person
类,使之支持小孩,所以我将为 Person
定义一个新字段:一个 Person
ArrayList
,它同样也有一些相关的方法,以便进行适当的封装。
接下来,由于大多数小孩都有父母,我还将添加两个字段来表示母亲和父亲,并增加适当的 accessor/mutator 方法。我将为 Person
类增加一个新的方法,使之可以创建一个新的 Person
,这个方法有一个贴切的名称,即
haveBaby
。此外还增加一些业务规则,以支持生小孩的生物学需求,并将这个新的小 Person
添加到为母亲和父亲字段创建的 children
ArrayList
中。做完这些之后,再将这个婴儿返回给调用者。
清单 2 显示,新定义的 Person
类可以处理这种多样性关系。
package com.tedneward.model; import java.util.List; import java.util.ArrayList; import java.util.Iterator; public class Person { public Person() { } public Person(String firstName, String lastName, Gender gender, int age, Mood mood) { this.firstName = firstName; this.lastName = lastName; this.gender = gender; this.age = age; this.mood = mood; } public String getFirstName() { return firstName; } public void setFirstName(String value) { firstName = value; } public String getLastName() { return lastName; } public void setLastName(String value) { lastName = value; } public Gender getGender() { return gender; } public int getAge() { return age; } public void setAge(int value) { age = value; } public Mood getMood() { return mood; } public void setMood(Mood value) { mood = value; } public Person getSpouse() { return spouse; } public void setSpouse(Person value) { // A few business rules if (spouse != null) throw new IllegalArgumentException("Already married!"); if (value.getSpouse() != null && value.getSpouse() != this) throw new IllegalArgumentException("Already married!"); spouse = value; // Highly sexist business rule if (gender == Gender.FEMALE) this.setLastName(value.getLastName()); // Make marriage reflexive, if it's not already set that way if (value.getSpouse() != this) value.setSpouse(this); } public Address getHomeAddress() { return addresses[0]; } public void setHomeAddress(Address value) { addresses[0] = value; } public Address getWorkAddress() { return addresses[1]; } public void setWorkAddress(Address value) { addresses[1] = value; } public Address getVacationAddress() { return addresses[2]; } public void setVacationAddress(Address value) { addresses[2] = value; } public Iterator<Person> getChildren() { return children.iterator(); } public Person haveBaby(String name, Gender gender) { // Business rule if (this.gender.equals(Gender.MALE)) throw new UnsupportedOperationException("Biological impossibility!"); // Another highly objectionable business rule if (getSpouse() == null) throw new UnsupportedOperationException("Ethical impossibility!"); // Welcome to the world, little one! Person child = new Person(name, this.lastName, gender, 0, Mood.CRANKY); // Well, wouldn't YOU be cranky if you'd just been pushed out of // a nice warm place?!? // These are your parents... child.father = this.getSpouse(); child.mother = this; // ... and you're their new baby. // (Everybody say "Awwww....") children.add(child); this.getSpouse().children.add(child); return child; } public String toString() { return "[Person: " + "firstName = " + firstName + " " + "lastName = " + lastName + " " + "gender = " + gender + " " + "age = " + age + " " + "mood = " + mood + " " + (spouse != null ? "spouse = " + spouse.getFirstName() + " " : "") + "]"; } public boolean equals(Object rhs) { if (rhs == this) return true; if (!(rhs instanceof Person)) return false; Person other = (Person)rhs; return (this.firstName.equals(other.firstName) && this.lastName.equals(other.lastName) && this.gender.equals(other.gender) && this.age == other.age); } private String firstName; private String lastName; private Gender gender; private int age; private Mood mood; private Person spouse; private Address[] addresses = new Address[3]; private List<Person> children = new ArrayList<Person>(); private Person mother; private Person father; } |
即使包括所有这些代码,清单 2 提供的家庭关系模型还是过于简单。在这个层次结构中的某些地方,必须处理那些 null 值。但是,在 db4o 中,那个问题更应该在对象建模中解决,而不是在对象操作中解决。所以现在我可以放心地忽略它。
|
|
对于清单 2 中的 Person
类,需要重点注意的是,如果以关系的方式,使用父与子之间分层的、循环的引用来建模,那肯定会比较笨拙。通过一个实例化的对象模型可以更清楚地看到我所谈到的复杂性,所以我将编写一个探察测试来实例化 Person
类。
注意,清单 3 中省略了 JUnit 支架(scaffolding);我假设您可以从其他地方,包括本系列之前的文章学习 JUnit 4 API。通过阅读本文的源代码,还可以学到更多东西。
@Test public void testTheModel() { Person bruce = new Person("Bruce", "Tate", Gender.MALE, 29, Mood.HAPPY); Person maggie = new Person("Maggie", "Tate", Gender.FEMALE, 29, Mood.HAPPY); bruce.setSpouse(maggie); Person kayla = maggie.haveBaby("Kayla", Gender.FEMALE); Person julia = maggie.haveBaby("Julia", Gender.FEMALE); assertTrue(julia.getFather() == bruce); assertTrue(kayla.getFather() == bruce); assertTrue(julia.getMother() == maggie); assertTrue(kayla.getMother() == maggie); int n = 0; for (Iterator<Person> kids = bruce.getChildren(); kids.hasNext(); ) { Person child = kids.next(); if (n == 0) assertTrue(child == kayla); if (n == 1) assertTrue(child == julia); n++; } } |
目前一切尚好。所有方面都能通过测试,包括小孩 ArrayList
的使用中的长嗣身份。但是,当我增加 @Before
和 @After
条件,以便用我的测试数据填充 db4o 数据库时,事情开始变得更有趣。
@Before public void prepareDatabase() { db = Db4o.openFile("persons.data"); Person bruce = new Person("Bruce", "Tate", Gender.MALE, 29, Mood.HAPPY); Person maggie = new Person("Maggie", "Tate", Gender.FEMALE, 29, Mood.HAPPY); bruce.setSpouse(maggie); bruce.setHomeAddress( new Address("5 Maple Drive", "Austin", "TX", "12345")); bruce.setWorkAddress( new Address("5 Maple Drive", "Austin", "TX", "12345")); bruce.setVacationAddress( new Address("10 Wanahokalugi Way", "Oahu", "HA", "11223")); Person kayla = maggie.haveBaby("Kayla", Gender.FEMALE); kayla.setAge(8); Person julia = maggie.haveBaby("Julia", Gender.FEMALE); julia.setAge(6); db.set(bruce); db.commit(); } |
注意,存储整个家庭所做的工作仍然不比存储单个 Person
对象所做的工作多。您可能还记得,在上一篇文章中,由于存储的对象具有递归的性质,当把 bruce
引用传递给 db.set()
调用时,从 bruce
可达的所有对象都被存储。不过眼见为实,让我们看看当运行我那个简单的探察测试时,实际上会出现什么情况。首先,我将测试当调用随 Person
存储的各种 Address
时,是否可以找到它们。然后,我将测试是否孩子们也被存储。
@Test public void testTheStorageOfAddresses() { List<Person> maleTates = db.query(new Predicate<Person>() { public boolean match(Person candidate) { return candidate.getLastName().equals("Tate") && candidate.getGender().equals(Gender.MALE); } }); Person bruce = maleTates.get(0); Address homeAndWork = new Address("5 Maple Drive", "Austin", "TX", "12345"); Address vacation = new Address("10 Wanahokalugi Way", "Oahu", "HA", "11223"); assertTrue(bruce.getHomeAddress().equals(homeAndWork)); assertTrue(bruce.getWorkAddress().equals(homeAndWork)); assertTrue(bruce.getVacationAddress().equals(vacation)); } @Test public void testTheStorageOfChildren() { List<Person> maleTates = db.query(new Predicate<Person>() { public boolean match(Person candidate) { return candidate.getLastName().equals("Tate") && candidate.getGender().equals(Gender.MALE); } }); Person bruce = maleTates.get(0); int n = 0; for (Iterator<Person> children = bruce.getChildren(); children.hasNext(); ) { Person child = children.next(); System.out.println(child); if (n==0) assertTrue(child.getFirstName().equals("Kayla")); if (n==1) assertTrue(child.getFirstName().equals("Julia")); n++; } } |
|
您可能会感到奇怪,清单 5 中显示的基于 Collection
的类型(ArrayList
)没有被存储为 Person
类型的 “dependents”,而是被存储为一个成熟的对象。这还说得过去,但是当对对象数据库中的 ArrayList
运行一个查询时,它可能,有时候也确实会导致返回奇怪的结果。由于目前数据库中只有一个 ArrayList
,所以还不值得运行一个探察测试,看看当对它运行一个查询时会出现什么情况。我把这作为留给您的练习。
自然地,存储在一个集合中的 Person
也被当作数据库中的一级实体,所以在查询符合某个特定标准(例如所有女性 Person
)的所有
Person
时,也会返回 ArrayList
实例中引用到的那些 Person
,如清单 6 所示。
@Test public void findTheGirls() { List<Person> girls = db.query(new Predicate<Person>() { public boolean match(Person candidate) { return candidate.getGender().equals(Gender.FEMALE); } }); boolean maggieFound = false; boolean kaylaFound = false; boolean juliaFound = false; for (Person p : girls) { if (p.getFirstName().equals("Maggie")) maggieFound = true; if (p.getFirstName().equals("Kayla")) kaylaFound = true; if (p.getFirstName().equals("Julia")) juliaFound = true; } assertTrue(maggieFound); assertTrue(kaylaFound); assertTrue(juliaFound); } |
注意,对象数据库将尽量地使引用 “correct” —
至少在知道引用的情况下如此。例如,分别在两个不同的查询中检索一个 Person
(也许是母亲)和检索另一个 Person
(假设是女儿),仍然认为她们之间存在一个双向关系,如清单 7 所示。
@Test public void findJuliaAndHerMommy() { Person maggie = (Person) db.get( new Person("Maggie", "Tate", Gender.FEMALE, 0, null)).next(); Person julia = (Person) db.get( new Person("Julia", "Tate", Gender.FEMALE, 0, null)).next(); assertTrue(julia.getMother() == maggie); } |
当然,您正是希望对象数据库具有这样的行为。还应注意,如果返回女儿对象的查询的激活深度被设置得足够低,那么对 getMother()
的调用将返回 null,而不是实际的对象。这是因为 Person
中的 mother
字段是相对于被检索的原本对象的另一个 “跳跃(hop)”。(请参阅 前一篇文章
,了解更多关于激活深度的信息。)
|
|
至
此,您已经看到了 db4o
如何存储和取出多个对象,但是对象数据库如何处理更新和删除呢?就像结构化对象一样,多对象更新或删除期间的很多工作都与管理更新深度有关,或者与级联删
除有关。现在您可能已经注意到,结构化对象与集合之间有很多相似之处,所以其中某一种实体的特性也适用于另一种实体。如果将 ArrayList
看作 “另一种结构化对象”,而不是一个集合,就很好理解了。
所以,根据到目前为止您学到的东西,我应该可以更新数据库中的某一个女孩。而且,为了更新这个对象,只需将她父母中的一个重新存储到数据库中,如清单 8 所示。
@Test public void kaylaHasABirthday() { Person maggie = (Person) db.get( new Person("Maggie", "Tate", Gender.FEMALE, 0, null)).next(); Person kayla = (Person) db.get( new Person("Kayla", "Tate", Gender.FEMALE, 0, null)).next(); kayla.setAge(kayla.getAge() + 1); int kaylasNewAge = kayla.getAge(); db.set(maggie); db.close(); db = Db4o.openFile("persons.data"); kayla = (Person) db.get( new Person("Kayla", "Tate", Gender.FEMALE, 0, null)).next(); assert(kayla.getAge() == kaylasNewAge); } |
还记得吗,在 前一篇文章 中,我必须显式地关闭到数据库的连接,以避免被误诊为重取已经位于工作内存中的对象。
对于多样性关系中的对象,其删除工作非常类似于上一篇文章介绍索的结构化对象的删除工作。只需注意级联删除,因为它对这两种对象可能都有影响。当执行级联删除时,将会从引用对象的每个地方彻底删除对象。如果执行一个级联删除来从数据库中删除一个 Person
,则那个 Person
的母亲和父亲在其 children
集合中突然有一个 null 引用,而不是有效的对象引用。
|
|
在很多方面,将数组和集合存储到对象数据库中并不总与存储常规的结构化对象不同,只是要注意数组不能被直接查询,而集合则可以。不管出于何种目的,这都意味着可以在建模时使用集合和数组,而不必等到持久引擎需要使用集合或数组时才使用它们。
发表评论
-
栈的简单应用--单词反转
2014-07-03 16:00 695我们知道栈是一种先进后出,也就是后进先出的数据 ... -
java实现简单的栈
2014-07-01 11:56 643栈--只允许访问第一个数据项即:最后插入的数据。最简单的一句 ... -
小心使用ArrayList和LinkedList
2014-06-30 16:32 785ArrayList内部是使用可増长数组实现的,所以是用ge ... -
有趣的Java算法(3)
2014-06-30 16:29 681给定两个排序后的数组A和B,其中A的末端有足够的空间容纳B ... -
有趣的Java算法(2)
2014-06-30 16:29 1057今天分享一个"将一个整数的每位数分解并按逆序输 ... -
有趣的Java算法
2014-06-20 17:00 747题目及源码分析: /* * 今天在BBS里面看到这 ... -
java 值传递 引用传递
2010-12-17 23:11 2065java方法用的是值传递还是引用传递。你在blogjava上还 ... -
用java代码编写堆栈
2010-05-03 17:39 1233public class Stack { int[] ... -
几种读取属性文件的JAVA实现方式
2010-04-30 19:20 1178转载:http://darkranger.iteye.com/ ... -
Site
2010-04-30 19:20 966http://www.szikao.com/computer/ ... -
JAVA对XML的几种解析方式
2010-04-29 19:53 957对于XML介绍比较全面的还是IBM的专栏: ... -
集合与通用集合
2010-04-29 19:44 1430URL: http://www.ibm.com/develop ... -
HashMap 和TreeMap
2010-04-29 19:41 1276本文重点介绍HashMap。首先介绍一下什么是Map。在数组中 ... -
TreeMap和HashMap的问题
2010-04-29 19:39 2090在一次面试的过程 ... -
实现单用户登陆session先进先出(session踢出)
2010-04-29 19:33 9478首先在系统增加sessionListener 来监听sessi ... -
Java单态模式的实现
2010-04-29 19:23 15801.饿汉式:public class Sing ... -
请教java反射机制里可以调用私有方法吗?
2010-04-27 19:17 1626如题:请教java反射机制里可以调用私有方法吗? Metho ... -
利用java反射机制调用类的私有方法
2010-04-27 18:59 14171.将getInstance()方法设置为private ... -
String是原始数据类型还是引用数据类型
2010-04-26 19:22 1711请教各位高手,String是原始数据类型还是引用数据类型?谢谢 ... -
java中堆(heap)和堆栈(stack)有什么区别
2010-04-26 19:13 2195stack 和 heap 都是内存的 ...
相关推荐
**db4o(Database for Objects)**是一款开源的对象数据库,专为Java开发人员设计,它允许开发者直接在数据库中存储和检索Java对象,无需进行传统的SQL映射。本指南的第三部分将深入探讨如何在db4o中进行数据库重构...
面向Java开发人员的db4o指南主要关注如何利用db4o进行结构化对象和集合的管理,从而提高开发效率和数据存储的灵活性。 **对象数据库** 与传统的关系型数据库(RDBMS)不同,对象数据库直接支持面向对象编程模型。在...
面向Java开发人员的db4o指南db4o中的数据库重构
**db4o 开发指南与实例详解** **一、db4o 简介** db4o(Database for Objects)是一款开源的对象关系数据库管理系统(Object-Relational Mapping, ORM),它允许开发者直接将Java对象存储到数据库中,无需编写SQL...
db4o 的安装非常简单,只需要从官方网站下载最新的版本,并在 Eclipse 中新建 Java 项目,将 db4o 对象数据库引擎包 db4o-5.5-java5.jar 导入进项目。 db4o 支持多种版本的 JDK,因此可以根据需要选择合适的 JAR 包...
Java数据库db4o,全称为“Database for Objects”,是一款开源的对象数据库系统,专门设计用于Java和.NET平台。它提供了一种直接在对象模型上进行数据操作的方式,无需传统的ORM(对象关系映射)层,简化了开发过程...
《db4o 权威指南》是一本深入探讨db4o这一开源面向对象数据库系统的专业书籍,对于Java开发者来说尤其有价值。db4o是Database for Objects的缩写,它允许开发者以自然、直观的方式存储和检索Java对象,无需编写SQL...
DB4O,全称为“Database for Objects”,是一个开源的对象数据库管理系统,专为Java和.NET平台设计。它允许开发者直接在数据库中存储、检索、更新和删除Java对象,无需中间的ORM(对象关系映射)层。这篇文档是DB4O ...
若需使用客户端/服务器版本或可选组件,还需要`db4o-8.0-cs-java5.jar`和`db4o-8.0-optional-java5.jar`。最简便的安装方式是将`db4o-8.0-all-java5.jar`添加到项目的CLASSPATH中。在集成开发环境(如Eclipse)中,...
**db4o (Database for Objects) 是一个开源的、基于Java和.NET的面向对象数据库系统。它允许开发者直接在数据库中存储和检索Java或.NET对象,而无需传统的SQL查询语言,极大地简化了数据管理和持久化的流程。** 在...
**db4o 8.0 详解及中文指南** db4o(Database for Objects)是一款开源的对象数据库系统,它允许开发者直接将Java或.NET对象存储到数据库中,无需进行ORM(对象关系映射)。db4o的目标是简化数据管理,提供更接近...
Db4o,全称为“Database for Objects”,是一个开源的对象数据库管理系统,主要应用于Java和.NET平台。这个项目专注于提供一种简单的方式来存储和检索Java对象,无需SQL或其他中间映射层。在“Db4o的简单操作项目”...
描述:这份资料是关于db4o数据库的使用教程,特别针对Java开发人员。db4o是一款开源的对象数据库,支持Java、.NET以及Mono平台。该教程旨在帮助用户快速上手db4o,并在开发过程中提供可靠的支持。 ### 一、db4o概述...
【db4o6.4-java】是一个针对Java平台的数据库引擎开发包,它包含了db4o的源代码、测试代码以及其他相关资源。db4o全称为"Database for Objects",是一个开源的对象数据库管理系统(ODBMS),它允许开发者直接在Java...
**db4o(Database for Objects)** 是一个开源的对象数据库管理系统(Object-Relational Mapping,ORM),它允许开发者直接在Java或.NET平台上存储和检索Java对象或.NET对象,无需进行SQL查询或者映射过程。db4o的...
**db4o-7.2-java** 是一个针对Java平台的开源对象数据库系统,它将传统的关系型数据库理念与面向对象编程相结合,为开发者提供了一种高效、灵活的数据存储解决方案。db4o允许开发者直接存取Java对象,而无需通过SQL...
总之,DB4O是Java开发小型桌面应用程序的一个理想选择,它的简单易用和强大的功能使其在对象数据库领域占有一席之地。通过理解和掌握DB4O,开发者可以更高效地构建数据驱动的应用程序,同时享受对象数据库带来的便利...
该资源为 db4o 之旅 系列文章: ...3.介绍面向对象数据库 db4o 的修改和删除,并对其中出现的问题进行细致分析,引入了“更新深度(update depth)”这一重要概念。 最后更新于2006 年 12 月 14 日 另外再推荐一个站点 ...
DB4O面向对象数据库使用指南
db4o(Database for Objects)是一款开源的对象关系数据库管理系统(Object-Relational Mapping,ORM),专为Java和.NET平台设计。它允许开发者直接存取Java或.NET对象到数据库,而无需传统的SQL查询语言,简化了...