JPA入门(2)--继承

本文根据<<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.MotorcycleCar继承自RoadVehicle.CoupeRoadster则继承自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方法实现继承层次. @Inheritancestrategy元素指明了我们使用的策略: 在这个用例中是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.MotorcycleCar类中定义的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.区分字段的值是根据代码中元注释指定的值.只有在我们的对象属性不是很多的情况下才使用此策略.

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值