Hibernate

Hibernate组成:
   1.pojo类:
                 特点:
           1.属性设为私有
           2.所有属性设置set(),get() 方法
           3.序列化类.实现Serializable接口
           4.创建一个默认的无参构造器
   2.hibernate配置文件:
       hiberna.cfg.xml
   3.映射文件
       pojo类名.hbm.xml
   -----------------------------------------------------------------------------                 
   hibernate配置文件的设置
   <session-factory>
<property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:1521:XE</property>
<property name="hibernate.connection.username">songwie</property>
<property name="hibernate.connection.password">465305858</property>
<property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>
<property name="hibernate.show_sql">true</property> 是否显示运行的sql语句
<property name="hibernate.hbm2ddl.auto">update</property> 
   update 表示:每次运行对表进行更新,原先的数据存在
   create 表示:每次运行对表进行重新创建,原先的数据不存在
<property name="hibernate.jdbc.batch_size">20</property><!-- 批处理大小
<mapping resource="com/briup/one2many/Person.hbm.xml" /> 
<mapping resource="com/briup/one2many/Address.hbm.xml" />
   映射文件的路径
</session-factory>
--------------------------------------------------------------------------
映射文件设置:
<?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
    <hibernate-mapping package="com.briup.one2many" >
    package:映射文件所在路径 
    ---------------------------------
<1> class配置:
   <class name="Person" table="t_person" dinamic-update="true">
   1.name所对应的值为映射类的名  
   2.table多对应的值为映射表的名
   3.dinamic-insert(true,false):表示没有设置值的列是否自动设置空值,
               默认为false:不设置
     true 为设置     
   4 dinamic-update(true,false)
                  默认会更新所有的列
      true:表示更新某条记录时,仅更新该条记录所对应的列,动态的生成update语句,而不是使用
           session所持有的缓存语句全部更新;
   5.lazy(true,false) 是否延时装载:
                         默认延时true   
                         延迟装载:(默认是延迟装载)
       延时装载的使用地方:
       1. 属性延时装载
       2.整个实体类延时装载
       3.关联映射(相关联的对象延迟装载)
           注:load才会使用延迟装载 
延迟加载的三种级别:
   1)字段级别
   2)类级别
   3)关联级别
注意:在Hibernate3.0中这三种级别默认都采用延迟加载.
        在使用延时装载的地方使用Hibernate.initialize(student);   
        
     --------------------------------                                       
    <2>. 主键的内置生成器的主键生成方式:
<id name="id" column="ID">
<generator class="increment"/>  
           主键内置生成器
          1. 高低值(可移植性高)
             id=hi_value*(max_lo+1)+lo_value
                                               其中hi_value会自动加1,下次使用继续叠加 ; 
                      lo_value每次会从0开始加,下次还是从0开始
       <generator class="hilo">   
<param name="table">t_high</param>  存放高值的表名
<param name="column">high_value</param>表中存放高值的列名
<param name="max_lo">1</param>
</generator>-->
2.通过序列(可移植性不高)
<!--<generator class="sequence">
<param name="sequence">seq_student_id</param>
</generator>  -->
3.高值与序列的结合(可移植性不高):
    id=hi_value*(max_lo+1)+lo_value(hi_value由序列产生)
<!--<generator class="seqhilo">
<param name="sequence">hi_value</param>
<param name="max_lo">1</param>
</generator>-->
4.自增长方式(根据表中的值加1)
   注:缺点,当并发访问时会出现问题
<!--<generator class="increment"/>-->
5.靠底层数据库自己生成:
  例如:
  mysql: id number primary key  aotu_increment;
<!--<generator class="identity"/>-->
6.uuid:
   根据ip,JVM启动时间,当前时间,内部增量产生一个16进制的数值,
   在根据其产生一个32位的字符串
<!--<generator class="uuid"/>-->
        7.assigned默认配置
                         自己手动设置主键
        8.native:hibernate会根据底层数据库,选择一种相应的方式
        9.foreign :唯一外键方式一种(主键根据外键生成)
             <generator class="foreign">    <!-- 主键生成方式根据外键生成 -->
<param name="property">address</param>
        </generator>      
</id>
<property name="name" type="string" column="NAME" length="10" />
type可以选择:
          1. java中的类型(必须加上包名:java.lang.Integer)
          2. hibernate的类型小写integer(java中所有类型hibernate都有有一种类型对应)
<property name="age" type="integer">
<column name="AGE" precision="3" scale="0"></column>
对应的表中的列可以用子标签表示也可以写在同一个标签
注:
  写在子标签的类型可以设置刻度精度等,而其他的不可以
</property>
<3>property的配置:
<property name="gender" type="string" column="GENDER" length="10" />
1.unique(true,false): 对应表中的唯一约束
2.not-null(true,false)对应表中的非空约束
3.precision (P,S)对应表中值的精度和刻度
<4>映射的方式
    注:当有多张表时的对应映射关系:  
    -------------------------------------------------------------      
 <<1>> one-2-one:(两种)
  
         注: 
     1 .级联保存保存必须加上cascade()
         //级联操作(保存任何一个就可以)但是必须加上
    // cascade取值(none,save-update,delete,all,delete-orphan,all-delete-orphan)
2. cascade 与查询没有关系(select默认为级联查询,不需设置)
       delete-orphan:当解除关系时同时把person中的对应的记录删掉(对象java角度)

    <1>. 唯一外键映射(第一种)  
            注: unique="true":  由于是一对一关系所以此处unique设置为true表示一对一
   person:(含有外键列的表)
    a.主键生成方式:
    <generator class="increment"/>  
    b.映射关系:
    <many-to-one name="address" class="Address" unique="true" cascade="all">
<column name="address_id"/>
</many-to-one>
   address:(没有外键的表)
             映射关系:
<one-to-one name="person" class="Person" cascade="all"/>

    <2>.主键映射 (主外键合一)(第二种)
    person:(含有外键列的表)
    a.主键生成方式:(注:此时主键生成方式foreig仅仅在此情况下使用)
      <generator class="foreign"><!-- 主键生成方式根据外键生成 -->
   <param name="property">address</param>
      </generator>
    b.映射关系: 
 <one-to-one name="address" class="Address" constrained="true"/>
 注:constrained(true,false) true表示加上外键关系
    address:(没有外键的表) 
            映射关系:
            <one-to-one name="person" class="Person" cascade="all"/>
           ------------------------------------------------- 
 <<2>> one-2-many:
   person:(含有外键列的表)
     <many-to-one name="address" class="Address"  cascade="all">
<column name="address_id"/>
     </many-to-one>

address:(没有外键的表)
<set name="persons" cascade="all" inverse="true">
<key column="address_id"/>
<one-to-many class="Person"/>
</set>

Address address = (Address) session.get(Address.class, 1);
    Person person = (Person) session.get(Person.class, 1);
address.getPersons().remove(person);
         <!-- inverse控制翻转,取值false,true默认为false
                                          当为false是address和person都会维护外键;
                                          当true时只有person来维护 -->>
                          只在一的那边写:
         //当inverse设为true 时,外键仅由person管理,person中的地址外键 无法 删除
    //当inverse设为false 时,外键由person,address共同管理,person中的地址外键 可以 删除
         -------------------------------------------
  <<3>> many-to-many
        1.Student.hbm.xml:
           <set name="course" table="t_stu_cou" >
  <key column="stu_id"/>
  <many-to-many column="cou_id" class="Course"/>
           </set>
        2.Course.hbm.xml:
           <set name="students" table="t_stu_cou" cascade="all" inverse="true" >
  <key column="cou_id"/>
  <many-to-many column="stu_id" class="Student"/>
           </set>
注意:
    inverse 必须有一个设为true 否则,桥表两边都会维护,都会插入数据会导致同时插入
            两个相同的数据,违反唯一约束
  <<4>>继承映射:
  1.办法一: 父类有表,每个子类也有表
  <class name="Payment" table="t_payment" >
<id name="id" column="ID">
<generator class="increment"/>
</id>
<property name="amount" type="double" column="amount" length="50" />
<joined-subclass name="ChequePayment" table="t_cheque_payment">
<key column="pay_id"/>
<property name="chequeNo" column="cheque_no" type="string"/>
</joined-subclass>
</class>
  2.办法二:父类没有表,每个子类有表
  <class name="Payment" table="t_payment" >
<id name="id" column="ID">
<generator class="increment"/>
</id>
<property name="amount" type="double" column="amount" length="50" />
<union-subclass name="CreditPayment" table="t_credit_payment">
<property name="cardType" column="card_type" type="string"/>
</union-subclass>
</class>
  3.办法三:父类子类合为一张表
  <class name="Payment" table="t_payment" >
<id name="id" column="ID">
<generator class="increment"/>
</id><!--
必须写在id后面
-->
<discriminator column="pay_type" type="string" /> //制定列名
<property name="amount"  type="double" column="amount" length="50" />
<subclass name="ChequePayment" discriminator-value="cheque">  //制定列值
<property name="chequeNo" column="cheque_no" type="string" />
</subclass>
</class>
  优缺点:
                                是否支持多态,         维护是否方便,            出具报表是否方便
            -----------------   ---------------          ------------------- 
                  每个类一张表                是                 是           不方便
                  每个子类一张表            否                   不方便 方便  
                  整个类一张表    是 方便 方便
   
   
  注:以上几种映射方式,全部为双向映射.
  attribute: String chequeNo;
  property:  setChequeNo(String chequeNo);
-----------------------------------------------------------------------
1.Hibernate的简介
1.基于ORM的实现持久化层的一个开源的持久化框架;
2.Hibernate3.0 author:Gravin King;
3.ORM中间件,位于业务逻辑层和数据库中间,提供对象-关系的映射,将一个持久化对象保
存到数据库中称为一条记录.
表现层(web层)
业务逻辑层
持久化层(jdbc Hibernate)
数据库
2.Hibernate的工作原理
1.Hibernate框架根据hibernate.cfg.xml的配置的信息(URL,Driver,User,Password)来和
数据库进行通信;
2.Hibernate框架根据具体的映射文件(如:student.hbm.xml,该文件配置类和表,类的属性
和表中字段的映射关系)来对对象
进行保存,更新,删除和查询;
3. hibernate 核心接口:
   1.Configration
   2.SessionFactoty
   3.Session
   4.Transaction
   5.Query
   6.Criteria
4.Hibernate的优点
  1.对JDBC做了轻量级的封装,底层的和数据库操作是基于jdbc的.
    Hibernate框架对外提供了一些操作数据库的接口方法(如:save()).
  2.减少了这个软件系统的开发周期.
  3.另外对JDBC的事务和JTA的分布式事务和JNDI也做了封装.
  4.所谓轻量级就是指应用程序可以直接使用Hibernate提供的API操作数据库,
    也可以绕过Hibernate使用jdbc提供的API操作数据库.
---------------------------------------------------------------
1.编写配置文件
2.写一个java类(pojo类)
3.编写映射文件
4.编写应用程序调用API
  a.根启动configure,
           加载所有配置文件config.config("路径");
  b.实例化SessionFactory
  c.实例化Session
  d 启动事务
  e 执行curd
  f 关闭资源
  
   Configuration config = new Configuration();
   config.configure("com/briup/one2many/hibernate.cfg.xml");
 
   SessionFactory factory = config.buildSessionFactory();
   Session session = factory.openSession();
 
   Transaction tran = session.beginTransaction();
   --------------------------------------------------------
   SessionFactory的四种特征:(重量级的)
    1.线程安全的
    2.建议一个数据库对应一个SessionFactory
    3.维护着一个缓存(2级缓存)默认情况下是不启用的
    4.对每一个实体类缓存了四条sql语句insert...
                                  delete...
                                  select...
                                  update...
   Session四种特征:(轻量级)
        1.线程不安全的
    2.建议一个事务对应一个Session
    3.维护着一个缓存(1级缓存)默认情况下是启用的
    4.对每一个实体类缓存了四条sql语句insert...
                                  delete...
                                  select...
     
                                  update...
 
-------------------------------------------------------------------
hibernate的类型:
 1 . 值类型(inerger等不可以被持久管理器保存)
 2 . 实体类型(创建的对象,可以被持久管理器保存)


---------------------------------------------------------------------------
hibernate操作持久化对象:
1. hibernate管理对象的状态:
 a: 自由态(transient): 自己new出一个对象
 b: 持久态(persistent): 用session从数据库查找到的,同时受session管理
 c: 游离态(detached): 用session从数据库查找到的,不受session管理
 
 状态                                   数据库中是否有对应记录                    是否受Session管理
 自由态                                         否                                      否
 持久态                                         是 是
 游离态                                         是 否
 -------------------------------------------------------------
 2. hibernate的脏检查机制:(仅对持有态有用)
    对于一个持久化的对象,当提交事务时,hibernate会自动的将对象的的信息同步到数据库当中,而不需要
  调用session的update方法;
 
 3.session中get与load的却别?
                                数据库中没有对应的记录               是否使用代理                      
 get null 否
 load   Exception                                       是
 
 ------------------------------------------------------------------------------
 常用的方法:
  save, savaOrUpdate, update,delete,load , get
  lock:能够操作游离态的对象,将游离态的对象变为持久态
  注:  (与update的区别就是它仅仅改变状态不会保存信息到数据库中)
  merge:能够操作游离态的对象,将信息保存到数据库当中,但是不改变对象的状态
  evict:将指定对象从session中清除,变为游离态
  注:与clear的区别是merge仅仅清除制定对象,而
  clear:全部清空
  flush: 整理缓存,将需要执行的sql执行
    
    对象保存:
    sava()
    1. 自由态:    当做新对象直接保存到数据库(增加一条新数据)
    2. 持久态:    不做任何操作
    3. 游离态 :    当做新对象直接保存到数据库(增加一条新数据)
    
    saveOrUpdate()
    1. 自由态:    当做新对象直接保存到数据库(增加一条新数据)
    2. 持久太:    更新对象到数据库
    3. 游离态 :   更新对象到数据库
    
    update()
    1. 自由态:    异常
    2. 持久太:    更新
    3. 游离态 :   更新对象到数据库  
    
    delete()
    1. 自由态:     不做任何操作
    2. 持久太:     直接删除
    3. 游离态 :     先变为持久态,后删除
    
 --------------------------------------------------------------------
 1. hibernate处理事务:
  1.oracle 中事务的边界:
  开始:a.连接到数据库
      b.一个事务结束,另一个事务开始
  结束:a. 提交 commit (DDL,DCL自动提交)
      b 回滚 rollback
  2.jdbc中:
  开始:connection.setAutoCommit(false);       设置事务提交为手动(jdbc中默认为系统自动提交)
  结束: connection.commit();
      connection.roolBack();
    3.hibernate当中:
    开始: Transaction tran  = session.beginTransaction();
    结束:tran.commit(); (必须手动提交事务 ) 默认在提交事务之前会session.flush();
2.jdbc与JTA(java 事务框架)
jdbc处理的是单个数据库的事务,JTA处理的多个数据库的事务
a. 单个数据库的事务:
session.begintransaction()----->开始一个新的事务
b. 多个数据的事务
session.begintransaction()
  1. 当时操作已经有事务,加入当前事务
  2.当前没有事务,开始一个新的事物
3. flush Session
1. 某些queue语句执行之前,整理缓存
2. 提交事务的的时候
3. 手动调用session.flush

session.setFlushMode(FlushMode.XXX);   设置flush整理的模式(什么时候整理缓存)
FlushMode.XXX:
           某些queue语句执行之前             提交事务                调用flush
-----------------------------------------------------------------
1.auto (默认) :     会                           会                            会
2.commit:           不会                      会                           会
3.never :           不会                      不会                      会

 4.事务的隔离级别:
 -------------------------------------------------------
 1.第一类丢失更新:一个事务的回滚导致其它事务的提交被覆盖
 
时间 事务一(取款)   事务二(转账)
T1 开始事务
T2                 开始事务
T3 查询余额为1000
T4                 查询余额为1000
T5                 存款100,余额变为1100
T6                 提交事务
T7 取款100,余额变为900
T8 撤销事务,余额变为1000
-------------------------------
2.脏读:一个事务读取到了另外一个事务还没有提交的数据

时间 事务一(取款)            事务二(转账)
T  开始事务
T2                    开始事务
T3 查询余额为1000
T4 取款100,余额变为900
T5                    查询余额为900(脏读)
T6 撤销事务,余额变为1000
T7                    转入100,余额变为1000
T8                    提交事务
 ---------------------------------------------
 3.重复读:一个事务读取到了另外一个事务已经提交的更新的数据,
                             一个事务对同一条数据读取两次,结果不相同

时间 事务一(取款)               事务二(转账)
T1 开始事务
T2                      开始事务
T3 查询余额为1000
T4                      查询余额为1000
T5 取款100,余额变为900
T6 提交事务
T7                      查询余额为900
T8                      到底是1000 还是900?
 
 -------------------------------------------------------
 
 4. 第二类丢失更新:一个事务的提交导致其它事务的提交被覆盖
 
时间 事务一(取款)                 事务二(转账)
T1 开始事务
T2                        开始事务
T3 查询余额为1000
T4                        查询余额为1000
T5 取款100,余额变为900
T6 提交事务
T7                        存款100,余额变为1100
T8                         提交事务
 -----------------------------------------------------
 5.虚读:一个事务读取到了另外一个事务已经提交的新插入的数据,
一个事务执行两次查询,查询结果包含的记录数不相同
时间 事务一(注册用户)                    事务二(统计人数)
T1 开始事务
T2                           开始事务
T3                           查询总人数为1000
T4 注册一个新用户
T5 提交事务
T6                           查询总人数为1001(虚读)
T7                           到底是1000 还是1001?
 
 隔离级别:                              设置级别     oracle支持     1        2         3        4         5
      ---------------------------------------------------------- -------- -------------------------
 1. 读未提交的数据     (read uncommited)      1       不             n        y          y       y         y      
 2. 读已提交的数据    read commited          2       支持 (默认)      n        n          y       n         n        
 3. 不可重复读               repeatable        4     不               n        n          n       y         y 
 4. 事务的最高级别   serializable           8        支持            n        n          n       n         n       
 在配置文件中设置隔离级别:
 <property name="HIbernate.connection.isolation">级别</property>
 
 -----------------------------
 6 局部线程的并发访问
  1. 悲观锁(hibernate认为一个事务在操作这条数据时另一个事务也会操作这条数据,所以在事务一开始就加锁
           hibernate本身并没有实现这种锁,完全依靠底层数据库来实现)(for update)
  LockMode.UPDGRADE(for update)              拿不到锁则等待 
  LockMode.UPDGRADE_NOWAIT (for update nowait )     拿不到锁则不等待,马上结束(导致异常)
  2. 乐观锁
  hibernate假想在一个事务操作一条资源的时候,不会有另一条记录来访问,hibernate通过版本号控制数据的一致性
 --------------------------------------------------------------------------------------
 
 7. 高级查询
  <1>. 如何检索对象信息?
  a. 通过唯一的OID
    session.get/load(xxx.class,OID);
  b. 导航对象图方式
    Address address = (Address)session.get(Address.class,1);
    Person person  = address.getPerson();导航对象图(级联)
  c. HQL:
    基本步骤:
    1.创建一个hql语句        String hql = "from Address where id < ?";
             查询所有的列,不用谢select 语句
             查询部分列,要设置select语句
    2.创建Query对象
    Query query= session.createQuery(hql);
    3.设置参数(不是必须的)
      String hql = "from Address where id < ?"
      queue.setInteger(0,100);        从零开始,jdbc中preparedStatement注册参数从1开始
                                 设置参数方式:
              a. 按照参数顺序
               query.setInteger(n,100); n=(0....n)
              b. 按照名字:
               String hql = "from Address where id < :id"
               query.setInteger("id",100);     : 后面的id就是设置中的id
              c. 直接拼hql
               public List method(int id){
                   String hql = "from Address where id="+id;
                   String hql = "from Address where name='"+name+"' ";
               }
                                        注:提取公共:
                  <query name="getPerson" ><![CDATA[from Person where id < ?]]> </query>
                  
                  Query query = session.getNamedQuery("getPerson");
         query.setInteger(0, 10);
           4.设置其他信息(比如分页查询)
             query.setFirstResult(0); (page-1)*maxCount
             query.setMaxResults(20);
           5.获取结果集:
             a. List list = query.list();
             b. Address address = (Address)query.uniqueResult();      仅仅有一条结果时
             c. Iterator iter = query.iterator();
        d. 通过命名的hql语句查询
        
    <2> hibernate连接   
          
                        特征                                   结果
    ------------------------------------------------------------------------
         1. 普通连接      只用 join                        对象类型的数组
         2. 迫切连接     有join, fetch                    特定类型的的对象
         3. 交叉连接     笛卡尔积                          对象类型的数组
         4. 隐式连接    属性链                             特定类型的的对象
      
      1. from Person p  join p.address
      2. from Person p  join fetch p.address
      3. from Person p, Address a
      4. from Person p  where p.address.country=?
 
    --------------------------------------------------------
    <3>投影查询(有select 查询部分列)
    <4>动态查询
    
    <5>Criteria 使用   (当连接条件不确定时候使用)
    
       BufferString hql = "from Person where 1=1 ";
       if(age!=null){
           hql.append("and age="+age);
       }
       
       Criteria c = session.createCriteria(Person.class);
       
       if(age!=null){
          c.add(Restrictions.eq("age",age));
       }
    
    Criteria c = session.createCriteria(Person.class);
    c.add(Restrictions.eq("name","sw"));
   
    <======>
        String hql = "from Person where name=?";
        Query query = session.createQuery(hql);    
        qruey.setString(0,"sw");
        
    <6>  Example(模板)(不推荐)
         Criteria c = session.createCriteria(Person.class);
         Person p = new Person();
         p.setName("sw");
         c.add(Example.create(p));
         
         List list = c.list();
    <7>  本地sql(不推荐)
        String sql = "select * from t_person where name = ?";   
        //String sql = "select s* from t_person where name = ?";   
        Query query = session.createSQLQuery(sql);
        query.setString(0,"sw");
        //query.addEntity(Person.class);
        List list = query.list(); (object 类型数组);
    <8>  hql操作DML语句
       String hql = "delete from Person where id < 100" ;
       Query query = session.createQuery(hql);
       query.executeUpdate();