Hibernate 读书笔记

来源:互联网 发布:金华淘宝大学 编辑:程序博客网 时间:2024/05/17 06:46
深入浅出 Hibernate》  台湾知名专家夏昕主编,读大学的时候读过这本书,但那是没有Hibernate开发经验,所以也只是知道些皮毛。工作以后在项目中用到这个开源框架,今天再次得到这本电子书,从头到位一口气读了一遍,发现句句真理。故,把一些高级特性记录下来。

Hibernate 优点:不需要写jdbc ,直接操作对象,适合操作简单对象的增删改查。缺点:不适合操作大量数据,不适合数据优化。
方言(dialect):hql 面对的是对象模型,通过方言可以把hql翻译成特定数据库的sql语句。

 

一   Session

         Configuration config=new Configuration().configure("myHibernate.cfg.xml");

        SessionFactory sessionFactory=config.buildSessionFactory();

        Session session=sessionFactory.openSession();

     注意:session设计是非线程安全的,一个session只能由一个线程使用.里面封装了一些持久化的方法:

    find、save、update、delete、

    查询提供两种方法:(1)、HQL 

                                           eg:       String HQL="from Tuser user where user.name like ?";

                                                      Query query=session.createQuery(HQL);

                                                      query.setParameter(0,"chen");

                                   (2)、面向对象查询的

                                          eg:        Criteria criteria= session.createCriteria(Tuser.class)

                                                        criteria.add(Expression.eq("name","chen"))

 

二 实体对象生命周期

     transient(自由态,实体对象在内存中自由存在,它与数据库的记录无关)

     persistent(持久态,实体对象已经纳入了Hibernate框架所管理的状态,一个对象对应一个数据库中的记录,当事务提交以后,Hibernian在清理缓存(脏数据)的时候会和数据库同步)

     持久态的对象不需要显示的调用object.save(),只要事务提交就可以保存到数据库

public saveUser(){

  Session session = null; 

  Tuser user = null;

  Transaction ts = null;

   try {

        session = super.getSession()

        ts = session.getTransaction();

        ts.beginTransaction();

      //transient 对象

       user = new Tuser();

       user.setName("张三");

       user.setPassWord("123");

       //persistent 对象

       session.save(user);

       //在此次 user.getId(); 会得到一个id值,后台会打印一条插入的sql语句

      user.setName("王五"); //此处对象的name 改变,在后台会打印一条update语句

       ts.commit();//事务提交,正式插入数据库

      

   } catch(Exception ex) {

         ts.rollback();

   } finally{

       session.close();

   } 

  //detached 对象

   user.setName("李四");

   try {

       session = super.getSession();

       ts = session.getTransaction();

       ts.beginTransaction();

       session.update(user);//此时的detached 对象又为persistent对象,后台打印一条update语句

       ts.commit();//事务提交,正式插入数据库 

   } catch(Exception ex) {

         ts.rollback();

   } finally{

       session.close();

   }

}//end saveUser()

Detached(游离态 处于Persisent状态的对象,其对应的session实例关闭后就处于游离状态)

session 是persistent对象的宿主,宿主消失了,则persistent对象立即转入Detached状态。

detached 显著特征:在数据库中有对应的记录(id) ,只是没有纳入session管理。detached对象一定是经过detached 到persistent 到detached的转换

Detached和transient的区别是:Detached状态的对象还可以关联到另一个session中去,而成为persistent对象.

是因为虽然脱离了曾经是persistent状态的对象已经和数据库中的一条记录有关系了(id 主键唯一性)。所以Detached状态的对象再次加入Hibernate管理时其状态又转换为Persistent状态了。当调用session.delete()方法是这个对象在数据库中被删除,即这个id不存在所以这个对象又为Transient状态。

detached和transient状态的对象统称为VO(value object).

Persistent状态的对象称之为PO(Persistent object) PO是加入了Hibernate实体容器管理中的,PO变化是反映到实际的数据库中去的。

 状态转换图:

 

 

在数据加载中,

Tuser user = (Tuser)get(Tuser.class,id)//注意这个id 是实现了序列化的id ,比如String Intger Long类型的.只要执行到这里后台就会发sql语句,进行查询。如果查找一个没有的记录,则user 为null

Tuser user = (Tuser)load(Tuser.class,id) //这个通过CGLIB返回一个代理类,只有在真正使用它的属性的时候(比如:user.getName())才会真真去数据库中查询数据,这就是延迟加载机制 lazy。如果查找一个没有的记录,并且使用它的属性,则抛出一个ObjectNotFoundException,并且后台打印sql。

 

 三 事务管理

       事务的死个特性

              原子性   事务中的操作是一个单元,要么都成功要么都失败

              一致性  只有合法数据才能写入数据库,如果数据和字段类型不一致,则应该回滚到初始状态

             隔离性  事务允许多个用户对数据库并发访问,而不破坏数据的正确性和完整性

             持久性 事务结束后,处理的数据必须固化。

 

四  数据加载

       前面已经讲过 查询有两种方法HQL 和Criteria

     

 Tuser user=session.load(Tuser.class,new Integer(2))  //通过id为2加载实体Tuser

session 在加载实例对象的时候做了那些事情

           1)、首先在Hibernate一级和二级缓冲中找有没有这个实体,有则返回。没有发起SQL查询

           2)、根据映射和相应的SQL得到ResultSet,创建相应的数据对象。

          3)、  将数据对象纳入一级和二级缓存。

          4)、返回数据对象。

 

 

五 关联映射
User.java    many
Group.java   one
(1) many-to-one 单向 在多的一方加入一个外键,指向一的一端。有多的一方来维护关系
<many-to-one name="group" column="gid" class="" cascade=""/>
name:属性
column:映射到数据库的字段
class:one 方的类
cascade: all,none,save-update,delete(有问题),级联操作,删除,修改,保存。

 a: cascade 默认为none的情况
     Group g = new Group();
     g.setName = "组1";  
     (1)
     User user = new User();
     user.setName("chen")
     user.SetGroup(g);
    
     session.save(user);
     session.getTransaction.commit();//这里会抛TransientObjectExcetpion 因为g是游离态的对象,在数据库里还没有生产主键。
   解决办法是在(1)地方 session.save(g);  
 b:cascade 设置为all  自动会级联把组和用户都存储进去

(2) one-to-many 单向 在多的一方加入一个外键,指向一的一端。由一的一方来维护关系
many-to-one 和one-to-many 的映射策略是一样的,都是在多的一方加入一外键,指向一的一方。只是角度不一样

Group.java

private Set users;

 <set name="users">
  <key column="gID" not-null="false"/>  //key在这里就会把gID(随便取) 这个字段 加入到学生表了
  <one-to-many class="cn.com.User"/>//指定users集合里是什么元素
 </set>
 
 eg1:
     User user = new User();
     user.setName("chen")
     session.save(user);
    
     User user2 = new User();
     user2.setName("chen");
     session.save(user2);
    
     Set<User> set = new Set<User>
     set.add(user);
     set.add(user2);
     Group g = new Group();
     g.setName("组1");
     g.setSet(set);
     session.save(g);
     session.getTransaction.commit();
     //日志
     Hibernate:insert into t_user(name) values(?)//首先还不能确定是按个组,所以只是把名字存进去
     Hibernate:insert into t_user(name) values(?)
     Hibernate:insert into t_group(name) values(?)
     Hibernate:update  t_user set gid=? where id=?//由一方来维护他们的关系,
     Hibernate:update  t_user set gid=? where id=?
  缺陷:(1)维护困难,sql 语句明显多,需要多发出update语句
        (2)<key column="gID" not-null="false"/> 如果设置为非空<not-null="true">肯定要出问题,在保存用户的时候gid还是null
 
(3) 一对多     双向
 1.配置文件

  多方
  <many-to-one name="group" column="gid" class="" cascade=""/>

  一方
  <set name="users">
    <key column="gid" not-null="false"/>
    <one-to-many class="cn.com.User"/>
  </set>
  
  这里一定要注意: key中的gid 一定要和many-to-one里面的column="gid" 一模一样
  2.属性inverse 反转,<key column="gid" not-null="true" inverse="true"/>
  反转以后,一方失效,控制由多方来维护,所以不会再有update语句
  eg1中的日志应该是
  Hibernate:insert into t_user(name,gid) values(?,?)//首先还不能确定是按个组,所以只是把名字存进去,gid为null
  Hibernate:insert into t_user(name,gid) values(?,?)
  Hibernate:insert into t_group(name) values(?)


六:HQL
 1.  所有属性(实体对象查询)
                    hql = "from Student " 或者 " select s from Student s " 
 1. 单个属性
      hql = "select name from Student";
 2. 多个属性
      hql = " select id,name  from new Student(id,name)";
      属性查询都返回对象数组 Object[]
 3. 统计查询
      hql = "select count(*) from Student";
      Long num = session.createQuery(hql).uniqueResult;//uniqueResult 用在单一值的情况下
     两个对象查询 返回的是object[]
      hql = " select c.name,count(s) from Student s (inner,left,right)jion s.classes c group by c.name order by c.name"
 4. 链接查询  set students = new HashSet();
     hql = " select c.name,s.name from Classes c left jion c.students s"
 5. 条件查询
     1)、通过拼凑字符串"+name+";
     2)、单个参数通过?  .setParameter(0,"");
     3)、多个参数,通过占位符(:ids)  setParameterList(ids,new Object[]{1,2,3,4});

     采用hql拼接的缺陷:(1) 程序可读性差。(2)、难以进行性能优化,数据库具有对sql解析和优化,并将处理结果放入缓存中,如果

之后有不同的参数,语法相同的sql命令,则直接以缓存结果执行。而如果才用hql并接的sql参数不同则认为是多条sql命令,缓存的不到

利用。(3)、安全风险,SQL 注入攻击。

   

 6. 原生sql 和sql addEntity 把数据库表的别名转换为对应的实体类
   String hql = "";
    hql+=" select {t.*} from t_user t where t.name="+chenl+";
           hql+= " and t_classes=......"
    List list = super.getSession().createSQLQuery(hql).addEntity("t",UserImpl.class).list();
    return list;
   }


  7.Hiternate 通用的增删改查
   增加:save(object)
   删除:delete(object)

   修改:saveOrUpdate(object) 

   查找:(1)、load和find这都是基于对象的,(2)、基于HQL查询(常用) (3)、原生SQL


  8:基于DML风格的insert update delete select (批量)
  相对于静态的数据可以使用DML风格的操作,尽量少用。因为数据库的更新和缓存不同步(发出sql以后不会在缓存中
  同时放一份,压力太大,如果更新10万条数据,就需要在缓存中存放10万个对象。而使用通用的Hibernate操作,如果save操作,会在缓存里同时放一份);
  session.createQuery("update Student s set s.name=? where id<?")
   .setParameter(0,"李四")
   .setParameter(1,3)
   .executeUpdate();


  9:HQL里面的list 和iterator
     list 不会使用缓存,一条语句直接从数据库里查询到所有学生。然后放在缓存里面。所以list只玩缓存里放数据而不会使用缓存
     iterator 则首先发去查询id列表的语句,去一级缓存(session)查询id对于的对象,然后如果一级缓存里面没有则通过id去数据库里面查询对应的数据也就是N+1问题。


7  缓存

  Hibernate 缓存机制,缓存的持久对象均保存在一个类似Map对象里面,Key值持久对象的标识符(id),Value 为这个持久对象的数据。
  (1)、一级缓存:一级缓存生命周期和session生命周期一致,它只缓存实体对象,但不会把普通属性放入一级缓存的
   load(Student.class,1)、get(Student.class,1)、HQL里的iterator、Serializable Long id = save()都使用缓存。
   一级缓存无法取消,但可以管理,可以通过session.clear()方法来清空缓存。如何避免大量数据入库导致内存溢出,
   通过session.flush()同步缓存里的数据到数据库  session.clear()(清除缓存数据)。
  (2)、二级缓存:二级缓存通常称为进程级别的缓存又称为sessionFactory,它可以被所有session共享。通过sessionFactory
   管理。它的生命周期和sessionFactory的生命周期是一致的。它只缓存实体对象。ehcatch.jar。
   如果开启两个session 通过每个session的load方法加载对象只会在调用第一个load方法时发一条sql语句,第二个load方法
   不会发sql 它会在二级缓存里去找。可以通过sessFactory来管理二级缓存。比如清除 sessionFactory.evict(Student.class)
   或者清除一个对象 sessionFactory.evict(Student.class,1);
   二级缓存模式设置
   session.setCacheMode(CacheMode.GET),仅仅向二级缓存读,而不向二级缓存写
   session.setCacheMode(CacheMode.PUT),仅仅向二级缓存写,而不向二级缓存读
   二级缓存适合于放入实体对象不会发生很大变化的数据。
   二级缓存需要手动配置。
   开启二级缓存,默认是自动开启的,也可以显示开启
   <property name="hibernate.cache.use_level_cache">true</property>
   指定二级缓存提供商
   <property name="hibernate.cache.provide_class">org.hibernate.cache.EhCacheProvider></property>
   指定缓存的类,可以在配置文件指定
   <class-cache class="com.cn.chenlly.model.Student" usage="read-only"/>
   (3)、查询缓存,主要用来缓存普通结果属性和实体对象id。
    查询缓存的生命周期不确定。它的生命周期和session 无关。在外界对数据库进行修改之前失效。如果数据库修改以后。查询缓存立即失效
    配置文件开启查询缓存
    <property name="hibernate.cache.use_query_cache">true</property>   
    在类中配置
    query.setCacheable(true);
   注意:查询缓存对Query的list()起作用,对iterator()不起作用。

 

   (4) 缓存对get()、load()、list()(HibernateTemplate 中对应find())、iterator() 影响

      Session 对象的load()方法。从当前第一级缓存里标识符属性(id)对应的数据,如果未命中,判断是否配置了二级缓存,如果配置从二级缓存里找;否则从数据库中查询数据,并缓存到对应的一级和二级缓存中。

     Session 对象的get()方法。首先从一级缓存里查找,如果未命中,直接查询数据库,并把数据放入一级缓存。

     Query 对象的list()方法。一条查询语句查询对象的集合。首先检查是否配置了查询缓存,如果配置了,则判断是否命中缓存。如果未命中,则直接从数据库中查询数据并缓存到相应的一级,二级和查询缓存中。

    Query 对象的iterator()方法。发送select 语句检索id字段,然后根据id 值在Session缓存和二级缓存中查找匹配的对象,如果存在,把它直接加到查询结果返回,如果不存在,根据刚才已经检索出来的id,执行额外的 select语句到数据库检索该对象。可能会出现n+1问题。

   list()往一级和二级缓存里添加数据,但不利用一,二级缓存。如果没有配置查询缓存,都会发起一条sql往数据库中检索数据。

   而iterato()充分利用一,二级缓存,如果缓存里有数据,直接拿,而不需要再次发起sql 检索数据库。

原创粉丝点击