我在学校时整理的笔记,我是个新手,请谅解我写不好的地方,在后面我会改,有问题可以留言,我们共同解决,共同进步,谢谢大家!
第一次课:Hibernate概念、运行流程
1. hibernate是什么?
1) hibernate 是一个框架(framework)
2) hibernate 是一个orm框架 []
l orm (object relation mapping) 对象关系映射 框架
o object -> 业务层(只对对象操作)
r relation-> 关系数据库
m mapping 对象关系映射文件
1) hibernate 处于我们项目的持久层位置(正因为如此,所以有人又把hibernate称为 持久层框架)
2) hibernate 实际上就是对jdbc进行了轻量级的封装.
3) hibernate 的基础还是我们java 反射机制
除了hiberante 这个orm框架,还有一些:
apache ojb / toplink / ibatis / ejb cmp
Apache OJB ()
Cayenne ()
Jaxor ()
Hibernate ()
iBatis ()
jRelationalFramework ()
mirage ()
SMYLE ()
TopLink ()
把对象持久化: 把对象的信息保存到数据库或者是文件.
总结: hibernate 是对jdbc进行轻量级封装的 orm 框架,充当项目的持久层.
2.Hibernate框架的特点.优势:
a) 一款持久层框架
b) 可以帮助我们提高开发效率:生成部分SQL语句、数据库源、缓存、抓取策略
c) 让我们的开发具有面向对象化
3.Hibernate环境搭建、流程分析:
a) Hibernate有主配置文件:配置了连接数据库的配置、ORM映射文件的配置
b) 我们需要创建一个HibernateSessionFactory:来加载hibernate配置文件.由于要读取XML所以此类设置为单例模式
4.创建Hibernate的主配置配置文件,Orm映射文件
a) 主配置文件,配置数据库类型与连接, 手动配置我们的hibernate.cfg.xml文件,该文件用于配置 连接的数据库的类型,driver,
,用户名,密码 ,url ....同时管理 对象关系映射文件 ,该文件的名称,我们一般不修改
hibernate.cfg.xml
1 <?xml version='1.0' encoding='UTF-8'?> 2 3 <!DOCTYPE hibernate-configuration PUBLIC 4 5 "-//Hibernate/Hibernate Configuration DTD 3.0//EN" 6 7 "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> 8 9 <hibernate-configuration> 10 11 <session-factory> 12 13 <!-- 这是hibernate的核心文件,它的主要作用包括1.配置连接数据库的类型... --> 14 15 <!-- 配置dialect方言,明确告诉hibernate连接是哪种数据库 --> 16 17 <property name="dialect">org.hibernate.dialect.SQLServerDialect</property> 18 19 <property name="connection.url">jdbc:jtds:sqlserver://localhost:1433/shopping</property> 20 21 <property name="connection.username">sa</property> 22 23 <property name="connection.password">123</property> 24 25 <!-- 配置使用的driver --> 26 27 <property name="connection.driver_class">net.sourceforge.jtds.jdbc.Driver</property> 28 29 <!-- 是否从控制台打印sql语句 --> 30 31 <property name="show_sql">true</property> 32 33 <!-- 让hibernate给我们自动创建表 create :如果没有该表则创建. --> 34 35 <property name="hbm2ddl.auto">create</property> 36 37 <!-- 这个是用于指定对象映射文件的 --> 38 39 <mapping resource="it/shopping/model/Goods.hbm.xml"/> 40 41 </session-factory> 42 43 </hibernate-configuration>
b)
orm映射文件
对象关系映射文件: 作用是用于指定 domain对象和表的映射关系. ,该文件的取名有规范:
domain对象.hbm.xml,一般我们放在 和domain对象同一个文件夹下(包下)
1 <?xml version="1.0" encoding="utf-8"?> 2 3 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 4 5 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 6 7 8 9 <hibernate-mapping> 10 11 <!-- 这是一个对象关系映射文件Goods和goods关联 --> 12 13 <class name="it.shopping.model.Goods" table="goods"> 14 15 <!-- id元素用于指定主键属性 --> 16 17 <id name="sgid" type="java.lang.Integer" column="sgid"> 18 19 <!--可选的<generator>子元素是一个Java类的名字, 用来为该持久化类的实例生成唯一的标识。如果这个生成器实例需要某些配置值或者初始化参数, 用<param>元素来传递。(生成策略) --> 20 21 <!-- 该元素用于指定主键值生成策略hilo native increment sequence uuid --> 22 23 <generator class="native"> 24 25 <param name=""></param> 26 27 </generator> 28 29 </id> 30 31 <property name="sgname" type="java.lang.String" column="sgname"/> 32 33 <property name="sgprice" type="java.lang.Double" column="sgprice" /> 34 35 <property name="sgpic" type="java.lang.String" column="sgpic"/> 36 37 </class> 38 39 </hibernate-mapping>
5.创建HibernateSessionFactory类(会话工厂类)
单例模式
1 /* 2 3 * 读取XML配置文件, 拿到SessionFactory 4 5 * 6 7 * 通过sessionFactory得到session(connection) 8 9 * */ 10 11 public class HibernateSessionFactory { 12 13 14 15 private static SessionFactory sessionFactory=null; 16 17 18 19 private HibernateSessionFactory() 20 21 {} 22 23 24 25 static{ 26 27 //加载xml文件(默认是hibernate.cfg.xml)如果文件名不是这个,则在configure(“写上hibernate主配置文件”); 28 29 //创建Configuration,该对象用于读取hibernate.cfg.xml,并完成初始化 30 31 Configuration cfg=new Configuration().configure(); 32 33 //创建SessoinFactory[这是一个会话工厂,是一个重量级的对象] 34 35 sessionFactory=cfg.buildSessionFactory(); 36 37 } 38 39 40 41 public static SessionFactory getSessionFactory() 42 43 { 44 45 return sessionFactory; 46 47 } 48 49 50 51 //创建Sessoin 相当于jdbc Connection[ servelt HttpSession ,也不是 jsp session] 52 53 public static Session getSession() 54 55 { 56 57 return sessionFactory.openSession(); 58 59 } 60 61 62 63 public static void closeSession(Session session) 64 65 { 66 67 if(session!=null && session.isOpen()) 68 69 { 70 71 session.close(); 72 73 } 74 75 } 76 77 }
6.调用session的增、删、查、改 方法完成数据的持久化操作
1 public class GoodsService implements IGoods { 2 3 4 5 @Override 6 7 public void deleteGoods(int sgid) { 8 9 //获得会话(相当于jdbc Connection) 10 11 Session session=HibernateSessionFactory.getSession(); 12 13 //对hiberate而言,要求程序员,在进行 增加,删除,修改的时候使用事务提交 14 15 //开始事务 16 17 session.getTransaction().begin(); 18 19 //删除记录 20 21 session.delete(new Goods(sgid)); 22 23 //提交事务 24 25 session.getTransaction().commit(); 26 27 HibernateSessionFactory.closeSession(session); 28 29 } 30 31 }
7. *注意
请解释什么是pojo类,它有什么要求:
1) pojo类是和一张表对应
2) 一般我们放在 com.xxx.domain下
3) pojo 需要一个主键属性(用于标识一个pojo对象)
4) 除了主键属性外,它应当还有其属性,属性的访问权限是private
5) 提供 set /get 方法
6) 它应当有一个无参的构造方法(hibernate 反射)
7) pojo类其实就是javabean/ 有些老程序员 叫他 date对象
8. hibernate的核心类和接口
① Configuration 类
它的用处是:
- 读取hibernate.cfg.xml
- 管理对象关系映射文件 <mapping resource=””>
- 加载hibernate 的驱动,url ,用户..
- 管理hibernate配置信息
② hibernate.cfg.xml
③ 对象关系映射文件(***.hbm.xml)
④ SessionFactory接口 (会话工厂)
- 可以缓存sql语句和数据(称为session级缓存)!!
- 是一个重量级的类,因此我们需要保证一个数据库,有一个SessionFactroy
这里我们讨论一个通过SessionFactory 获取 Session的两个方法 openSession() 一个 getCurrentSession();
1) openSession() 是获取一个新的session
2) getCurrentSession () 获取和当前线程绑定的session,换言之,在同一个线程中,我们获取的session是同一session,这样可以利于事务控制
<!-- 下面的配置表示支持 通过 getCurrentSession()获取session 这样session在通过一线程中始终是一个,这样利于事务的控制 --> <!--<property name="current_session_context_class">thread</property> -->
3) 如果希望使用 getCurrentSession 需要配置 hibernate.cfg.xml中配置.
4) 如何选择
原则:
①如果需要在同一线程中,保证使用同一个Session则,使用getCurrentSession()
②如果在一个线程中,需要使用不同的Session,则使用opentSession()
- 通过 getCurrentSession() 获取的session在事务提交后,会自动关闭,通过openSession()获取的session则必须手动关闭
- 如果是通过getCurrentSession() 获取 sesssion ,进行查询需要事务提交.
全局事务和本地事务
jndi
l 如何确定你的session有没有及时关闭
windows cmd netstat –an [oracle 1521 mysql 3306 sql server 1433]
linux/unix netstat –anp top
② session接口
它的主要功能和作用是:
- Session一个实例代表与数据库的一次操作(当然一次操作可以是crud组合)
- Session实例通过SessionFactory获取,用完需要关闭。
- Session是线程不同步的(不安全),因此要保证在同一线程中使用,可以用getCurrentSessiong()。
- Session可以看做是持久化管理器,它是与持久化操作相关的接口
9. get vs load
l 如果查询不到数据,get 会返回 null,但是不会报错, load 如果查询不到数据,则报错ObjectNotFoundException
l 使用get 去查询数据,(先到一级/二级)会立即向db发出查询请求(select ...), 如果你使用的是 load查询数据,(先到一级、二级))即使查询到对象,返回的是一个代理对象,如果后面没有使用查询结果,它不会真的向数据库发select ,当程序员使用查询结果的时候才真的发出select ,这个现象我们称为懒加载(lazy)
l 通过修改配置文件,我们可以取消懒加载
l <class name="Employee" lazy="false" table="employee">
l 如何选择使用哪个: 如果你确定DB中有这个对象就用load(),不确定就用get()(这样效率高)
10. 我们对获取session的工具类,升级,让它可以直接返回 全新的session和线程相关的session
代码:
1 package com.hsp.util; 2 3 import org.hibernate.Session; 4 5 import org.hibernate.SessionFactory; 6 7 import org.hibernate.cfg.Configuration; 8 9 final public class HibernateUtil { //SqlHelper 10 11 private static SessionFactory sessionFactory=null; 12 13 //使用线程局部模式 14 15 private static ThreadLocal<Session> threadLocal=new ThreadLocal<Session>(); 16 17 private HibernateUtil(){}; 18 19 static { 20 21 sessionFactory=new Configuration().configure("com/hsp/config/hsp.cfg.xml").buildSessionFactory(); 22 23 } 24 25 26 27 //获取全新的全新的sesession 28 29 public static Session openSession(){ 30 31 return sessionFactory.openSession(); 32 33 } 34 35 //获取和线程关联的session 36 37 public static Session getCurrentSession(){ 38 39 40 41 Session session=threadLocal.get(); 42 43 //判断是否得到 44 45 if(session==null){ 46 47 session=sessionFactory.openSession(); 48 49 //把session对象设置到 threadLocal,相当于该session已经和线程绑定 50 51 threadLocal.set(session); 52 53 } 54 55 return session; 56 57 } 58 59 }
第二次课:反向工程生成Pojo类及对象关系映射配置文件和(Hql)Query 删查改的实现
1. 反向工程,根据数据库的表生成pojo实体类,***.hbm.xml对象关系映射文件,以及更新hibernate.cfg.xml配置文件(前提是已经导入Hibernate包(用Eclipce软件导入,如果没有高版本,则先导入软件的最高版本,用拷贝到lib文件夹,然后删除包,把最新版的包复制到lib文件夹中去))
a) 打开DB Browser,配置数据库
b) 打开dbo中的表,右键Hibernate Reverse Engineering
c) 选择文件夹及要生成所在的包
d) 修改一下对象关系映射文件的class属性
<class name="it.shopping.model.Goods" table="goods" schema="dbo" dynamic-insert="true" dynamic-update="true">
去掉对应的数据库名称,添加dynamic-insert=”true” dynamice-update=”true” 意思是hibernate生成的sql语句 如有没有赋值,则在sql语句没显示出来,动态生成sql语句,如果用到二级缓存的话,则dynamice-update=”true”则有问题
2. 为什么要学习hql(hibernate query language)->这个是官方推荐,功能强大
a) session方式
? 删除
session.delete(对象) -> 批量删除
? 添加
session.save session.persist
? 修改->批量修改
sessin.update(对象)
查询 对象 obj
obj.setXXX();
? 查询
load get
b) hql详解:
hibernate 设计者 推荐我们在设计表的时候,应当每张表有一个主键,而且该主键最好不含业务逻辑
我们现在使用hibernate工具,自动生成 domain 对象 和映射文件,如果我们的表有主外键的关系,则应当先映射主表,再映射从表
* uniqueResult方法
如果我们检索一个对象,明确知道最多只有一个对象,则建议使用该方法:
具体用法如下:
Student s=(Student) session.createQuery("from Student where sid='20050003'").uniqueResult();
System.out.println(s.getSname());
*distinct的用法
过滤重复的记录
//比如,显示所有学生的性别和年龄.
1 List list=session.createQuery("select distinct sage,ssex from Student").list(); 2 3 for(int i=0;i<list.size();i++){ 4 5 Object [] objs=(Object[]) list.get(i); 6 7 System.out.println(objs[0].toString()+" "+objs[1].toString()); 8 9 }
*between and..
1 List list=session.createQuery("select distinct sage,ssex,sname from Student where sage between 20 and 22").list(); 2 3 for(int i=0;i<list.size();i++){ 4 5 Object [] objs=(Object[]) list.get(i); 6 7 System.out.println(objs[0].toString()+" "+objs[1].toString()+objs[2].toString());
*in /not in
//查询计算机系和外语系的学生信息
1 List<Student> list=session.createQuery("from Student where sdept in ('计算机系','外语系')").list(); 2 3 //取出1. for 增强 4 5 for(Student s:list){ 6 7 System.out.println(s.getSname()+" "+s.getSaddress()+" "+s.getSdept()); 8 9 }
*group by使用
//显示各个系的学生的平均年龄
1 List<Object[]> list=session.createQuery("select avg(sage),sdept from Student group by sdept").list(); 2 3 //取出1. for 增强 4 5 for(Object[] obj:list){ 6 7 System.out.println(obj[0].toString()+" "+obj[1].toString()); 8 9 }
*having的使用
//1.对分组查询后的结果,进行筛选:比如请显示人数大于3的系名称
1 //a. 查询各个系分别有多少学生. 2 3 List<Object[]> list=session.createQuery("select count(*) as c1,sdept from Student group by sdept having count(*)>3").list(); 4 5 //取出1. for 增强 6 7 for(Object[] obj:list){ 8 9 System.out.println(obj[0].toString()+" "+obj[1].toString()); 10 11 }
3.hibernate开发的三种方式中的:
编写domain object + 映射文件 ------> 创建出对应的数据库,
这里我们说明如果要自动的创建出对应的数据库,需要做配置(hibernate.cfg.xml).
<property name="hbm2ddl.auto">create</property>
这里有四个配置值: create , update , create-drop, validate
create : 当我们的应用程序加载hibernate.cfg.xml [ new Configuration().config(); ]就会根据映射文件,创建出数据库, 每次都会重新创建, 原来表中的数据就没有!!!
update: 如果数据库中没有该表,则创建,如果有表,则看有没有变化,如果有变化,则更新.
create-drop: 在显示关闭 sessionFactory时,将drop掉数据库的schema
validate: 相当于每次插入数据之前都会验证数据库中的表结构和hbm文件的结构是否一致
在开发测试中,我们配置哪个都可以测试,但是如果项目发布后,最好自己配置一次,让对应的数据库生成,完后取消配置,
domain对象的细节:
1. 需要一个无参的构造函数(用于hibernate反射该对象)
2. 应当有一个无业务逻辑的主键属性.
3. 给每个属性提供 get set方法.
4. 在domian对象中的属性,只有配置到了对象映射文件后,才会被hiberante管理.
5. 属性一般是private范围
4. 对对象关系映射文件的说明
对象关系文件中,有些属性是可以不配,hibernate会采用默认机制,比如
<class table=”?” > table 值不配,则以类的小写做表名
<property type=”?”> type不配置,则hibernate会根据类的属性类型,选择一个适当的类型
hibernate对象的三种状态,转换图:
面试图:如果判断一个对象处于怎样的状态?
主要的依据是: 1. 看该对象是否处于session, 2, 看在数据库中有没有对应的记录
瞬时态: 没有session管理,同时数据库没有对应记录
持久态: 有session管理,同时在数据库中有记录
脱管态/游离态: 没有session管理,但是在数据库中有记录.
5.(Hql) Query查询实现、分页:
a)Hibernate提供的面向对象的查询语句 HQL,它查询的对象不是表.而是类,在通过映射文件找到相关的表和表的字段
在session.createQuery(“Hql语句”);默认select * 不用写,From 对象类名)
b)适用范围:多条记录的操作 (Query没有插入对象的实现)
6. BETWEEN、AND、IN、聚合查询、基于XML的SQL语句配置
获取query引用[这里 Goods不是表.而是model类名]
[where 后面的条件可以是类的属性名,也可以是表的字段,安照hibernate规定,我们还是应该使用类的属性名.]
通过list方法获取结果,这个list会自动的将封装成对应的domain对象
所以我们jdbc进行二次封装的工作没有
a) AND BETWEEN 与分页的用法
1 SimpleDateFormat simpleDate = new SimpleDateFormat("yyyy-MM-dd"); 2 3 //要转换的日期格式类型 simpleDate.parse(“string类型的日期”); 4 5 List<Goods> goodsList = session.createQuery( 6 7 "FROM Goods g WHERE g.sgname LIKE :sgname AND g.sgdate BETWEEN :start AND :end").setString("sgname", 8 9 "%" + sgname + "%") 10 11 .setDate("start",simpleDate.parse("2011-01-01")) 12 13 .setDate("end", simpleDate.parse("2013-01-01")) 14 15 .setFirstResult((currentPage-1)*SIZE) //分页操作 setFirstResult配置要提取的首记录 currentPage当页的页数 16 17 .setMaxResults(SIZE) // setMaxResults每次加载的记录数 SIZE 私有的静态成员常量(要显示页的行数) 18 19 .list();
b) IN多参数实现
1 List<Goods> goodsList = session.createQuery( 2 3 "FROM Goods g WHERE g.sgname LIKE :sgname AND g.sgprice IN (:sgprice) ") 4 5 .setString("sgname","%" + sgname + "%") 6 7 .setParameterList("sgprice",new Double[]{24.0,12d,34.56})//多参数时用setParameterList数组 8 9 .setFirstResult((currentPage-1)*SIZE) // setFirstResult配置要提取的首记录 10 11 .setMaxResults(SIZE) // setMaxResults每次加载的记录数 12 13 .list();
c) xml的sql语句配置
在对象关系映射配置文件后面配置
1 <query name="getCountPage"> 2 3 <![CDATA[ 4 5 select count(*) from Goods g where g.sgname like :sgname 6 7 ]]> 8 9 </query>
使用时
1 String count=session.getNamedQuery("getCountPage") 2 3 .setString("sgname", "%"+sgname+"%") 4 5 .uniqueResult().toString();
7. Query与Session比较
Query对于多条记录 Session对于一条记录
8. criteria 接口的简单使用
快如入门:
1 Session session=HibernateUtil.getCurrentSession(); 2 3 Transaction ts=null; 4 5 try { 6 7 ts=session.beginTransaction(); 8 9 Criteria cri=session.createCriteria(Goods.class). 10 11 setMaxResults(2).addOrder(Order.desc("id") ); 12 13 List<Goods> list=cri.list(); 14 15 for(Goods e: list){ 16 17 System.out.println(e.getSgid ()); 18 19 } 20 21 ts.commit(); 22 23 } catch (Exception e) { 24 25 if(ts!=null){ 26 27 ts.rollback(); 28 29 } 30 31 throw new RuntimeException(e.getMessage()); 32 33 }finally{ 34 35 //关闭session 36 37 if(session!=null&&session.isOpen()){ 38 39 session.close(); 40 41 } 42 43 }
第三次课:Hibernate关联映射、抓取策略、懒加载
1. 一对多、多对一关联查询
a) List Set: Set不能存储重复的对象, 而且是无序的
b) 在Hibernate中如果使用List会出现很多NULL值. 所以推荐使用Set
1 <many-to-one name="scategory" class="it.shopping.model.Scategory" lazy="false" fetch="join" > 2 3 <column name="scid"/> 4 5 </many-to-one>
d) 多对一对象关系映射文件配置
1 <set name="goodses" lazy="false" fetch="subselect"> 2 3 <!-- 只是子表中的外键 --> 4 5 <key column="scid"></key> 6 7 <one-to-many class="it.shopping.model.Goods"/> 8 9 </set>
e)多对多原理图
1 <set name="studcourses" inverse="true"> 2 3 <key> 4 5 <column name="CID" precision="22" scale="0" /> 6 7 </key> 8 9 <one-to-many class="com.sina.domain.Studcourse" /> 10 11 </set>
1 <set name="studcourses" inverse="true"> 2 3 <key> 4 5 <column name="SID" precision="22" scale="0" /> 6 7 </key> 8 9 <one-to-many class="com.sina.domain.Studcourse" /> 10 11 </set>
Studcourse类对象关系映射文件配置
1 <many-to-one name="student" class="com.sina.domain.Student" fetch="select"> 2 3 <column name="SID" precision="22" scale="0" /> 4 5 </many-to-one> 6 7 <many-to-one name="course" class="com.sina.domain.Course" fetch="select"> 8 9 <column name="CID" precision="22" scale="0" /> 10 11 </many-to-one>
1. Hibernate抓取策略测试
2. lazy="false":配置是否查询关联表
3. fetch="join" 配置如何查询关联表
HQL:如果要查询关联表, 采用SELECT的话,会出现N+1的问题
Hibernate抓取策略
懒加载
1. 懒加载的概念
懒加载(Load On Demand)是一种独特而又强大的数据获取方法 ,
是指程序推迟访问数据库,这样做可以保证有时候不必要的访问数
据库,因为访问一次数据库是比较耗时的。
2. 简述:当我们查询一个对象的时候,在默认情况下,返回的只是该对象的普通属性,当用户去使用对象属性时,才会向数据库发出再一次的查询.这种现象我们称为 lazy现象.
解决方法可以这样:
a) 显示初始化 Hibernate.initized(代理对象)
b) 修改对象关系文件 lazy 改写 lazy=false
c) 通过过滤器(web项目) openSessionInView
第四次课:多对一联接优化、一级缓存、二级缓存级联
1. 多对一连接查询解决方案:
a) 基于HQL的连接查询
i. FROM Goods g,Scategory s WHERE g.scategory.scid=s.scid AND sgname LIKE :sgname
ii. FROM Goods g RIGHT JOIN g.scategory WHERE sgname LIKE :sgname
b) 基于SQL的连接查询: Hibernate提供了原生态的SQL实现.
1 List<Object> goodsList=session.createSQLQuery("SELECT * FROM goods g JOIN scategory s ON g.scid=s.scid WHERE sgname LIKE :sgname") 2 3 .addEntity(Goods.class) 4 5 .addEntity(Scategory.class) 6 7 .setString("sgname", "%" + sgname + "%") 8 9 .list();
2. 基于Session级别的一级缓存:每个session都有一级缓存,一级缓存自动创建,自动消失,一级缓存对数据的在读取没有任何意义,而是为了事务的存在而存在的
a) Hibernate对象的三中状态:
b) 瞬时态:简单的说就是你在程序里面new一个对象,还没有和session关联
c) 持久态:对象和session有了关联,对象处于hibernate框架管理中
d) 游离态:在持久态的情况下,对象和session失去了关联,比如session.close()或session.flush()
e) 后,即游离态,但数据已经存储到了数据库
3. 基于SessionFactory级别的二级缓存:优化数据的读取
a) <property name="hibernate.cache.use_second_level_cache">true</property>
b) <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
c) <class-cache class="" usage="read-only"/>
缓存主要是为了提高查询效率, 但是缓存要注意数据的同步问题
Session.get支持二级缓存, 在查询之前先到二级缓存查询.如果有则不会查询数据库
Session. Delete update 都支持二级缓存(Session的操作也会更新二级缓存)
Query的List不支持二级缓存.直接在数据库中查询.但是查询出来的数据会存放到二级缓存中:List是查询多条记录,二级缓存的命中率低,因此数据库直接读取
read-only:二级缓存,只读,可以提高性能
session.save(): 可以提交
如果二级缓存有数据, 在delete update() 操作会报异常,如果配置了二级缓存为ready only 则不能delete update 但是可以用save get() 取代的是HQL的Query操作来取代delete update
eg:
1 <!-- 配置二级缓存 --> 2 3 <property name="hibernate.cache.use_second_level_cache">true</property> 4 5 <property name="cache.provider_class">org.hibernate.cache.OSCacheProvider</property> 6 7 <property name="hibernate.generate_statistics">true</property> 8 9 <!-- 这个是用于指定对象映射文件的 --> 10 11 <mapping resource="it/shopping/model/Goods.hbm.xml" /> 12 13 <mapping resource="it/shopping/model/Scategory.hbm.xml" /> 14 15 <!-- 指定对象要缓存 --> 16 17 <class-cache usage="read-write" class="it.shopping.model.Goods" /> 18 19 <class-cache usage="read-write" class="it.shopping.model.Scategory"/>
一二级缓存结论:
1. Session 一级缓存是为事务准备的 随着事务的提交而存在
2. List 只填充缓存,不会利用缓存
3. Hibernate会自行维护一、二级缓存中的数据,以保证缓存中的数据和数据库中的真实数据的一致性,而原生态SQL语句不会同步数据
* 为什么需要缓存?
看一个案例:->原理图
从上图看出: 当我们去查询对象的时候,首先到一级缓存去取数据,如果有,则不到数据库中取,如果没有则到数据库中取,同时在一级缓存中放入对象.
*一级缓存的细节
① 什么操作会向一级缓存放入数据
save,update,saveOrUpdate,load,get,list,iterate,lock
save 案例:
1 //添加一个学生 2 3 Student student=new Student(); 4 5 student.setName("小东"); 6 7 s.save(student);//放入一级缓存 8 9 //我马上查询 10 11 Student stu2=(Student) s.get(Student.class, student.getId()); //select 12 13 System.out.println("你刚刚加入的学生名字是"+stu2.getName());
② 什么操作会从一级缓存取数据.
get / load / list
get / load 会首先从一级缓存中取,如没有.再有不同的操作[get 会立即向数据库发请求,而load 会返回一个代理对象,直到用户真的去使用数据,才会向数据库发请求]
?list 会不会从session缓存取数据?
案例:
1 //查询45号学生 2 3 Student stu=(Student) s.get(Student.class, 45); 4 5 System.out.println("|||||||||||||||||||"); 6 7 String hql="from Student where id=45"; 8 9 Student stu2=(Student) s.createQuery(hql).uniqueResult(); 10 11 System.out.println(stu2.getName());
从上面的案例,我看出 query.list() query.uniueResut() 不会从一级缓取数据! 但是query.list 或者query.uniqueRestu() 会向一级缓存放数据的.
③ 一级缓存不需要配置,就可以使用,它本身没有保护机制,所以我们程序员要考虑这个问题,我们可以同 evict 或者 clear来清除session缓存中对象. evict 是清除一个对象,clear是清除所有的sesion缓存对象
④ session级缓存中对象的生命周期, 当session关闭后,就自动销毁.
⑤ 我们自己用HashMap来模拟一个Session缓存,加深对缓存的深入.
1 package com.hsp.view; 2 3 import java.util.ArrayList; 4 5 import java.util.HashMap; 6 7 import java.util.List; 8 9 import java.util.Map; 10 11 public class MyCache { 12 13 //使用map来模拟缓存 14 15 static Map<Integer,Student> maps=new HashMap<Integer,Student>(); 16 17 public static void main(String[] args) { 18 19 // TODO Auto-generated method stub 20 21 getStudent(1); 22 23 getStudent(1); 24 25 getStudent(1); 26 27 getStudent(1); 28 29 getStudent(3); 30 31 getStudent(3); 32 33 } 34 35 public static Student getStudent(Integer id){ //s.get() 36 37 //先到缓存去 38 39 if(maps.containsKey(id)){ 40 41 //在缓存有 42 43 System.out.println("从缓存取出"); 44 45 return maps.get(id); 46 47 }else{ 48 49 System.out.println("从数据库中取"); 50 51 //到数据库取 52 53 Student stu=MyDB.getStudentFromDB(id); 54 55 //放入缓存 56 57 maps.put(id, stu); 58 59 return stu; 60 61 } 62 63 } 64 65 } 66 67 //我的数据库 68 69 class MyDB{ 70 71 static List<Student> lists=new ArrayList<Student>(); 72 73 //初始化数据库,假设有三个学生 74 75 static{ 76 77 Student s1=new Student(); 78 79 s1.setId(1); 80 81 s1.setName("aaa"); 82 83 Student s2=new Student(); 84 85 s2.setId(2); 86 87 s2.setName("bbb"); 88 89 Student s3=new Student(); 90 91 s3.setId(3); 92 93 s3.setName("ccc"); 94 95 lists.add(s1); 96 97 lists.add(s2); 98 99 lists.add(s3); 100 101 } 102 103 public static Student getStudentFromDB(Integer id){ 104 105 for(Student s: lists){ 106 107 if(s.getId().equals(id)){ 108 109 return s; 110 111 } 112 113 } 114 115 return null;// 在数据库中没有. 116 117 } 118 119 } 120 121 class Student{ 122 123 private Integer id; 124 125 private String name; 126 127 public Integer getId() { 128 129 return id; 130 131 } 132 133 public void setId(Integer id) { 134 135 this.id = id; 136 137 } 138 139 public String getName() { 140 141 return name; 142 143 } 144 145 public void setName(String name) { 146 147 this.name = name; 148 149 } 150 151 }
*为什么需要二级缓存?
因为一级缓存有限(生命周期短),所以我们需要二级缓存(SessionFactory缓存)来弥补这个问题
1. 需要配置
2. 二级缓存是交给第三方去处理,常见的Hashtable , OSCache , EHCache
3. 二级缓存的原理
4. 二级缓存的对象可能放在内存,也可能放在磁盘.
* 快速入门案例
使用OsCache来演示二级缓存的使用.
1. 配置二级缓存
对配置说明:
<property name="hbm2ddl.auto">update</property> <!-- 启动二级缓存 --> <property name="cache.use_second_level_cache">true</property> <!-- 指定使用哪种二级缓存 --> <property name="cache.provider_class">org.hibernate.cache.OSCacheProvider</property> <mapping resource="com/hsp/domain/Department.hbm.xml" /> <mapping resource="com/hsp/domain/Student.hbm.xml" /> <!-- 指定哪个domain启用二级缓存 特别说明二级缓存策略: 1. read-only 2. read-write 3. nonstrict-read-write 4. transcational --> <class-cache class="com.hsp.domain.Student" usage="read-write"/>
2. 可以文件放在 src目录下,这样你可以指定放入二级缓存的对象capacity 大小. 默认1000
3 使用
1 // TODO Auto-generated method stub 2 3 //通过获取一个sesion,让hibernate框架运行(config->加载hibernate.cfg.xml) 4 5 Session s=null; 6 7 Transaction tx=null; 8 9 try { 10 11 //我们使用基础模板来讲解. 12 13 s=HibernateUtil.openSession(); 14 15 tx=s.beginTransaction(); 16 17 //查询45号学生 18 19 Student stu1=(Student) s.get(Student.class, 45);//45->一级缓存 20 21 System.out.println(stu1.getName()); 22 23 tx.commit(); 24 25 } catch (Exception e) { 26 27 e.printStackTrace(); 28 29 if(tx!=null){ 30 31 tx.rollback(); 32 33 } 34 35 }finally{ 36 37 if(s!=null && s.isOpen()){ 38 39 s.close(); 40 41 } 42 43 } 44 45 System.out.println("*********************************"); 46 47 try { 48 49 //我们使用基础模板来讲解. 50 51 s=HibernateUtil.openSession(); 52 53 tx=s.beginTransaction(); 54 55 //查询45号学生 56 57 Student stu1=(Student) s.get(Student.class, 45); 58 59 System.out.println(stu1.getName()); 60 61 Student stu3=(Student) s.get(Student.class, 46); 62 63 System.out.println(stu3.getName()); 64 65 tx.commit(); 66 67 } catch (Exception e) { 68 69 e.printStackTrace(); 70 71 if(tx!=null){ 72 73 tx.rollback(); 74 75 } 76 77 }finally{ 78 79 if(s!=null && s.isOpen()){ 80 81 s.close(); 82 83 } 84 85 } 86 87 //完成一个统计,统计的信息在Sessfactory 88 89 //SessionFactory对象. 90 91 Statistics statistics= HibernateUtil.getSessionFactory().getStatistics(); 92 93 System.out.println(statistics); 94 95 System.out.println("放入"+statistics.getSecondLevelCachePutCount()); 96 97 System.out.println("命中"+statistics.getSecondLevelCacheHitCount()); 98 99 System.out.println("错过"+statistics.getSecondLevelCacheMissCount());
3. 在配置了二级缓存后,请大家要注意可以通过 Statistics,查看你的配置命中率高不高
级联操作
所谓级联操作就是说,当你进行某个操作(添加/修改/删除...),就由hibernate自动给你完成.
比如: Department <---->Student 对象关系,我希望当我删除一个department ,那么就自动删除该部门的所有学生?
再比如: bbs项目
主帖<---->回帖 , 把主帖删除,那我们就希望把该主帖的回帖自动删除,这样我们可以使用级联(cascade)操作
\
案例:如何配置级联操作,当删除某个部门的时候,我们自动删除其学生.
首先我们在 配置文件中修改:
1 <!-- 配置one-to-many关系 2 3 cascade="delete" 当删除该部门的时候(主对象,则级联删除它的学生从对象) --> 4 5 <set name="stus" cascade="delete"> 6 7 <!-- 指定Student类对应的外键 --> 8 9 <key column="dept_id" /> 10 11 <one-to-many class="Student" /> 12 13 </set>
java代码中操作:
//演示删除级联
//获取到某个部分
Department department=(Department) s.get(Department.class, 41);
s.delete(department);
*演示save-update
配置文件:
1 <set name="stus" cascade="save-update"> 2 3 <!-- 指定Student类对应的外键 --> 4 5 <key column="dept_id" /> 6 7 <one-to-many class="Student" /> 8 9 </set>
代码:
1 //添加学生 2 3 Department department=new Department(); 4 5 department.setName("业务部门3"); 6 7 Student stu1=new Student(); 8 9 stu1.setName("顺平6"); 10 11 // stu1.setDept(department); 12 13 Student stu2=new Student(); 14 15 stu2.setName("小明6"); 16 17 // stu2.setDept(department); 18 19 Set<Student> students=new HashSet<Student>(); 20 21 students.add(stu1); 22 23 students.add(stu2); 24 25 department.setStus(students); 26 27 s.save(department);
说明:
① 在集合属性和普通属性中都能使用cascade
② 一般讲cascade配置在one-to-many(one的一方,比如Employee-Department),和one-to-one(主对象一方)
* 主键增长策略
① increment
自增,每次增长1, 适用于所有数据库. 但是不要使用在多进程,主键类型是数值型
select max(id) from Student
② identity
自增,每次增长1, 适用于支持identity的数据(mysql,sql server), 主键类型是数值
③ sequence
④ native
会根据数据类型来选择,使用identity,sequence ,hilo
select hibernate_sequence.nextval from dual
主键类型是数值long , short ,int
<id name="id" type="java.lang.Integer"> <generator class="native"/> </id>
⑤ hilo
hilo标识符生成器由Hibernate按照一种high/low算法生成标识符
用法:
1 <id name=”id” type=”java.lang.Integer” column=”ID”> 2 3 <generator class=”hilo”> 4 5 <param name=”table”>my_hi_value</param> 6 7 <param name=”column”>next_value</param> 8 9 </generator> 10 11 </id>
⑥ uuid
会根据uuid算法,生成128-bit的字串
主键属性类型不能是数值型,而是字串型
⑦ assigned
用户自己设置主键值,所以主键属性类型可以是数值,字串
⑧ 映射复合主键
⑨ foreign
在one-to-one的关系中,有另一张表的主键(Person) 来决定 自己主键/外键( IdCard)
给出一个简单原则:
针对oracle [主键是int/long/short 建议使用 sequence] 主键是String 使用uuid或者assinged
针对 mysql [主键是 int/long/short 建议使用increment/assigend ,如果是字串 UUId/assigned]
针对 sql server [主键是 int/long/short 建议使用 identity/native/assinged ,如果主键是字串,使用uuid/assigned ]
one-to-one 又是基于主键的则使用foreign
u hibernate最佳实践(在什么项目中使用最好)
对于数据量大,性能要求高系统,不太使用使用hiberante.
主要用于事务操作比较多的项目(oa/某个行业软件[石油、税务、crm, 财务系统.]
olap->hibernate用的比较少 oltp->hibernate