zoukankan      html  css  js  c++  java
  • Hibernate的状态,缓存和映射

    Hibernate的状态,缓存和映射

    1.对象的状态

    1.1.对象状态的概念和分类

    在使用Hibernate操作数据库的时候,我们先创建了JavaBean对象,然后使用session来保存或者更新到数据库,然后使用事务来提交更新。在这整个过程中,JavaBean中的对象经历了几个不同的状态,这些状态都是针对持久层的。

    Hibernate中对象的状态分为:临时/瞬时状态,持久化状态,游离状态

    1.临时状态
    在JavaBean创建了一个对象,在Hibernate中,如果想用这个对象,就需要new一下:User user = new User();
    在new完这个对象之后,session还没有操作这个对象之前,这个对象是的状态就叫临时状态。此时的对象还没有根数据库产生关联。
    特点:

    • 直接new出来的对象
    • 不处于session的管理
    • 数据库中没有对象的记录

    2.持久化状态
    当对对象调用session的save/saveOrUpdate/get/load/list 等方法的时候,对象就是持久化状态。处于持久化状态的对象,当对对象属性进行更改的时候,就会反映到数据库中!

    特点:

    • 处于session的管理
    • 数据库中有对应的记录
    • session对数据库的操作在事务提交的时候一起对数据库进行操作

    3.游离状态
    session关闭之后,持久化对象就变成离线对象,离线表示这个对象不能再与数据库保持同步,他们不再受Hibernate的管理
    特点:

    • 不处于session的管理
    • 数据库中有对应的记录
    • session关闭后,对象的状态就是游离状态

    1.2.状态之间的相互转化

    2.缓存

    2.1.一级缓存

    1.Session缓存:
    Hibernate的一级缓存是由Session提供的,也就是说Hibernate的一级缓存就是Session的缓存,它存在与Session的整个生命周期,当程序调用save()/update()/saveOrupdate()/get()等,以及调用查询接口方法list()/iterator()方法的时候,如果Session中不存在该对象,那么会先将本次的对象存储到一级缓存中便于以后使用,当Session关闭时同时清空一级缓存数据clear()/evict().

    2.Session的缓存作用:
    减少访问数据库的次数,进而提高效率,保证缓存中的对象与数据库的记录保持同步,当魂村的对象改变后,Session不会立即执行sql,而是将多个sql语句合并为一条sql进行执行,提高效率

    3.Session的缓存举例:
    当用户需要对指定的对象进行修改的时候,如果对于同一个属性修改了多次,其实Hibernate的Session婚讯并不是执行多个Update语句,而是以最后一个更新为准而发送一条更新的sql。

    2.2.几个方法的作用

    1.缓存相关的几个方法的作用:
    session.flush; 让一级缓存域与数据库同步
    session.evict(arg0); 清空一级缓存中指定的对象
    session.clear(); 清空一级缓存中缓存的素所有对象

    什么情况会使用上面的方法呢?
    在需要批量操作时使用。
    session.flush(); 先与数据库同步
    session.clear(); 再清空一级缓存内容

    代码示例:

    
    
    import java.util.HashSet;
    import java.util.Set;
    
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;
    import org.hibernate.classic.Session;
    import org.junit.Test;
    
    public class App2_cache {
    	
    	private static SessionFactory sf;
    	static {
    		sf = new Configuration()
    			.configure()
    			.buildSessionFactory();
    	}
    
    	//缓存测试
    	@Test
    	public void testCache() throws Exception {
    		Session session = sf.openSession();
    		session.beginTransaction();
    		User user = null;
    		// 查询
    		user = (User) session.get(User.class, 5);// 先检查缓存中时候有否数据,如果有不查询数据库,直接从缓存中获取(此时缓存中没有)
    		user = (User) session.get(User.class, 5);// 先检查数据库中是否有数据,如果有,不查询数据,直接从缓存中获取(此时缓存中有了)
    		//此处只有一条向数据库查询的语句,当第二个get时,缓存中已经存在就不再从数据库中获取
    		session.getTransaction().commit();
    		session.close();
    	}
    	
    	
    	//flush数据库同步方法
    	@Test
    	public void flush() throws Exception {
    		Session session = sf.openSession();
    		session.beginTransaction();
    		
    		User user = null;
    		user = (User) session.get(User.class, 5);
    		user.setUserName("Jack555");
    		//缓存数据与数据库同步
    
            //flush将缓存中的数据同步到数据库,如果此处没有使用flush方法,
            // 则只会把tomcat222更新到数据库,Jack555不会对数据库进行任何操作
            session.flush();
            user.setUserName("tomcat222");
    
    
            session.getTransaction().commit();  // session.flush();
    		session.close();
    	}
    	
        //clear()和evict()清空方法
    	@Test
    	public void clear() throws Exception {
    		Session session = sf.openSession();
    		session.beginTransaction();
    		
    		User user = null;
    		// 查询
    		user = (User) session.get(User.class, 5);
    		// 清空缓存内容
    		session.clear(); // 清空所有
    //		session.evict(user);// 清除指定缓存对象
    		
    		user = (User) session.get(User.class, 5);
            
    		session.getTransaction().commit();  // session.flush();
    		session.close();
    	}
    	
    }
    
    
    

    2.3.相关问题

    1.不同的Session是否会共享缓存数据?

    不会。在程序中,如果创建了两个Session中,然后分别存储了数据,则session有两个不同的缓存区,两者之间不会共享数据

    2.list和Iterator查询的区别?

    list():用一条sql语句把所有的记录都查询出来,查询的结果会放入缓存,但不会从缓存中获取数据
    Iterator:N+1查询:N表示所有的记录总数。即会先发送一条语句查询所有记录的主键(1),再根据每一个主键去数据库查询(N).查询的结果会放入缓存,也会从缓存中取数据

    代码示例:

    
    
    import java.util.Iterator;
    import java.util.List;
    
    import org.hibernate.Query;
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;
    import org.hibernate.classic.Session;
    import org.junit.Test;
    
    public class App3_list_iterator {
    	
    	private static SessionFactory sf;
    	static {
    		sf = new Configuration()
    			.configure()
    			.buildSessionFactory();
    	}
    	/**
    	 * list与iterator的区别
    	 * 1. list 使用方法
    	 * 2. iterator 使用方法
    	 */
    	//1.  list 使用方法
    	@Test
    	public void list() throws Exception {
    		Session session = sf.openSession();
    		session.beginTransaction();
    		// HQL查询
    		Query q = session.createQuery("from User ");//此时没有查询查询数据库
    		// list()方法
    		List<User> list = q.list();//此时查询了数据库
    
    		//遍历出结果
    		for (int i=0; i<list.size(); i++){
    			System.out.println(list.get(i));
    		}
    		
    		session.getTransaction().commit();  
    		session.close();
    	}
    	
    	//2. iterator 使用方法
    	@Test
    	public void iterator() throws Exception {
    		Session session = sf.openSession();
    		session.beginTransaction();
    		// HQL查询
    		Query q = session.createQuery("from User ");
    		// iterator()方法
    		Iterator<User> it = q.iterate();
    
            //遍历出结果
    		while(it.hasNext()){
    			// 得到当前迭代的每一个对象
    			User user = it.next();
    			System.out.println(user);
    		}
    		
    		session.getTransaction().commit();  
    		session.close();
    	}
    }
    
    
    

    3.懒加载

    get,load方法的区别?
    get:及时加载,只要调用get方法就立刻向数据库查询
    load:默认使用懒加载,只用当使用数据的时候才向数据库查询

    懒加载:(lazy)
    概念:当用到数据的时候才向数据库查询,这就是Hibernate的懒加载特性。
    目的:提供程序执行效率

    lazy的值:
    lazy的值在对象的映射文件中设置,例如:<class name="Dept" table="t_dept" lazy="false">
    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

    代码示例:

    
    
    import org.hibernate.Hibernate;
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;
    import org.hibernate.classic.Session;
    import org.junit.Test;
    
    public class App {
    	
    	private static SessionFactory sf;
    	static {
    		sf = new Configuration()
    			.configure()
    			.buildSessionFactory();
    	}
        
    	//1. 主键查询,及区别
    	@Test
    	public void get_load() {
    		
    		Session session = sf.openSession();
    		session.beginTransaction();
    		Dept dept = new Dept();
    		// get,及时查询
    		dept = (Dept) session.get(Dept.class, 9);
    		System.out.println(dept.getDeptName());
    		
    		// load,默认懒加载,及在使用数据的时候,才向数据库发送查询的sql语句
    		dept = (Dept)session.load(Dept.class, 9);//此时没有向数据库发送sql语句
    
    
            /**
             *  session关闭后想用懒加载数据的方法
             */
    
            //方式1.在session关闭前先使用一下数据
    		dept.getDeptName();
    		// 方式2,强迫代理对象初始化
    		Hibernate.initialize(dept);
    		// 方式3:关闭懒加载,在映射文件中设置lazy=false
    
            //方式4,在使用数据之后再关闭session
    		
    		session.getTransaction().commit();
    		session.close();
    		
    		// session关闭后,在这里使用数据
    		System.out.println(dept.getDeptName());
    	}
    	
    }
    
    

    4.一对一映射

    之前讲过一对多或者多对一,以及多对多的映射配置方式,但是实际中有时候还需要使用到一对一的映射方式。
    下面举个例子;
    需求:火车票的用户与身份证信息,即一条用户记录对应一条身份证信息,一对一的关系!
    设计数据库

    javaBean
    映射配置

    4.1.基于外键的映射

    思想:在t_idcard表中使用t_user表的外键来关联到身份证信息到用户。t_idcard有自己的主键和一个外键

    javaBean对象:

    // 身份证
    public class IdCard {
    
    	// 身份证号(主键)
    	private String cardNum;// 对象唯一表示(Object Identified, OID)
    	private String place; //  身份证地址
    	// 身份证与用户,一对一的关系
    	private User user;
    
    // 用户
    public class User {
    
    	private int userId;
    	private String userName;
    	// 用户与身份证信息, 一对一关系
    	private IdCard idCard;
    

    映射文件:
    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_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>
    
    
    

    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>
    

    4.2.基于主键的映射

    思想:也是使用外键来关联到t_user表,同时将这个外键作为t_idcard的主键

    // 身份证
    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.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>
    		
    		<!-- 
    			一对一映射,有外键方
    			(基于主键的映射)
    			 constrained="true"  指定在主键上添加外键约束
    		 -->
    		<one-to-one name="user" class="User" constrained="true"  cascade="save-update"></one-to-one>
    			
    	</class>
    	
    
    </hibernate-mapping>
    

    5.组件映射

    java中类的关系有:
    组合关系:一个类中包含了另外一个类,这2个类就是组合关系。
    继承关系:一个类继承于另外一个类,这2个类就是继承关系

    对于组合关系,在维护一个类的时候,如果一个类中包含了另一个类,需要按照使用组合映射将两个类生成到一个关系表中。即一类中包含了另外一个类,所以需要在映射文件中把这里两个类都映射出来。
    举个例来解释
    需求:汽车和车轮
    数据:t_car(主键,汽车名称,轮子大小,个数)
    JavaBean:

    //汽车
    public class Car {
    
    	private int id;
    	private String name;
    	// 车轮
    	private Wheel wheel;
    }
    
    // 车轮
    public class Wheel {
    
    	private int count;
    	private int size;
    }
    

    映射文件

    <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>
    
    
    

    6.继承映射

    6.1.简单继承映射

    需求:动物,猫继承动物,映射出猫的表

    JavaBean对象,Cat继承Animal父类

    // 动物类
    public abstract class Animal {
    
    	private int id;
    	private String name;
    
    public class Cat extends Animal {
        //继承了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="e_extend">
    	
    	<class name="Cat" table="t_cat" >
    		<!--简单继承映射:父类属性直接写-->
    
    		<id name="id">
    			<!--手动指定主键-->
    			<generator class="native"></generator>
    		</id>	
    		<property name="name" length="20"></property>
    
    		<property name="catchMouse"></property>
    		
    
    		 
    		 
    	</class>
    	
    
    </hibernate-mapping>
    
    

    测试

    @Test
    	public void getSave() {
    		
    		Session session = sf.openSession();
    		session.beginTransaction();
    		
    		// 保存
    //		Cat cat = new Cat();
    //		cat.setName("大花猫");
    //		cat.setCatchMouse("抓小老鼠");
    //		session.save(cat);
    		
    		// 获取时候注意:当写hql查询的使用,通过父类查询必须写上类的全名
    		Query q = session.createQuery("from cn.itcast.e_extends1.Animal");
    		List<Animal> list = q.list();
    		System.out.println(list);
    		
    		session.getTransaction().commit();
    		session.close();
    		
    	}
    

    总结:对于简单继承映射,有多少个子类,就写多少个映射文件!

    6.2.继承映射

    需求:猫和猴子两个子类继承自动物。如何映射子类的表

    1.所有子类映射到一张表(1张表)
    什么情况用?
    子类较多,且子类较为简单,即只有个别属性。
    好处是:由于使用一个映射文件,减少了映射文件的个数
    缺点是:不符合数据库设计原则

    映射文件Animal.hbm.xml,需要区分是哪一个子类的信息。

    在数据库设计的时候需要额外添加一个字段来表示是哪一个子类。不推荐使用

    2.每个子类映射到一张表(3张表)

    数据库:
    t_animal(存储父类信息)

    1 大花猫

    t_cat(引用父类的主键)

    1 抓小老鼠

    t_monkey(引用父类的主键)

    代码示例:
    JavaBean设计是cat和monkey继承Animal。
    所有子类都在一个映射文件中配置,但是数据是保存在三个不同的表中。
    映射文件:
    Animal.hbm.xml

    <!-- 
    	继承映射, 每个类对应一张表(父类也对应表)
     -->
    <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 指定t_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>      
    

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

    3.(推荐)每个子类映射到一张表,父类不对应表(2张表)
    父类不对应表,只需要每个子类对应一张表:
    数据库:
    t_cat表和t_monkey表
    映射文件

    
    <!--
    	继承映射
        abstract="true" 表示这个类不对应表
     -->
    <hibernate-mapping package="cn.itcast.e_extends3">
    	
    	<class name="Animal" abstract="true" >
    		<id name="id">
    <!-- 如果使用 union-subclass节点,主键生成策略不能为自增长 -->
    			<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>                                    
    
    

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

    所有的映射
    目前Hibernate中的映射关系已经全部学完,他们是:
    多对一
    一对多
    多对多
    一对一(多对一的特殊应用)
    组件
    继承

  • 相关阅读:
    【原】Coursera—Andrew Ng机器学习—课程笔记 Lecture 15—Anomaly Detection异常检测
    【原】Coursera—Andrew Ng机器学习—课程笔记 Lecture 14—Dimensionality Reduction 降维
    【原】Coursera—Andrew Ng机器学习—课程笔记 Lecture 13—Clustering 聚类
    【原】Coursera—Andrew Ng机器学习—课程笔记 Lecture 12—Support Vector Machines 支持向量机
    【原】机器学习公开课 目录(课程笔记、测验习题答案、编程作业源码)...持续更新...
    【原】Coursera—Andrew Ng机器学习—Week 11 习题—Photo OCR
    【原】Coursera—Andrew Ng机器学习—Week 10 习题—大规模机器学习
    【原】Coursera—Andrew Ng机器学习—Week 9 习题—异常检测
    【原】Coursera—Andrew Ng机器学习—Week 8 习题—聚类 和 降维
    【原】Coursera—Andrew Ng机器学习—Week 7 习题—支持向量机SVM
  • 原文地址:https://www.cnblogs.com/cenyu/p/6284894.html
Copyright © 2011-2022 走看看