业务需求:
某网上影像店,专门经营图书和DVD。图书和DVD需要管理的信息非常类似,都包括产品编号、名称、生产厂家;
但是二者也存在差别,图书需要管理页数信息,而DVD需要管理区域代码信息。
分析该需求,得到两个对象,即图书和DVD,因此应该创建两个实体类,即Book类和DVD类。
由于Book类和DVD类存在相同的属性,因此应该抽象出一个父类,为Product产品类,Book和DVD类基于Product类进行扩展。
创建实体类,并分别扩展两个子类,Book和DVD
Product类
package vo;
public class Product {
private Integer id;
private String name;
private String manufacturer;
public Product() {
super();
}
public Product(Integer id) {
super();
this.id = id;
}
public Product(Integer id, String name, String manufacturer) {
super();
this.id = id;
this.name = name;
this.manufacturer = manufacturer;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getManufacturer() {
return manufacturer;
}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
}
Book类
package vo;
public class Book extends Product{
private Integer pagecount;
public Book() {
// TODO Auto-generated constructor stub
}
public Book(Integer id, String name, String manufacturer,Integer pagecount) {
super(id, name, manufacturer);
this.pagecount=pagecount;
}
public Integer getPagecount() {
return pagecount;
}
public void setPagecount(Integer pagecount) {
this.pagecount = pagecount;
}
}
DVD类
package vo;
public class DVD extends Product{
private String regioncode;
public DVD() {
// TODO Auto-generated constructor stub
}
public DVD(Integer id, String name, String manufacturer,String regioncode) {
super(id, name, manufacturer);
this.regioncode=regioncode;
}
public String getRegioncode() {
return regioncode;
}
public void setRegioncode(String regioncode) {
this.regioncode = regioncode;
}
}
创建表结构
create table product(
id int not null primary key,
name varchar(50),
manufacturer varchar(50)
);
create table book(
id int not null primary key,
pagecount int,
foreign key(id) references product(id)
);
create table dvd(
id int not null primary key,
regioncode varchar(20),
foreign key(id) references product(id)
)
上述的SQL语句将创建3张表,其中product是主表,存储所有产品的共有字段;
book和dvd是从表,通过主键参考product表,扩展了book和dvd的独有字段。
基于这样的设计,只需要创建一个与父类同名的hbm.xml文件即可,即Product.hbm.xml,子类采用<joined-subclass>属性进行配置。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="vo.Product" table="product" catalog="mldn">
<id name="id" type="java.lang.Integer" >
<column name="id" />
<generator class="assigned"/>
</id>
<property name="name" type="java.lang.String">
<column name="name" length ="50" />
</property>
<property name="manufacturer" type="java.lang.String">
<column name="manufacturer" length="50"/>
</property>
<joined-subclass name="vo.Book" table="book" catalog="mldn">
<key column="id"></key>
<property name="pagecount" column ="pagecount"></property>
</joined-subclass>
<joined-subclass name="vo.DVD" table="dvd" catalog="mldn">
<key column="id"></key>
<property name="regioncode" column ="regioncode"></property>
</joined-subclass>
</class>
</hibernate-mapping>
测试类:
TestTPS
public static void main(String[] args) {
Book book = new Book(10,"SSH","KuangJia",300);
DVD dvd = new DVD(20,"Java Web","coding","Asia");
Session session = HibernateSessionFactory.getSession();
Transaction tran = session.beginTransaction();
session.save(book);
session.save(dvd);
tran.commit();
}
执行成功,插入数据,显示结果:
使用如下方式测试:
public static void main(String[] args) {
Session session = HibernateSessionFactory.getSession();
String hql="from Book";
List<Book> list = session.createQuery(hql).list();
for(Book b:list){
System.out.println(b.getId()+" "+b.getName()+" "+b.getManufacturer()+" "+b.getPagecount());
}
}
上述代码中,使用from Book 语句查询Book实例,因为Book作为Product的<joined-subclass>存在,
所以运行上述代码,将连接查询product和book表,得到Book实例的所有属性。
创建表:
create table product(
id int not null primary key,
category varchar(10),
name varchar(50),
manufacture varchar(50),
pagecount int,
regioncode varchar(20)
);
上述SQL将之创建一张表product,将book和dvd记录存储到一张表中,使用category字段作为区分值字段,区别book和dvd。
修改配置文件Product.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="vo.Product" table="product" catalog="mldn">
<id name="id" type="java.lang.Integer" >
<column name="id" />
<generator class="assigned"/>
</id>
<discriminator column="category" type="java.lang.String"></discriminator>
<property name="name" type="java.lang.String">
<column name="name" length ="50" />
</property>
<property name="manufacturer" type="java.lang.String">
<column name="manufacturer" length="50"/>
</property>
<subclass name="vo.Book" discriminator-value="1">
<property name="pagecount" type="java.lang.Integer">
<column name="pagecount"/>
</property>
</subclass>
<subclass name="vo.DVD" discriminator-value="2">
<property name="regioncode" type="java.lang.String">
<column name="regioncode"/>
</property>
</subclass>
</class>
</hibernate-mapping>
上述hbm.xml 文件中的关键点是<discriminator>和<subclass>,
使用<discriminator column="category" type="java.lang.String"></discriminator>将category字段指定为区分值字段,
进一步使用<subclass name="vo.Book" discriminator-value="1">配置了子类Book的区分值为1,
并在<subclass>标签体中配置了子类扩展的属性pagecount的映射关系。
测试类:TestTPH.java
public static void main(String[] args) {
Book book = new Book(10,"SSH","KuangJia",300);
DVD dvd = new DVD(20,"Java Web","coding","Asia");
Session session = HibernateSessionFactory.getSession();
Transaction tran = session.beginTransaction();
session.save(book);
session.save(dvd);
tran.commit();
}
运行结果:
使用如下代码进行测试:
public static void main(String[] args) {
Session session = HibernateSessionFactory.getSession();
String hql="from Book";
List<Book> list = session.createQuery(hql).list();
for(Book b:list){
System.out.println(b.getId()+" "+b.getName()+" "+b.getManufacturer()+" "+b.getPagecount());
}
}
上述代码中,使用from Book语句查询所有的Book实例,由于Book类的区分值是1,所有Hibernate将查询product表中category字段值为1的所有记录,并封装成Book实例返回
创建表:
create table book(
<span style="white-space:pre"> </span>id int not null primary key,
<span style="white-space:pre"> </span>name varchar(50),
<span style="white-space:pre"> </span>manufacturer varchar(50),
<span style="white-space:pre"> </span>pagecount int
);
create table dvd(
<span style="white-space:pre"> </span>id int not null primary key,
<span style="white-space:pre"> </span>name varchar(50),
<span style="white-space:pre"> </span>manufacturer varchar(50),
<span style="white-space:pre"> </span>regioncode varchar(30)
)
上述SQL语句,将创建两张毫无关系的表book和dvd,分别存储book和dvd的记录。
然而,实体类层次上依然应该采用继承关系,因为book和dvd中存在相同的字段id、name、manufacturer。
基于这样的设计,只提供与父类Product对应的hbm.xml文件即可,即Product.hbm.xml。
该文件中将Product类使用abstract=true设置为抽象的,因为Product类没有对应的表与其映射。
进一步使用<union-subclass>将子类扩展的属性进行配置。
Product.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="vo.Product" table="product" abstract ="true" catalog="mldn">
<id name="id" type="java.lang.Integer" >
<column name="id" />
<generator class="assigned"/>
</id>
<property name="name" type="java.lang.String">
<column name="name" length ="50" />
</property>
<property name="manufacturer" type="java.lang.String">
<column name="manufacturer" length="50"/>
</property>
<union-subclass name="vo.Book" table="book">
<property name="pagecount" type="java.lang.Integer">
<column name="pagecount"></column>
</property>
</union-subclass>
<union-subclass name="vo.DVD" table="dvd">
<property name="regioncode" type="java.lang.String">
<column name="regioncode" length="20"></column>
</property>
</union-subclass>
</class>
</hibernate-mapping>
上述hbm.xml文件中的关键点是<union-subclass>元素的配置,
使用name配置了子类的名称,使用table配置了子类映射的表,并通过<property>标签配置了子类中扩展属性的映射关系。
测试类
TestTPC.java
public static void main(String[] args) {
Book book = new Book(10,"SSH","KuangJia",300);
DVD dvd = new DVD(20,"Java Web","coding","Asia");
Session session = HibernateSessionFactory.getSession();
Transaction tran = session.beginTransaction();
session.save(book);
session.save(dvd);
tran.commit();
}
显示结果:
使用如下代码测试:
public static void main(String[] args) {
Session session = HibernateSessionFactory.getSession();
String hql="from Book";
List<Book> list = session.createQuery(hql).list();
for(Book b:list){
System.out.println(b.getId()+" "+b.getName()+" "+b.getManufacturer()+" "+b.getPagecount());
}
}
运行上述代码,Hibernate将从book表中查询所有记录,封装为Book实例返回,
结果如下:
HQL中的查询语句支持多态查询,如下:
from Product;
上述语句不仅能返回Product的实例,还将返回Product子类的实例。
使用TPC的实例测试多态查询的使用,代码如下:
public static void main(String[] args) {
Session session = HibernateSessionFactory.getSession();
String hql = "from Product";
List<Product> list = session.createQuery(hql).list();
for(Product p:list){
if(p instanceof Book){
Book b = (Book)p;
System.out.println(b.getId()+" "+b.getName()+" "+b.getManufacturer()+" "+b.getPagecount());
}
if(p instanceof DVD){
DVD d= (DVD)p;
System.out.println(d.getId()+" "+d.getName()+" "+d.getManufacturer()+" "+d.getRegioncode());
}
}
}
上述代码中,使用from Product进行查询,将返回Product类所有子类的实例,也就是讲查询到Book和DVD的实例,即为多态查询。
执行后,显示结果: