在前面几篇文章中,分别学习了hibernate的关联映射,基本的一对一,一对多,多对
多等都有学习,今天我会给大家带来hibernate中关于"组合映射"和"继承映射"的学习。
## 组合映射##
为什么要学习组合映射,它和一般的映射有什么区别吗??我们先来看一下下面这种情况:
上图,可以看出,user包含了username,address包含了homeaddress,contact包含了qq,phone,这些字段,由于这些属性没有什么必然联系,我们可以将这些字段放入到一个Tuser表里,因为这样的话,对于数据库的维护,已经性能都是有很大的提升。我们可以将Address当做User的属性,将Contact当做Address的属性。这样将所有的属性,只映射到一个Tuser表里,就是组合映射。下面分别来看这三个实体类以及怎么根据这些实体类实现组合映射
Contact.java
public class Contact implements Serializable{
private String qq;
private String phone;
public String getQq() {
return qq;
}
public void setQq(String qq) {
this.qq = qq;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public Contact(String qq, String phone) {
super();
this.qq = qq;
this.phone = phone;
}
public Contact() {
super();
}
}
Address.java
public class Address implements Serializable {
private String homeAddress;
private Contact contact = null;
public Contact getContact() {
return contact;
}
public void setContact(Contact contact) {
this.contact = contact;
}
public String getHomeAddress() {
return homeAddress;
}
public void setHomeAddress(String homeAddress) {
this.homeAddress = homeAddress;
}
public Address(String homeAddress, Contact contact) {
super();
this.homeAddress = homeAddress;
this.contact = contact;
}
public Address() {
super();
}
}
UserInfo.java
package com.mydb.entity;
import java.io.Serializable;
public class UserInfo implements Serializable {
private int userId;
private String userName;
private String userPass;
private Address address = null;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPass() {
return userPass;
}
public void setUserPass(String userPass) {
this.userPass = userPass;
}
@Override
public String toString() {
return "UserInfo [userId=" + userId + ", userName=" + userName
+ ", userPass=" + userPass + "]";
}
}
可以看到这里,我是将contact变成Address的属性,再将address变成User的属性。此时,UserInfo对应的映射文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- name="全类名" table="表名" -->
<class name="com.mydb.entity.UserInfo" table="userinfo">
<!-- name="属性名" column="主键"-->
<id name="userId" column="uid">
<!-- 主键的生成策略:native(主键自增),assigned(指派) -->
<generator class="increment"></generator>
</id>
<component name="address">
<property name="homeAddress"></property>
<component name="contact">
<property name="phone"></property>
<property name="qq"></property>
</component>
</component>
<!-- name="属性名" column="字段名" -->
<property name="userName" column="uname"></property>
<property name="userPass" column="upass"></property>
</class>
</hibernate-mapping>
可以看到映射到的userinfo表的主键是自增的,对于组合映射,将address映射到user中,将contact映射到address中,是这样做的:
<component name="address">
<property name="homeAddress"></property>
<component name="contact">
<property name="phone"></property>
<property name="qq"></property>
</component>
</component>
我们通过component标签来实现组合映射。接下来编写测试类:
//解析hibernate.cfg.xml文件
Configuration cfg = new Configuration().configure();
//创建SessionFactory(创建连接池)
SessionFactory factory = cfg.buildSessionFactory();
//创建session
Session session = factory.openSession();
UserInfo userInfo = new UserInfo();
userInfo.setUserName("gaga");
userInfo.setUserPass("123456");
userInfo.setAddress(new Address("深圳",new Contact("456754433","12367544554")));
//创建以及开启事物对象
Transaction transaction = null;
try {
transaction = session.beginTransaction();
session.save(userInfo);
transaction.commit();
} catch (HibernateException e) {
if (transaction != null) {
transaction.rollback();
}
e.printStackTrace();
} finally {
if (session != null) {
session.close();
}
}
这里,我存储了一个userinfo到映射的userinfo表里,此时看下userinfo表都有哪些字段:
此时我们就将address类和contact以及userinfo类中的属性都映射到一个表里边了。
继承映射
为什么要有继承映射,首先我们咋面向对象中,经常会用到继承这种方式来实现代码复用,那么如果是一个实体类,有继承关系,但是在数据库表中是没有这样的继承关系的,在数据库当中,只有关联,并且继承是有多态的,那么当我们从数据库当中,查找出来的数据,如何确保封装成正确的格式呢??由此继承映射就应运而生吧。
下面看下我们的实体类:
Animal.java
public class Animal implements Serializable {
private int animalId;
public int getAnimalId() {
return animalId;
}
public void setAnimalId(int animalId) {
this.animalId = animalId;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Pig.java
public class Pig extends Animal implements Serializable {
private int weight;//重量
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
}
Dog.java
public class Dog extends Animal implements Serializable {
private String dark;
public String getDark() {
return dark;
}
public void setDark(String dark) {
this.dark = dark;
}
}
可以看到dog和pig这两个类,有一个共同的属性name,又各自有自己的属性。映射文件如下:
<hibernate-mapping package="com.mydb.entity.jicheng">
<!-- name="全类名" table="表名" -->
<class name="Animal" table="t_animal">
<!-- name="属性名" column="主键"-->
<id name="animalId">
<!-- 主键的生成策略:native(主键自增),assigned(指派) -->
<generator class="increment"></generator>
</id>
<discriminator column="discriminator" type="string"></discriminator>
<property name="name"></property>
<subclass name="Dog" discriminator-value="D">
<property name="dark"></property>
</subclass>
<subclass name="Pig" discriminator-value="P">
<property name="weight"></property>
</subclass>
</class>
</hibernate-mapping>
说明一下,subclass标签指定继承自animal的子类,discriminator是用来正确封装我们查询的数据到指定子类,discriminator是在animal表中增加一个列,用来存储每一行是pig类型还是dog类型的数据。可以看到如果是Dog类型的数据,则该列的值是”D”,如果是pig类型的数据,那么该列的值是”P”.
编写测试类:
//解析hibernate.cfg.xml文件
Configuration cfg = new Configuration().configure();
//创建SessionFactory(创建连接池)
SessionFactory factory = cfg.buildSessionFactory();
//创建session
Session session = factory.openSession();
Dog dog = new Dog();
dog.setName("哈士奇");
dog.setDark("wangwang");
Pig pig = new Pig();
pig.setName("胖猪");
pig.setWeight(300);
//创建以及开启事物对象
Transaction transaction = null;
try {
transaction = session.beginTransaction();
session.save(pig);
session.save(dog);
transaction.commit();
} catch (HibernateException e) {
if (transaction != null) {
transaction.rollback();
}
e.printStackTrace();
} finally {
if (session != null) {
session.close();
}
}
上面的测试方法分别给t_animal表中存储了一个Dog和Pig这两个类型的数据,下面看看数据库的内容:
可以看到在t_animal表格当中已经增加了一列discirminator,用来区分该类存储的类型的具体子类。
此时当我再次通过session查询的时候,就会查询出正确的类型,可以看到表格里边id=1的行存储了一个Pig类型的数据:
Pig pig = (Pig) session.get(Animal.class,1);
System.out.println(pig.getName());
此时类型将会正确的转换,但是如果此时强制将Pig转换成Dog类型,hibernate将会抛出下面的异常:
基于接口的隐式多态查询
hibernate同样提供查询实现某一个接口的所有具体的实体类,我这里新建一个接口:
public interface AnimalInterface {
}
然后让Pig和Dog这两个类同时实现该接口,接下来利用hql来实现基于接口的隐式多态查询:
Session session = factory.openSession();
String hql = "from com.mydb.entity.jicheng.AnimalInterface";
Query query = session.createQuery(hql);
List list = query.list();
for (Object object : list) {
if (object instanceof Dog) {
System.out.println(((Dog) object).getName()+"叫声:"+((Dog) object).getDark());
} else if (object instanceof Pig) {
System.out.println(((Pig) object).getName()+"体重是:"+((Pig) object).getWeight());
}
}
继承映射(每个子类一张表)
接下来给大家介绍第二种继承映射,上面继承映射的实现方式是将所有的子类放入到一张表中来存储,然后通过discriminator标签来实现多台查询时候的正确转型,下面带大家实现第二种继承映射即:每一个子类映射一张表,同样是animal,dog,pig这三个类,只需要这样配置映射文件即可:
<hibernate-mapping package="com.mydb.entity.jicheng">
<!-- name="全类名" table="表名" -->
<class name="Animal" table="t_animal">
<!-- name="属性名" column="主键" -->
<id name="animalId">
<!-- 主键的生成策略:native(主键自增),assigned(指派) -->
<generator class="increment"></generator>
</id>
<joined-subclass name="Dog" table="t_dog">
<key column="aid"></key>
<property name="dark"></property>
</joined-subclass>
<joined-subclass name="Pig" table="t_pig">
<key column="aid"></key>
<property name="weight"></property>
</joined-subclass>
</class>
</hibernate-mapping>
joined-subclass标签,name属性填写子类的类名,table填写子类映射到的表名称,<key column="aid"></key>
表示关联animal表的外键。此时会在数据库中生成”t_animal”,”t_dog”,”t_pig”这三个表:
t_animal
t_dog
t_pig
hibernate中set集合映射
在之前一对一,和多对多的映射中,我们利用set集合来映射,但是如果是基本的类型,应该怎么办呢??我如我一个User有很多张图片:比如
我现在给userinfo中增加一个
private Set<String>photoes = new HashSet();
并且设置get和set方法,此时配置文件需要这么写:
<set name="photoes" table="t_photo">
<key column="photoid"></key>
<element column="photoAddrs" type="string"></element>
</set>
table=”t_photo”表示photo将单独映射到一张表里,column=”photoid”表示所映射到的”t_photo”表所对应userinfo表的外键。
可以看到此时photo表中的photoid是userinfo表中的外键。
那么问题有来了,如果是对象类型的set集合呢??比如我在userinfo类中在增加这样一个set集合:
private Set<Contact>contacts = new HashSet<>();
并提供get和set方法。此时需要这样配置:
<set name="contacts" table="t_contacts">
<key column="contact_id"></key>
<composite-element class="com.mydb.entity.Contact">
<property name="phone"></property>
<property name="qq"></property>
</composite-element>
</set>
ok,关于hibernate关联映射的学习就到这里了,希望大家能够喜欢。