站内搜索: 请输入搜索关键词

当前页面: 开发资料首页Java 专题Java数据对象技术JDO初步概览

Java数据对象技术JDO初步概览

摘要: 作为异军突起的新型语言,Java定义了一个标准的运行环境,用户定义的类在其中得到执行
  作为异军突起的新型语言,Java定义了一个标准的运行环境,用户定义的类在其中得到执行。这些用户自定义类的实例代表了真实环境中的数据,包括储存在数据库、文件或某些大型事务处理系统中的数据,而小型系统通常也需要一种在本地负责控制数据存储的机制。

   由于数据访问技术在不同的数据源类型中是不一样的,因此对数据进行访问成了给程序开发人员的一种挑战,程序员需要对每一种类型的数据源使用特定的编程接口(API),即必须至少知道两种语言来基于这些数据源开发业务应用:Java语言和由数据源所决定的数据访问语言。这种数据访问语言一般根据数据源的不同而不同,这使得学习使用某种数据源的开发成本相应提升。

  在Java数据对象技术(JDO)发布之前,通常有三种方式用于存储Java数据:串行化(即Serialization,也称序列化)、JDBC和EJB中的CMP(容控存储)方式。串行化用于将某个对象的状态,以及它所指向的其它对象结构图全部写到一个输出流中(比如文件、网络等等),它保证了被写入的对象之间的关系,这样一来,在另一时刻,这个对象结构图可以完整地重新构造出来。但串行化不支持事务处理、查询或者向不同的用户共享数据。它只允许在最初串行化时的粒度(指访问对象的接口精细程度)基础上进行访问,并且当应用中需要处理多种或多次串行化时很难维护。串行化只适用于最简单的应用,或者在某些无法有效地支持数据库的嵌入式系统中。

  JDBC要求你明确地处理数据字段,并且将它们映射到关系数据库的表中。开发人员被迫与两种区别非常大的数据模型、语言和数据访问手段打交道:Java,以及SQL中的关系数据模型。在开发中实现从关系数据模型到Java对象模型的映射是如此的复杂,以致于多数开发人员从不为数据定义对象模型;他们只是简单地编写过程化的Java代码来对底层的关系数据库中的数据表进行操纵。最终结果是:他们根本不能从面向对象的开发中得到任何好处。

  EJB组件体系是被设计为支持分布式对象计算的。它也包括对容器管理持续性Container Managed Persistence(参见术语表)的支持来实现持续性。主要由于它们的分布式特性,EJB应用比起JDO来复杂得多,对资源的消耗也大得多。不过,JDO被设计成具有一定的灵活性,这样一来,JDO产品都可以用来在底层实现EJB的存储处理,从而与EJB容器结合起来。如果你的应用需要对象存储,但不需要分布式的特性,你可以使用JDO来代替EJB组件。在EJB环境中最典型的JDO使用方案就是让EJB中的对话组件(Session Bean)直接访问JDO对象,避免使用实体组件(Entity Bean)。EJB组件必须运行在一个受控(Managed,参见术语表)的应用服务环境。但JDO应用可以运行在受控环境中,也可以运行在不受控的独立环境中,这些使你可以灵活地选择最合适的应用运行环境。

   如果你将精力集中在设计Java对象模型上,然后用JDO来进行存储你的数据类的实例,你将大大提高生产力和开发效率。你只需要处理一种信息模型。而JDBC则要求你理解关系模型和SQL语言(译者注:JDO并不是要取代JDBC,而是建立在JDBC基础上的一个抽象的中间层,提供更简单的数据存储接口)。即使是在使用EJB CMP(即容控存储,参见术语表)的时候,你也不得不学习与EJB体系相关的许多其它方面的内容,并且在建模方面还有一些JDO中不存在的局限性。
JDO规范了JDO运行环境和你的可存储对象类之间的约定。JDO被设计成支持多种数据源,包括一般情况下考虑不到的数据库之类的数据源。从现在开始,我们使用数据库(参见术语表)这一概念来表示任何你通过JDO来访问的底层数据源。

   本章将会展开讨论JDO的基本能力,这些基于对一个虚拟的Media Mania公司所开发的一个小型应用进行细致的分析。这个公司在遍布美国的很多商店中出租和出售多种形式的娱乐音像产品。他们的商店中有一些售货亭,提供一些电影以及电影中的演员的信息。这些信息对客户和商店的职员开放,以帮助选择适合客户口味的商品。

  定义数据对象模型


  我们将建立一个UML类图,显示一个公司的对象模型的相关类以及相互之间的关系。一个Movie(电影)对象表示一部特定的电影。每个至少在一部电影中出演角色的演员由一个Actor(演员)对象代表。而Role(角色)类表示某个演员在某部电影中扮演的特定角色,因此Role类也表示了电影和演员之间的一种关系,这种关系包含一个属性(电影中的角色名)。每部电影包含一到多个角色。每个演员可以在不同的电影中扮演不同的角色,甚至在同一部电影中扮演多个角色。

  我们会将这些数据类以及操纵这些数据类实例的的程序放到com.mecdiamania.prototype包中。

  需要存储的类


  我们定义Movie、Actor和Role这几个类为可持续的,表示它们的实例是可以被储存到数据库中的。首先我们看看每个类的完整的源代码。每个类中有一个package语句,因此可以很清楚地看到本例用到的每个类分别在哪个包中。

  例1-1显示了Movie类的源代码。JDO是定义在javax.jdo 包中的,注意这个类并不一定要导入任何具体的JDO类。Java中的引用和java.util包中的Collection及相关子类(接口)被用来表示我们的类之间的关系,这是大多数Java应用中的标准方式。

  Movie类中的属性使用Java中的标准类型,如String、Date、int等等。你可以将属性声明为private的,不需要对每一属性定义相应的get和set方法。Movie类中还有一些用于访问这些私有属性的方法,尽管这些方法在程序中的其它部分会用到,但它们并不是JDO所要求的。你可以使用属性包装来提供仅仅是抽象建模所需要的方法。这个类还有一些静态属性(static的),这些属性并不存储到数据库。

   "genres"属性是一个String型的,内容是该电影所属的电影风格(动作、爱情、诡异等等)。一个Set接口用来表示该电影的演员表中的角色集合。"addRole()"方法将元素加入到演员表中,而"getCast()"方法返回一个不可以更改的集合,该集合中包含演员表。这些方法并不是JDO规定的,只是为了方便应用编程而编写的。"parseReleaseDate()"方法和"formatReleaseDate()"方法用于将电影的发行日期标准化(格式化)。为了保持代码的简单,如果parseReleaseDate()的参数格式不对,将会返回null。

  例1-1 Movie.java

<table borderColor=#ffcc66 width="90%" align=center bgColor=#deebe8 border=1> <tr> <td>
package com.mediamania.prototype;

import java.util.Set;

import java.util.HashSet;

import java.util.Collections;

import java.util.Date;

import java.util.Calendar;

import java.text.SimpleDateFormat;

import java.text.ParsePosition;

public class Movie {

    private static SimpleDateFormat yearFmt = new SimpleDateFormat("yyyy");

    public static final String[] MPAAratings = {

        "G", "PG", "PG-13", "R", "NC-17", "NR"};

    private String title;

    private Date releaseDate;

    private int runningTime;

    private String rating;

    private String webSite;

    private String genres;

    private Set cast; // element type: Role

    private Movie() {}

    public Movie(String title, Date release, int duration, String rating,

                 String genres) {

        this.title = title;

        releaseDate = release;

        runningTime = duration;

        this.rating = rating;

        this.genres = genres;

        cast = new HashSet();

    }

    public String getTitle() {

        return title;

    }

    public Date getReleaseDate() {

        return releaseDate;

    }

    public String getRating() {

        return rating;

    }

    public int getRunningTime() {

        return runningTime;

    }

    public void setWebSite(String site) {

        webSite = site;

    }

    public String getWebSite() {

        return webSite;

    }

    public String getGenres() {

        return genres;

    }

    public void addRole(Role role) {

        cast.add(role);

    }

    public Set getCast() {

        return Collections.unmodifiableSet(cast);

    }

    public static Date parseReleaseDate(String val) {

        Date date = null;

        try {

            date = yearFmt.parse(val);

        } catch (java.text.ParseException exc) {}

        return date;

    }

    public String formatReleaseDate() {

        return yearFmt.format(releaseDate);

    }

}
</td></tr></table>
   JDO对一个需要存储的类强加了一个要求:一个无参数的构造器。如果你在类代码中不定义任何构造器,编译器会自动产生一个无参数的构造器;而如果你定义了带参构造器,你就必须再定义一个无参构造器,可以将其声明为private以禁止外部访问。如果你不定义这个无参构造器,一些JDO产品会自动为你产生一个,但这只是具体的JDO产品提供的功能,是不可移植的。

  例1-2显示了Actor类的源码。在我们的目标中,所有的演员都有一个不会重复的名字来标识自己,可以是与出生时的姓名不同的化名。基于此,我们用一个String来表示演员的姓名。每个演员可能扮演一到多个角色,类中的"roles"成员表示Actor与Role关系中Actor的这一边的属性。第①行的注释仅仅为了文档化,它并不为JDO实现任何特殊的功能。第②行和第③行“addRole()”和“removeRole()”方法使程序可以维护某个Actor实例和它所关联的Role实例集。

  例1-2 Actor.java

<table borderColor=#ffcc66 width="90%" align=center bgColor=#deebe8 border=1> <tr> <td>
package com.mediamania.prototype;

import java.util.Set;

import java.util.HashSet;

import java.util.Collections;

public class Actor {

    private String name;

①  private Set roles; // element type: Role

    private Actor() {}

    public Actor(String name) {

        this.name = name;

        roles = new HashSet();

    }

    public String getName() {

        return name;

    }

②  public void addRole(Role role) {

        roles.add(role);

    }

③  public void removeRole(Role role) {

        roles.remove(role);

    }

    public Set getRoles() {

        return Collections.unmodifiableSet(roles);

    }

}
</td></tr></table>
  最后,例1-3给出了Role类的源码。这个类代表了Movie类和Actor类之间的关系,并且包含某个演员在某部电影中扮演的具体角色的名字。其构造器初始化了对Movie和Actor对象的引用,并且通过调用处于关系的另一端的addRole()方法来保持逻辑一致性。

  例1-3 Role.java

<table borderColor=#ffcc66 width="90%" align=center bgColor=#deebe8 border=1> <tr> <td>
package com.mediamania.prototype;

public class Role {

    private String name;

    private Actor actor;

    private Movie movie;

    private Role() {}

    public Role(String name, Actor actor, Movie movie) {

        this.name = name;

        this.actor = actor;

        this.movie = movie;

        actor.addRole(this);

        movie.addRole(this);

    }

    public String getName() {

        return name;

    }

    public Actor getActor() {

        return actor;

    }

    public Movie getMovie() {

        return movie;

    }

}
</td></tr></table>
  至此,我们已经了解了在数据库中有实例存在的每个类的源码。这些类并不需要导入或使用任何JDO相关的具体类。进一步,除了无参的构造器,无须任何数据或方法来标明这些类为可存储的。用于访问或更新属性数据并维护实例间的关系的代码与大多数Java应用中的标准代码是一模一样的。




↑返回目录
前一篇: 全面挖掘Java Excel API 使用方法
后一篇: Eclipse快速上手指南之使用ANT