一、继承实现的三种策略
1.1,单表继承。每棵类继承树使用一个表(table per class hierarchy) ——>本文主要介绍的继承策略
类继承树对应多个类,要把多个类的信息存放在一张表中,必须有某种机制来区分哪些记录是属于哪个类的。这种机制就是,在表中添加一个字段,用这个字段的值来进行区分。用hibernate实现这种策略的时候,有如下步骤:
1,父类用普通的<class>标签定义,在父类中定义一个discriminator,即指定这个区分的字段的名称和类型
如:<discriminator column=”XXX” type=”string”/>
2,子类使用<subclass>标签定义,在定义subclass的时候,需要注意如下几点:Subclass标签的name属性是子类的全路径名;在Subclass标签中,用discriminator-value属性来标明本子类的discriminator字段(用来区分不同类的字段)的值;Subclass标签,既可以被class标签所包含(这种包含关系正是表明了类之间的继承关系),也可以与class标签平行。 当subclass标签的定义与class标签平行的时候,需要在subclass标签中,添加extends属性,里面的值是父类的全路径名称。子类的其它属性,像普通类一样,定义在subclass标签的内部。
PS:唯一字段隔离,也就是所谓的type,通过type确定子类的类型,每条数据中包含了需要的所有信息字段
1.2,每个具体类一个表(table per concrete class)(有一些限制)
这种策略使用joined-subclass标签来定义子类。父类、子类,每个类都对应一张数据库表。在父类对应的数据库表中,实际上会存储所有的记录,包括父类和子类的记录;在子类对应的数据库表中,这个表只定义了子类中所特有的属性映射的字段。子类与父类,通过相同的主键值来关联。实现这种策略的时候,有如下步骤:
1,父类用普通的<class>标签定义即可,父类不再需要定义discriminator字段
2,子类用<joined-subclass>标签定义,在定义joined-subclass的时候,需要注意如下几点:Joined-subclass标签的name属性是子类的全路径名;Joined-subclass标签需要包含一个key标签,这个标签指定了子类和父类之间是通过哪个字段来关联的。如:<key column=”PARENT_KEY_ID”/>,这里的column,实际上就是父类的主键对应的映射字段名称。
3,Joined-subclass标签,既可以被class标签所包含(这种包含关系正是表明了类之间的继承关系),也可以与class标签平行。 当Joined-subclass标签的定义与class标签平行的时候,需要在Joined-subclass标签中,添加extends属性,里面的值是父类的全路径名称。子类的其它属性,像普通类一样,定义在joined-subclass标签的内部。
PS:表隔离,每个表中都只有属于自身对应的属性,如果要获取完整的数据,则需要通过外键联合查询
1.3,具体表继承。每个子类一个表(table per subclass)
这种策略使用union-subclass标签来定义子类。每个子类对应一张表,而且这个表的信息是完备的,即包含了所有从父类继承下来的属性映射的字段(这就是它跟joined-subclass的不同之处,joined-subclass定义的子类的表,只包含子类特有属性映射的字段)。实现这种策略的时候,有如下步骤:
1,父类用普通<class>标签定义即可
2,子类用<union-subclass>标签定义,在定义union-subclass的时候,需要注意如下几点:Union-subclass标签不再需要包含key标签(与joined-subclass不同);Union-subclass标签,既可以被class标签所包含(这种包含关系正是表明了类之间的继承关系),也可以与class标签平行。 当Union-subclass标签的定义与class标签平行的时候,需要在Union-subclass标签中,添加extends属性,里面的值是父类的全路径名称。子类的其它属性,像普通类一样,定义在Union-subclass标签的内部。这个时候,虽然在union-subclass里面定义的只有子类的属性,但是因为它继承了父类,所以,不需要定义其它的属性,在映射到数据库表的时候,依然包含了父类的所有属性的映射字段。
PS:表隔离,与每个具体类不同的是,这种策略只会为每个具体的子类生成表单,而在每个表单里,既包含了父类的属性,又包含了自身的属性。当需要查询一条完整数据的时候,不需要联合外键!
二、实例介绍继承映射
2.1,建立实体类和映射
Animal类:
<span style="font-family:KaiTi_GB2312;font-size:18px;">package com.angel.hibernate; public class Animal { private int id; private String name; private boolean sex; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isSex() { return sex; } public void setSex(boolean sex) { this.sex = sex; } } </span>
Bird类:
<span style="font-family:KaiTi_GB2312;font-size:18px;">package com.angel.hibernate; public class Bird extends Animal { private int height; public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } } </span>
Pig类:
<span style="font-family:KaiTi_GB2312;font-size:18px;">package com.angel.hibernate; public class Pig extends Animal { private int weight; public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } } </span>
映射文件:
<span style="font-family:KaiTi_GB2312;font-size:18px;"><?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.angel.hibernate.Animal" table="t_animal" lazy="false"> <id name="id"> <generator class="native" /> </id> <!-- 加入鉴别标签,且必须放在id后面 --> <discriminator column="type" type="string"/> <property name="name" /> <property name="sex" type="boolean" /> <subclass name="com.angel.hibernate.Pig" discriminator-value="P"> <property name="weight" /> </subclass> <subclass name="com.angel.hibernate.Bird" discriminator-value="B"> <property name="height" /> </subclass> </class> </hibernate-mapping> </span>
2.2,测试类
<span style="font-family:KaiTi_GB2312;font-size:18px;">package com.angel.hibernate; import org.hibernate.Session; import junit.framework.TestCase; public class test_extend extends TestCase { public void testSave() { Session session = null; try { // 创建session对象 session = HibernateUtils.getSession(); // 开启事务 session.beginTransaction(); Pig pig = new Pig(); pig.setName("daddy pig"); pig.setSex(true); pig.setWeight(200); session.save(pig); Bird bird = new Bird(); bird.setName("mummy bird"); bird.setSex(true); bird.setHeight(100); session.save(bird); session.getTransaction().commit(); } catch (Exception e) { e.printStackTrace(); session.getTransaction().rollback(); } finally { HibernateUtils.closeSession(session); } } public void testLoad(){ Session session=null; try { session = HibernateUtils.getSession(); session.beginTransaction(); Animal a = (Animal)session.load(Animal.class, 1); //load默认支持lazy,所以我们看到的是Animal的代理,采用instanceof无法鉴别出真正的类型Pig //如果想要load支持多态查询,则需要将lazy值设置为false if (a instanceof Pig) { System.out.println(a.getName()); }else { System.out.println("不是猪!"); } session.getTransaction().commit(); } catch (Exception e) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } } } </span>
三、多态查询
多态查询:hibernate在加载数据的时候,能够采用instancof鉴别出其真正的类型
在Hibernate中,支持多台查询的有:get、Hql。而对于load,因为其默认支持延迟加载lazy,所以它不支持多态查询,返回的只是一个代理值。如果想要让load支持多态查询,则需要在配置文件中将其lazy属性设置为false。
四、总结
继承映射在实际运用中还是很广泛中,在做这个例子的时候,突然想起来多租户数据隔离的三个策略,其实跟这个差不多。额,说明我脑袋里还是装东西了嘛。然后对于继承映射的三种策略,可以看出的是,单表继承是比较简单而且效率高的,只需要维护一个表。而具体表策略则需要进行外键维护等,相对来说单表继承是一个比较好的选择!