Hibernate学习笔记

 

我在学校时整理的笔记,我是个新手,请谅解我写不好的地方,在后面我会改,有问题可以留言,我们共同解决,共同进步,谢谢大家!

第一次课: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 类

它的用处是:

  1. 读取hibernate.cfg.xml
  2. 管理对象关系映射文件 <mapping resource=””>
  3. 加载hibernate 的驱动,url ,用户..
  4. 管理hibernate配置信息

② hibernate.cfg.xml

③ 对象关系映射文件(***.hbm.xml)

④ SessionFactory接口 (会话工厂)

  1. 可以缓存sql语句和数据(称为session级缓存)!!
  2. 是一个重量级的类,因此我们需要保证一个数据库,有一个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()

  1. 通过 getCurrentSession() 获取的session在事务提交后,会自动关闭,通过openSession()获取的session则必须手动关闭
  2. 如果是通过getCurrentSession() 获取 sesssion ,进行查询需要事务提交.

 

全局事务和本地事务

 

jndi

l 如何确定你的session有没有及时关闭

windows  cmd  netstat –an      [oracle 1521 mysql 3306 sql server 1433]

linux/unix  netstat –anp  top 

 

② session接口

它的主要功能和作用是:

  1. Session一个实例代表与数据库的一次操作(当然一次操作可以是crud组合)
  2. Session实例通过SessionFactory获取,用完需要关闭。
  3. Session是线程不同步的(不安全),因此要保证在同一线程中使用,可以用getCurrentSessiong()。
  4. 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

c) 一对多对象关系映射文件配置

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)多对多原理图

course类对象关系映射文件配置

 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>

 

sstudents类对象关系映射文件配置

 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

转载于:https://www.cnblogs.com/zhjcnblog/archive/2012/10/13/2722434.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值