Hibernate 3.2 学习笔记 映射继承关系

由于之前的书上讲的方法 有的很不安全 Bug不断 结构也不够清晰 在做例子程序是反复调试还是出错

在加上书上所用的 ant 和 hibernate 还有 hibernate tools 版本太旧

固自己研究reference 做总结

一 Table per class hierarchy (每个类分层结构一张表)

例子

Company 和 Employee 一对多 Employee 有两个子类 HourlyEmployee 和 SalaruedEmployee
Company.hbm.xml

<? xmlversion="1.0" ?>
<! DOCTYPEhibernate-mappingPUBLIC
"-//Hibernate/HibernateMappingDTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
>

< hibernate-mapping >

< class name ="ergal.Company" table ="COMPANIES" lazy ="true" >
< id name ="id" type ="long" column ="ID" >
< generator class ="native" />
</ id >

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

< set name ="employees"
inverse
="true"
lazy
="true" >
< key column ="COMPANY_ID" />
< one-to-many class ="ergal.Employee" />
</ set >

</ class >

</ hibernate-mapping >


Employee.hbm.xml

<? xmlversion="1.0" ?>
<! DOCTYPEhibernate-mappingPUBLIC
"-//Hibernate/HibernateMappingDTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
>

< hibernate-mapping >

< class name ="ergal.Employee" table ="EMPLOYEES" lazy ="true" >
< id name ="id" type ="long" column ="ID" >
< generator class ="native" />
</ id >
< discriminator column ="EMPLOYEE_TYPE" type ="string" />

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

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

< subclass name ="ergal.HourlyEmployee" discriminator-value ="HE" >
< property name ="rate" column ="RATE" type ="double" />
</ subclass >

< subclass name ="ergal.SalariedEmployee" discriminator-value ="SE" >
< property name ="salary" column ="SALARY" type ="double" />
</ subclass >
</ class >

</ hibernate-mapping >


hbm2java
工具会自动产生
Employee.java
HourlyEmplyee.java
SalariedEmployee.java
并自动完整他们得继承关系

hbm2ddl

工具会自动产生

2个表

COMPANYS和EMPLOYEES

EMPLOYEES中多了个EMPLOYEE_TYPE字段

EMPLOYEES

6 个字段

1 - ID

2 - EMPLOYEE_TYPE

3- NAME

4 - COMPANY_ID

5 - RATE

6 - SALARY

优点:方便查询 只需建立一张表 支持多态查询和多态关联 不需要查询时连接 可维护性较强

缺点:有额外的字段

load()/get() Inheritance strategy Polymorphic many-to-one Polymorphic one-to-one Polymorphic one-to-many Polymorphic many-to-many Polymorphic Polymorphic queries Polymorphic joins Outer join fetching
table per class-hierarchy <many-to-one> <one-to-one> <one-to-many> <many-to-many> s.get(Payment.class, id) from Payment p from Order o join o.payment p supported

二 Table per subclass (每个子类一张表)

例子同上

Company 和 Employee 一对多 Employee 有两个子类 HourlyEmployee 和 SalaruedEmployee

Company.hbm.xml

没有变化

Employee.hbm.xml

<? xmlversion="1.0" ?>
<! DOCTYPEhibernate-mappingPUBLIC
"-//Hibernate/HibernateMappingDTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
>

< hibernate-mapping >

< class name ="ergal.Employee" table ="EMPLOYEES" lazy ="true" >
< id name ="id" type ="long" column ="ID" >
< generator class ="native" />
</ id >

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

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

< joined-subclass name ="ergal.HourlyEmployee" table ="HOURLY_EMPLOYEE" >
< key column ="EMPLOYEE_ID" />
< property name ="rate" column ="RATE" type ="double" />
</ joined-subclass >

< joined-subclass name ="ergal.SalariedEmployee" table ="SALARUED_EMPLOYEE" >
< key column ="EMPLOYEE_ID" />
< property name ="salary" column ="SALARY" type ="double" />
</ joined-subclass >

</ class >

</ hibernate-mapping >

运行hbm2ddl工具会在数据库中产生4个表

COMPSNYS EMPLOYEES HOURLYE_MPLOYEE 和SALARIED_EMPLOYEE

EMPLOYEES 3个字段

1 - ID

2 - NAME

3 - COMPANY_ID

HOURLYE_MPLOYEE 2个字段

1 - EMPLOYEE_ID

2 - RATE

SALARIED_EMPLOYEE 2个字段

1 - EMPLOYEE_ID

2 - SALARY

运行hbn2java工具

会自动产生
Employee.java
HourlyEmplyee.java
SalariedEmployee.java
并使他们具备完整的继承关系

优点:支持多态查询和多态关联 某个类要修改可以修改相应的这个类对应的表

缺点:表的数目多 表之间有外键参照关系 查询时需要连接

load()/get() Inheritance strategy Polymorphic many-to-one Polymorphic one-to-one Polymorphic one-to-many Polymorphic many-to-many Polymorphic Polymorphic queries Polymorphic joins Outer join fetching
table per class-hierarchy <many-to-one> <one-to-one> <one-to-many> <many-to-many> s.get(Payment.class, id) from Payment p from Order o join o.payment p supported
table per subclass <many-to-one> <one-to-one> <one-to-many> <many-to-many> s.get(Payment.class, id) from Payment p from Order o join o.payment p supported

三 Table per subclass, using a discriminator (使用标识符的 每个子类一张表)

例子同上

Company 和 Employee 一对多 Employee 有两个子类 HourlyEmployee 和 SalaruedEmployee

Company.hbm.xml

没有变化

Employee.hbm.xml

<? xmlversion="1.0" ?>
<! DOCTYPEhibernate-mappingPUBLIC
"-//Hibernate/HibernateMappingDTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
>

< hibernate-mapping >

< class name ="ergal.Employee" table ="EMPLOYEES" lazy ="true" >
< id name ="id" type ="long" column ="ID" >
< generator class ="native" />
</ id >
< discriminator column ="EMPLOYEE_TYPE" type ="string" />

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

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

< subclass name ="ergal.HourlyEmployee" discriminator-value ="HE" >
< join table ="HOURLY_EMPLOYEE" >
< key column ="EMPLOYEE_ID" />
< property name ="rate" column ="RATE" type ="double" />
</ join >
</ subclass >

< subclass name ="ergal.SalariedEmployee" discriminator-value ="SE" >
< join table ="SALARUED_EMPLOYEE" >
< key column ="EMPLOYEE_ID" />
< property name ="rate" column ="RATE" type ="double" />
</ join >
</ subclass >


</ class >

</ hibernate-mapping >

运行hbm2ddl工具会在数据库中产生4个表

COMPANIES EMPLOYEES HOURLYEMPLOYEE 和SALARIEDEMPLOYEE

其中在EMPLOYEE中多了用来标识的EMPLOYEE_TYPE 取值为 HE和SE

EMPLOYEES 4个字段

1 - ID

2 - NAME

3 - EMPLOYEE_TYPE

4 - COMPANY_ID

HOURLY_EMPLOYEE 2个字段

1 - EMPLOYEE_TPYE

2 - RATE

SALARIED_EMPLOYEE 2个字段

1 - EMPLOYEE_TPYE

2 - SALARY

运行hbn2java工具

会自动产生
Employee.java
HourlyEmplyee.java
SalariedEmployee.java
并使他们具备完整的继承关系

只是介绍这种映射方式---结合标识器 并没有什么突出的特征和用途

四 Mixing table per class hierarchy with table per subclass (混合模式)

不做更多解释 本身这种映射的意义就不大

reference上的例子

< class name ="Payment" table ="PAYMENT" >
< id name ="id" type ="long" column ="PAYMENT_ID" >
< generator class ="native" />
</ id >
< discriminator column ="PAYMENT_TYPE" type ="string" />
< property name ="amount" column ="AMOUNT" />
...
< subclass name ="CreditCardPayment" discriminator-value ="CREDIT" >
< join table ="CREDIT_PAYMENT" >
< property name ="creditCardType" column ="CCTYPE" />
...
</ join >
</ subclass >
< subclass name ="CashPayment" discriminator-value ="CASH" >
...
</ subclass >
< subclass name ="ChequePayment" discriminator-value ="CHEQUE" >
...
</ subclass >
</ class >

疑问

reference上的这句

对上述任何一种映射策略而言,指向根类Payment的 关联是使用<many-to-one>进行映射的。
<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>

我之前没有做这项工作

并没有报错 而且这么做的话 我原来的多对一 Employee对Company该怎么映射

五 Table per concrete class (每个具体的类一张表) 这里把它叫做 每个具体类一张表(union-subclass) 好一点

它有两种方式来实现

一种是显式多态

一种是隐式多态

显式多态采用的为 @hibernate.union-subclass 的方式,reference上也把这种映射方式叫做每个具体的类(union-subclass)

隐式多态则采用每个具体类的 PO 独立建表的策略,在它的映射文件中将看不出任何的和接口、抽象类的关系,同时对于抽象类,需要指明其 abstract=”true” 。

这里采用的是显式多态

例子同上

Company 和 Employee 一对多 Employee 有两个子类 HourlyEmployee 和 SalaruedEmployee

Company.hbm.xml

没有变化

Employee.hbm.xml

<? xmlversion="1.0" ?>
<! DOCTYPEhibernate-mappingPUBLIC
"-//Hibernate/HibernateMappingDTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
>

< hibernate-mapping >

< class name ="ergal.Employee" table ="EMPLOYEES" lazy ="true" >
< id name ="id" type ="long" column ="ID" >
< generator class ="native" />
</ id >

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

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

< union-subclass name ="ergal.HourlyEmployee" table ="HOURLY_EMPLOYEES" >
< property name ="rate" column ="RATE" type ="double" />
</ union-subclass >

< union-subclass name ="ergal.SalariedEmployee" table ="SALARIED_EMPLOYEES" >
< property name ="salary" column ="SALARY" type ="double" />
</ union-subclass >



</ class >

</ hibernate-mapping >

同样会产生4个表 但是这个映射是有限制的

官方的reference这么写到

The limitation of this approach is that if a property is mapped on the superclass, the column name must be the same on all subclass tables. (We might relax this in a future release of Hibernate.) The identity generator strategy is not allowed in union subclass inheritance, indeed the primary key seed has to be shared accross all unioned subclasses of a hierarchy.

主要限制在不允许在联合子类(union subclass)的继承层次中使用标识生成器策略(identity generator strategy)

再加上 这种方法下 生成的表多 而且字段有重复

查询时如果查询父类必须查询子类的表 修改父类的表 子类的表也要修改

既不支持多态查询也不支持多态关联

所以不推荐使用

但是还是要说一说

毕竟要让它跑起来嘛 再说 既然有这个方法 也是有它存在的理由

在调试中 ID的属性没有设置正确

这正是

不允许在联合子类(union subclass)的继承层次中使用标识生成器策略(identity generator strategy)

而且子类不该设置ID

看了很多资料

主要参考了一个和我遇到同样问题的帖子

地址

http://forum.hibernate.org/viewtopic.php?t=963635

最后让它可以运行了

Empolyee.hbm.xnl

修改成如下:

<? xmlversion="1.0" ?>
<! DOCTYPEhibernate-mappingPUBLIC
"-//Hibernate/HibernateMappingDTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
>

< hibernate-mapping >

< class name ="ergal.Employee" table ="EMPLOYEES" lazy ="true" >
< id name ="id" type ="long" column ="ID" >
< generator class ="hilo" >
< param name ="table" > hi_value </ param >
< param name ="column" > next_value </ param >
< param name ="max_lo" > 100 </ param >
</ generator >
</ id >

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

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

< union-subclass name ="ergal.HourlyEmployee" table ="HOURLY_EMPLOYEES" >
< property name ="rate" column ="RATE" type ="double" />
</ union-subclass >

< union-subclass name ="ergal.SalariedEmployee" table ="SALARIED_EMPLOYEES" >
< property name ="salary" column ="SALARY" type ="double" />
</ union-subclass >



</ class >

</ hibernate-mapping >

把generator 换成了 hilo

这样在hbm2ddl的时候又5个表

COMPANIES EMPLOYEES HOURLY_EMPLOYEE 和SALARIED_EMPLOYEE 多了个 Hi_Value

EMPLOYEES

1 - ID

2 - NAME

3 - COMPANY_ID

HOURLY_EMPLOYEE

1 - ID

2 - NAME

3 - COMPANY_ID

4 - RATE

SALARIED_EMPLOYEE

1 - ID

2 - NAME

3 - COMPANY_ID

4 - SALARY

Hi_Value

1 - next_value

测试代码

BusinessService.java

package ergal;

import java.util. * ;
import org.hibernate. * ;
import org.hibernate.cfg. * ;

public class BusinessService
... {
publicstaticSessionFactorysessionFactory;
static
...{
try
...{
Configurationconfig
=newConfiguration();
sessionFactory
=config.configure().buildSessionFactory();
}

catch(Exceptione)
...{
e.printStackTrace();
}

}


publicvoidsaveEmployee(Employeeemployee)throwsException
...{
Sessionsession
=sessionFactory.openSession();
Transactiontx
=null;
try
...{
tx
=session.beginTransaction();
session.save(employee);
tx.commit();
}

catch(Exceptione)
...{
if(tx!=null)
...{
tx.rollback();
}

throwe;
}

finally
...{
session.close();
}

}


publicCompanyloadCompany(Longid)throwsException
...{
Sessionsession
=sessionFactory.openSession();
Transactiontx
=null;
try
...{
tx
=session.beginTransaction();

Companycompany
=(Company)session.load(Company.class,id);

tx.commit();
returncompany;
}

catch(Exceptione)
...{
if(tx!=null)
...{
tx.rollback();
}

throwe;
}

finally
...{
session.close();
}

}


publicvoidtest()throwsException
...{
Companycompany
=loadCompany(newLong(1));
doubled1=150;
doubled2=5000;
Employeehe
=newHourlyEmployee("Tom",company,d1);
Employeese
=newSalariedEmployee("Mary",company,d2);
saveEmployee(he);
saveEmployee(se);
}


publicvoidfind()throwsException
...{
Sessionsession
=sessionFactory.openSession();
Transactiontx
=null;
try
...{
tx
=session.beginTransaction();

Listresult
=session.createQuery("fromEmployee").list();

for(Iteratorit=result.iterator();it.hasNext();)
...{
Employeec
=(Employee)it.next();
System.out.println(
"Employee'sID:"+c.getId());
System.out.println(
"Employee'sname:"+c.getName());
}

tx.commit();

}

catch(Exceptione)
...{
if(tx!=null)
...{
tx.rollback();
}

throwe;
}

finally
...{
session.close();
}

}


publicstaticvoidmain(String[]args)throwsException
...{
newBusinessService().find();
sessionFactory.close();
}

}

运行后我有疑问没有解决

查询和插入都没有问题

可以正常显示结果

但是

EMPLOYEE表始终是个空表

不像前面的映射 不管多少个表 怎么映射EMPLOYEE表都是存在的而且里面的值会自动和 子类的表同步

六 Table per concrete class, using implicit polymorphism每个具体类一个表 隐式多态

这就是实现 每个具体的类一张表的 另一种方法 隐式多态

代码如下

Employee.hbm.xml

<? xmlversion="1.0" ?>
<! DOCTYPEhibernate-mappingPUBLIC
"-//Hibernate/HibernateMappingDTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
>

< hibernate-mapping >

< class name ="ergal.Employee" table ="EMPLOYEES" >
< id name ="id" type ="long" column ="EMPLOYEE_ID" >
< generator class ="native" />
</ id >

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

< any name ="employee" meta-type ="string" id-type ="long" >
< meta-value value ="HE" class ="ergal.HourlyEmployee" />
< meta-value value ="SE" class ="ergal.SalariedEmployee" />
< column name ="EMPLOYEE_TYPE" />
< column name ="SUB_EMPLOYEE_ID" />

</ any >

</ class >

</ hibernate-mapping >

HourlyEmployee.hbm.xml

<? xmlversion="1.0" ?>
<! DOCTYPEhibernate-mappingPUBLIC
"-//Hibernate/HibernateMappingDTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
>

< hibernate-mapping >

< class name ="ergal.HourlyEmployee" table ="HOURLY_EMPLOYEES" polymorphism ="implicit" >
< id name ="id" type ="long" column ="HOURLY_EMPLOYEE_ID" >
< generator class ="native" />
</ id >
< property name ="name" column ="NAME" type ="string" />
< property name ="rate" column ="RATE" type ="double" />


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

</ class >

</ hibernate-mapping >

SalariedEmployee.hbm.xml

<? xmlversion="1.0" ?>
<! DOCTYPEhibernate-mappingPUBLIC
"-//Hibernate/HibernateMappingDTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
>

< hibernate-mapping >


< class name ="ergal.SalariedEmployee" table ="SALARUED_EMPLOYEES" polymorphism ="implicit" >
< id name ="id" type ="long" column ="SALARUED_EMPLOYEE_ID" >
< generator class ="native" />
</ id >
< property name ="name" column ="NAME" type ="string" />
< property name ="salary" column ="SALARY" type ="double" />



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



</ class >




</ hibernate-mapping >

运行hbm2ddl

产生表如下 4个

COMPANIES EMPLOYEES HOURLYEMPLOYEE 和SALARIEDEMPLOYEE

EMPLOYEES

1 - EMPLOYEE_ID

2 - NAME

3 - EMPLOYEE_TYPE

4 - SUB_EMPLOYEE_ID

5 - COMPANY_ID

HOURLYEMPLOYEE

1 - HOURLYEMPLOUYEE_ID

2 - NAME

3 - RATE

4 - COMPANY_ID

SALARIEDEMPLOYEE

1 - SALARIEDEMPLOYEE_ID

2 - NAME

3 - SALAR

4 - COMPANY_ID

测试代码有所变化

BusinessService.java

Companycompany = loadCompany( new Long( 1 ));
double d1 = 150 ;

HourlyEmployeehe
= new HourlyEmployee( " Tom " ,d1,company);
saveEmployee(he);

Employeeemployee
= new Employee();
employee.setEmployee(he);

saveEmployee(employee);

这样在储存时必须先持久化 一个子类 再用set方法加到父类中 再持久化父类

否则会抛出

[java] org.hibernate.TransientObjectException: object references an unsaved
transient instance - save the transient instance before flushing: ergal.HourlyE
mployee

这样的好处是什么?

还有这里使用了any

我要思考一下

最后一种

另外这里使用了any

简单记一下

<any> 是隐式的 我是这么理解的

从字面上看 就是这个属性可以关联到多个其他的对象

所以你可能更本看不出它是子类接口 还是别的关联属性 在父类里显示为一个对象

对相应的类型做相应的标识

like this

<any name="payment" meta-type="string" id-type="long">
    <meta-value value="CREDIT" class="CreditCardPayment"/>
    <meta-value value="CASH" class="CashPayment"/>
    <meta-value value="CHEQUE" class="ChequePayment"/>
    <column name="PAYMENT_CLASS"/>
    <column name="PAYMENT_ID"/>
</any>

七 Mixing implicit polymorphism with other inheritance mappings隐式多态和其他继承映射混合使用

我要疯了

看官方文档吧 如果用到再仔细研究 这已经花了我两天的时间了

对这一映射还有一点需要注意。因为每个子类都在各自独立的元素<class> 中映射(并且Payment只是一个接口),每个子类可以很容易的成为另一 个继承体系中的一部分!(你仍然可以对接口Payment使用多态查询。)
<class name="CreditCardPayment" table="CREDIT_PAYMENT">
<id name="id" type="long" column="CREDIT_PAYMENT_ID">
<generator class="native"/>
</id>
<discriminator column="CREDIT_CARD" type="string"/>
<property name="amount" column="CREDIT_AMOUNT"/>
...
<subclass name="MasterCardPayment" discriminator-value="MDC"/>
<subclass name="VisaPayment" discriminator-value="VISA"/>
</class>

<class name="NonelectronicTransaction" table="NONELECTRONIC_TXN">
<id name="id" type="long" column="TXN_ID">
<generator class="native"/>
</id>
...
<joined-subclass name="CashPayment" table="CASH_PAYMENT">
<key column="PAYMENT_ID"/>
<property name="amount" column="CASH_AMOUNT"/>
...
</joined-subclass>
<joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
<key column="PAYMENT_ID"/>
<property name="amount" column="CHEQUE_AMOUNT"/>
...
</joined-subclass>
</class>

我们还是没有明确的提到Payment。 如果我们针对接口Payment执行查询 ——如from Payment—— Hibernate 自动返回CreditCardPayment(和它的子类,因为 它们也实现了接口Payment)、 CashPayment和Chequepayment的实例, 但不返回NonelectronicTransaction的实例。

限制

对“每个具体类映射一张表”(table per concrete-class)的映射策略而言,隐式多态的 方式有一定的限制。而<union-subclass>映射的限制则没有那 么严格。

下面表格中列出了在Hibernte中“每个具体类一张表”的策略和隐式多态的限制。

load()/get() Inheritance strategy Polymorphic many-to-one Polymorphic one-to-one Polymorphic one-to-many Polymorphic many-to-many Polymorphic Polymorphic queries Polymorphic joins Outer join fetching
table per class-hierarchy <many-to-one> <one-to-one> <one-to-many> <many-to-many> s.get(Payment.class, id) from Payment p from Order o join o.payment p supported
table per subclass <many-to-one> <one-to-one> <one-to-many> <many-to-many> s.get(Payment.class, id) from Payment p from Order o join o.payment p supported
table per concrete-class (union-subclass) <many-to-one> <one-to-one> <one-to-many> (for inverse="true" only) <many-to-many> s.get(Payment.class, id) from Payment p from Order o join o.payment p supported
table per concrete class (implicit polymorphism) <any> not supported not supported <many-to-any> s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult() from Payment p not supported not supported

终于差不多结束这一重要的章节

还有很多值得研究的 毕竟实际运用中 接口 抽象类 一对一 多对一 一对多 多对多 等等关系复杂

而且我们要为数据库的性能着想 选择最好的查询性能的映射方式 最节约储存空间的映射方式等等

你可能感兴趣的:(Hibernate)