hibernate原理分析笔记

来源:互联网 发布:网上贷款软件排行 编辑:程序博客网 时间:2024/06/06 12:38
*********************************************
***此笔记是看了hibernate_传智视频教程后写的**
*********************************************
———————————————————————————
Hibernate大概执行流程
   1、应用程序先调用Configuration类,该类读取Hibernate配置文件及映射文件中的信息,
   2、并用这些信息生成一个SessionFactory对象,
   3、然后从SessionFactory对象生成一个Session对象,
   4、并用Session对象生成Transaction对象;
    A、可通过Session对象的get(),load(),save(),update(),delete()和saveOrUpdate()等方法对PO进行加载、保存、更新、删除、等操作;
    B、在查询的情况下,可通过Session对象生成一个Query对象,然后利用Query对象执行查询操作;如果没有异常,Transaction对象将提交这些操作到数据库中。
   5、关闭Session
   6、关闭SesstionFactory
———————————————————————————

hibernate工作原理:
   1.通过Configuration().configure();读取并解析hibernate.cfg.xml配置文件
   2.由hibernate.cfg.xml中的<mapping resource="com/xx/User.hbm.xml"/>读取并解析映射信息
   3.通过config.buildSessionFactory();//创建SessionFactory
   4.sessionFactory.openSession();//打开Sesssion
   5.session.beginTransaction();//创建事务Transation
   6.persistent operate持久化操作
   7.session.getTransaction().commit();//提交事务
   8.关闭Session
   9.关闭SesstionFactory
———————————————————————————

主流的ORM框架有Hibernat、TopLink、OJB

———————————————————————————

Session接口的几种主要方法
1. save,persist 保存数据,persist在事务外不会产生insert语句;
2. delete 删除对象;
3. update 更新对象,如果数据库中没有记录,会出异常
4. get 根据ID查,会立刻访问数据库,load 也是根据ID查,返回的是代理,不会立刻访问数据库,并且返回对象永远不为空;
5. saveOrUpdate,merge 根据ID和version的值来确定是save或update,调用merge你的对象还是托管的;
6. lock 把对象变成持久对象,但不会同步对象的状态,即不能被修改。 

transient(瞬时)、persistent(持久)、detached(脱管)

Query接口的几种常用方法
1. Query query = s.createQuery(hql); 
2. query .setXXX("XX","XX");
3. query.setFirstResult(200); //从第几条开始
   query.setMaxResults(10);   //取10条记录
4. List<User> list = query.list();// 相当于executQuery();
5. User u = (User) query.uniqueResult(); //获取一条唯一的记录,如果query 有多条记录使用此方法会报异常。

例子代码:
static void query(String name) {
Session s = null;
try {
s = HibernateUtil.getSession();
String hql = "from User as user where user.name=:n";// from Object
Query query = s.createQuery(hql);
// query.setString(0, name);
query.setString("n", name);

query.setFirstResult(200);
query.setMaxResults(10);

List<User> list = query.list();// executQuery();

User u = (User) query.uniqueResult();
System.out.print(u);

for (User user : list) {
System.out.println(user.getName());
}
} finally {
if (s != null)
s.close();
}
}

Criteria接口的几种主要方法
1. Criteria c = s.createCriteria(User.class);
2. c.add(Restrictions.eq("name", name));    //第一个name是实体的属性
   c.add(Restrictions.lt("birthday", new Date()));
3. c.setFirstResult(200); //从第几条开始
   c.setMaxResults(10);   //取10条记录
4. List<User> list = c.list();// 相当于executQuery();
5. User u = (User) c.uniqueResult(); //获取一条唯一的记录,如果Criteria 有多条记录使用此方法会报异常。

例子代码:
static void cri(String name) {
Session s = null;
try {
s = HibernateUtil.getSession();
Criteria c = s.createCriteria(User.class);
c.add(Restrictions.eq("name", name));
c.add(Restrictions.lt("birthday", new Date()));

c.setFirstResult(100);
c.setMaxResults(10);

List<User> list = c.list();// executQuery();

User u = (User) c.uniqueResult();
System.out.print(u);

for (User user : list) {
System.out.println(user.getName());
}
} finally {
if (s != null)
s.close();
}
}

———————————————————————————
关联关系
多对一(最常用)
一对多(少用),效率低
一对一
多对多(少用),有中间表,效率低

集合映射(set、list、array、map)
这些集合类都是Hibernate实现的类和JAVA中集合类不完全一样,set,list,map分别和JAVA的Set,List,Map接口对应,bag映射成JAVA的List。

集合的简单使用原则:大部份情况下用set(没有重复项),需要保证集合中的顺序用list,想用java.util.List又不需要保证顺序用bag。

———————————————————————————

cascade(级联)
cascade用来说明当对主对象进行某种操作时是否对其关联的从对象也作类似操作。
常用的cascade有:none,all,save-update,delete,lock,refresh,evict,replicate,persist,merge,delete-orphan(one-to-many)。一般对many-to-one,many-to-many不设置级联,在one-to-one和one-to-many中设置设联。

inverse是否放弃维护关联关系(即在java里两个对象产生关联时,对数据库表的影响),可以提高效率,在one-to-many和many-to-many的集合定义中使用,inverse="true"表示该对象不维护关联关系;该属性的值一般在使用有序集合时设置成false(注意hibernate的缺省值是false)。

one-to-many维护关联关系就是更新外键,inverse="true"设置在one这边,例如:部门与员工,在部门设置inverse="true";
many-to-many维护关联关系就是在中间表增减记录;inverse="true"可以任意设置一边,但不能同时都设置,例如:老师与学生,任意在一边设置都可以。

———————————————————————————

懒加载
通过asm和cglib两个包实现,Domain是非final的,主要是用来提高性能,比较少使用。
1. session.load懒加载
2. one-to-one(元素)懒加载:
  必需同时满足下面三个条件时才能实现懒加载
  1)lazy!=false  2)constrained=true  3)fetch=select
3. one-to-many(元素)懒加载: 1)lazy!=false  2)fetch=select,如果不加lazy配置是默认懒加载的;
4. many-to-one(元素)懒加载: 1)lazy!=false  2)fetch=select,如果不加lazy配置是默认懒加载的;
5. many-to-many(元素)懒加载: 1)lazy!=false  2)fetch=select,如果不加lazy配置是默认懒加载的;
6. 能够懒加载的对象都是被改写过的代理对象,当相关联的session没有关闭时,访问这些懒加载对象(代理对象)的属性(getId和getClass除外)hibernate会初始化这些代理,或用hibernate.initalize(proxy)来初始化代理对象;当相关联的session关闭后,再访问懒加载的对象将出现异常。

——————————————————————————— 


缓存
缓存的作用主要用来提高性能,可以简单的理解成一个Map,使用缓存涉及到三个操作:把数据放入缓存、从缓存中获取数据、删除缓存中的无效数据。
一级缓存
    session级共享,默认开启,save、update、saveOrUpdate、load、get、list、iterate、lock这些方法都会将对象放在一级缓存中,一级缓存不能控制缓存的数量,所以要注意大批量操作数据时可能造成内存溢出,可以用evict、clear方法清除缓存中的内容,如果session关闭后是无效的,Query和Criteria不能读缓存数据。

二级缓存
    sessionFactory级共享,默认开启,通过修改cache.proviider_class参数来改变,hibernate内置了对EhCache、OSCache、TreeCache、SwarmCache的支持,也可以通过实现CacheProvider和Cache接口来加入Hibernate不支持的缓存实现。
    
    在hibernate.cfg.xml中加入:
    <class-cache class="className" usage="read-only"/>
    
    或者在映射文件的class元素加入子元素:
    <cache usage="read-write"/>
    
    其中usage参数有:read-only(只读,效率最高,如果修改实体会报异常)、read-write(读写,一般使用这个)、nonstrict-read-write(可容错性读写)、transactional(事务性缓存,JBoss缓存使用)。
    
    session的save也会填充缓存,这个方法不适合native生成方式的主键,update、saveOrUpdate、list、iterator、get、load,以及Query和Criteria都会填充二级缓存,但查询缓存方式只有get、load和iterator会从二级缓存中取数据(iterator可能存在N+1次查询)
    
    Query和Criteria查询缓存由于命中率较低,所以hibernate缺省是关闭,修改cache.use_query_chche为true打开对查询的缓存,并且调用query.setCacheable(true)或criteria.setCacheable(true)。

    sessionFactory中提供了evictXXX()方法用来清除缓存中的内容。
  
    统一信息打开generate_statistics,用sessionFactory.getSatistics()获取统计信息。


缓存的读取顺序:先读一级缓存(session),再读二级缓存(sessionFactory),再读数据库。 

———————————————————————————

缓存
    分布式缓存(即每个服务器都配置缓存)和中央缓存(独立一台服务做缓存服务,所有缓存都放在这台服务器,这种比较流行)
    使用缓存的条件:
1. 读取次数大于修改次数;
2. 数据量不能大于内存容量;
3. 对数据要有独立的控制(例如:A系统查询数据并把数据放入缓存,B系统修改同样的数据,A系统不知道B系统的操作,导致缓存数据无效);
4. 可以容忍出现无效数据,使用缓存都避免不了的,如果要求非常中不能出现无效数据最好就不要使用缓存。

———————————————————————————

OpenSessionInView
    含义:它是一个过滤器类,view层使用session完毕后再关闭session,实现延迟加载(lazy),好像是使用ThreadLocal实现
    作用:实现延迟加载(lazy)和控制事务
    弊端:1: 放大事务边界(一般事务放在数据层(dao层),使用OpenSessionInView后,事务放在控制层(c层)之前);
          2: 如果网络不好,session打开时间会变长,影响性能。

OpenSessionInView流程如下(WebWork+Hibernate+FreeMarker架构模型):

Request 
   | 
   |---other filters... 
         | 
         |---OpenSessionInView Filter 
                  | 
                  |-----WebWork Controller 
                          | 
                          |---Action 
                          | 
                          |---FreeMarker Result(对response.getWriter()做process()操作) 
                  | 
                  | 
         |---OpenSessionInView Filter 
         | 
   |---other filters... 
   | 
Request 

———————————————————————————

悲观锁和乐观锁
    悲观锁由数据库来实现;乐观锁hibernate用version和timestamp来实现。

———————————————————————————

Session是非线程安全的,生命周期较短,代表一个与数据库的连接,在B/S系统中一般不会超过一个请求;内部维护一级缓存和数据库连接,如果session长时间打开,会长时间占用内存和数据库连接

SessionFactory是线程安全的,一个数据库对应一个SessionFactory,生命周期长,一般在整个系统生命周期内有效;SessionFactory保存着与数据库连接的相关信息(user,password,url)和映射信息,以及Hibernate运行时要用到一些信息。

session struts1 servlet filter等     都是非线程安全
sessionFactory struts2 hashtable等   都是线程安全

———————————————————————————

flush方法是将一级缓存与数据库同步,一般不要随意调用此方法。

大批处理
    大量操作数据时可能造成内存溢出,解决办法如下:
1: 清除session中的数据
   for(int i=0;i<100000;i++){
session.save(obj);
if(i % 50 == 0){
    session.flush(); //把一级缓存数据保存到数据库
             session.clear(); //清除全部一级缓存
}
   }

2: 用StatelessSession接口:它不和一级、二级缓存交互,也不触发任何事件、监听器、拦截器,通过该接口的操作会立刻发给数据库,与JDBC的功能一样。
   StatelessSession s = sessionFactory.openStatelessSession(); //该接口的方法与session类似

3: Query.executeUpdate()执行批量更新,会清除相关的类二级缓存(sessionFactory.evict(class)),也可能会造成级联和乐观锁出现问题。

总结:Hibernate处理大批量操作都不是很理想。

———————————————————————————

根据ID查询一个实体请使用load或者get,因为load和get是会查询缓存,使用Query的createQuery(hql)方法不查询缓存,Criteria也不查询缓存。

———————————————————————————

N+1次查询和懒加载
1. 用Query.iterator可能会有N+1次查询
2. 懒加载时获取关联对象
3. 如果打开对查询的缓存即使用list也可能有N+1次查询
 
Iterator工作原理是把所有ID查询出,然后根据ID去一级缓存找,如果找不到再去二级缓存找,如果都找不到就去数据库找,导致出现多出一条查询所有记录ID的SQL语句,效率比较低,如果不确定有一级、二级缓存情况下建议不要使用iterate()方法。
部份代码:
Query q = s.createQuery("from User");
Iterator<User> users = q.iterate();
while(users.hasNext()){
  System.out.println(users.next().getName().getFirstName());
}

———————————————————————————

拦截器与事件
   拦截器与事件都是hibernate的扩展机制,Interceptor接口是老的实现机制,现在改成事件监听机制;他们都是hibernate的回调a接口,hibernate在save,delete,update等会回调用这些类。

———————————————————————————

hibernate不适合的场景
1. 不适合OLAP(On-Line Analytical Processing联机分析处理),以查询分析数据为主的系统;适合OLTP(on-line transaction processing联机事务处理);
2. 对于此关系模型设计不合理的老系统,也不能发挥hibernate优势;

3. hibernate不能处理数据量巨大,性能要求苛刻的系统,批量操作数据的效率也不高。

———————————————————————————

对象状态



实体对象三种状态



0 0