kodojdo指南第四章

类别:Java 点击:0 评论:0 推荐:

第四章 PersistenceCapable

  在JDO里,所有自定义的持久类都必须实现javax.jdo.spi.PersistenceCapable接口.这个接口包含了一些复杂的方法,使JDO能够管理类实例的持久性字段.幸运的是,你不必亲自实现这个接口,实际上,编写一个持久类和编写普通的类没有分别.没有特别的类需要继承,不必使用特殊的字段类型,不必写特殊的方法.这也是JDO使持久性对开发者来说完全"透明"的重要体现.

4.1 JDO增强者
  为使你免受错综复杂的PersistenceCapable 接口的困扰,许多JDO的实现品提供了一个名为"增强者"的工具.增强者是一个这样的工具:你编写持久性的类,然后增强者会自动增加代码到你的类里.虽然一些厂商会用源代码增强者修改你的JAVA代码,但是通常来说,增强者主要在.class文件上起作用.JAVA编译器编译你的文件,生成字节代码,增强者处理这些代码.增加一些必要的字段和方法来实现PersistenceCapable 接口.修改后的JAVA字节代码按照堆栈顺序完美的保留,并且完全兼容于JAVA调试器.所以增强的操作并不影响调试.
 

  上图描述了一个持久性类的编译过程.典型的JDO实现品会包含一个Ant的增强task,所以你可以把增强作用作为编译的一部分,自动进行.

  所有JDO增强者都要求做到彼此之间字节兼容.这意味着经过"增强"的类不但能用于增强这个类的JDO实现品,而且能用于所有其他JDO实现品.字节兼容的特性要求确保你在包装你的持久类之后,你可以分发给其他开发人员使用,而不用担心他们使用的是哪个厂商提供的JDO实现品.这也意味着你选择某个厂商的JDO之后,不必重新编译你的持久类.

4.2 比较"能持久"的和"该持久的"
  已增强的实现了PersistenceCapable 接口的类可以归为"能持久的".直接访问能持久类的public或者protected持久性字段的类称为"该持久的".该持久的类也必须"增强"--每次当该持久的类直接访问能持久类的持久字段的时候,增强者增加些代码,通知JDO实现品正被访问的字段将要被读或者写.这可以使JDO实现品能根据数据的需要同步字段的值.除非该持久类也是能持久类,否则增强者不会增加代码使之实现PersistenceCapable 接口

  通常,除了有利于封装状态的标准参数,最好使你所有的持久字段都是public,如果是protected,则保证他们只被持久的子类访问.这样可以避免这种情况:当非持久类访问能持久类的字段时,到底哪个非持久类应该被增强为"该持久的".

4.3. 持久类的限制

  使用持久类只有很少的约束.而且,这些约束并不影响你熟悉JDO,它支持什么和不支持什么.


4.3.1. 缺省的无参构造函数

  JDO规范要求所有能持久的类都必须有一个无参的构造函数,并且必须是private类型.如果需要的话,当没有定义其他构造函数的时候,编译器会自动制造缺省的无参构造函数.假如类定义了构造函数,则必须同时提供一个private的无参构造函数.

4.3.2. 继承

  JDO完全支持可持久类的继承机制.可持久类可以继承自非持久类,非持久类可以继承可持久类,甚至可以生成持久的继承层次(?),然而,有些重要的限制必须要注意:
  持久类不能继承某些由本地系统实现的类,例如java.net.Socket和java.lang.Thread.
  如果持久类继承了非持久类,非持久类的字段不能被持久化.
  所有在继承树的类必须使用相同的JDO标识类型.如果他们使用相同的应用程序标识,他们也必须使用相同的标识类.或者他们必须声明他们使用分隔的类,这些类的继承层次和修饰符(例如是否abstract)必须精确映射持久类的继承层次.不久我们会讨论JDO标识.


  JDO管理所有持久字段的状态.在访问字段之前,JDO保证它已从数据存储中读出.当你设置字段值的时候,JDO会记录它已被改变,使新值持久.这种特性使你可以把持久性字段当作任何其他字段一样看待.这也是JDO"透明持久性"的另一方面.
  JDO支持许多最普遍的类型,这些类型可以粗略的划分为三类:不变类型,可变类型和关系.

  除非重新指定字段的新值,否则不变类型的值一经创建就保持不变.JDO支持以下几种类型作为持久性字段:

  所有基本类型(int, float, byte, etc)

  所有基本类型的包装类(java.lang.Integer, java.lang.Float, java.lang.Byte, etc)

  java.lang.String
  java.math.BigInteger
  java.math.BigDecimal
  java.lang.Number
  java.util.Locale

  无须指定新值,可变类型的持久字段的值可被改变.可变类型可被自身的方法直接修改.JDO规范要求JDO必须支持以下的可变字段类型
 
  java.util.Date
  java.util.HashSet

  许多JDO实现品并不允许你将嵌套的可变类型持久化.例如日期的HashSet类型.

  Kodo JDO 支持下列的可变类型:
  java.util.Date
  java.util.List
  java.util.ArrayList
  java.util.LinkedList
  java.util.Vector
  java.util.Set
  java.util.HashSet
  java.util.SortedSet
  java.util.TreeSet
  java.util.Map
  java.util.HashMap
  java.util.SortedMap
  java.util.TreeMap
  java.util.Hashtable
  java.util.Properties


  JDO实现品透明地支持可变字段,它用字段定义类型的子类实例代替字段的值.例如,如果你的持久性对象包含一个字段,字段的值包含java.util.Date类型,JDO实现品会在运行时用厂商提供的Date类的子类代替java.util.Date,这种厂商提供的Date类可以称为JDODate.这个JDODate子类的任务是跟踪字段的修改,从而重载所有Date的改变方法,通知JDO字段的值已经被改变.下次,JDO就会把字段的新值写入数据库.
  当然,当你开发使用可持久类的时候,这些机制都是透明的.你就像习惯的那样编写可变类型的方法.但是,了解JDO如何支持可变类型的机制是重要的,这样你可以理解为什么JDO在处理数组的时候特别棘手.JDO运行你使用数组作为字段,当数组中的值改变时,它会自动检查什么时候这些字段被赋值或者被设置为null.但是,JDO不能检查对数组索引的赋值,因为数组没有子类.如果你设置了持久数组的索引,那么你必须同时重置数组的值,或者明确告诉JDO你改变了数组的值.这称为"弄脏"字段."弄脏"会由JDOHelper的makeDirty方法完成.
(这里的翻译不太好,尤其是数组索引那里)

  正如上面描述的父-子例子,除支持目前提到的JAVA类型之外,JDO还支持持久对象之间的关系.所有JDO实现品都应支持自定义的持久类和自定义持久类的集合作为可持久字段的类型.集合类可以保存可持久关系,这依赖于JDO实现品到底支持哪类可变字段类型.一些JDO实现品会支持map类型字段的关键字,值,或者两者同时和其他持久对象存在关系.
  多数JDO实现品还支持那些不知道具体类型的字段.例如声明为java.lang.Object或者自定义接口类型的字段.因为这些字段是如此普遍,所以使用他们有些限制.例如,他们不能被查询,装载和排序的时候效率较低.

注意:
  Kodo JDO 支持自定义可持久对象作为任何被支持的集合类型的元素.并且支持它作为任何支持的map类型的关键字和值.
  Kodo JDO 通过序列化字段的值,并且作为一连串字节代码排序的方式来支持java.lang.Object类型字段的可持久化.并且支持接口字段的可持久化.它排列存储在字段里的对象的唯一标识,当装载字段的时候,它重新取回相应对象.集合和map类型的元素是可以java.lang.Object或者接口类型.

4.3.4. 结论

本节详细列出了JDO对可持久化类的限制.看起来有许多的信息,其实,当你开发的时候,你很少接触到这些限制.加之,通过使用JDO的其他特性,你可以绕开这些限制.下节会介绍JDO的一些强大特性来达到这个目的.

4.4实例复查

  你的可持久类可以实现javax.jdo.InstanceCallbacks接口,这样在某些JDO生命周期事件发生时,你能够取得回滚.这个接口由以下四个方法组成:

1)jdoPostLoad 方法:当你类默认取出的字段组从数据存储中读取之后,JDO实现品会调用jdoPostLoad 方法.默认取出组会在JDO metadata一节中介绍.暂时,你可以把默认取出组想象为对象的所有基本字段.在这个方法中,不能访问其他持久化字段.
 jdoPostLoad 通常用来给非持久字段赋值.这些非持久字段的值依赖于持久化字段.下面提供了这方面的例子.

2)在对象的持久化值存储到数据库之前,会调用jdoPreStore 方法.在这个方法你可以访问所有持久化字段.
  jdoPreStore 与jdoPostLoad互为补充.jdoPostLoad 通常用于从持久化数据中初始化非持久值,而jdoPreStore 把缓存在非持久字段的值设置到持久化字段中.

3)jdoPreClear 在对象的持久化字段被清除之前调用.有几种理由使得JDO实现品要清除对象的持久化状态.这个方法可用来清除非持久字段的缓存数据和空关系.在这个方法中你不能访问持久化字段.
 
4)jdoPreDelete 在对象从数据存储中删除前被调用.在这个方法你可以访问持久化字段.使用这个方法删除相关对象,你必须删除对象的相关关系

和PersistenceCapable 接口不一样,假如你想得到生存期复查,你必须显示实现InstanceCallbacks 接口

4.5JDO 标识

  JAVA有两类对象标识:数字一标识和性质标识.假如两个引用的数值相同,那么他们指向内存中相同的JVM实例.你可以用==操作符来测试这种情况.性质标识根据自定义的标准检查两个对象是否相等.通常用equals方法来测试性质标识.默认情况下,这个方法通常使用数字标识.
  JDO引入了另一种对象标识,叫JDO标识.它用来测试两个持久化对象是否提供相同的数据状态
  每个持久化实例的JDO标识都封装在JDO标识对象里.你可以调用JDOHelper的getObjectId 方法,取得持久化实例的JDO标识对象.如果使用equals方法后得到两个JDO标识对象相等,那么他们都提供相同的数据状态.

  如果你仅使用一个PersistenceManager,使用==操作符就能测试两个持久化对象是否提供相同的数据状态.JDO要求每个PersistenceManager只能包含虚拟机里的一个对象,这个对象指向唯一的数据记录.在这个PersistenceManager管理对象的缓存里,JDO标识和数字标识等价.这就是将要谈到的"单值性"要求.
  单值性要求非常重要,否则不能保证数据完整性.请想想这样的情形,在同一个PersistenceManager里有两个不同的对象,他们提供相同的持久化数据.假如你更改了其中一个对象,那么是哪个对象的数据该写到数据存储里,是更改后的数据呢,还是更改前的数据?当看到同一数据的两个不同"版本",你该如何编写程序的逻辑处理部分?该感谢单值性,使我们无须回答这个问题.


有三种类型的JDO标识.不过对于多数程序来说,只有两种是重要的:数据存储标识和应用程序标识.多数JDO实现品最低限度支持数据存储标识,有些也支持应用程序标识.所有在继承树的持久化类必须使用相同的JDO标识.

4.5.1. 数据存储标识

  数据存储标识由JDO实现品管理.它和你的持久化字段的值无关.你不必背诵哪个类会被JDO标识对象使用.也不必记着什么数据拿来创建标识的值.你只需记住下面准则:
  类必须是public
  类必须序列化
  类所有非静态字段必须是public和序列化
  类必须有个公开的无参构造函数
  类必须有个公开的字符串参数的构造函数.类必须重载toString方法.这个构造函数用来创建新的JDO标识对象,....

注意:
KODO允许你自定义数据存储标识的风格

4.5.2.1. 应用程序标识

  应用程序标识由开发者自己管理.在应用程序标识之下,一个对象持久化字段的值决定它的JDO标识.决定对象标识的字段值称为主键字段.在同一类型的所有对象中,每个对象的主键字段的值必须是唯一的.
  当使用应用程序标识时,能持久类的equals和hashCode方法均依赖于主键字段.
  同样,使用应用程序标识时优于支持类被JDO标识对象使用的做法.应用程序标识必须遵守所有数据存储标识的准则.并且遵守下面的要求:
  类的非静态字段名称必须包含相应能持久类主键字段的名称,且字段类型必须一致.
  类的equals和hashCode方法必须使用所有关联主键字段的字段的值.
  内嵌类必须是静态的.
  同一继承层次的所有类必须使用相同的应用程序标识.或者每个类有自己的应用程序标识类,这个类的继承层次映射了拥有它的类的继承层次.(请看4.5.2.1应用标识层次)
  所有主键字段都必须是基本类型,或者基本类型的包装,字符串,特别地,其他持久化实例不能作为主键字段使用.

  这些准则使用你可以这样创建一个应用程序标识对象:或者从持久类实例的主键字段的值,或者从另一个标识对象的toString方法返回的字符串来创建.
  虽不必要的,你最好用你的应用程序标识把相应的可持久类注册到JVM中.典型的由应用程序标识类代码的静态部分完成.就像下面例子那样,这个注册过程能够避免这种情况,没有被你应用程序使用的类型会查找不到.

4.5.2.1 应用程序标识层次

  有种方法可以避免使用单一的应用程序标识类标识整个继承层次.那就是在每个继承层次使用一个应用程序标识类.使用准则如下:
  1)应用程序标识类的继承层次能够明确反映持久类确定的层次.例如,如果有个抽象类Person,另一个抽象类Employee继承Person,非抽象类FullTimeEmployee继承Employee,非抽象类Manager 继承FullTimeEmployee,那么相应的应用程序标识类则为:抽象的PersonID类,抽象的继承PersonID的EmployeeID 类,非抽象的继承EmployeeID的FullTimeEmployeeID 类,非抽象的继承FullTimeEmployeeID 的ManagerID 类

  2)在对象标识层次的子类必须定义额外的主键字段,直到父层次变为非抽象.在前述例子,PersonID 必须定义一个ID字段称为pk1, EmployeeID 必须定义pk2,FullTimeEmployeeID 定义pk3,而ManagerID 不必定义额外的主键字段,因为它是非抽象类的子类

  3)不必为每个非抽象应用程序标识类定义主键.在前面的例子中,PersonID 和EmployeeID 类可以不定义主键字段,而继承它们的非抽象类则必须定义一个或多个主键字段.

4.6 结论
  本章介绍了JDO中所有编写持久类的规定.然而,除非你创建JDO元数据,否则JDO并不能使用你的持久类.下章会详细介绍元数据.

本文地址:http://com.8s8s.com/it/it13790.htm