Hibernate(四)

1. 抓取策略

  • 抓取策略:怎样发出sql语句
  • 懒加载:什么时候发出sql语句

    配置:<set fetch="join/select/subselect">

Hibernate(四)_第1张图片

Hibernate(四)_第2张图片

Hibernate(四)_第3张图片

Class.hbm.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
	<class name="cn.itcast.hiberate.sh.domain.Classes">
		<id name="cid" length="5" type="java.lang.Long">
			<generator class="increment"></generator>
		</id>
		<property name="cname" length="20" type="java.lang.String"></property>
		
		<property name="description" length="100" type="java.lang.String"></property>
		<!-- 
			set元素对应类中的set集合
			通过set元素使classes表与student表建立关联
			   key是通过外键的形式让两张表建立关联
			   one-to-many是通过类的形式让两个类建立关联
			
			cascade 级联
			   save-update
			   	1、当 保存班级的时候,对学生进行怎么样的操作
			   	     如果学生对象在数据库中没有对应的值,这个时候会执行save操作
			   	     如果学生对象在数据库中有对应的值,这个时候会执行update操作
			   delete
			   all
			inverse  维护关系
			   true      不维护关系     
			   false     维护关系
			   default   false
		 -->
		<set name="students" cascade="save-update" inverse="true" lazy="false" fetch="join">
			<!-- 
				key是用来描述外键
			 -->
			<key>
				<column name="cid"></column>
			</key>
			<one-to-many class="cn.itcast.hiberate.sh.domain.Student"/>
		</set>
	</class>
</hibernate-mapping>
FetchTest.java
package cn.itcast.hibernate.sh.test;

import java.util.List;
import java.util.Set;

import org.hibernate.Session;
import org.junit.Test;

import cn.itcast.hiberate.sh.domain.Classes;
import cn.itcast.hiberate.sh.domain.Student;
import cn.itcast.hibernate.sh.utils.HiberanteUtils;

public class FetchTest extends HiberanteUtils{
	static{
		url = "hibernate.cfg.xml";
	}
	
	/**
	 * n+1的问题
	 * ------------------------------------
	 * n:4个班级,班级表中,表的记录数
	 * 1:查询班级
	 * 查询班级1条 + 每个班级查询一条学生(4条) = 5条
	 * ------------------------------------
	 * select:5条
	 * subselect:2条。如果需要分析翻译成sql语句存在子查询,这个时候用该策略效率最高
	 * ------------------------------------
	 * 针对这种问题:采用抓去策略解决
	 * 抓取策略:通过一个对象查询关联对象
	 *    解决问题的方案:子查询  fetch="subselect"
	 */
	@Test
	public void testAll_Classes(){
		Session session = sessionFactory.openSession();
		List<Classes> cList = session.createQuery("from Classes").list();
		for(Classes classes:cList){
			Set<Student> students = classes.getStudents();
			for(Student student:students){
				System.out.println(student.getSname());
			}
		}
		session.close();
	}
	
	/**
	 * n+1的问题
	 *    解决问题的方案:子查询  fetch="subselect"
	 */
	@Test
	public void testClasses_Some(){
		Session session = sessionFactory.openSession();
		List<Classes> cList = session.createQuery("from Classes where cid in(1,2,3)").list();
		for(Classes classes:cList){
			Set<Student> students = classes.getStudents();
			for(Student student:students){
				System.out.println(student.getSname());
			}
		}
		session.close();
	}	
	
	/**
	 * 先查询班级,再查询学生
	 * -----------------------------
	 * subselect:2条
	 * join:1条
	 */
	@Test
	public void testQueryClasses_Id(){
		Session session = sessionFactory.openSession();
		Classes classes = (Classes)session.get(Classes.class, 1L);
		Set<Student> students = classes.getStudents();
		for(Student student:students){
			System.out.println(student.getSname());
		}
		session.close();
	}
}


2. session的产生方式:

2.1. sessionFactory.openSession()

Hibernate(四)_第4张图片

Hibernate(四)_第5张图片

2.2. sessionFactory.getCurrentSession()

session.getCurrentSession的用法

  • 在hibernate的配置文件中:

        <property name="current_session_context_class">thread</property>

  • 不需要写session.close方法,在事务提交的时候会自动关闭(由hibernate内部完成)
  • crud都需要事务
  1.  因为是一个线程,所以整个方法中一个session,一个事务
  2. 保证了整个业务操作的安全性   
Hibernate(四)_第6张图片

Hibernate(四)_第7张图片

Hibernate(四)_第8张图片

Hibernate(四)_第9张图片

代码:SessionCreateTest.java


package cn.itcast.hibernate.sh.test;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import cn.itcast.hiberate.sh.domain.Classes;
import cn.itcast.hibernate.sh.utils.HiberanteUtils;

/**
 * session的产生方式。
 * @author Administrator
 *
 */
public class SessionCreateTest extends HiberanteUtils{
	static{
		url = "hibernate.cfg.xml";
	}
	
	/**
	 * 只要执行sessionFactory.openSession就会新创建一个session
	 */
	@Test
	public void testOpenSession(){
		Session session =  sessionFactory.openSession();
	}
	
	/**
	 * crud操作必须在事务的环境下运行。
	 * 注意:此刻的查询也必须开启事务
	 * ----------------------------------------------------------------
	 * session和事务绑定在一起了:说明hibernate让强制使用事务
	 *   原因:1.为了不让出现多个事务。2.
	 * -----------------------------------------------------------------
	 * 告诉hibernate,session由当前线程产生,必须得在hibernate.cfg.xml中如下配置
	 * <property name="current_session_context_class">thread</property>
	 * 
	 * -------------------------------------------------------------------
	 * 利用sessionFactory.getCurrentSession();在事务提交的那一刹那,session自动关闭;饿
	 */
	@Test
	public void testGetGurrentSession(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction  = session.beginTransaction();
		Classes classes = (Classes)session.get(Classes.class, 1L);
		transaction.commit();
		//session.close();
	}
}

3. session的一级缓存

Hibernate(四)_第10张图片

Hibernate(四)_第11张图片

Hibernate(四)_第12张图片

SessionCacheTest.java


package cn.itcast.hibernate.sh.test;

import java.util.Set;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import cn.itcast.hiberate.sh.domain.Classes;
import cn.itcast.hiberate.sh.domain.Student;
import cn.itcast.hibernate.sh.utils.HiberanteUtils;

/**
 * session的缓存
 * 没发出sql说明存在缓存,是从缓存中拿数据
 * @author Administrator
 *
 */
public class SessionCacheTest extends HiberanteUtils{
	static{
		url = "hibernate.cfg.xml";
	}	
	/**
	 * session.get方法把数据存放在一级缓存中了
	 * 如果第二次在查询时,没有发出sql语句,则证明放到了缓存中
	 */
	@Test
	public void testGet(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		
		Classes classes = (Classes)session.get(Classes.class, 1L);
		classes = (Classes)session.get(Classes.class,1L);//没sql
		
		transaction.commit();//提交事务的时候,已经关闭了session
	}
	
	/**
	 * session.load方法把数据存放在一级缓存中
	 */
	@Test
	public void testLoad(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		
		Classes classes = (Classes)session.load(Classes.class, 1L);
		classes.getCname();
		
		classes = (Classes)session.load(Classes.class,1L);
		classes.getCname();//没sql
		
		transaction.commit();
	}
	
	/**
	 * session.save方法把数据保存在一级缓存中
	 * 先保存。如果在get的时候没有发出sql语句,则说明存在缓存
	 */
	@Test
	public void testSave(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		
		Classes classes = new Classes();
		classes.setCname("aaa");
		classes.setDescription("asfd");
		
		session.save(classes);
		classes = (Classes)session.get(Classes.class, classes.getCid());//没sql
		
		transaction.commit();
	}
	
	/**
	 * session.update
	 */
	@Test
	public void testUpdate(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		
		Classes classes = (Classes)session.get(Classes.class, 1L);
		session.evict(classes);//classes对象从session中清空了
		session.update(classes);//把classes对象放入到了session缓存中
		classes = (Classes)session.get(Classes.class, 1L);//没发出sql
		
		transaction.commit();
	}
	
	/**
	 * session.clear
	 */
	@Test
	public void testClear(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		
		Classes classes = (Classes)session.get(Classes.class, 1L);
		session.clear();//classes对象从session中清空了
		classes = (Classes)session.get(Classes.class, 1L);
		
		transaction.commit();
	}
	
	/**
	 * 回顾一下典型的错误
	 * 标示符的意义-----不同的对象标示符必须不一样
	 * 两个持久化对象标识符相同
	 */
	@Test
	public void testClearTest(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		
		Classes classes  = (Classes)session.get(Classes.class, 1L);
		session.clear();//如果不加这句话,两个不同的对象,相同的ID值,所以得把其中的一个清空
		
		Classes classes2 = new Classes();
		classes2.setCid(1L);
		classes2.setCname("asfd");
		session.update(classes2);
		
		transaction.commit();
	}
	
	/**
	 * 把数据库中的数据刷新到缓存中
	 */
	@Test
	public void testRefresh(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		
		Classes classes = (Classes)session.get(Classes.class, 1L);
		classes.setCname("66");
		session.refresh(classes);//把cid为1的值从数据库刷到了缓存中
		System.out.println(classes.getCname());
		
		transaction.commit();
	}
	
	/**
	 * session.flush
	 * 
	 */
	@Test
	public void testFlush(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		
		Classes classes  =(Classes)session.get(Classes.class, 1L);
		classes.setCname("afdsasdf");
		Set<Student> students = classes.getStudents();
		for(Student student:students){
			student.setDescription("asdf");
		}
		session.flush();
		
		transaction.commit();
	}
	
	/**
	 * 插入100000万条数据到数据库中
	 */
	@Test
	public void testSaveBatch(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		
		for(int i=6;i<1000000;i++){
			Classes classes = new Classes();
			classes.setCname("aaa");
			classes.setDescription("afds");
			session.save(classes);
			if(i%50==0){
				session.flush();
				session.clear();
			}
		}
		
		transaction.commit();
	}
	
	/**
	 * session.flush只是发出SQL语句了,并没有清空session缓存
	 */
	@Test
	public void testFlush2(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		
		Classes classes = (Classes)session.get(Classes.class, 1L);
		session.flush();
		classes = (Classes)session.get(Classes.class, 1L);
		
		transaction.commit();
	}
}

    区别:

     flush: 进行清理缓存(此时缓存中的数据并不丢失)的操作,让缓存和数据库同步 执行一些列sql语句,但不提交事务,

     commit:先调用flush() 方法,然后提交事务. 则意味着提交事务意味着对数据库操作永久保存下来。

     reresh:刷新,session和数据库同步,执行查询,把数据库的最新信息显示出来,更新本地缓存的对象状态.

     clear:清空缓存,等价于list.removeAll(); 

4. 二级缓存

Hibernate(四)_第13张图片

Hibernate(四)_第14张图片

Hibernate(四)_第15张图片

4.1 二级缓存

Classes.hbm.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
	<class name="cn.itcast.hiberate.sh.domain.Classes">
		<!-- 
			缓存策略
		 -->
		<cache usage="read-write"/>
		
		<id name="cid" length="5" type="java.lang.Long">
			<generator class="increment"></generator>
		</id>
		<property name="cname" length="20" type="java.lang.String"></property>
		
		<property name="description" length="100" type="java.lang.String"></property>
		<!-- 
			set元素对应类中的set集合
			通过set元素使classes表与student表建立关联
			   key是通过外键的形式让两张表建立关联
			   one-to-many是通过类的形式让两个类建立关联
			
			cascade 级联
			   save-update
			   	1、当 保存班级的时候,对学生进行怎么样的操作
			   	     如果学生对象在数据库中没有对应的值,这个时候会执行save操作
			   	     如果学生对象在数据库中有对应的值,这个时候会执行update操作
			   delete
			   all
			inverse  维护关系
			   true      不维护关系     
			   false     维护关系
			   default   false
		 -->
		<set name="students" cascade="save-update" inverse="true" fetch="select">
			<!-- 
				key是用来描述外键
			 -->
			<key>
				<column name="cid"></column>
			</key>
			<one-to-many class="cn.itcast.hiberate.sh.domain.Student"/>
		</set>
	</class>
</hibernate-mapping>

hibernate.cfg.xml


<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
	<!-- 
		一个session-factory只能连接一个数据库
	-->
<session-factory>
	<!-- 
		数据库的用户名
	-->
	<property name="connection.username">root</property>
	<!-- 
		密码
	-->
	<property name="connection.password">root</property>
	<!-- 
		url
	-->
	<property name="connection.url">
		jdbc:mysql://localhost:3306/itcast_sh_hibernate
	</property>
	<!-- 
		作用:根据持久化类和映射文件生成表
		validate
		create-drop
		create
		update
	-->
	<property name="hbm2ddl.auto">update</property>
	<!-- 
		显示hibernate内部生成的sql语句
	-->
	<property name="show_sql">true</property>
	<property name="format_sql">true</property>
	<!-- 
		告诉hibernate,session由当前线程产生
	-->
	<property name="current_session_context_class">thread</property>

	<!-- 
		开启二级缓存
	-->
	<property name="cache.use_second_level_cache">true</property>

	<!-- 
		二级缓存的提供商
	-->
	<property name="cache.provider_class">
		org.hibernate.cache.EhCacheProvider
	</property>
	<property name="cache.use_query_cache">true</property>

	<mapping resource="cn/itcast/hiberate/sh/domain/Classes.hbm.xml" />
	<mapping resource="cn/itcast/hiberate/sh/domain/Student.hbm.xml" />
	<!-- 
		<class-cache usage="read-only" class="cn.itcast.hiberate.sh.domain.Classes"/>
	-->
</session-factory>
</hibernate-configuration>


SecondCacheTest.java

package cn.itcast.hibernate.sh.test;

import java.util.List;
import java.util.Set;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import cn.itcast.hiberate.sh.domain.Classes;
import cn.itcast.hiberate.sh.domain.Student;
import cn.itcast.hibernate.sh.utils.HiberanteUtils;

/**
 * hibernate的二级缓存
 * @author Administrator
 *
 */
public class SecondCacheTest extends HiberanteUtils{
	static{
		url = "hibernate.cfg.xml";
	}	
	
	/**
	 * session.get
	 *    把数据存在一级缓存和二级缓存
	 * 数据从一级缓存找---二级缓存---数据库,如果数据中也找不到,报错
	 */
	@Test
	public void testGet(){
		Session session = sessionFactory.openSession();
		Classes classes = (Classes)session.get(Classes.class, 1L);
		session.close();
		session = sessionFactory.openSession();
		classes = (Classes)session.get(Classes.class, 1L);//不发出
		session.close();
	}
	
	/**
	 * session.load
	 *   同上
	 */
	@Test
	public void testLoad(){
		Session session = sessionFactory.openSession();
		Classes classes = (Classes)session.load(Classes.class, 1L);
		classes.getCname();
		session.close();
		session = sessionFactory.openSession();
		classes = (Classes)session.load(Classes.class, 1L);//不发出
		classes.getCname();
		session.close();
	}
	
	/**
	 * session.update
	 */
	@Test
	public void testUpdate(){
		Session session = sessionFactory.openSession();
		//session.beginTransaction();
		Classes classes = new Classes();
		classes.setCid(1L);
		classes.setCname("aaa");
		session.update(classes);
		
		session.close();//放倒了一级缓存,并关闭session
		session = sessionFactory.openSession();
		classes = (Classes)session.get(Classes.class, 1L);// 如果发出语句,说明在一级缓存。不发出,在二级缓存中
		session.close();
	}
}

4.2 查询缓存


package cn.itcast.hibernate.sh.test;

import java.util.List;
import java.util.Set;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import cn.itcast.hiberate.sh.domain.Classes;
import cn.itcast.hiberate.sh.domain.Student;
import cn.itcast.hibernate.sh.utils.HiberanteUtils;

/**
 * 查询缓存
 * -----------------------------
 * 如果系统中设计大量的查询,例如大量报表。则利用查询缓存很有优势
 * @author Administrator
 *
 */
public class QueryCacheTest extends HiberanteUtils{
	static{
		url = "hibernate.cfg.xml";
	}	
	
	@Test
	public void testQuery(){
		Session session = sessionFactory.openSession();
		
		Query query = session.createQuery("from Classes");//此处不能在.list(),而是返回Query对象
		query.setCacheable(true);//classes里的所有的数据要往查询缓存中存放了。通过query放倒了缓存中
		List<Classes> classesList = query.list();
		
		query = session.createQuery("from Classes");//只有和上面一样的sql查询才能起到查询缓存的作用。否则又要重新查询
		query.setCacheable(true);//这个query把缓存中的数据查出来。//如果这句话没有,发出两条sql
		classesList = query.list();
		
		session.close();
	}
}


4.3 查询缓存解决大量数据的查询

    将查询出来的数据放在硬盘上,必须借助类路径下的ehcache.xml

ehcache.xml


<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
         
    <diskStore path="C:\\TEMP1"/>
    <defaultCache
            maxElementsInMemory="12"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            maxElementsOnDisk="10000000"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"
     />
            
   	<Cache
            name="cn.itcast.hiberate.sh.domain.Classes"
            maxElementsInMemory="5" 
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
            maxElementsOnDisk="10000000"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"
       />
</ehcache>



	/**
	 * 查询缓存---是建立在二级缓存之上
	 * ----------------------------------
	 * 针对超大数据的解决方案:将数据可以从数据库中拿出来放到硬盘上。
	 * 需要借助类路径下的ehcache.xml配置
	 */
	@Test
	public void testAllClasses(){
		Session session = sessionFactory.openSession();
		List<Classes> classesList = session.createQuery("from Classes").list();
		session.close();
		try {
			Thread.sleep(1000); //因为代码的执行速度慢。缓存下,就可以看到
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}








你可能感兴趣的:(Hibernate(四))