- 浏览: 285178 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
SpringJava:
摘过来的
小心使用ArrayList和LinkedList -
jingjing0907:
我要成为第一个赞的人!呵呵,
小心使用ArrayList和LinkedList -
SpringJava:
cilendeng 写道应该用ConcurrentHashMa ...
实现单用户登陆session先进先出(session踢出) -
lingxiajiudu:
不错,完美解决我了的问题,赞一个。
子窗体给父窗体传值 javascript opener -
cilendeng:
应该用ConcurrentHashMap
实现单用户登陆session先进先出(session踢出)
面向对象应用程序大量使用继承,并且它们常常使用继承(或者 “是一个”)关系来分类和组织给定系统中的对象。在关系存储模式中使用继承比较困难,因为这种模式没有内在的继承概念,但它是 OODNBMS 中的一个核心功能。在本期的面向 Java™ 开发人员的 db4o 指南 中,您将会发现,作为一个核心功能,在 db4o 中创建查询时使用继承竟是如此的简单(而且功能强大)。
在本系列文章中,我使用 Person
类型来演示 db4o 的所有基本原理。您已经学会了如何创建完整的 Person
对象图,以细粒度方式(使用 db4o 本身的查询功能来限制返回的实际对象图)对其进行检索,以及更新和删除全部的对象图(设定一些限制条件)等等。实际上,在面向对象的所有特性中,我们只漏掉了其中一个,那就是继承。
|
我将演示的这个例子的最终目标是一个用于存储雇员数据的数据管理系统,我一直致力于开发我的 Person
类型。我需要这样一个系统:存储某个公司的员工及其配偶和子女的信息,但是此时他们仅仅是该系统的 Person
(或者,可以说 Employees
是一个
Person
,但是 Persons
不是一个
Employee
)。而且,我不希望 Employee
的行为属于 Person
API 的一部分。从对象建模程序的角度公平地讲,按照 is-a
模拟类型的能力就是面向对象的本质。
我会用 Person
类型中的一个字段来模拟雇佣
的概念。这是一种关系方法,而且不太适合用于对象设计。幸运的是,与大多数 OODBMS 系统一样,db4o
系统对继承有一个完整的理解。在存储系统的核心使用继承可以轻松地 “重构” 现有系统,可以在设计系统时更多地使用继承,而不会使查询工具变得复杂。您将会看到,这也使查询特定类型的对象变得更加容易。
清单 1 回顾了
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 Person getFather() { return this.father; } public Person getMother() { return this.mother; } 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; } |
跟本系列的其他文章一样,我不会在每次更改时都展示完整的
Person
类,只逐步展示每次更改。在这个例子中,我实际上并没有更改 Person
,因为我将要扩展 Person
,而不是修改它。
|
|
需要做的第一件事是使我的雇员管理系统能够区别普通的
Person
(例如雇员的配偶和/或子女)和 Employee
。从纯粹建模的立场来说,这个更改很简单。我只是向
Person
引入了一个新的派生类,这个类和目前涉及到的其他类都在同一个包中。毫无疑问,我将会调用这个类
Employee
,如清单 2 所示:
package com.tedneward.model; public class Employee extends Person { public Employee() { } public Employee(String firstName, String lastName, String title, Gender gender, int age, Mood mood) { super(firstName, lastName, gender, age, mood); this.title = title; } public String getTitle() { return title; } public void setTitle(String value) { title = value; } public String toString() { return "[Employee: " + getFirstName() + " " + getLastName() + " " + "(" + getTitle() + ")]"; } private String title; } |
Employee
类的全部代码都在清单 2 中。从 OODBMS 的角度看, Employee
中的其他方法意义不大。在本讨论中需要记住的是
Employee
是 Person
的一个子类(如果更加关心系统的建模过程,可以设想 Employee
中的其他方法,例如 promote()
、demote()
、getSalary()
、setSalary()
和 workLikeADog()
)。
|
|
对新模型的探察测试简单明了。我创建一个叫做 InheritanceTest
的 JUnit 类,目前为止,它是第一个较为复杂的对象集,充当 OODBMS 最初的工作内容。为了使输出(将会在清单 6 中见到)更加清晰,我在清单 3 中展示了带有 @Before
注释的
prepareDatabase()
调用:
@Before public void prepareDatabase() { db = Db4o.openFile("persons.data"); // The Newards Employee ted = new Employee("Ted", "Neward", "President and CEO", Gender.MALE, 36, Mood.HAPPY); Person charlotte = new Person("Charlotte", "Neward", Gender.FEMALE, 35, Mood.HAPPY); ted.setSpouse(charlotte); Person michael = charlotte.haveBaby("Michael", Gender.MALE); michael.setAge(14); Person matthew = charlotte.haveBaby("Matthew", Gender.MALE); matthew.setAge(8); Address tedsHomeOffice = new Address("12 Redmond Rd", "Redmond", "WA", "98053"); ted.setHomeAddress(tedsHomeOffice); ted.setWorkAddress(tedsHomeOffice); ted.setVacationAddress( new Address("10 Wannahokalugi Way", "Oahu", "HA", "11223")); db.set(ted); // The Tates Employee bruce = new Employee("Bruce", "Tate", "Chief Technical Officer", 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); bruce.setHomeAddress( new Address("5 Maple Drive", "Austin", "TX", "12345")); bruce.setWorkAddress( new Address("5701 Downtown St", "Austin", "TX", "12345")); // Ted and Bruce both use the same timeshare, apparently bruce.setVacationAddress( new Address("10 Wanahokalugi Way", "Oahu", "HA", "11223")); db.set(bruce); // The Fords Employee neal = new Employee("Neal", "Ford", "Meme Wrangler", Gender.MALE, 29, Mood.HAPPY); Person candi = new Person("Candi", "Ford", Gender.FEMALE, 29, Mood.HAPPY); neal.setSpouse(candi); neal.setHomeAddress( new Address("22 Gritsngravy Way", "Atlanta", "GA", "32145")); // Neal is the roving architect neal.setWorkAddress(null); db.set(neal); // The Slettens Employee brians = new Employee("Brian", "Sletten", "Bosatsu Master", Gender.MALE, 29, Mood.HAPPY); Person kristen = new Person("Kristen", "Sletten", Gender.FEMALE, 29, Mood.HAPPY); brians.setSpouse(kristen); brians.setHomeAddress( new Address("57 Classified Drive", "Fairfax", "VA", "55555")); brians.setWorkAddress( new Address("1 CIAWasNeverHere Street", "Fairfax", "VA", "55555")); db.set(brians); // The Galbraiths Employee ben = new Employee("Ben", "Galbraith", "Chief UI Director", Gender.MALE, 29, Mood.HAPPY); Person jessica = new Person("Jessica", "Galbraith", Gender.FEMALE, 29, Mood.HAPPY); ben.setSpouse(jessica); ben.setHomeAddress( new Address( "5500 North 2700 East Rd", "Salt Lake City", "UT", "12121")); ben.setWorkAddress( new Address( "5600 North 2700 East Rd", "Salt Lake City", "UT", "12121")); ben.setVacationAddress( new Address( "2700 East 5500 North Rd", "Salt Lake City", "UT", "12121")); // Ben really needs to get out more db.set(ben); db.commit(); } |
跟本系列早先的探察测试示例一样,在每次测试完成后,我使用带
@After
注释的 deleteDatabase()
方法来删除数据库,以使各部分能够很好地分隔开。
在实际运行这个方法之前,我将会检查在系统中使用 Employee
会有哪些效果(如果有的话)。希望从数据库获取所有 Employee
信息,这很正常 — 或许当公司破产时他们将会全部被解雇(是的,我知道,这样想很残酷,但我只是对 2001 年的
dot-bomb
事故还有点心有余悸)。最初的测试看起来很简单,正如清单 4 所示:
@Test public void testSimpleInheritanceQueries() { ObjectSet employees = db.get(Employee.class); while (employees.hasNext()) System.out.println("Found " + employees.next()); } |
当进行测试时,将会产生一个有趣的结果:数据库(我自己、Ben、Neal、Brian 和 Bruce)中只返回了 Employee
。OODBMS 识别出查询受到子类型 Employee
的显式约束,并且只选择了符合返回条件的对象。因为其他对象(配偶或者孩子)不属于 Employee
类型,他们不符合条件,所以没有被返回。
当运行一个返回所有 Person
的查询时,将会更加有趣,如下所示:
@Test public void testSimpleNonEmployeeQuery() { ObjectSet persons = db.get(Person.class); while (persons.hasNext()) System.out.println("Found " + persons.next()); } |
当运行这个查询时,每个单一对象 — 包括以前返回的所有 Employee
— 都被返回了。从某种程度上说,这是有意义的。因为 Employee
是一个
Person
,由于建立在 Java 代码中的实现继承关系,因此满足返回查询的必须条件。
db4o 中的继承(以及多态)其实就是这么简单。没有用于查询语言的复杂的 IS
扩展,就不会引入不同于 Java 类型系统中现有概念的 “类型”
概念。我所指的只是期望作为查询的一部分的类型,而且这些是构成查询的主要成分。这跟在 SQL
查询中加入表格很相似,方法就是选择其数据应为查询结果一部分的表格。额外的好处是,“父类型” 也是作为查询的一部分隐式地 “加入” 的。清单 6
显示了 清单 3
中 InheritanceTest
的输出:
.Found [Employee: Ted Neward (President and CEO)] Found [Person: firstName = Charlotte lastName = Neward gender = FEMALE age = 35 mood = HAPPY spouse = Ted ] Found [Person: firstName = Michael lastName = Neward gender = MALE age = 14 mood = CRANKY ] Found [Person: firstName = Matthew lastName = Neward gender = MALE age = 8 mood = CRANKY ] Found [Employee: Bruce Tate (Chief Technical Officer)] Found [Person: firstName = Maggie lastName = Tate gender = FEMALE age = 29 mood = HAPPY spouse = Bruce ] Found [Person: firstName = Kayla lastName = Tate gender = FEMALE age = 0 mood = CRANKY ] Found [Person: firstName = Julia lastName = Tate gender = FEMALE age = 0 mood = CRANKY ] Found [Employee: Neal Ford (Meme Wrangler)] Found [Person: firstName = Candi lastName = Ford gender = FEMALE age = 29 mood = HAPPY spouse = Neal ] Found [Employee: Brian Sletten (Bosatsu Master)] Found [Person: firstName = Kristen lastName = Sletten gender = FEMALE age = 29 m ood = HAPPY spouse = Brian ] Found [Employee: Ben Galbraith (Chief UI Director)] Found [Person: firstName = Jessica lastName = Galbraith gender = FEMALE age = 29 mood = HAPPY spouse = Ben ] |
您可能会感到奇怪,不管如何查询,返回的对象仍然是适当的子类型对象。例如,跟预期的一样,在上面的查询中当 toString()
被每个返回的 Person
对象调用时,Person.toString()
也正被每个
Person
调用。然而,因为
Employee
有一个重写的
toString()
方法,因此关于动态绑定的常用规则就不适用了。存储在 Employee
中的 Person
的各部分不会被 “切掉”,而当定期 SQL 查询未能成功地将派生子类表加入到 table-per-class 模型中时,这种被 “切掉” 的现象就会发生。
|
|
当然,当继承条件扩展到原生查询中时,其功能就跟我所做过的简单对象查询一样强大。进行调用时,查询语法将会更加复杂,但是基本上遵循我以前所使用的语法,如清单 7 所示:
@Test public void testNativeQuery() { List<Person> spouses = db.query(new Predicate<Person>() { public boolean match(Person candidate) { return (candidate.getSpouse() instanceof Employee); } }); for (Person spouse : spouses) System.out.println(spouse); } |
下面的查询与我以前做过的查询在思想上类似,考虑系统中所有的 Person
,但是设置一个约束条件,只查找配偶也是一个 Employee
的 Person
— 调用 getSpouse()
,将返回值传递给 Java
instanceof
运算符,这样就完成了查询(记住 match()
调用只返回 true 或者 false,表示候选对象是否应该返回)。
请注意如何通过更改在 query()
调用中传递的 Predicate
来更改隐式选择的类型条件,如清单 8 所示:
@Test public void testEmployeeNativeQuery() { List<Employee> spouses = db.query(new Predicate<Person>() { public boolean match(Person candidate) { return (candidate.getSpouse() instanceof Employee); } }); for (Person spouse : spouses) System.out.println(spouse); } |
当执行此查询时,不会产生什么结果,因为现在此查询只查找配偶也在公司工作的 Employee
。目前,数据库中的雇员都不满足这个条件。如果公司雇佣 Charlotte,那么会返回两个 Employee
:Ted 和 Charlotte(但是人家说办公司恋情永远不会发生)。
在很大程度上,就是这样。继承不会对更新、删除和激活深度产生任何影响,只会影响到对象的查询方面。但是回想起 Java 平台提供的两种形式的继承:实现继承和接口继承。前者通过各种 extends 子句来实现,而后者通过 implements 来实现。如果 db4o 支持 extends ,那么它也一定支持 implements ,您将会看到,这有利于实现强大的查询功能。
|
|
就像任何 Java (或 C#)编程人员使用了一段时间这种语言后认识到的,接口对于建模非常有用。尽管不会经常看到,接口具有强大的 “隔离” 交叉在传统实现继承行中的对象的能力;通过使用接口,我可以声明某些类型为 Comparable
或者 Serializable
,或者在本例中,Employable
(是的,从设计的角度说这是大材小用了,但是用于教学还是很不错的)。
package com.tedneward.model; public interface Employable { public boolean willYouWorkForUs(); } |
|
要看接口是如何工作的,我需要
Employable
接口的一个实体类继承,并且 — 或许您已经猜测到 — 这意味着创建一个 EmployablePerson
子类型来扩展 Person
和实现 Employable
。我不会再次演示这些代码(没有必要演示,除了将 ** EMPLOYABLE **
添加到 Person
的 toString()
末尾以外, Person
在 EmployablePerson.toString()
方法中)。我也会修改 prepareDatabase()
调用以返回 “Charlotte
是一个 EmployablePerson
,而不只是一个 Person
” 的事实。
现在,我会编写一个遍历数据库的查询,查找愿意为本公司工作的雇员的配偶或亲人,如清单 10 所示。
@Test public void testEmployableQuery() { List<Employable> potentialEmployees = db.query(new Predicate<Employable>() { public boolean match(Employable candidate) { return (candidate.willYouWorkForUs()); } }); for (Employable e : potentialEmployees) System.out.println("Eureka! " + e + " has said they'll work for us!"); } |
毫无疑问,Charlotte 被返回了,说明她可能为本公司工作。更好的是,这意味着我引入的任何接口都变成了一种限制查询的新方式,不需要人工添加包含此信息的字段;只有 Charlotte 符合查询条件,因为她实现了这个接口,而其他配偶都没有实现(至少到目前为止)。
如 果说对象和继承就像巧克力和花生酱的话,那对象和多态就好比手和手套。这两个元素就像经理和他/她的高薪一样般配。检索数据时,任何存储对象的系统都不得 不将继承的概念引入它的存储媒介和过滤器中。幸运的是,面向对象的 DBMS 使得这很容易实现,而且不必引入新的 query-predicate 术语。从长远看来,引入继承会使 OODBMS 容易使用 得多 。
发表评论
-
栈的简单应用--单词反转
2014-07-03 16:00 658我们知道栈是一种先进后出,也就是后进先出的数据 ... -
java实现简单的栈
2014-07-01 11:56 603栈--只允许访问第一个数据项即:最后插入的数据。最简单的一句 ... -
小心使用ArrayList和LinkedList
2014-06-30 16:32 757ArrayList内部是使用可増长数组实现的,所以是用ge ... -
有趣的Java算法(3)
2014-06-30 16:29 651给定两个排序后的数组A和B,其中A的末端有足够的空间容纳B ... -
有趣的Java算法(2)
2014-06-30 16:29 1034今天分享一个"将一个整数的每位数分解并按逆序输 ... -
有趣的Java算法
2014-06-20 17:00 718题目及源码分析: /* * 今天在BBS里面看到这 ... -
java 值传递 引用传递
2010-12-17 23:11 2038java方法用的是值传递还是引用传递。你在blogjava上还 ... -
用java代码编写堆栈
2010-05-03 17:39 1205public class Stack { int[] ... -
几种读取属性文件的JAVA实现方式
2010-04-30 19:20 1149转载:http://darkranger.iteye.com/ ... -
Site
2010-04-30 19:20 931http://www.szikao.com/computer/ ... -
JAVA对XML的几种解析方式
2010-04-29 19:53 934对于XML介绍比较全面的还是IBM的专栏: ... -
集合与通用集合
2010-04-29 19:44 1400URL: http://www.ibm.com/develop ... -
HashMap 和TreeMap
2010-04-29 19:41 1241本文重点介绍HashMap。首先介绍一下什么是Map。在数组中 ... -
TreeMap和HashMap的问题
2010-04-29 19:39 2066在一次面试的过程 ... -
实现单用户登陆session先进先出(session踢出)
2010-04-29 19:33 9432首先在系统增加sessionListener 来监听sessi ... -
Java单态模式的实现
2010-04-29 19:23 15471.饿汉式:public class Sing ... -
请教java反射机制里可以调用私有方法吗?
2010-04-27 19:17 1596如题:请教java反射机制里可以调用私有方法吗? Metho ... -
利用java反射机制调用类的私有方法
2010-04-27 18:59 13911.将getInstance()方法设置为private ... -
String是原始数据类型还是引用数据类型
2010-04-26 19:22 1680请教各位高手,String是原始数据类型还是引用数据类型?谢谢 ... -
java中堆(heap)和堆栈(stack)有什么区别
2010-04-26 19:13 2159stack 和 heap 都是内存的 ...
相关推荐
NULL 博文链接:https://springjava.iteye.com/blog/373973
面向Java开发人员的db4o指南db4o中的数据库重构
只是一篇文章 所以我不需要什么积分 大家共享就好
DB4O面向对象数据库使用指南
该资源为 db4o 之旅 系列文章: ...3.介绍面向对象数据库 db4o 的修改和删除,并对其中出现的问题进行细致分析,引入了“更新深度(update depth)”这一重要概念。 最后更新于2006 年 12 月 14 日 另外再推荐一个站点 ...
db4o 是一个开源的纯面向对象数据库引擎,对于 Java 与 .NET 开发者来说都是一个简单易用的对象持久化工具,使用简单。同时,db4o 已经被第三方验证为具有优秀性能的面向对象数据库, 下面的基准测试图对 db4o 和...
本资料转自网上,仅供学习DB4O开发教程使用
db4o 权威指南 pdf 非常好
对象数据库db4o的示例程序,对象数据库db4o, 对象数据库db4o
db4o8.0以及db4o中文操作指南
DB4O Java开发应用之起步篇.doc
java 版的db4o桌面数据引擎,适用于快速开发小型桌面应用程序。
描述了db4o的信息要点和使用指南,讲述了一些特殊类,及其基本的使用
If you are working with db4o for Java, it is recommended to use the interactive HTML tutorial: ./doc/tutorial/index.html If you are working with db4o for .NET, it is recommended to run the ...
http://www.db4o.com db4o的官网,可下载最新版本,jar包在项目的database目录下,若构建路径报错请重新添加
db4o 是面向对象的数据库引擎。执行效率较高。 Java 开发的db4o 浏览器插件,可安装在Eclipse开发工具中。 个人没有做实际测试。
java开发用的一个小型数据库,有完整版的也有精简版的
nosql面向对象的数据库,只需引入jar包
db4o最后发布的版本,包含全部源码、jar包,eclipse下的对象查看插件、全部说明文档等。
Android开发-面向对象的数据库Db4o