`
dev_liu
  • 浏览: 109495 次
  • 性别: Icon_minigender_1
  • 来自: 成都
最近访客 更多访客>>
社区版块
存档分类
最新评论

Hibernate映射继承关系的三种方式(1)zt

阅读更多
Company类和Employee类之间为一对多的双向关联关系(假定不允许雇员同时在多个公司兼职),Employee为抽象类因此不能被实例化,它有两个具体的子类;HourlyEmployee类和SalariedEmployee类。

由于Java只允许一个类最多有一个直接的父类,因此Employ类、HourlyEmployee类和SalariedEmployee类构成了一棵继承关系树。

 

        

          图 继承关系树

多态在图中的表现:

List employees=businessService.findAllEmployees();

Iterator it=employees.iterator();

while(it.hasNext()){

  Employee e=(Employee)it.next();

  if(e instanceof HourlyEmployee){

    System.out.println(e.getName()+""+((HourlyEmployee)e).getRate());

  }else{

    System.out.println(e.getName()+""+((SalariedEmployee)e).getSalary());

  }

第一种方式:是将每个具体的类映射为一张表也是最简单的。

 

继承关系树的每个具体类对应一张表:这种映射方式关系数据模型完全不支持域模型中的继承关系和多态。

 

            

              图 1 每个具体类对应的表

    

            图 2 持久化类、映射文件和数据表之间的对应关系

 

     如果Employee类不是抽象类(这里Employee类被设计成了抽象类),即Employee类也能被实例化,那么还需要为Employee类创建对应的Employees表,此时HOURLY_EMPLOYEES表和SALARIED_EMPLOYEES表结构仍然和图1相同,只不过多了一个EMPLOYEES表。

 

                图 3 每个具体类对应的表

 

另外,还需要为Employee类创建单独的Employee.hbm.xml文件。

在这里我只讨论设计成常用的抽象类的模式。

 

    从Company类到Employee类是多态关联,但是由于关系数据<nobr>模型</nobr>没有描述Employee类和它的两个子类的继承关系,因此无法映射Company类的employees集合。Company.hbm.xml文件的代码,该文件仅仅映射了Company类的id和name属性。

例程:Company.hbm.xml

 

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping >

  <class name="mypack.Company" table="COMPANIES" >
    <id name="id" type="long" column="ID">
      <generator class="increment"/>
    </id>

    <property name="name" type="string"  column="NAME" />

  </class>
</hibernate-mapping>

 

       HourlyEmployee.hbm.xml文件用于把HourlyEmployee类映射到HE表,在这个映射文件中,除了需要映射HourlyEmployee类本身的rate属性,还需要映射从Employee类中继承的name属性,此外还需要映射从Employee类中继承与Company类的关联关系。

 

例程:HourlyEmployee.hbm.xml

 

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping >

   <class name="mypack.HourlyEmployee" table="HOURLY_EMPLOYEES">
      <id name="id" type="long" column="ID">
        <generator class="increment"/>
      </id>
  
      <property name="name" type="string" column="NAME" />
 
      <property name="rate" column="RATE" type="double" />

      <many-to-one
        name="company"
        column="COMPANY_ID"
        class="mypack.Company"
      />

    </class>
 
</hibernate-mapping>

 

    SalariedEmployee.hbm.xml文件用于把SalariedEmployee类映射到SE表,在这个映射文件中,除了需要映射SalariedEmployee类本身的salary<nobr>属性</nobr>,还需要映射从Employee类中继承的name属性,此外还需要映射从Employee类中继承的与Company类的关联关系。

例程:SalariedEmployee.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping >

   <class name="mypack.SalariedEmployee" table="SALARIED_EMPLOYEES">
      <id name="id" type="long" column="ID">
        <generator class="increment"/>
      </id>
  
      <property name="name" type="string" column="NAME" />
 
      <property name="salary" column="SALARY" type="double" />

      <many-to-one
        name="company"
        column="COMPANY_ID"
        class="mypack.Company"
      />

    </class>
 
</hibernate-mapping>

 

由于Employee类没有映射文件,因此在初始化Hibernate时,只需向Configuration对象中加入Company类、HourlyEmployee类和SalariedEmployee类:

 

Configuration config=new Configuration();

config.addClass(Company.class)

.addClass(HourlyEmployee.class)

.addClass(SalariedEmployee.class);

 

这中方式不支持多态查询,所以

 

List employees=session.find("from Employee");

 

会抛出异常。因为Employee是抽象类,但如果Employee类是具体类的话也只会查询出EMPLOYEE表的记录不会检索出它的两个子类的实例。

 

源码如下:

hibernate.properties

 

hibernate.dialect=net.sf.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class=com.mysql.jdbc.Driver
hibernate.connection.url=jdbc:mysql://localhost:3306/SAMPLEDB
hibernate.connection.username=root
hibernate.connection.password=1234
hibernate.show_sql=true

BusinessService.java

 

package mypack;

import net.sf.hibernate.*;
import net.sf.hibernate.cfg.Configuration;
import java.util.*;
import java.sql.*;

public class BusinessService{
  public static SessionFactory sessionFactory;
  static{
     try{
      // Create a configuration based on the properties file we've put
       Configuration config = new Configuration();
       config.addClass(Company.class)
             .addClass(HourlyEmployee.class)
             .addClass(SalariedEmployee.class);
      // Get the session factory we can use for persistence
      sessionFactory = config.buildSessionFactory();
    }catch(Exception e){e.printStackTrace();}

  }

  public void saveEmployee(Employee employee) throws Exception{
      Session session = sessionFactory.openSession();
      Transaction tx = null;
      List results=new ArrayList();
      try {
       tx = session.beginTransaction();
       session.save(employee);
       tx.commit();
    }catch (Exception e) {
      if (tx != null) {
        // Something went wrong; discard all partial changes
        tx.rollback();
      }
      throw e;
    } finally {
      // No matter what, close the session
      session.close();
    }
  }


  public List findAllEmployees() throws Exception{
      Session session = sessionFactory.openSession();
      Transaction tx = null;

      try {
       List results=new ArrayList();
       tx = session.beginTransaction();
       List hourlyEmployees=session.find("from HourlyEmployee");
       results.addAll(hourlyEmployees);

       List salariedEmployees=session.find("from SalariedEmployee");
       results.addAll(salariedEmployees);

      tx.commit();
      return results;
    }catch (Exception e) {
      if (tx != null) {
        // Something went wrong; discard all partial changes
        tx.rollback();
      }
      throw e;
    } finally {
      // No matter what, close the session
      session.close();
    }
  }

  public Company loadCompany(long id) throws Exception{
      Session session = sessionFactory.openSession();
      Transaction tx = null;
      try {
       tx = session.beginTransaction();
       Company company=(Company)session.load(Company.class,new Long(id));

       List hourlyEmployees=session.find("from HourlyEmployee h where h.company.id="+id);
       company.getEmployees().addAll(hourlyEmployees);

       List salariedEmployees=session.find("from SalariedEmployee s where s.company.id="+id);
       company.getEmployees().addAll(salariedEmployees);

      tx.commit();
      return company;
    }catch (Exception e) {
      if (tx != null) {
        // Something went wrong; discard all partial changes
        tx.rollback();
      }
      throw e;
    } finally {
      // No matter what, close the session
      session.close();
    }
  }

   public void test() throws Exception{
      List employees=findAllEmployees();
      printAllEmployees(employees.iterator());

      Company company=loadCompany(1);
      printAllEmployees(company.getEmployees().iterator());

      Employee employee=new HourlyEmployee("Mary",300,company);
      saveEmployee(employee);

  }

  private void printAllEmployees(Iterator it){
     while(it.hasNext()){
        Employee e=(Employee)it.next();
        if(e instanceof HourlyEmployee){
          System.out.println(((HourlyEmployee)e).getRate());
        }else
          System.out.println(((SalariedEmployee)e).getSalary());
      }
  }
  public static void main(String args[]) throws Exception {
    new BusinessService().test();
    sessionFactory.close();
  }
}

Company.java

 

package mypack;

import java.io.Serializable;
import java.util.Set;
import java.util.HashSet;

public class Company implements Serializable {

    private Long id;
    private String name;
    private Set employees=new HashSet();

    /** full constructor */
    public Company(String name, Set employees) {
        this.name = name;
        this.employees = employees;
    }

    /** default constructor */
    public Company() {
    }

    /** minimal constructor */
    public Company(Set employees) {
        this.employees = employees;
    }

    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Set getEmployees() {
        return this.employees;
    }

    public void setEmployees(Set employees) {
        this.employees = employees;
    }
}

Company.hbm.xml

 

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping >

  <class name="mypack.Company" table="COMPANIES" >
    <id name="id" type="long" column="ID">
      <generator class="increment"/>
    </id>

    <property name="name" type="string"  column="NAME" />

  </class>
</hibernate-mapping>

 

Employee.java

 

package mypack;

import java.io.Serializable;

abstract public class Employee implements Serializable {

    private Long id;
    private String name;
    private Company company;

    /** full constructor */
    public Employee(String name,Company company) {
        this.name = name;
        this.company = company;
    }

    /** default constructor */
    public Employee() {
    }

    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Company getCompany() {
        return this.company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }
}

HourlyEmployee.java

 

package mypack;

import java.io.Serializable;

public class HourlyEmployee extends Employee{

    private double rate;

    /** full constructor */
    public HourlyEmployee(String name, double rate,Company company) {
        super(name,company);
        this.rate=rate;
    }

    /** default constructor */
    public HourlyEmployee() {
    }

    public double getRate() {
        return this.rate;
    }

    public void setRate(double rate) {
        this.rate = rate;
    }
   
}

HourlyEmployee.hbm.xml

 

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping >

   <class name="mypack.HourlyEmployee" table="HOURLY_EMPLOYEES">
      <id name="id" type="long" column="ID">
        <generator class="increment"/>
      </id>
  
      <property name="name" type="string" column="NAME" />
 
      <property name="rate" column="RATE" type="double" />

      <many-to-one
        name="company"
        column="COMPANY_ID"
        class="mypack.Company"
      />

    </class>
 
</hibernate-mapping>

 

SalariedEmployee.java

 

package mypack;

import java.io.Serializable;

public class SalariedEmployee extends Employee {

    private double salary;

    /** full constructor */
    public SalariedEmployee(String name, double salary,Company company) {
        super(name,company);
        this.salary=salary;
       
    }

    /** default constructor */
    public SalariedEmployee() {
    }

   public double getSalary() {
        return this.salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }
}

SalariedEmployee.hbm.xml

 

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping >

   <class name="mypack.SalariedEmployee" table="SALARIED_EMPLOYEES">
      <id name="id" type="long" column="ID">
        <generator class="increment"/>
      </id>
  
      <property name="name" type="string" column="NAME" />
 
      <property name="salary" column="SALARY" type="double" />

      <many-to-one
        name="company"
        column="COMPANY_ID"
        class="mypack.Company"
      />

    </class>
 
</hibernate-mapping>

 

sampledb.sql

 

drop database if exists SAMPLEDB;
create database SAMPLEDB;
use SAMPLEDB;

create table COMPANIES (
   ID bigint not null,
   NAME varchar(15),
   primary key (ID)
);
create table HOURLY_EMPLOYEES (
   ID bigint not null,
   NAME varchar(15),
   RATE double precision,
   COMPANY_ID bigint,
   primary key (ID)
);
create table SALARIED_EMPLOYEES (
   ID bigint not null,
   NAME varchar(15),
   SALARY double precision,
   COMPANY_ID bigint,
   primary key (ID)
);
alter table HOURLY_EMPLOYEES add index IDX1_COMPANY(COMPANY_ID), add constraint FK1_COMPANY foreign key (COMPANY_ID) references COMPANIES (ID);
alter table SALARIED_EMPLOYEES add index IDX2_COMPANY(COMPANY_ID), add constraint FK2_COMPANY foreign key (COMPANY_ID) references COMPANIES (ID);

insert into COMPANIES(ID,NAME) values(1,'ABC Company');

insert into HOURLY_EMPLOYEES(ID,NAME,RATE,COMPANY_ID) values(1,'Tom',100,1);

insert into HOURLY_EMPLOYEES(ID,NAME,RATE,COMPANY_ID) values(2,'Mike',200,1);

insert into SALARIED_EMPLOYEES(ID,NAME,SALARY,COMPANY_ID)
values(1,'Jack',5000,1);

insert into SALARIED_EMPLOYEES(ID,NAME,SALARY,COMPANY_ID)
values(2,'Linda',6000,1);

总结:

 

BusinessServic的main()方法调用test()方法,test()方法依次调用以下方法。

* findAllEmployees():检索数据库中所有的Employee对象

* loadCompany():加载一个Company对象

* saveEmployee():保存一个Employee对象

 

(1) 运行findAllEmployee()方法,它的代码如下:

 

List results=new ArrayList();

tx=session.beginTransaction();

List hourlyEmployees=session.find("from HourlyEmployee");

results.addAll(hourlyEmployee);

 

List salariedEmployees=session.find("from SalariedEmployee");

results.addAll(salariedEmployees);

 

tx.commit();

return results;

 

    为了检索所有的Employee对象,必须分别检索所有的HourlyEmployee实例和SalariedEmployee实例,然后将它们合并到一个集合中去。在运行Session的第一个find()方法时,Hibernate执行以下select语句:

     select * from HOURLY_EMPLOYEES;

   select * from COMPANIES where ID=1;

    从HourlyEmployee类到Company类不是多态关联,在加载HourlyEmployee对象时,会同时加载与它关联的Company对象。

    在运行Session的第二个find()方法时,Hibernate执行以下select语句:

  select * from SALARIED_EMPLOYEES;

    从SalariedEmployee类到Company类不是多态关联,在加载SalariedEmployee对象时,会同时加载与它关联的Company对象。在本测试数据中,所有的HourlyEmployee实例和SalariedEmployee实例都与OID为1的Company对象关联,由于该Company对象已经被加载到内存中,所以Hibernate不再需要执行检索该对象的 select语句。

 

(2) 运行loadCompany()方法,它的代码如下:

  

  tx=session.beginTransaction();

  Company company=(Company)session.load(Company.class,new Long(id));

  

  List hourlyEmployees=session.find("from HourlyEmployee h where h.company.id="+id);

  company.getEmployees().addAll(hourlyEmployees);

  //抽象类不能用new来创建

 

  List salariedEmployees=session.find("from SalariedEmployee s where s.company.id="+id);

  company.getEmployees().addAll(salariedEmployees);

 

  tx.commit();

  return company;

  

  由于这种映射方式不支持多态关联,因此由Session的load()方法加载的Company<nobr>对象</nobr>的employees<nobr>集合</nobr>中不包含任何Employee对象。BusinessService类必须负责从数据库中检索出所有与Company对象关联的HourlyEmployee对象以及SalariedEmployee对象,然后把它们加入到employees集合中。

 

(3) 运行saveEmployee(Employee employee)方法,它的代码如下:

  tx=session.beginTransaction();

  session.save(employee);

  tx.commit();

  

  在test()方法中,创建了一个HourlyEmployee实例,然后调用saveEmployee()方法保存这个实例:

 

  Employee employee=new HourlyEmployee("Mary",300,company);

  saveEmployee(employee);

  

  Session的save()方法能判断employee变量实际引用的实例的类型,如果employee变量引用HourlyEmployee实例,就向HE表中插入一条记录,执行如下insert<nobr>语句</nobr>:

  

  insert into HOURLY_EMPLOYEES (ID,NAME,RATE,CUSTOMER_ID) values(3,'Mary',300,1);

 

  如果employee<nobr>变量</nobr>引用SalariedEmployee<nobr>实例</nobr>,就向SE表插入一条记录。

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics