本文根据<<Mastering Enterprise JavaBean 3.0>> PartⅢ-chapter 9改写.
继承
在我们在讲解代码之前,我们需要讨论下支持继承的映射策略.就像第6章讨论的那样,我们需要对象关系映射在面向对象技术(Java)中的继承与关系型数据库技术中的继承之间建立一个桥梁.为了在关系型数据库中支持面向对象的概念,这边有几种映射策略.它们是:
l Single table per class hierarchy
l Separate table per subclass
l Single table per concrete entity class
图9.1展示了一个简单的对象模型,我们将用它来说明以上的各种策略.
在这个模型中,根类是RoadVehicle.Motorcycle和Car继承自RoadVehicle.Coupe和Roadster则继承自Car.源码9.1-9.5展示了这个模型的Java实现.这是种直接使用 extends Java关键字的继承.我们将为这些类添加元注释使它们成为实体,同时实现上面的各种策略.
package org.jceun.model;
public class RoadVehicle {
public enum AcceleratorType {PEDAL,THROTTLE};
protected int numPassengers;
protected int numWheels;
protected String make;
protected String model;
// setters and getters go here
...
public String toString() {
return “Make: “+make+
“, Model: “+model+
“, Number of passengers: “+numPassengers;
}
}
Source 9.1 RoadVehicle.java root class.
package org.jceun.model;
public class Motorcycle extends RoadVehicle {
public final AcceleratorType acceleratorType =
AcceleratorType.THROTTLE;
public Motorcycle() {
numWheels = 2;
numPassengers = 2;
}
public String toString() {
return “Motorcycle: “+super.toString();
}
}
Source 9.2 Motorcycle.java.
package org.jceun.model;
public class Car extends RoadVehicle {
public final AcceleratorType acceleratorType =
AcceleratorType.PEDAL;
public Car() {
numWheels = 4;
}
public String toString() {
return “Car: “+super.toString();
}
}
Source 9.3 Car.java.
package org.jceun.model;
public class Roadster extends Car {
public enum CoolFactor {COOL,COOLER,COOLEST};
private CoolFactor coolFactor;
public Roadster() {
numPassengers = 2;
}
// setters and getters go here
...
public String toString() {
return “Roadster: “+super.toString();
}
}
Source 9.4
package org.jceun.model;
public class Coupe extends Car {
public enum BoringFactor {BORING,BORINGER,BORINGEST};
private BoringFactor boringFactor;
public Coupe() {
numPassengers = 5;
}
// setters and getters go here
...
public String toString() {
return “Coupe: “+super.toString();
}
}
Source 9.5 Coupe.java.
Single Table per Class Hierarchy
在这种策略中只使用一个表格来展现整个类层次.一个多态字段用于区分这些子类.这个多态字段将根据所代表的Java类型生成不同的值.这种策略的优点是效率高且支持多态.缺点是表格必须要有一个字段对应类层次中类对应的域.如果继承层次深的话,这不仅会产生一个极其宽的表格,更重要的是每个子类的属性所映射的域必须是可以为空的.这使得表格的中每一行代表着许多不同的类型,这将不可能定义全部字段为非空.让我们来看下这种策略的实现方法.(见源码9.6-9.10)我们从源码9.6开始分析吧.我们的第一个元注释是@Entity元注释.它标记普通的老式Java对象(POJO)使其成为应用服务器中的实体.
package org.jceun.model;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
@Entity(name="RoadVehicleSingle")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="DISC",
discriminatorType=DiscriminatorType.STRING)
@DiscriminatorValue("ROADVEHICLE")
public class RoadVehicle implements Serializable {
public enum AcceleratorType {PEDAL,THROTTLE};
@Id
protected int id;
protected int numPassengers;
protected int numWheels;
protected String make;
protected String model;
public RoadVehicle() {
id = (int) System.nanoTime();
}
public int getId(){
return this.id;
}
public int getNumPassengers(){
return this.numPassengers;
}
public void setNumPassengers(int numPassengers){
this.numPassengers=numPassengers;
}
public int getNumWheels(){
return this.numWheels;
}
public void setNumWheels(int numWheels){
this.numWheels=numWheels;
}
public String getMake(){
return this.make;
}
public void setMake(String make){
this.make=make;
}
public String getModel(){
return this.model;
}
public void setModel(String model){
this.model=model;
}
public String toString() {
return "Make: "+make+
", Model: "+model+
", Number of passengers: "+numPassengers;
}
}
Source 9.6 RoadVehicle with entity annotations.
package org.jceun.model;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.DiscriminatorValue;
@Entity
@DiscriminatorValue("MOTORCYCLE")
public class Motorcycle extends RoadVehicle implements Serializable {
public final AcceleratorType acceleratorType =AcceleratorType.THROTTLE;
public Motorcycle() {
super();
numWheels = 2;
numPassengers = 2;
}
public String toString() {
return "Motorcycle: "+super.toString();
}
}
Source 9.7 Motorcycle.java with entity annotations.
package org.jceun.model;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.DiscriminatorValue;
@Entity
@DiscriminatorValue("CAR")
public class Car extends RoadVehicle implements Serializable{
public final AcceleratorType acceleratorType =AcceleratorType.PEDAL;
public Car() {
super();
numWheels = 4;
}
public String toString() {
return "Car: "+super.toString();
}
}
Source 9.8 Car.java with entity annotations.
package org.jceun.model;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.DiscriminatorValue;
/**
*Roadster 跑车
*/
@Entity
@DiscriminatorValue("ROADSTER")
public class Roadster extends Car implements Serializable {
public enum CoolFactor {COOL,COOLER,COOLEST};
private CoolFactor coolFactor;
public Roadster() {
super();
numPassengers = 2;
}
public CoolFactor getCoolFactor(){
return this.coolFactor;
}
public void setCoolFactor(CoolFactor coolFactor){
this.coolFactor=coolFactor;
}
public String toString() {
return "Roadster: "+super.toString();
}
}
Source 9.9 Roadster with entity annotations. (continued)
package org.jceun.model;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.DiscriminatorValue;
/**
*Roadster 采伐
*/
@Entity
@DiscriminatorValue("COUPE")
public class Coupe extends Car implements Serializable {
public enum BoringFactor {BORING,BORINGER,BORINGEST};
private BoringFactor boringFactor;
public Coupe() {
super();
numPassengers = 5;
}
public BoringFactor getBoringFactor(){
return this.boringFactor;
}
public void setBoringFactor(BoringFactor boringFactor){
this.boringFactor=boringFactor;
}
public String toString() {
return "Coupe: "+super.toString();
}
}
Source 9.10 Coupe with entity annotations.
在部署时期,元注释将被检测并激发相应的动作. @Inheritance, @DiscriminatorColumn,和@DiscrimintaorValue元注释告诉应用服务器,我们使用single-table方法实现继承层次. @Inheritance的strategy元素指明了我们使用的策略: 在这个用例中是InheritanceType.SINGLE_TABLE. @DiscriminatorColumn元注释指明了我们用于区别不同子类的字段.如果没有完全指明,也不用担心.对象关系映射技术将为我们自动生动.
这里我们使用"DISC"字段,其值为字符串类型(由DiscriminatorType.STRING指明).最后我们使用@DiscriminatorValue元注释指明RoadVehicle类型的对象持久化时区分字段的值.这边是"ROADVEHICLE".
我们还添加了实体所必须有的@Id元注释.我们使用的是简单的主键(在这个用例中是int).这个主键由RoadVehicle的构造器产生.它的子类在它们的构造器中通过调用super()方法,设置它们Id值.这些实体必须要有一唯一ID.观察源码9.7-9.10,我们只为这些类添加@Entity, @DiscriminatorValue元注释.它的区别值将区别表中不同类型.就如前面所描述的,每个子类通过调用super()方法,设置它们唯一的Id值.每个实体类还实现了Serializable接口,这使得游离对象可以在客户端的方法中传递.你应该注意到了@Entity元注释指明了name元素.因为我们要实现其它策略,为了区别这两个名为RoadVehicler实体,我们将一个命名为RoadVehicleSingle另一个为RoadVehicleJoined.
当我们将这两个实体打包在同一个持久性单元且部署到服务器上,一个表格(如果不存在)将根据代码中的元注释创建.为了更好地理解背后发生的情景,让我们来看下生成表格的结构.源码9.11展示了生成表格的数据定义语言(DDL).
CREATE TABLE ROADVEHICLE (
ID INTEGER NOT NULL,
DISC VARCHAR(31),
NUMWHEELS INTEGER,
MAKE VARCHAR(255),
NUMPASSENGERS INTEGER,
MODEL VARCHAR(255),
ACCELERATORTYPE INTEGER,
COOLFACTOR INTEGER,
BORINGFACTOR INTEGER
);
Source 9.11 DDL for table generated from entities.
注意到类层次中所涉及到的所有属性均在表格中有所体现.有父类中ID, NUMWHEELS, NUMPASSENGERS, MAKE,和 MODEL.有Motorcycle和Car类中定义的ACCELERATORTYPE.Roadster类中定义的COOLFACTOR.以及Coupe类中定义的BORINGFACTOR.还有个额外的DISC字段.这个是我们通过元注释在RoadVehicle根类中定义的区别子类的字段.这个字段根据持久化的对象将有不同的值.还注意到ID字段是唯一的不能为空的字段.如果一映射的字段个定义为非空,而持久化时不设置该值则将会出错.例如,如果BORINGFACTOR域被设置为非空,那么我们在持久化Roadster对象时,将因为Roadster对象没有BORINGFACTOR域所以BORINGFACTOR域值为空而出错.让我们来看一看持久化这些实体的代码.同时我们将看下数据库中对应的数据.源码9.11是生成这些实体实例的代码.
package org.jceun.test;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import junit.framework.TestCase;
import org.jceun.model.Coupe;
import org.jceun.model.Coupe.BoringFactor;
import org.jceun.model.Motorcycle;
import org.jceun.model.Roadster;
import org.jceun.model.Roadster.CoolFactor;
/**
*
* @author ceun
*/
public class VehicleTest extends TestCase {
protected void setUp() throws Exception {
}
protected void tearDown() throws Exception {
}
public void testAdd(){
EntityManagerFactory emf=Persistence.createEntityManagerFactory("JpaApp1PU");
EntityManager em=emf.createEntityManager();
em.getTransaction().begin();
Coupe c = new Coupe();
c.setMake("Bob");
c.setModel("E400");
c.setBoringFactor(BoringFactor.BORING);
em.persist(c);
Roadster r = new Roadster();
r.setMake("Mini");
r.setModel("Cooper S");
r.setCoolFactor(CoolFactor.COOLEST);
em.persist(r);
Motorcycle m = new Motorcycle();
em.persist(m);
em.getTransaction().commit();
}
}
Source 9.12 Let’s persist some entities!
注意我们像创建其他Java对象一样创建不同的实体对象.我们使用EntityManager接口的
persist方法保持与底层数据库的同步.表9.1展示了执行这段代码后插入到数据库中的数据.
观察代表Coupe对象的记录,它的CoolFactor属性的值是NULL(因为它没有CoolFactor属性)同时BoringFactor属性的值的值是0.代表Roadster对象的记录CoolFactor属性的值为2,而由于它没有BoringFactor 属性所以对应的该值为NULL.区分字段的值是根据代码中元注释指定的值.只有在我们的对象属性不是很多的情况下才使用此策略.