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.区分字段的值是根据代码中元注释指定的值.只有在我们的对象属性不是很多的情况下才使用此策略.

 

 

 

### Spring Data JPA 入门教程 #### 创建 Maven 或 Gradle 项目结构 为了开始使用 Spring Data JPA,首先需要设置好开发环境。这通常意味着创建一个新的 Maven 或 Gradle 项目,并添加必要的依赖项来支持 Spring Boot 和 Spring Data JPA 功能。 对于 Maven 用户,在 `pom.xml` 文件中加入如下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> ``` 而对于 Gradle 用户,则应在 `build.gradle` 中添加相应的行: ```groovy implementation 'org.springframework.boot:spring-boot-starter-data-jpa' ``` #### 定义实体类 定义一个简单的 Java 类作为持久化对象模型的一部分。例如,可以基于给定的图书类例子[^3] 来构建自己的实体类。注意这里的注解用于指定表名、字段映射以及关系管理等细节。 ```java @Getter @Setter @Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer bookId; private String bookName; private Float price; private Date pubdate; @ManyToOne(fetch = FetchType.LAZY) private Author author; } ``` #### 配置数据源和 JPA 设置 在应用程序属性文件 (`application.properties`) 中提供连接到目标数据库所需的信息。如果希望简化初期的学习过程,可以选择内存型数据库如 H2 数据库来进行练习,这样就不必担心实际部署中的复杂配置问题[^2]。 ```properties spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password=password spring.jpa.database-platform=org.hibernate.dialect.H2Dialect ``` #### 编写 Repository 接口 接下来就是实现 DAO (Data Access Object) 层了。按照所提供的代码片段[^4] ,只需继承自 `JpaRepository<T, ID>` 即可获得一组丰富的 CRUD 方法集合而不需要额外编写任何 SQL 查询语句。 ```java @Repository public interface UserRepository extends JpaRepository<User, String> {} ``` 同样地,针对上面提到过的书籍实体也可以建立对应的仓库接口: ```java public interface BookRepository extends JpaRepository<Book, Integer> {} ``` #### 测试功能 完成上述准备工作之后便可以通过 RESTful API 或者命令行工具 POSTMAN 对服务端点发起请求验证各项操作是否正常工作[^1] 。此外还可以利用内置的支持机制快速执行单元测试而不必真的启动整个应用上下文环境。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值