hibernate(2)(二)(一对一,组合映射,继承映射 )
一对一映射:两种情况:
1.外键作为普通列并且添加唯一约束(有外键和唯一约束)
2.外键作为主键(有外键和主键约束)

1.外键,唯一的情况:
实例:
User.java
public class User { private int userId; private String userName; // 用户与身份证信息, 一对一关系 private IdCard idCard;
User.hbm.xml(一对一主键做外键有点奇怪,不用指定外键列名)
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.itcast.c_one2one"> <class name="User" table="t_user"> <id name="userId"> <generator class="native"></generator> </id> <property name="userName" length="20"></property> <!-- 一对一映射: 没有外键方 --> <one-to-one name="idCard" class="IdCard"></one-to-one> </class> </hibernate-mapping>
IdCard.java
// 身份证
public class IdCard {
// 身份证号(主键)
private String cardNum;// 对象唯一表示(Object Identified, OID)
private String place; // 身份证地址
// 身份证与用户,一对一的关系
private User user;
IdCard.hbm.xml(将一个属性映射成外键字段只能用many-to-one(再加上唯一约束,就是一对一)
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.itcast.c_one2one"> <class name="IdCard" table="t_IdCard"> <id name="cardNum"> <generator class="assigned"></generator> </id> <property name="place" length="20"></property> <!-- 一对一映射,有外键方 unique="true" 给外键字段添加唯一约束 --> <many-to-one name="user" unique="true" column="user_id" class="User" cascade="save-update"></many-to-one> </class> </hibernate-mapping>
测试类:
public class App { private static SessionFactory sf; static { sf = new Configuration() .configure() .addClass(IdCard.class) .addClass(User.class) // 测试时候使用 .buildSessionFactory(); } @Test public void getSave() { Session session = sf.openSession(); session.beginTransaction(); // 用户 User user = new User(); user.setUserName("Jack"); // 身份证 IdCard idCard = new IdCard(); idCard.setCardNum("441202XXX"); idCard.setPlace("广州XXX"); // 关系(这样设置关联关系的原因是,配置了级联在IdCard这边) idCard.setUser(user); // ----保存----保存idCard的原因是级联配置在该类 session.save(idCard); session.getTransaction().commit(); session.close(); }
2.外键做主键:
User.java
// 用户 public class User { private int userId; private String userName; // 用户与身份证信息, 一对一关系 private IdCard idCard;
User.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.itcast.c_one2one2"> <class name="User" table="t_user"> <id name="userId"> <generator class="native"></generator> </id> <property name="userName" length="20"></property> <!-- 一对一映射: 没有外键方 --> <one-to-one name="idCard" class="IdCard"></one-to-one> </class> </hibernate-mapping>
IdCard.java
// 身份证 public class IdCard { private int user_id; // 身份证号 private String cardNum; private String place; // 身份证地址 // 身份证与用户,一对一的关系 private User user;
IdCard.hbm.xml(主键和属性对应的对象是关联的)
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="cn.itcast.c_one2one2"> <class name="IdCard" table="t_IdCard"> <id name="user_id"> <!-- id 节点指定的是主键映射, 即user_id是主键 主键生成方式: foreign 即把别的表的主键作为当前表的主键; property (关键字不能修改)指定引用的对象,与下面user对应
对象的全名 cn..User、 对象映射 cn.User.hbm.xml、 table(id) --> <generator class="foreign"> <param name="property">user</param> </generator> </id> <property name="cardNum" length="20"></property> <property name="place" length="20"></property> <!--
上面那个user和下面这个user属性值必须u是对应的 一对一映射,有外键方 (基于主键的映射) constrained="true" 指定在主键上添加外键约束 --> <one-to-one name="user" class="User" constrained="true" cascade="save-update"></one-to-one> </class> </hibernate-mapping>
测试:
private static SessionFactory sf; static { sf = new Configuration() .configure() .addClass(IdCard.class) .addClass(User.class) // 测试时候使用 .buildSessionFactory(); } @Test public void getSave() { Session session = sf.openSession(); session.beginTransaction(); // 用户 User user = new User(); user.setUserName("Jack"); // 身份证 IdCard idCard = new IdCard(); idCard.setCardNum("441202XXX"); idCard.setPlace("广州XXX"); // 关系 idCard.setUser(user); // ----保存---- session.save(idCard); session.getTransaction().commit(); session.close();
组件映射与继承映射
类的关系
组合关系(更像是包含关系好不)
一个类中包含了另外一个类。这2个类中就是组合关系。
需求: 汽车与车轮
继承关系
一个类继承另外一个类。这2个类中就是继承关系。
需求:动物
猫
猴
组件映射:
类组合关系的映射,也叫做组件映射!:组件类和被包含的组件类,共同映射到一张表!
需求: 汽车与车轮
数据库:
T_car:主键id 汽车名称 轮子大小 个数
有时候只整个对象的一部,可以通过组合对象的方式,拼凑一个对象映射到一张表里面:
实例:
Car.java(省略get/set)
public class Car { private int id; private String name; // 车轮 private Wheel wheel;
Wheel.java(省略get/set)
// 车轮 public class Wheel { private int count; private int size;
Car.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <!-- 组件映射 --> <hibernate-mapping package="cn.itcast.d_component"> <class name="Car" table="t_car"> <id name="id"> <generator class="native"></generator> </id> <property name="name" length="20"></property> <!-- 组件映射 --> <component name="wheel"> <property name="size"></property> <property name="count"></property> </component> </class> </hibernate-mapping>
测试类:
private static SessionFactory sf; static { sf = new Configuration() .configure() .addClass(Car.class) .buildSessionFactory(); } @Test public void getSave() { Session session = sf.openSession(); session.beginTransaction(); // 轮子 Wheel wheel = new Wheel(); wheel.setSize(38); wheel.setCount(4); // 汽车 Car car = new Car(); car.setName("BMW"); car.setWheel(wheel); // 保存 session.save(car); session.getTransaction().commit(); session.close(); }
简单继承映射:
特点:简单继承映射,有多少个子类,写多少个映射文件!
Animal.java(省略get/set)
// 动物类 public abstract class Animal { private int id; private String name;
Cat.java(省略get/set)
public class Cat extends Animal{ // 抓老鼠 private String catchMouse;
Cat.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <!-- 简单继承 --> <hibernate-mapping package="cn.itcast.e_extends1"> <class name="Cat" table="t_Cat"> <!-- 简单继承映射: 父类属性直接写 --> <id name="id"> <generator class="native"></generator> </id> <property name="name"></property>
<property name="catchMouse"></property> </class> </hibernate-mapping>
测试类:
public class App { private static SessionFactory sf; static { sf = new Configuration() .configure() .addClass(Cat.class) .buildSessionFactory(); } @Test public void getSave() { Session session = sf.openSession(); session.beginTransaction(); // 保存 // Cat cat = new Cat(); // cat.setName("大花猫"); // cat.setCatchMouse("抓小老鼠"); // session.save(cat); // 获取时候注意:当写hql查询的使用,通过父类查询必须写上类的全名;如果是子类Cat可以不用全名 Query q = session.createQuery("from cn.itcast.e_extends1.Animal"); List<Animal> list = q.list(); System.out.println(list); session.getTransaction().commit(); session.close(); }
数据库效果:

继承映射多种情况:
需求:猫、猴子、动物
1.所有子类映射到一张表 (1张表):
什么情况用?
子类教多,且子类较为简单,即只有个别属性!
好处:因为使用一个映射文件, 减少了映射文件的个数。
缺点:(不符合数据库设计原则:每条记录都有空字段值)
数据库(通过type_字段来指明具体类型的数据)
T_animal (要存储所有的子类信息) “鉴别器”
Id name catchMouse eatBanana type_(区别是哪个子类)
1 大马猴 NULL 吃10个香蕉 猴子
2 大花猫 不抓老鼠 NULL 猫
实例:(都是省略get/set)
Animal.java
// 动物类
public abstract class Animal {
private int id;
private String name;
Cat.java
public class Cat extends Animal{ // 抓老鼠 private String catchMouse;
Monkey.java
public class Monkey extends Animal { // 吃香蕉 private String eatBanana;
Animal.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <!-- 继承映射, 所有的子类都映射到一张表 --> <hibernate-mapping package="cn.itcast.e_extends2"> <class name="Animal" table="t_animal"> <id name="id"> <generator class="native"></generator> </id> <!-- 指定鉴别器字段(区分不同的子类) --> <discriminator column="type_"></discriminator>
<property name="name"></property> <!-- 子类:猫 每个子类都用subclass节点映射 注意:一定要指定鉴别器字段,否则报错! 鉴别器字段:作用是在数据库中区别每一个子类的信息, 就是一个列 discriminator-value="cat_" 指定鉴别器字段,即type_字段的值 如果不指定,默认为当前子类的全名 --> <subclass name="Cat" discriminator-value="cat_"> <property name="catchMouse"></property> </subclass> <!-- 子类:猴子 --> <subclass name="Monkey" discriminator-value="monkey_"> <property name="eatBanana"></property> </subclass> </class> </hibernate-mapping>
测试类:
public class App { private static SessionFactory sf; static { sf = new Configuration() .configure() .addClass(Animal.class) .buildSessionFactory(); } @Test public void getSave() { Session session = sf.openSession(); session.beginTransaction(); // 保存 Cat cat = new Cat(); cat.setName("大花猫"); cat.setCatchMouse("抓小老鼠"); Monkey m = new Monkey(); m.setName("猴子"); m.setEatBanana("吃10个香蕉"); // 保存 session.save(cat); session.save(m); session.getTransaction().commit(); session.close();
数据库效果:

总结:
写法较为简单:所有子类用一个映射文件,且映射到一张表!
但数据库设计不合理!
(不推荐用。)
2.每个类映射一张表(3张表)
实例(省略get/set)
Animal.java
// 动物类 public abstract class Animal { private int id; private String name;
Cat.java
public class Cat extends Animal{ // 抓老鼠 private String catchMouse;
Monkey.java
public class Monkey extends Animal { // 吃香蕉 private String eatBanana;
Animal.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <!-- 继承映射, 每个类对应一张表(父类也对应表) --> <hibernate-mapping package="cn.itcast.e_extends3"> <class name="Animal" table="t_animal"> <id name="id"> <generator class="native"></generator> </id> <property name="name"></property> <!-- 子类:猫 t_cat key 指定_cat表的外键字段 --> <joined-subclass name="Cat" table="t_cat"> <key column="t_animal_id"></key> <property name="catchMouse"></property> </joined-subclass> <!-- 子类:猴子 t_monkey --> <joined-subclass name="Monkey" table="t_monkey"> <key column="t_animal_id"></key> <property name="eatBanana"></property> </joined-subclass> </class> </hibernate-mapping>
测试类:
public class App { private static SessionFactory sf; static { sf = new Configuration() .configure() .addClass(Animal.class) .buildSessionFactory(); } @Test public void getSave() { Session session = sf.openSession(); session.beginTransaction(); // 保存 Cat cat = new Cat(); cat.setName("大花猫"); cat.setCatchMouse("抓小老鼠"); Monkey m = new Monkey(); m.setName("猴子"); m.setEatBanana("吃10个香蕉"); // 保存 session.save(cat); session.save(m); session.getTransaction().commit(); session.close(); }
数据库效果:对应三个表:T_anmal (存储父类信息),T_cat (引用父类的主键),T_monkey (引用父类的主键)



总结:
一个映射文件,配置所有的子类; 子类父类都对应表;
缺点:
表结构比较复杂,插入一条子类信息,需要用2条sql: 往父类插入、往子类插入!
3.(推荐)每个子类映射一张表, 父类不对应表(2张表)
1.同union-subclass 组合的配置映射表,特别注意表的主键不能是自增长,可以用uuid 2.默认父类也是生成表的,需要设置abstract=true
数据库:
T_cat : Id name catchMounse
T_monkey:Id name eatBanana
实例:
Animal.java
public class Animal { private String id; private String name;
Cat.java
public class Cat extends Animal{ // 抓老鼠 private String catchMouse;
Monkey.java
public class Monkey extends Animal { // 吃香蕉 private String eatBanana;
Animal.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <!-- 继承映射, 每个类对应一张表(父类不对应表) --> <hibernate-mapping package="cn.itcast.e_extends4"> <!-- abstract="true" 指定实体类对象不对应表,即在数据库段不生成表 --> <class name="Animal" abstract="true"> <!-- 如果用union-subclass节点,主键生成策略不能为自增长! --> <id name="id"> <generator class="uuid"></generator> </id> <property name="name"></property> <!-- 子类:猫 t_cat union-subclass table 指定为表名, 表的主键即为id列 --> <union-subclass name="Cat" table="t_cat"> <property name="catchMouse"></property> </union-subclass> <!-- 子类:猴子 t_monkey --> <union-subclass name="Monkey" table="t_monkey"> <property name="eatBanana"></property> </union-subclass> </class> </hibernate-mapping>
测试类:
private static SessionFactory sf; static { sf = new Configuration() .configure() .addClass(Animal.class) .buildSessionFactory(); } @Test public void getSave() { Session session = sf.openSession(); session.beginTransaction(); // 保存 Cat cat = new Cat(); cat.setName("大花猫"); cat.setCatchMouse("抓小老鼠"); Monkey m = new Monkey(); m.setName("猴子"); m.setEatBanana("吃10个香蕉"); // 保存 session.save(cat); session.save(m); session.getTransaction().commit(); session.close(); }
总结:
所有的子类都写到一个映射文件;
父类不对应表; 每个子类对应一张表

浙公网安备 33010602011771号