接续上一个主题,我们来看看继承关系映像的第三种方式,我们给予父类别与每个子类别一个表格,与第一个方法不同的是,父类别映像的表格与子类别映像的表 格共享相同的主键值,父类别表格只记录本身的属性,如果要查询的是子类别,则透过外键参考从父类别表格中取得继承而来的属性数据。
直接以图片说明会比较容易理解,我们使用前一个主题中的User、PowerUser与GuestUser类别作说明,类别继承图如下:
其映像至数据库表格的关系如下:
其中POWER_USER与GUEST_USER表格的主键值将与USER表格的主键值相同,POWER_USER_ID与GUEST_USER_ID作为一个外键参考,以取得父类别映像表格的NAME与PASSWORD数据。
在映射文件中要实现这种映像,我们使用<joined-subclass>卷标,并使用<key>卷标指定子类别表格与父类别表格共享的主键值,映像文件的撰写方式如下:
直接以图片说明会比较容易理解,我们使用前一个主题中的User、PowerUser与GuestUser类别作说明,类别继承图如下:
其映像至数据库表格的关系如下:
其中POWER_USER与GUEST_USER表格的主键值将与USER表格的主键值相同,POWER_USER_ID与GUEST_USER_ID作为一个外键参考,以取得父类别映像表格的NAME与PASSWORD数据。
在映射文件中要实现这种映像,我们使用<joined-subclass>卷标,并使用<key>卷标指定子类别表格与父类别表格共享的主键值,映像文件的撰写方式如下:
User.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="onlyfun.caterpillar.User" table="USER">
<id name="id" type="string" unsaved-value="null">
<column name="USER_ID"/>
<generator class="uuid.hex"/>
</id>
<property name="name" type="string" not-null="true">
<column name="NAME" length="16" not-null="true"/>
</property>
<property name="password" type="string" not-null="true">
<column name="PASSWORD" length="16" not-null="true"/>
</property>
<joined-subclass name="onlyfun.caterpillar.PowerUser" table="POWER_USER">
<key column="POWER_USER_ID"/>
<property name="level" type="int" column="POWER_USER_LEVEL"/>
<property name="otherOfPower" type="string" column="POWER_OTHER"/>
</joined-subclass>
<joined-subclass name="onlyfun.caterpillar.GuestUser" table="GUEST_USER">
<key column="GUEST_USER_ID"/>
<property name="otherOfGuest" type="string" column="GUEST_OTHER"/>
</joined-subclass>
</class>
</hibernate-mapping>
您可以自行建立数据库与表格,当然使用SchemaExportTask帮您自动建立表格是更方便的,下面是SchemaExportTask自动建立表格时所使用的SQL:
[schemaexport] alter table GUEST_USER drop constraint FKB62739F25ED19688;
[schemaexport] alter table POWER_USER drop constraint FK38F5D2E586CA74B5;
[schemaexport] drop table if exists USER;
[schemaexport] drop table if exists GUEST_USER;
[schemaexport] drop table if exists POWER_USER;
[schemaexport] create table USER (
[schemaexport] USER_ID varchar(255) not null,
[schemaexport] NAME varchar(16) not null,
[schemaexport] PASSWORD varchar(16) not null,
[schemaexport] primary key (USER_ID)
[schemaexport] );
[schemaexport] create table GUEST_USER (
[schemaexport] GUEST_USER_ID varchar(255) not null,
[schemaexport] GUEST_OTHER varchar(255),
[schemaexport] primary key (GUEST_USER_ID)
[schemaexport] );
[schemaexport] create table POWER_USER (
[schemaexport] POWER_USER_ID varchar(255) not null,
[schemaexport] POWER_USER_LEVEL integer,
[schemaexport] POWER_OTHER varchar(255),
[schemaexport] primary key (POWER_USER_ID)
[schemaexport] );
[schemaexport] alter table GUEST_USER add index FKB62739F25ED19688 (GUEST_USER_ID),
add constraint FKB62739F25ED19688 foreign key (GUEST_USER_ID) references USER (USER_ID);
[schemaexport] alter table POWER_USER add index FK38F5D2E586CA74B5 (POWER_USER_ID),
add constraint FK38F5D2E586CA74B5 foreign key (POWER_USER_ID) references USER (USER_ID);
至于在Java程序的撰写方面,您可以直接使用前一个主题中所写的测试程序(您可以看到,Hibernate将程序撰写与数据库处理的细节分开了),假设我们使用上一个主题的测试程序新增了两笔数据,则数据库表格的结果如下:
mysql> select * from user;
+----------------------------------+-------------+----------+
| USER_ID | NAME | PASSWORD |
+----------------------------------+-------------+----------+
| 297e3dbdff0af72900ff0af72d4b0001 | caterpillar | 123456 |
| 297e3dbdff0af72900ff0af72d4b0002 | momor | 654321 |
+----------------------------------+-------------+----------+
2 rows in set (0.05 sec)
mysql> select * from power_user;
+----------------------------------+------------------+-------------------+
| POWER_USER_ID | POWER_USER_LEVEL | POWER_OTHER |
+----------------------------------+------------------+-------------------+
| 297e3dbdff0af72900ff0af72d4b0001 | 1 | PowerUser's field |
+----------------------------------+------------------+-------------------+
1 row in set (0.00 sec)
mysql> select * from guest_user;
+----------------------------------+-------------------+
| GUEST_USER_ID | GUEST_OTHER |
+----------------------------------+-------------------+
| 297e3dbdff0af72900ff0af72d4b0002 | GuestUser's field |
+----------------------------------+-------------------+
1 row in set (0.00 sec)
了解一下储存数据至数据库时所使用的SQL语句有助于您了解底层的运作方式,新增两笔数据的SQL如下:
Hibernate: insert into USER (NAME, PASSWORD, USER_ID) values (?, ?, ?)
Hibernate: insert into POWER_USER (POWER_USER_LEVEL, POWER_OTHER, POWER_USER_ID) values (?, ?, ?)
Hibernate: insert into USER (NAME, PASSWORD, USER_ID) values (?, ?, ?)
Hibernate: insert into GUEST_USER (GUEST_OTHER, GUEST_USER_ID) values (?, ?)
有兴趣的话,也可以看一下查询数据时的SQL语句,我们可以看到实际上使用inner join来查询数据,以下是查询PowerUser所使用的SQL:
select poweruser0_.POWER_USER_ID as USER_ID,
poweruser0_.POWER_USER_LEVEL as POWER_US2_1_,
poweruser0_.POWER_OTHER as POWER_OT3_1_,
poweruser0__1_.NAME as NAME0_,
poweruser0__1_.PASSWORD as PASSWORD0_
from POWER_USER poweruser0_
inner join USER poweruser0__1_ on poweruser0_.POWER_USER_ID=poweruser0__1_.USER_ID