JPA - 高级映射

  • 简述

    JPA 是一个与 Java 规范一起发布的库。因此,它支持实体持久性的所有面向对象概念。到目前为止,我们已经完成了对象关系映射的基础知识。本章将带您了解对象和关系实体之间的高级映射。
  • 继承策略

    继承是面向对象语言的核心概念,因此我们可以在实体之间使用继承关系或策略。JPA 支持 SINGLE_TABLE、JOINED_TABLE 和 TABLE_PER_CONCRETE_CLASS 三种类型的继承策略。
    让我们考虑一个 Staff、TeachingStaff、NonTeachingStaff 类及其关系的示例,如下所示:
    继承策略
    在上图中,Staff 是一个实体,TeachingStaff 和 NonTeachingStaff 是 Staff 的子实体。这里我们将在所有三种继承策略中讨论上面的例子。
  • 单表策略

    单表策略采用所有类字段(超类和子类)并将它们映射到称为 SINGLE_TABLE 策略的单个表中。在这里,鉴别器值在区分一张表中三个实体的值方面起着关键作用。
    让我们考虑上面的例子,TeachingStaff 和 NonTeachingStaff 是类 Staff 的子类。提醒继承的概念(是一种通过子类继承超类属性的机制),因此sid,sname是属于TeachingStaff和NonTeachingStaff的字段。创建一个 JPA 项目。本项目所有模块如下:

    创建实体

    创建一个名为的包 ‘com.jc2182.eclipselink.entity’ 在下面 ‘src’包裹。创建一个名为的新 java 类Staff.java在给定的包下。Staff实体类如下所示:
    
    package com.jc2182.eclipselink.entity;
    import java.io.Serializable;
    import javax.persistence.DiscriminatorColumn;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.Inheritance;
    import javax.persistence.InheritanceType;
    import javax.persistence.Table;
    @Entity
    @Table
    @Inheritance( strategy = InheritanceType.SINGLE_TABLE )
    @DiscriminatorColumn( name = "type" )
    public class Staff implements Serializable {
       @Id
       @GeneratedValue( strategy = GenerationType.AUTO )
       
       private int sid;
       private String sname;
       
       public Staff( int sid, String sname ) {
          super( );
          this.sid = sid;
          this.sname = sname;
       }
       
       public Staff( ) {
          super( );
       }
       
       public int getSid( ) {
          return sid;
       }
       
       public void setSid( int sid ) {
          this.sid = sid;
       }
       
       public String getSname( ) {
          return sname;
       }
       
       public void setSname( String sname ) {
          this.sname = sname;
       }
    }
    
    在上面的代码中 @DescriminatorColumn 指定字段名称 (type) 并且它的值显示了剩余的(Teaching 和 NonTeachingStaff)字段。
    创建一个名为 Staff 类的子类(类) TeachingStaff.java 在下面 com.jc2182.eclipselink.entity包裹。TeachingStaff 实体类如下所示:
    
    package com.jc2182.eclipselink.entity;
    import javax.persistence.DiscriminatorValue;
    import javax.persistence.Entity;
    @Entity
    @DiscriminatorValue( value="TS" )
    public class TeachingStaff extends Staff {
       private String qualification;
       private String subjectexpertise;
       public TeachingStaff( int sid, String sname, 
       
       String qualification,String subjectexpertise ) {
          super( sid, sname );
          this.qualification = qualification;
          this.subjectexpertise = subjectexpertise;
       }
       public TeachingStaff( ) {
          super( );
       }
       public String getQualification( ){
          return qualification;
       }
       public void setQualification( String qualification ){
          this.qualification = qualification;
       }
       public String getSubjectexpertise( ) {
          return subjectexpertise;
       }
       public void setSubjectexpertise( String subjectexpertise ){
          this.subjectexpertise = subjectexpertise;
       }
    }
    
    创建一个名为 Staff 类的子类(类) NonTeachingStaff.java 在下面 com.jc2182.eclipselink.entity包裹。NonTeachingStaff 实体类如下所示:
    
    package com.jc2182.eclipselink.entity;
    import javax.persistence.DiscriminatorValue;
    import javax.persistence.Entity;
    @Entity
    @DiscriminatorValue( value = "NS" )
    public class NonTeachingStaff extends Staff {
       private String areaexpertise;
       public NonTeachingStaff( int sid, String sname, String areaexpertise ) {
          super( sid, sname );
          this.areaexpertise = areaexpertise;
       }
       public NonTeachingStaff( ) {
          super( );
       }
       public String getAreaexpertise( ) {
          return areaexpertise;
       }
       public void setAreaexpertise( String areaexpertise ){
          this.areaexpertise = areaexpertise;
       }
    }
    

    持久化文件

    Persistence.xml 文件包含数据库的配置信息和实体类的注册信息。xml文件如下所示:
    
    <?xml version="1.0" encoding="UTF-8"?>
    <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
       http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
       <persistence-unit name="Eclipselink_JPA" transaction-type="RESOURCE_LOCAL">
       
          <class>com.jc2182.eclipselink.entity.Staff</class>
          <class>com.jc2182.eclipselink.entity.NonTeachingStaff</class>
          <class>com.jc2182.eclipselink.entity.TeachingStaff</class>
          
          <properties>
             <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/jpadb"/>
             <property name="javax.persistence.jdbc.user" value="root"/>
             <property name="javax.persistence.jdbc.password" value="root"/>
             <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
             <property name="eclipselink.logging.level" value="FINE"/>
             <property name="eclipselink.ddl-generation" value="create-tables"/>
          </properties>
          
       </persistence-unit>
    </persistence>
    

    服务类

    服务类是业务组件的实现部分。在下面创建一个包‘src’ 包命名 ‘com.jc2182.eclipselink.service’.
    在给定的包下创建一个名为 SaveClient.java 的类来存储 Staff、TeachingStaff 和 NonTeachingStaff 类字段。SaveClient 类如下所示:
    
    package com.jc2182.eclipselink.service;
    import javax.persistence.EntityManager;
    import javax.persistence.EntityManagerFactory;
    import javax.persistence.Persistence;
    import com.jc2182.eclipselink.entity.NonTeachingStaff;
    import com.jc2182.eclipselink.entity.TeachingStaff;
    public class SaveClient {
       public static void main( String[ ] args ) {
       
          EntityManagerFactory emfactory = Persistence.createEntityManagerFactory( "Eclipselink_JPA" );
          EntityManager entitymanager = emfactory.createEntityManager( );
          entitymanager.getTransaction( ).begin( );
          //Teaching staff entity 
          TeachingStaff ts1=new TeachingStaff(1,"Gopal","MSc MEd","Maths");
          TeachingStaff ts2=new TeachingStaff(2, "Manisha", "BSc BEd", "English");
          
          //Non-Teaching Staff entity
          NonTeachingStaff nts1=new NonTeachingStaff(3, "Satish", "Accounts");
          NonTeachingStaff nts2=new NonTeachingStaff(4, "Krishna", "Office Admin");
          //storing all entities
          entitymanager.persist(ts1);
          entitymanager.persist(ts2);
          entitymanager.persist(nts1);
          entitymanager.persist(nts2);
          
          entitymanager.getTransaction().commit();
          
          entitymanager.close();
          emfactory.close();
       }
    }
    
    编译并执行上述程序后,您将在 Eclipse IDE 的控制台面板中收到通知。检查 MySQL 工作台的输出。表格格式的输出如下所示:
    Sid Type Sname Areaexpertise Qualification Subjectexpertise
    1 TS Gopal MSC MED Maths
    2 TS Manisha BSC BED English
    3 NS Satish Accounts
    4 NS Krishna Office Admin
    最后,您将获得包含所有三个类的字段的单个表,并且与名为的鉴别器列不同 ‘Type’ (字段)。
  • 连接表策略

    连接表策略是共享包含唯一值的引用列以连接表并进行轻松事务。让我们考虑与上面相同的例子。
    创建一个 JPA 项目。所有项目模块如下所示:

    创建实体

    创建一个名为的包 ‘com.jc2182.eclipselink.entity’ 在下面 ‘src’包裹。创建一个名为的新 java 类Staff.java在给定的包下。Staff实体类如下所示:
    
    package com.jc2182.eclipselink.entity;
    import java.io.Serializable;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.Inheritance;
    import javax.persistence.InheritanceType;
    import javax.persistence.Table;
    @Entity
    @Table
    @Inheritance( strategy = InheritanceType.JOINED )
    public class Staff implements Serializable {
       @Id
       @GeneratedValue( strategy = GenerationType.AUTO )
       
       private int sid;
       private String sname;
       
       public Staff( int sid, String sname ) {
          super( );
          this.sid = sid;
          this.sname = sname;
       }
       
       public Staff( ) {
          super( );
       }
       
       public int getSid( ) {
          return sid;
       }
       
       public void setSid( int sid ) {
          this.sid = sid;
       }
       
       public String getSname( ) {
          return sname;
       }
       
       public void setSname( String sname ) {
          this.sname = sname;
       }
    }
    
    创建一个名为 Staff 类的子类(类) TeachingStaff.java 在下面 com.jc2182.eclipselink.entity包裹。TeachingStaff 实体类如下所示:
    
    package com.jc2182.eclipselink.entity;
    import javax.persistence.DiscriminatorValue;
    import javax.persistence.Entity;
    @Entity
    @PrimaryKeyJoinColumn(referencedColumnName="sid")
    public class TeachingStaff extends Staff {
       private String qualification;
       private String subjectexpertise;
       public TeachingStaff( int sid, String sname, 
       
       String qualification,String subjectexpertise ) {
          super( sid, sname );
          this.qualification = qualification;
          this.subjectexpertise = subjectexpertise;
       }
       public TeachingStaff( ) {
          super( );
       }
       public String getQualification( ){
          return qualification;
       }
       public void setQualification( String qualification ){
          this.qualification = qualification;
       }
       public String getSubjectexpertise( ) {
          return subjectexpertise;
       }
       public void setSubjectexpertise( String subjectexpertise ){
          this.subjectexpertise = subjectexpertise;
       }
    }
    
    创建一个名为 Staff 类的子类(类) NonTeachingStaff.java 在下面 com.jc2182.eclipselink.entity包裹。NonTeachingStaff 实体类如下所示:
    
    package com.jc2182.eclipselink.entity;
    import javax.persistence.DiscriminatorValue;
    import javax.persistence.Entity;
    @Entity
    @PrimaryKeyJoinColumn(referencedColumnName="sid")
    public class NonTeachingStaff extends Staff {
       private String areaexpertise;
       public NonTeachingStaff( int sid, String sname, String areaexpertise ) {
          super( sid, sname );
          this.areaexpertise = areaexpertise;
       }
       public NonTeachingStaff( ) {
          super( );
       }
       public String getAreaexpertise( ) {
          return areaexpertise;
       }
       public void setAreaexpertise( String areaexpertise ) {
          this.areaexpertise = areaexpertise;
       }
    }
    

    持久化文件

    Persistence.xml 文件包含数据库的配置信息和实体类的注册信息。xml文件如下所示:
    
    <?xml version = "1.0" encoding = "UTF-8"?>
    <persistence version = "2.0" xmlns = "http://java.sun.com/xml/ns/persistence" 
       xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" 
       xsi:schemaLocation = "http://java.sun.com/xml/ns/persistence 
       http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
       
       <persistence-unit name = "Eclipselink_JPA" transaction-type = "RESOURCE_LOCAL">
          <class>com.jc2182.eclipselink.entity.Staff</class>
          <class>com.jc2182.eclipselink.entity.NonTeachingStaff</class>
          <class>com.jc2182.eclipselink.entity.TeachingStaff</class>
          
          <properties>
             <property name = "javax.persistence.jdbc.url" value = "jdbc:mysql://localhost:3306/jpadb"/>
             <property name = "javax.persistence.jdbc.user" value = "root"/>
             <property name = "javax.persistence.jdbc.password" value = "root"/>
             <property name = "javax.persistence.jdbc.driver" value = "com.mysql.jdbc.Driver"/>
             <property name = "eclipselink.logging.level" value = "FINE"/>
             <property name = "eclipselink.ddl-generation" value = "create-tables"/>
          </properties>
          
       </persistence-unit>
    </persistence>
    

    服务类

    服务类是业务组件的实现部分。在下面创建一个包‘src’ 包命名 ‘com.jc2182.eclipselink.service’.
    在给定的包下创建一个名为 SaveClient.java 的类来存储 Staff、TeachingStaff 和 NonTeachingStaff 类字段。然后 SaveClient 类如下:
    
    package com.jc2182.eclipselink.service;
    import javax.persistence.EntityManager;
    import javax.persistence.EntityManagerFactory;
    import javax.persistence.Persistence;
    import com.jc2182.eclipselink.entity.NonTeachingStaff;
    import com.jc2182.eclipselink.entity.TeachingStaff;
    public class SaveClient {
       public static void main( String[ ] args ) {
          EntityManagerFactory emfactory = Persistence.createEntityManagerFactory( "Eclipselink_JPA" );
          EntityManager entitymanager = emfactory.createEntityManager( );
          entitymanager.getTransaction( ).begin( );
          //Teaching staff entity 
          TeachingStaff ts1 = new TeachingStaff(1,"Gopal","MSc MEd","Maths");
          TeachingStaff ts2 = new TeachingStaff(2, "Manisha", "BSc BEd", "English");
          
          //Non-Teaching Staff entity
          NonTeachingStaff nts1 = new NonTeachingStaff(3, "Satish", "Accounts");
          NonTeachingStaff nts2 = new NonTeachingStaff(4, "Krishna", "Office Admin");
          //storing all entities
          entitymanager.persist(ts1);
          entitymanager.persist(ts2);
          entitymanager.persist(nts1);
          entitymanager.persist(nts2);
          entitymanager.getTransaction().commit();
          entitymanager.close();
          emfactory.close();
       }
    }
    
    编译并执行上述程序后,您将在 Eclipse IDE 的控制台面板中收到通知。对于输出检查 MySQL 工作台如下:
    这里创建了三个表,结果如下 staff 表格形式的表格如下所示:
    Sid Dtype Sname
    1 TeachingStaff Gopal
    2 TeachingStaff Manisha
    3 NonTeachingStaff Satish
    4 NonTeachingStaff Krishna
    TeachingStaff的结果表格形式的表格如下所示:
    Sid Qualification Subjectexpertise
    1 MSC MED Maths
    2 BSC BED English
    上表中sid为外键(参考字段表staff表)的结果 NonTeachingStaff 表格形式的表格如下所示:
    Sid Areaexpertise
    3 Accounts
    4 Office Admin
    最后,三个表分别使用它们的字段创建,SID 字段由所有三个表共享。在人员表中 SID 是主键,在其余(TeachingStaff 和 NonTeachingStaff)表中 SID 是外键。
  • 每班策略表

    Table per class 策略是为每个子实体创建一个表。将创建员工表,但它将包含空记录。Staff 表的字段值必须由 TeachingStaff 和 NonTeachingStaff 表共享。
    让我们考虑与上面相同的例子。本项目所有模块如下图所示:

    创建实体

    创建一个名为的包 ‘com.jc2182.eclipselink.entity’ 在下面 ‘src’包裹。创建一个名为的新 java 类Staff.java在给定的包下。Staff实体类如下所示:
    
    package com.jc2182.eclipselink.entity;
    import java.io.Serializable;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.Inheritance;
    import javax.persistence.InheritanceType;
    import javax.persistence.Table;
    @Entity
    @Table
    @Inheritance( strategy = InheritanceType.TABLE_PER_CLASS )
    public class Staff implements Serializable {
       @Id
       @GeneratedValue( strategy = GenerationType.AUTO )
       private int sid;
       private String sname;
       public Staff( int sid, String sname ) {
          super( );
          this.sid = sid;
          this.sname = sname;
       }
       public Staff( ) {
          super( );
       }
       public int getSid( ) {
          return sid;
       }
       public void setSid( int sid ) {
          this.sid = sid;
       }
       public String getSname( ) {
          return sname;
       }
       public void setSname( String sname ) {
          this.sname = sname;
       }
    }
    
    创建一个名为 Staff 类的子类(类) TeachingStaff.java 在下面 com.jc2182.eclipselink.entity包裹。TeachingStaff 实体类如下所示:
    
    package com.jc2182.eclipselink.entity;
    import javax.persistence.DiscriminatorValue;
    import javax.persistence.Entity;
    @Entity
    public class TeachingStaff extends Staff {
       private String qualification;
       private String subjectexpertise;
       public TeachingStaff( int sid, String sname, String qualification, String subjectexpertise ) {
          super( sid, sname );
          this.qualification = qualification;
          this.subjectexpertise = subjectexpertise;
       }
       public TeachingStaff( ) {
          super( );
       }
       public String getQualification( ){
          return qualification;
       }
       
       public void setQualification( String qualification ) {
          this.qualification = qualification;
       }
       public String getSubjectexpertise( ) {
          return subjectexpertise;
       }
       public void setSubjectexpertise( String subjectexpertise ){
          this.subjectexpertise = subjectexpertise;
       }
    }
    
    创建一个名为 Staff 类的子类(类) NonTeachingStaff.java 在下面 com.jc2182.eclipselink.entity包裹。NonTeachingStaff 实体类如下所示:
    
    package com.jc2182.eclipselink.entity;
    import javax.persistence.DiscriminatorValue;
    import javax.persistence.Entity;
    @Entity
    public class NonTeachingStaff extends Staff {
       private String areaexpertise;
       public NonTeachingStaff( int sid, String sname, String areaexpertise ) {
          super( sid, sname );
          this.areaexpertise = areaexpertise;
       }
       public NonTeachingStaff( ) {
          super( );
       }
       public String getAreaexpertise( ) {
          return areaexpertise;
       }
       public void setAreaexpertise( String areaexpertise ) {
          this.areaexpertise = areaexpertise;
       }
    }
    

    持久化文件

    Persistence.xml 文件包含数据库的配置信息和实体类的注册信息。xml文件如下所示:
    
    <?xml version="1.0" encoding = "UTF-8"?>
    <persistence version = "2.0" xmlns = "http://java.sun.com/xml/ns/persistence"
       xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" 
       xsi:schemaLocation = "http://java.sun.com/xml/ns/persistence 
       http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
       <persistence-unit name = "Eclipselink_JPA" transaction-type = "RESOURCE_LOCAL">
          <class>com.jc2182.eclipselink.entity.Staff</class>
          <class>com.jc2182.eclipselink.entity.NonTeachingStaff</class>
          <class>com.jc2182.eclipselink.entity.TeachingStaff</class>
          
          <properties>
             <property name = "javax.persistence.jdbc.url" value = "jdbc:mysql://localhost:3306/jpadb"/>
             <property name = "javax.persistence.jdbc.user" value = "root"/>
             <property name = "javax.persistence.jdbc.password" value = "root"/>
             <property name = "javax.persistence.jdbc.driver" value = "com.mysql.jdbc.Driver"/>
             <property name = "eclipselink.logging.level" value = "FINE"/>
             <property name = "eclipselink.ddl-generation" value="create-tables"/>
          </properties>
          
       </persistence-unit>
    </persistence>
    

    服务类

    服务类是业务组件的实现部分。在下面创建一个包‘src’ 包命名 ‘com.jc2182.eclipselink.service’.
    创建一个名为的类 SaveClient.java在给定的包下存储 Staff、TeachingStaff 和 NonTeachingStaff 类字段。SaveClient 类如下所示:
    
    package com.jc2182.eclipselink.service;
    import javax.persistence.EntityManager;
    import javax.persistence.EntityManagerFactory;
    import javax.persistence.Persistence;
    import com.jc2182.eclipselink.entity.NonTeachingStaff;
    import com.jc2182.eclipselink.entity.TeachingStaff;
    public class SaveClient {
       public static void main( String[ ] args ) {
          EntityManagerFactory emfactory = Persistence.createEntityManagerFactory( "Eclipselink_JPA" );
          EntityManager entitymanager = emfactory.createEntityManager( );
          entitymanager.getTransaction( ).begin( );
          //Teaching staff entity 
          TeachingStaff ts1 = new TeachingStaff(1,"Gopal","MSc MEd","Maths");
          TeachingStaff ts2 = new TeachingStaff(2, "Manisha", "BSc BEd", "English");
          
          //Non-Teaching Staff entity
          NonTeachingStaff nts1 = new NonTeachingStaff(3, "Satish", "Accounts");
          NonTeachingStaff nts2 = new NonTeachingStaff(4, "Krishna", "Office Admin");
          //storing all entities
          entitymanager.persist(ts1);
          entitymanager.persist(ts2);
          entitymanager.persist(nts1);
          entitymanager.persist(nts2);
          entitymanager.getTransaction().commit();
          entitymanager.close();
          emfactory.close();
       }
    }
    
    编译并执行上述程序后,您将在 Eclipse IDE 的控制台面板中收到通知。对于输出,检查 MySQL 工作台如下:
    这里创建了三个表,并且 Staff 表包含空记录。
    的结果 TeachingStaff 以表格形式显示如下:
    Sid Qualification Sname Subjectexpertise
    1 MSC MED Gopal Maths
    2 BSC BED Manisha English
    上表TeachingStaff 包含Staff 和TeachingStaff 实体的字段。
    的结果 NonTeachingStaff 以表格形式显示如下:
    Sid Areaexpertise Sname
    3 Accounts Satish
    4 Office Admin Krishna
    上表 NonTeachingStaff 包含 Staff 和 NonTeachingStaff 实体的字段。