美文网首页
Hibernate的缓存和几种特殊的映射(09)

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

作者: 小Q逛逛 | 来源:发表于2017-05-26 09:38 被阅读19次

    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>
    

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

    相关文章

      网友评论

          本文标题:Hibernate的缓存和几种特殊的映射(09)

          本文链接:https://www.haomeiwen.com/subject/hbptfxtx.html