Hibernate检索方式

1:Hibernate 提供了以下检索对象的方式
	1、导航对象图检索方式:在程序中获得持久的实体对象,通过对象关联,获得相关的实体对象。比如:session.get(Department.class,1); dept.getEmps()
	2、OID检索方式:session的get或load根据OID查找对象
	3、HQL检索方式:Query接口。使用面向对象的HQL语句进行查询
	4、QBC(Query By Criteria)检索方式:Criteria接口。提供了更加面向对象的查询接口。
	5、本地SQL检索方式:SQLQuery。性能要求较高的情况下用。Hibernate会负责把检索到的JDBC ResultSet结果集映射为持久化对象图。一般不需要
2. Hibernate的链接查询类型
	内连接    inner join或join
	迫切内连接  inner join fetch或join fetch
	隐式内连接  
	左外链接  left outer join或 left join
	迫切左外链接 left outer join fetch或 left join fetch返回数据不同
	右外连接 right outer join或right join
	交叉链接 ClassA,ClassB
	**区分左连接和迫切左外链接的区别
3. HQL查询特殊用法
	1). 多态查询
		查询出所有的实体(当前类和所有子类的实例)
			session.createQuery("from Customer”)
		检索出所有实现serializable接口的实例
			session.createQuery(“ from java.io.Serializable”)
		检索出所有的持久化对象
			Query query = session.createQuery(“ from java.lang.Object”)
	2). 分页查询:
		•setFirstResult(int firstResult): 设定从哪一个对象开始检索, 参数 firstResult 表示这个对象在查询结果中的索引位置, 索引位置的起始值为 0. 默认情况下, Query 从查询结果中的第一个对象开始检索
		•setMaxResult(int maxResults): 设定一次最多检索出的对象的数目. 在默认情况下, Query 和 Criteria 接口检索出查询结果中所有的对象
	3). 唯一结果集
		query.setMaxResults(1);//做多返回几条记录
		Customer c = (Customer)query.uniqueResult();//返回0到1条数据
	4). 参数绑定
		方式1:指定名称参数绑定,使用setString/setInteger/query.setParameter(0,"Tom")
		方式2:指定参数的位置绑定,query.setString(0,"Tom")/query.setParameter(0,"Tom");
	5). 在配置文件中指定查询语句
			<query name="findCustomersByName">
				 <![CDATA[from Customer c where c.name like ?]]>
			</query>
			------------------------------------------------------------------
			Query query = session.getNamedQuery(“findCustomersByName”);
	6). 迫切左外连接
		使用的sql语句仍然是左外连接,它将返回一个对象,要想获取和它关联对象,需要遍历循环。
		缺点:循环太多,返回的结果集过于复杂
	7). 左外连接
		如果查询一个对象,则返回Object对象
		如果查询的是多个对象,则返回Object数组对象
	8). 投影查询结果仅包含实体的部分属性. 通过 SELECT 关键字实现.
		返回的是Object数组或者是一个Object对象
	9). 构造函数查询
		 new com.baidu.CustomerRow(c.name,o.orderNumber,o.price) from Customer
4. 本地SQL查询
	方式一:
		Query query = session.createSQLQuery(“select o.name from CUSTOMERS c where c.name=‘tom’”);
		query.list();
	方式二:参数查询
		Query query = session.createSQLQuery(“select * from CUSTOMERS c where c.name=?”);
		query.setString(“0”,’tom’)
		query.list();
		此时返回Object数组或者是返回一个Object对象(这里如果查询结果是1个值,就是Object对象,如果查询结果是多个值就是Object数组
	方式三:Sql语句封装对象
		SQLQuery sqlquery = session.createSQLQuery("select {c.*} from CUSTOMERS c where c.name =:customerName");
		// 动态绑定参数
		sqlquery.setString("customerName", “tom");
		//“c”用来引用数据表的别名,例如以上代码中{c.*}表示使用c来作为customers表别名。  把sql查询返回的关系数据映射为对象
		sqlquery.addEntity("c", Customer.class);
		// 执行sql select语句,返回查询结果。
		List list = sqlquery.list();
5. QBC查询		
	要由Criteria、Criterion接口和Expression类组成,他支持在运行时动态生成查询语句
	方法链编程:
		session.createCriteria(Customer.class).add(Restrictions.eq("name", "tom1")).list();

 

二级缓存

1. 二级缓存的意义
	内存中存放经常操作的数据,减少与数据库的交互,提升性能
2. 二级缓存的作用
	1). 因为二级缓存是公开的缓存,是进程级别的缓存,所以二级缓存存放公开的数据,私有的数据是不能存放在这里的
	2). 当把一个数据放入到二级缓存中以后,只要sessionFactory没有关闭,二级缓存中的数据就能够存在
	3). hibernate本身对于二级缓存并没有实现
3. 配置二级缓存
	1)、在hibernate的配置文件中
		//开启二级缓存
		<property name="cache.use_second_level_cache">true</property>
		//指定二级缓存的供应商
		<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
		//开启统计机制
		<property name="hibernate.generate_statistics">true</property>
	2)、指定类或者集合开启二级缓存,可以有两种方案
		在hibernate的配置文件中
			//开启类的二级缓存
			<class-cache usage="read-only" class="cn.itheima03.hibernate.domain.Classes"/>
			//开启集合的二级缓存
			//说明:开启集合的二级缓存的条件必须是Student类的二级缓存也开启
			<collection-cache usage="read-only" collection="students"/>
		在映射文件中
			//开启类的二级缓存
			<class name="cn.itheima03.hibernate.domain.Classes" table="classes">
				<cache usage="read-write"/>
			//开启集合的二级缓存
			<set name="students">
				<cache usage="read-only"/>
4. 二级缓存的内存结构
		二级缓存由缓存提供者提供(Cache Provider),包含四部分:类缓存区、集合缓存区、查询缓存区、更新时间戳
		<!-- 配置类级别的二级缓存 -->
			<class-cache usage="read-write" class="com.itheima.domain.Department"/>
			<class-cache usage="read-write" class="com.itheima.domain.Employee"/>
		<!-- 配置集合级别的二级缓存 -->
			<collection-cache usage="read-write" collection="com.itheima.domain.Department.emps"/>
	1). 类缓存区
		第一次查询时,把数据存放到一级缓存和二级缓存中,但存放形式不一样。一级缓存存放的是实体对象的引用(即内存地址),而二级缓存存放的是对象中的数据(散列数据id:1 name:d1name)
		***注意:一级缓存没有关闭的情况下,再次查询同样的实体记录,返回的是对象的引用,因此两次从一级缓存中取出的对象内存地址一致。而一级缓存关闭后,从二级缓存中取出的数据因为是散列数据,需要重新封装到新对象中,所以,内存地址会不同
		get和load 可以读取类级别二级缓存,Query.list 只能存,不能取,list会再次查询
	2). 集合级别的二级缓存
		* 存放的是对象的OID,如果要想获取真正的实体对象,还要到类级别的二级缓存中获取
		总结:集合级别的缓存依赖于类级别的缓存
	3). 查询缓存区
		a. 对于经常使用的查询语句, 如果启用了查询缓存, 当第一次执行查询语句时, Hibernate 会把查询结果存放在查询缓存中. 以后再次执行该查询语句时, 只需从缓存中获得查询结果, 从而提高查询性能
		b. 查询缓存使用于如下场合:
			应用程序运行时经常使用查询语句
			很少对与查询语句检索到的数据进行插入, 删除和更新操作
		c. HQL的from Department的数据保存在类缓存区的,查询缓存区存放对象的ID
			如果配置了查询缓存:将以SQL语句为key,查询结果为value存放的
		d. 配置步骤:
			启动查询缓存
				hibernate.cfg.xml:
				<property name=“hibernate.cache.use_query_cache">true</property>
			程序中使用:query.setCacheable(true);
	4). 更新时间戳
		更新时间戳级别的缓存区域
		 * (1)将查询的对象放置到类、集合、查询级别的二级缓存中一份,而且设置放置对象的时间T1
		 * (2)当执行增、删、改操作的时候,更新时间戳会记录一个时间T2
		如果当T1>T2的话,说明查询在后,更新在前,说明二级缓存中存放的数据是最新数据,那么此时从二级缓存中获取数据,不会查询数据库
		
5. 缓存中存放的数据
	适合放入二级缓存中的数据:
		很少被修改
		不是很重要的数据, 允许出现偶尔的并发问题
	不适合放入二级缓存中的数据:
		经常被修改
		财务数据, 绝对不允许出现并发问题
		与其他应用数据共享的数据

事务处理

1. 处理并发
	不考虑事务隔离级别造成的问题:脏读、不可重复读、虚度;(研究丢失更新)
	丢失更新:两个线程中的不同事务同时操作一条数据,后提交的事务覆盖了先提交事务的数据
	解决丢失更新办法:
		悲观锁:认为丢失更新一定发生。采用数据库的锁机制(一般用排它锁)。即在查询数据时对数据进行锁定
			共享锁(读):一张表可以添加多个读锁。select * from XXX lock in share mode;
			多把读锁,任何一方都不能写。
			排它锁(写):一张表只能添加一个锁,与任何锁排斥.select * from XXX update;
			执行任何的改语句都会自动加一把排它锁
		乐观锁:假设不会发生丢失更新。不依赖底层数据库的锁。给实体增加一个版本字段
2. 在hibernate配置文件中设置事务隔离级别
	脏读、不可重复读、虚读通过设置隔离级别来防止
	1	READ UNCOMMITTED
	2	READ COMMITTED
	4	REPEATABLE-READ 
	8	SERIALIZABLE
	通过配置参数hibernate.connection.isolation=1|2|4|8进行设置
	注:MySQL默认是4;Oracle默认是2
3. 用外部数据源维护数据库连接池:配置C3P0
	1). 把C3P0的jar包加入到构建路径
	2). hibernate.cfg.xml加入以下配置(参考:etc/hibernate.properties)
	<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>