Hibernate的缓存和几种特殊的映射(09)

Hibernate的缓存和几种特殊的映射

一. 理解对象的状态

Hibernate中对象的几种状态: 临时状态,持久化状态,游离状态

临时状态: 直接new关键字创建的普通对象,不在session下管理,数据库中没有对应的记录,即普通的一个实例.

持久化状态: 当调用session的save/saveOrUpdate/get/load/list等方法的时候,对象就是持久化状态,处于持久化状态的对象,当对对象的属性进行修改的时候,会反映到数据库中.
特点: 处于session的管理中,数据库中有对应的记录.

游离状态: 不处于session的管理,数据库有对应的记录,session关闭后,对象的状态会不存在.

二. 一级缓存

使用缓存的目的: 减少对数据库的访问次数,提升Hibernate的执行效率,Hibernate中的缓存分为一级缓存和二级缓存.

一级缓存基本概念:
* Hibernate的一级缓存: 也叫做session的缓存,可以在session范围内减少数据库的访问次数,只在session范围内有用,session关闭,一级缓存失效.
* 当调用session的save/saveOrUpdate/get/load/list/iterator 等方法的时候,都会把对象放入session的缓存中.
* session的缓存由Hibernate维护,用户不能操作缓存的内容,如果需要操作缓存的内容,必须通过Hibernate提供的evict/clear方法操作.

特点: 只在当前session有效,作用时间断,效果不是特别明显.在短时间内多处操作数据库,效果比较明显.

缓存相关的几个方法:
    session.flush() : 刷新缓存,让一级缓存与数据库同步
    session.evict(obj) 清空一级缓存中指定的对象
    session.clear() 清空一级缓存中的所有对象
    
    使用场景: 在批量操作的时候
        session.flush(); //先与数据库同步
        session.clear(); //再清空一级缓存内容


注意点:
    不同的session不会共享缓存数据. 比如Dept dept = session1.get(Dept.class,1);
    
    session2.update(dept);//dept放入session2的缓存.
    dept.setDeptName("name");//如果生成2条update SQL语句,说明不同的session使用不同的缓存区,不能共享数据.
    
    list和iterator的区别:
    list():会一次性把所有记录都查询出来,会放入缓存,但是不会从缓存中获取数据.
    
    iterator: N+1次查询,N表示总的记录数,即会先发送一条语句查询所有的记录的主键(1次)
    再根据每一个主键去数据库查询(N)
    会把数据放入缓存,也会从缓存中取数据.

  • Hibernate的一级缓存测试:

Javabean设计

Dept.java

package com.mrq.domain;

public class Dept {
    private Integer deptId;
    private String deptName;
    public Integer getDeptId() {
        return deptId;
    }
    public void setDeptId(Integer deptId) {
        this.deptId = deptId;
    }
    public String getDeptName() {
        return deptName;
    }
    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }
    @Override
    public String toString() {
        return "Dept [deptId=" + deptId + ", deptName=" + deptName + "]";
    }
}

Dept.hbm.xml配置

<?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 package="com.mrq.domain">
    <class name="Dept" table="t_dept2">
        <id name="deptId">
            <generator class="native"></generator>
        </id>
        
        <property name="deptName" type="string"></property>
        
    </class>
</hibernate-mapping>

  • 缓存测试
    @Test
    public void testCache() throws Exception{
        Session session = sf.openSession();
        session.beginTransaction();
        
        Dept dept = null;
        dept = (Dept)session.get(Dept.class, 1);//向数据库查询
        System.out.println(dept);
        dept = (Dept)session.get(Dept.class, 1);//不查询,使用session缓存
        
        session.getTransaction().commit();
        session.close();
    }

运行结果类似:

Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=1, deptName=人事部]

从运行的结果分析:这里使用了两个get方法,但是只进行了一次SQL查询,说明第二次的get是从缓存中获取

  • flush方法同步数据
    @Test
    public void flush() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();
        
        Dept dept = null;
        dept = (Dept) session.get(Dept.class, 5);
        dept.setDeptName("行政部");
        // 刷新数据,和数据库同步
        session.flush();
        
        dept.setDeptName("研发部");
        
        session.getTransaction().commit();  // 相当于session.flush();
        session.close();
    }

结果如下:

Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Hibernate: update t_dept2 set deptName=? where deptId=?
Hibernate: update t_dept2 set deptName=? where deptId=?

进行了2次update,一次是flush,一次是commit操作.

  • clear清除缓存
    @Test
    public void clear() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();
        
        Dept dept = null;
        //放入缓存
        dept = (Dept) session.get(Dept.class, 5);
        // 清除缓存对象 
        //session.clear(); // 全部清除
        session.evict(dept);// 清除特定对象       
        dept = (Dept) session.get(Dept.class, 5);//重新从数据库获取对象

        session.getTransaction().commit();  // session.flush();
        session.close();
    }

结果:

Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
清除缓存后
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?

进行了2次select操作,一次是在清除缓存之后

  • 不同的session不共享数据
@Test
    public void sessionTest() throws Exception {
        Session session1 = sf.openSession();
        session1.beginTransaction();
        Session session2 = sf.openSession();
        session2.beginTransaction();
        
        // dept放入session1中
        Dept dept = (Dept) session1.get(Dept.class, 1);
        // dept放入session2中
        session2.update(dept);
        
        //
        dept.setDeptName("人事部");

        session1.getTransaction().commit();  // session1.flush();
        session1.close();
        session2.getTransaction().commit();  // session2.flush();
        session2.close();
    }

运行结果:

Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Hibernate: update t_dept2 set deptName=? where deptId=?
Hibernate: update t_dept2 set deptName=? where deptId=?

生成2条update SQL语句,说明不同的session使用不同的缓存区,不能共享数据.

2.1 list和iterator的缓存

  • list操作
    @Test
    public void list() {
        Session session = sf.openSession();
        session.beginTransaction();
        
        //HQL查询
        Query query = session.createQuery("from Dept");
        List<Dept> list = query.list();
        System.out.println(list);
        
        session.getTransaction().commit();
        session.close();
    }

运行结果:

Hibernate: select dept0_.deptId as deptId0_, dept0_.deptName as deptName0_ from t_dept2 dept0_
[Dept [deptId=1, deptName=事业部], Dept [deptId=2, deptName=文化部1], Dept [deptId=3, deptName=文化部2], Dept [deptId=4, deptName=文化部3], Dept [deptId=5, deptName=研发部]]

可以看到,list操作会一次性的把数据读取出来

  • iterator操作
    @Test
    public void iterator() {
        Session session = sf.openSession();
        session.beginTransaction();
        
        //HQL查询
        Query query = session.createQuery("from Dept");
        Iterator<Dept> iterator = query.iterate();
        while (iterator.hasNext()) {
            Dept dept = (Dept) iterator.next();
            System.out.println(dept);
        }
        session.getTransaction().commit();
        session.close();
    }

运行结果:

Hibernate: select dept0_.deptId as col_0_0_ from t_dept2 dept0_     //执行一次次数查询操作
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=1, deptName=事业部]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=2, deptName=文化部1]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=3, deptName=文化部2]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=4, deptName=文化部3]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=5, deptName=研发部]

可以看到,iterator方法,会先进行一次次数查询,再进行N次对象查询.

  • list和iterator的缓存测试

list:

        Session session = sf.openSession();
        session.beginTransaction();
        
        Query query = session.createQuery("from Dept");
        List<Dept> list = query.list();
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        
        System.out.println("==============List====");
        list = query.list();
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        
        session.getTransaction().commit();
        session.close();

运行结果1:

Hibernate: select dept0_.deptId as deptId0_, dept0_.deptName as deptName0_ from t_dept2 dept0_
Dept [deptId=1, deptName=事业部]
Dept [deptId=2, deptName=文化部1]
Dept [deptId=3, deptName=文化部2]
Dept [deptId=4, deptName=文化部3]
Dept [deptId=5, deptName=研发部]
==============List====
Hibernate: select dept0_.deptId as deptId0_, dept0_.deptName as deptName0_ from t_dept2 dept0_
Dept [deptId=1, deptName=事业部]
Dept [deptId=2, deptName=文化部1]
Dept [deptId=3, deptName=文化部2]
Dept [deptId=4, deptName=文化部3]
Dept [deptId=5, deptName=研发部]

从list方法运行结果分析: 两次list都进行了同样的向数据库发送SQL语句进行查询.表示list不会从session中获取数据.

iterator:

        /*
         * Iterator
         * */
        Session session = sf.openSession();
        session.beginTransaction();
        Query query2 = session.createQuery("from Dept");
        Iterator<Dept> iterator = query2.iterate();
        while (iterator.hasNext()) {
            Dept dept = (Dept) iterator.next();
            System.out.println(dept);
        }
        
        System.out.println("====iterator===");
        iterator = query2.iterate();
        while (iterator.hasNext()) {
            Dept dept = (Dept) iterator.next();
            System.out.println(dept);
        }
        
        
        session.getTransaction().commit();
        session.close();

运行结果2:

Hibernate: select dept0_.deptId as col_0_0_ from t_dept2 dept0_
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=1, deptName=事业部]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=2, deptName=文化部1]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=3, deptName=文化部2]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=4, deptName=文化部3]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=5, deptName=研发部]
====iterator===
Hibernate: select dept0_.deptId as col_0_0_ from t_dept2 dept0_
Dept [deptId=1, deptName=事业部]
Dept [deptId=2, deptName=文化部1]
Dept [deptId=3, deptName=文化部2]
Dept [deptId=4, deptName=文化部3]
Dept [deptId=5, deptName=研发部]

iterator结果分析: 先进行一次次数查询,再执行N次查询对象. 第二次iterator查询,只进行了一次次数查询,并不进行对象查询,而是直接从缓存中获取.

下面的测试也证明:list获取会把数据缓存到session,iterator操作也可以从list缓存的数据中获取对象

@Test
    public void list_iterator() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();
        Query q = session.createQuery("from Dept");
        
        // 
        List<Dept> list = q.list(); 
        for (int i=0; i<list.size(); i++){
            System.out.println(list.get(i));
        }
        
        // 
        Iterator<Dept> it = q.iterate();
        while(it.hasNext()){
            Dept user = it.next();
            System.out.println(user);
        }
        
        session.getTransaction().commit();  
        session.close();
    }

运行结果如下

Hibernate: select dept0_.deptId as deptId0_, dept0_.deptName as deptName0_ from t_dept2 dept0_
Dept [deptId=1, deptName=事业部]
Dept [deptId=2, deptName=文化部1]
Dept [deptId=3, deptName=文化部2]
Dept [deptId=4, deptName=文化部3]
Dept [deptId=5, deptName=研发部]
Hibernate: select dept0_.deptId as col_0_0_ from t_dept2 dept0_
Dept [deptId=1, deptName=事业部]
Dept [deptId=2, deptName=文化部1]
Dept [deptId=3, deptName=文化部2]
Dept [deptId=4, deptName=文化部3]
Dept [deptId=5, deptName=研发部]

三. 懒加载

get方法和load方法的区别:
    get: 及时加载,只要调用get方法立即向数据库查询
    load. 默认使用懒加载,当用到数据的时候才向数据库查询
    
懒加载(lazy) :当用到数据的时候才向数据库查询,是Hibernate的懒加载特性,主要的目的是提供程序的执行相率

lazy的取值:
    TRUE: 使用懒加载
    FALSE: 关闭懒加载
    extra: 在集合数据懒加载的时候提升效率,在真正使用数据的时候才向数据库发送查询SQL语句;如果调用集合的size()/isEmpty()方法,只是统计,不真正查询数据
    
    •   懒加载异常
Session关闭后,不能使用懒加载数据!
如果session关闭后,使用懒加载数据报错:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
        如何解决session关闭后不能使用懒加载数据的问题?
         // 方式1: 先使用一下数据
        //dept.getDeptName();
        // 方式2:强迫代理对象初始化
        Hibernate.initialize(dept);
        // 方式3:关闭懒加载
            设置lazy=false;
        // 方式4: 在使用数据之后,再关闭session! 

四: 特别的多对一映射(一对一映射)

一对一映射的例子:身份证与用户的关系

映射有两种方式:基于外键映射(用户使用外键,引用身份证ID)和基于主键映射(用户ID左外身份证ID),两种方式设计都差不多,主要在于映射文件的设计

4.1 基于外键的映射配置

javabean设计.

User.java

package com.mrq.domain;


public class User {
    private Integer userId;
    private String userName;
    private IdCard idCard;
    public Integer getUserId() {
        return userId;
    }
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public IdCard getIdCard() {
        return idCard;
    }
    public void setIdCard(IdCard idCard) {
        this.idCard = idCard;
    }
}

身份证IdCard.java

package com.mrq.entity;

//身份证类
public class IdCard {
    private String cardNum;//身份证主键
    private String address;//身份证地址
    private User user;//身份证与用户一对一关系
    public String getCardNum() {
        return cardNum;
    }
    public void setCardNum(String cardNum) {
        this.cardNum = cardNum;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
}

  • 映射文件的配置要点:
一对一映射,有外键方
    unique="true"   给外键字段添加唯一约束
        

User.hbm.xml

<?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 package="com.mrq.entity">
    <class name="User" table="t_user2">
        <id name="userId">
            <generator class="native"></generator>
        </id>
        
        <property name="userName" type="string"></property>
        
       <!-- 
            一对一映射:没有外键方
        -->
        
        <one-to-one name="idCard" class="IdCard"></one-to-one>
        
    </class>
</hibernate-mapping>

IdCard.hbm.xml

<?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 package="com.mrq.entity">
    <class name="IdCard" table="t_idCard">
        <id name="cardNum">
            <generator class="assigned"></generator>
        </id>
        
        <property name="address" type="string"></property>
        
       <!-- 
            一对一映射有外键方
            unique="true":给外键字段添加唯一约束
        -->
        
        <many-to-one name="user" unique="true" column="user_id" class="User" cascade="save-update"></many-to-one>
        
    </class>
</hibernate-mapping>

测试方法

//一对一映射:基于外键
    @Test
    public void testOne2One() {
        Configuration configuration = new Configuration();
        SessionFactory sf = configuration.configure().buildSessionFactory();
        Session session = sf.openSession();
        session.beginTransaction();
        
        User user = new User();
        user.setUserName("小明!");
        
        IdCard idCard = new IdCard();
        idCard.setAddress("山西长治");
        idCard.setCardNum("8673287979Ax");
        
        idCard.setUser(user);
        session.save(idCard);
        session.getTransaction().commit();
        session.close();
    }

4.2 基于主键的设计

一对一映射,有外键方
            (基于主键的映射)
             constrained="true"  指定在主键上添加外键约束
主键生成方式: foreign  即把别的表的主键作为当前表的主键;
                property (关键字不能修改)指定引用的对

User bean保持不变

IdCard.java

package com.mrq.domain;

//身份证类
public class IdCard {
    private Integer user_id;//主键
    private String cardNum;//身份证主键
    private String address;//身份证地址
    private User user;//身份证与用户一对一关系
    
    
    public Integer getUser_id() {
        return user_id;
    }
    public void setUser_id(Integer user_id) {
        this.user_id = user_id;
    }
    public String getCardNum() {
        return cardNum;
    }
    public void setCardNum(String cardNum) {
        this.cardNum = cardNum;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
}

映射配置文件

IdCard.hbm.xml

<?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 package="com.mrq.domain">
    <class name="IdCard" table="t_idCard2">
        <id name="user_id">
            <!-- 
                id节点指定的是主键映射,即user_id是主键
                主键生成方式:foreign,即把别的表的主键作为当前表的主键:
                property: 指定引用的对象,对象的全名
             -->
            
            <generator class="foreign">
                <param name="property">user</param>
            </generator>
        </id>
        
        <property name="address" type="string"></property>
        <property name="cardNum" length="20"></property>
        
       <!-- 
            一对一映射有外键方
            基于主键
            constrained="true" 指定在主键上添加外键约束
        -->
        
    <one-to-one name="user" class="User" constrained="true" cascade="save-update"></one-to-one>        
    </class>
</hibernate-mapping>

测试:

//一对一映射,基于主键
    @Test
    public void testOne2OneOther() {
        Configuration configuration = new Configuration();
        SessionFactory sf = configuration.configure().buildSessionFactory();
        Session session = sf.openSession();
        session.beginTransaction();
        
        User user = new User();
        user.setUserName("嘻嘻");
        
        IdCard idCard = new IdCard();
        idCard.setAddress("山西长治");
        idCard.setCardNum("SDASDS3233Ax");
        
        idCard.setUser(user);
        session.save(idCard);
        session.getTransaction().commit();
        session.close();
    }

4.3 组合(组件)映射

  • 简单理解类的关系:
类的关系:
    组合关系:一个类中包含了另外一个类.这两个类就是组合关系
        比如: 汽车和车轮,礼物盒子和礼物
    继承关系: 一个类继承另外一个类,即为继承关系
    

组合映射的例子:

  • 组件类和被包含的组件类,共同映射到一张表

汽车和车轮

Car.java

package com.mrq.component;

public class Car {
    
    private Integer id;
    private String name;
    private Wheel wheel;
    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 Wheel getWheel() {
        return wheel;
    }
    public void setWheel(Wheel wheel) {
        this.wheel = wheel;
    }
    @Override
    public String toString() {
        return "Car [id=" + id + ", name=" + name + ", wheel=" + wheel + "]";
    }
}

wheel.java

package com.mrq.component;

public class Wheel {
    private Integer count;
    private Integer size;
    public Integer getCount() {
        return count;
    }
    public void setCount(Integer count) {
        this.count = count;
    }
    public Integer getSize() {
        return size;
    }
    public void setSize(Integer size) {
        this.size = size;
    }
    @Override
    public String toString() {
        return "Wheel [count=" + count + ", size=" + size + "]";
    }
}

  • 组合关系映射的要点: component节点的使用

Car.hbm.xml

<?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 package="com.mrq.component">
    <class name="Car" table="t_car">
        <id name="id">
            <generator class="native"></generator>
        </id>
        
        <property name="name" type="string"></property>
        
       <!-- 
            组件映射
        -->
        
        <component name="wheel" class="Wheel">
            <property name="size"></property>
            <property name="count"></property>
        </component>
        
    </class>
</hibernate-mapping>

4.4 继承映射的4种配置方式

  • 方式一:简单继承关系映射,直接写父类的属性,每个子类对应一个表

Animal.java

package com.mrq.extends1;

public abstract class Animal {
    
    private Integer id;
    private String name;
    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;
    }
}

Cat类继承Animal类

package com.mrq.extends1;

public class Cat extends Animal{
    private String catchMouse;

    public String getCatchMouse() {
        return catchMouse;
    }

    public void setCatchMouse(String catchMouse) {
        this.catchMouse = catchMouse;
    }
    
}

简单继承的映射配置,和普通的类类似

<?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 package="com.mrq.extends1">
    <class name="Cat" table="t_cat">
    <!-- 简单继承映射,父类属性直接写 -->
        <id name="id">
            <generator class="native"></generator>
        </id>
        
        <property name="name" type="string"></property>
        <property name="catchMouse"></property>
        
    </class>
</hibernate-mapping>
  • 方式二:所有子类映射到一张表
什么情况用?
    子类教多,且子类较为简单,即只有个别属性!
    好处:因为使用一个映射文件, 减少了映射文件的个数。
    缺点:(不符合数据库设计原则)
一个映射文件: Animal.hbm.xml
    通过鉴别器区分是哪个子类的信息

Animal.hbm.xml

  • 关键节点discriminator和sub-class
<?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 package="com.mrq.extends2">
    <class name="Animal" table="t_animal">
    <!-- 简单继承映射,父类属性直接写 -->
        <id name="id">
            <generator class="native"></generator>
        </id>
        
        <!-- 指定鉴别字段:区分不同的类 -->
        <discriminator column="class_type"></discriminator>
        
        
        <property name="name" type="string"></property>
        <!-- 
            子类猫:
            使用subclass节点映射一个子类
            要指定鉴别器字段的值,如果不设置,默认为子类的全名
            
         -->
         <subclass name="Cat" discriminator-value="cat_">
            <property name="catchMouse"></property>
         </subclass>
         
         <subclass name="Tiger" discriminator-value="tiger_">
            <property name="eatSheep"></property>
         </subclass>
         
         
        
    </class>
</hibernate-mapping>

写法较为简单:所有子类用一个映射文件,且映射到一张表!
但数据库设计不合理!
(不推荐用。)

  • 方式三: 每一个类都映射一张表,包括父类

关键节点:joined-subclass

Animal.hbm.xml

<?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 package="com.mrq.extends3">
    <class name="Animal" table="t_animal2">
    <!-- 简单继承映射,父类属性直接写 -->
        <id name="id">
            <generator class="native"></generator>
        </id>
        
        
        <property name="name" type="string"></property>
        <!-- 
            子类猫:
            key:指定外键
            
         -->
         <joined-subclass name="Cat" table="t_cat2">
            <key column="t_animal_id"></key>
            <property name="catchMouse"></property>
         </joined-subclass>
         <!-- 子类:老虎 -->
        <joined-subclass name="Tiger" table="t_tiger2">
            <key column="t_animal_id"></key>
            <property name="eatSheep"></property>
        </joined-subclass>
         
         
        
    </class>
</hibernate-mapping>

一个映射文件,存储所有的子类; 子类父类都对应表;
缺点:表结构比较负责,插入一条子类信息,需要用2条sql: 往父类插入、往子类插入!

  • 方式四: 每个子类映射一张表, 父类不对应表(推荐使用)

关键节点:union-class,同时主键不能使用自增长的策略

Animal.hbm.xml

<?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 package="com.mrq.extends4">
<!-- 
    abstract="true"指定实体类不对应表,数据库不会生成对应的表
 -->
    <class name="Animal" abstract="true">
        <!-- 如果使用union-subclass节点,主键生成策略不能自增长 -->
        <id name="id">
            <generator class="uuid"></generator>
        </id>
        
        
        <property name="name" type="string"></property>
        <!-- 
            子类猫:
            union-subclass
            table 对应的表,表的主键为ID列
            
         -->
         <union-subclass name="Cat" table="t_cat4">
            <property name="catchMouse"></property>
         </union-subclass>
         <!-- 子类:老虎 -->
        <union-subclass name="Tiger" table="t_tiger4">
            <property name="eatSheep"></property>
        </union-subclass>
         
         
        
    </class>
</hibernate-mapping>

所有的子类都写到一个映射文件;
父类不对应表; 每个子类对应一张表

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • 对象的状态 Hibernate中对象的状态 : 临时/瞬时状态、持久化状态、游离状态临时状态特点:直接new出来的...
    奋斗的老王阅读 891评论 0 49
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,293评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 一. Java基础部分.................................................
    wy_sure阅读 3,731评论 0 11
  • 感恩早起锻炼遇见了一个5岁的小女孩,她特别开心的跟我分享了她的生活,跟她聊天我也特别开心,感恩她让我种下了关心和陪...
    rainlove2011阅读 144评论 0 0