hibernate初步学习

来源:互联网 发布:语言理解软件 编辑:程序博客网 时间:2024/05/22 11:42
一 ORM:ORM<object relation mapping>关系数据库.
1.1:产生的原因:对象数据库的不同步.
ORM是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。
简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,
将java程序中的对象自动持久化到关系数据库中。
本质上就是将数据从一种形式转换到另外一种形式.
1.2  常见ORM框架
   1.Hibernate
灵活的设计,优秀的性能,齐全的文档
   2.Entity Bean(ejb)
重量级,成本高,开发周期长,学习曲线非常陡
   3.IBATIS  apache子项目
SQL-MAPPING,相对于hibernate的完全面向对象,IBATIS更加灵活。
但需要的代码量更大,而且需要直接编写SQL语句。
   4.TOPLink  (收费) 
   5.JDO  java-data-object

 二 hibernate
    1 什么是Hibernate
    Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合
   ,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任
    2 Hibernate的核心类和接口:
   <1>Configuration
负责管理Hibernate的配置信息,这些配置信息都是从配置文件hibernate.cfg.xml或者
Hibernate.properties读取的,当然也可以自定义文件名称,只要在实例化Configuration
的时候指定具体的路径就可以了
   <2>SessionFactory
Configuration的实例会根据当前的配置信息,构造SessionFactory实例。
SessionFactory是线程安全的,一般情况下一个应用中一个数据库共享一个SessionFactory实例。
   <3>Session
一般的持久化方法(CRUD)都是通过Session来调用的,Session是非线程安全的
   <4>Transaction
事务管理
   <5>Query
查询的接口

   3 Hibernate的原理及步骤
1. 读取并解析配置文件 
//采用默认的hibernate.cfg.xml来启动一个Configuration的实例
例:Configuration configuration=new Configuration().configure();
//如要修改默认配置文件名 当然你可以选择不修改
Configuration configuration=new Configuration().configure(new File("src/xxx.xml"));
2. 读取并解析映射信息,创建SessionFactory
例:SessionFactory  sessionFactory = configuration.buildSessionFactory();
3. 打开Session实例(它是线程不安全的,是共享同一个session,所以要ThreadLocal模式来控制)
例:Session session = sessionFactory.openSession();
4. 创建事务Transaction
例:Transaction transaction = (Transaction) session.beginTransaction();
5. CRUD操作(执行数据操作 CRUD 增删查改)
例: 增加
    session.save(pojo对象);
    增加或者修改
    session.saveOrOpdate(pojo对象)//当修改时候,id不存在将被抛出异常
                     删除
                     session.delete(pojo对象)
    //根据主键加载
                    p1=(Person)session.load(pojo类.class, 主键值);

6. 提交事务
例:transaction.commit();
7. 关闭Session实例及SessionFactory
例:   session.close();
              sessionFactory.close();

   示例一个Hibernate的流程操作:

ORM  对象关系映射  O---pojo   R---创建数据库的表   M---person.hbm.xml
1首先导入所有的包,2建立一张表并且建立属性和它对应的pojo(javaBean),3然后在src下面建立tableName.hbm.xml来映射表与pojo的关系,
<?xml version="1.0" encoding="gb2312"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
   "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!--将"pojo" 映射到表 table="demo"-->
<class name="包.pojo类名" table="表名">
<!-- name是pojo属性 column是映射的表字段 、若不写表示默认相同  -->
<id name="id" column="id">
   <generator class="identity"/>
</id>
<property name="name"/>
<property name="age"/>
   </class>
</hibernate-mapping>

4 在src下面建立 hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
   <session-factory>
<!-- Database connection settings  连接数据库-->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/j2ee</property>
<property name="connection.username">root</property>
<property name="connection.password">123456</property>
<!-- JDBC connection pool (use the built-in) 连接数 -->
<property name="connection.pool_size">5</property>
<!-- SQL dialect 映射到mysql-->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 是否显示生成的sql语句-->
<property name="show_sql">true</property>
<!--  可以从对象生成 表 也可以从表生成对象
      它包含4个属性:
create : 会根据你的model类来生成表,但是每次运行都会删除上一次的表,重新生成表,哪怕2次没有任何改变
create-drop : 根据model类生成表,但是sessionFactory一关闭,表就自动删除
update : 最常用的属性,也根据model类生成表,即使表结构改变了,表中的行仍然存在,不会删除以前的行
validate : 只会和数据库中的表进行比较,不会创建新表,但是会插入新值
-->
<property name="hbm2ddl.auto">update</property>
      <!---   hibernate  mapping  将上面的映射表放到的hibernate的xml  -->
<mapping resource="person.hbm.xml"/>
</session-factory>
</hibernate-configuration>

    5 客户端使用
public class Client {
   public static void main(String[] args) {
      //读取配置文件
      Configuration configuration = new Configuration().configure();
      //创建Session
      SessionFactory  sessionFactory = configuration.buildSessionFactory();
      //打开Session
      Session    session = sessionFactory.openSession();
      //获取事物
      Transaction tx = session.beginTransaction();
      修改 删除 增加
                        //增加

                        session.saveOrUpdate(pojo对象)//增加或修改pojo
                        session.delete(pojo对象)//删除
                        p1=(Person)session.load(Person.class, 2);


      //提交事物
      tx.commit();
   {color:black}}
}

    
        演示 1增加 2 修改 3   删除 4  根据主键查询 5查询 6事务


     4.Session的管理
   Session是Hibernate运作的中心,对象的生命周期、事务的管理、数据库的存取,都与 Session息息相关,
   就如同在编写JDBC时需关心Connection的管理,以有效的方法创建、利用与回收Connection,以减少资源的消耗,
   增加系统执行效能一样,有效的Session管理,也是Hibernate应用时需关注的焦点。
    
   Session是由SessionFactory所创建, SessionFactory是线程安全的(Thread-Safe),您可以让多个
   线程同时存取SessionFactory而不会有数据共享的问题,然而Session则不是设计为线程安全的,所以试图让
   多个线程共享一个 Session,将会发生数据共享而发生混乱的问题。  
   在各种Session 管理方案中, ThreadLocal 模式得到了大量使用。ThreadLocal 是Java中一种较为特殊的
   线程绑定机制。通过ThreadLocal存取的数据,总是与当前线程相关,也就是说,JVM 为每个运行的线程,
   绑定了私有的本地实例存取空间,从而为多线程环境常出现的并发访问问题提供了一种隔离机制。
   首先,我们需要知道,SessionFactory负责创建Session,SessionFactory是线程安全的,多个并发线程
   可以同时访问一个SessionFactory 并从中获取Session 实例。而Session并非线程安全,也就是说,如果
   多个线程同时使 用一个Session实例进行数据存取,则将会导致Session 数据存取逻辑混乱
     
         ThreadLocal例子

public class HibernateUtil
{

   public static final SessionFactory sessionFactory;
   static
{
try
{
   //采用默认的hibernate.cfg.xml来启动一个Configuration的实例
Configuration configuration=new Configuration().configure();
//由Configuration的实例来创建一个SessionFactory实例
   sessionFactory = configuration.buildSessionFactory();
}
catch (Throwable ex)
{
   // Make sure you log the exception, as it might be swallowed
   System.err.println("Initial SessionFactory creation failed." + ex);
   throw new ExceptionInInitializerError(ex);
}
   }

   //ThreadLocal并不是线程本地化的实现,而是线程局部变量。也就是说每个使用该变量的线程都必须为
//该变量提供一个副本,每个线程改变该变量的值仅仅是改变该副本的值,而不会影响其他线程的该变量
//的值.


//ThreadLocal是隔离多个线程的数据共享,不存在多个线程之间共享资源,因此不再需要对线程同步    
public static final ThreadLocal session = new ThreadLocal();


   public static Session currentSession() throws HibernateException
{
Session s = (Session) session.get();
//如果该线程还没有Session,则创建一个新的Session
if (s == null)
{
   s = sessionFactory.openSession();
   //将获得的Session变量存储在ThreadLocal变量session里
   session.set(s);
}
return s;
   }


   public static void closeSession() throws HibernateException {
Session s = (Session) session.get();
if (s != null)
   s.close();
session.set(null);
   }
}


    5   Hibernate三种查询方式 1 hql 2 qbc《qbe  --- query by example》 3 sql
        
  1.hql    

           1.1 冒号法
    Query query=session.createQuery(" from Person p where name=:xxx");//from后面跟的是PO对象名,而不是表名
      query.setString("xxx", "ttt");
      List<Person> list=query.list();
for(Person p:list)
System.out.println(p.getName())
           1.2 问号法
    Query query=session.createQuery(" from Person p where name=?");
query.setString(0, "ttt");
List<Person> list=query.list();
for(Person p:list)
System.out.println(p.getName())
            
    模糊查询 举例
      问号法
     Query query=session.createQuery(" from Person p where name like ?");
      query.setString(0, "%a%");
      冒号法
       Query query=session.createQuery(" from Person p where name like :xxx");
query.setString("xxx", "%w%");


      2.QBC (  query by Criteria ) 
Criteria接口与DetachedCriteria(二者的区别就是DetachedCriteria可以离线操作,而Criteria不可以)
    
     <1>利用Restrictions进行条件筛选
            方法             說明 
Restrictions.eq 等於 
Restrictions.allEq 使用Map,使用key/value進行多個等於的比對 
Restrictions.gt 大於 > 
Restrictions.ge 大於等於 >= 
Restrictions.lt 小於 < 
Restrictions.le 小於等於 <= 
Restrictions.between 對應SQL的BETWEEN子句 
Restrictions.like 對應SQL的LIKE子句 
Restrictions.in 對應SQL的in子句 
Restrictions.and and關係 
Restrictions.or or關係 
Restrictions.sqlRestriction SQL限定查詢 

     
     <2>criteria.addOrder(Order.asc("id"));//排序
     
     举例1
     Criteria criteria = session.createCriteria(Person.class);
     criteria.add(Restrictions.like("name", "ad%"));  //模糊查询,姓名以ad开头,注意要加%号
     criteria.add(Restrictions.between("age", 20, 30));  //年龄在20到30之间
      criteria.addOrder(Order.asc("id"));//
     List<Person> list = criteria.list();
     for (Person p : list) {
 System.out.println(p);
     }
             上面是与查询 ,如果是或查询    查询等于20或者或者年龄为空的记录
      Criteria criteria = session.createCriteria(User.class);
               criteria.add(Restrictions.or( 
                   Restrictions.eq("age", new Integer(20)), 
                   Restrictions.isNull("age") 
               )); 
               List users = criteria.list();



            DetachedCriteria 使用  //?离线操作的意义何在?
            DetachedCriteria dc = DetachedCriteria.forClass(Person.class);
dc.add(Restrictions.eq("name","aaa"));
     List<Person> list = dc.getExecutableCriteria(session).list();
     for (Person p : list) {
 System.out.println(p.getId());
     }



      3.Sql
           <3.1>采用addScalar方法 一个字段一个字段指定类型
     SQLQuery sQLQuery = session.createSQLQuery("select * from user");  //普通的SQL语句
  
      sQLQuery.addScalar("id",Hibernate.INTEGER);
      sQLQuery.addScalar("name",Hibernate.STRING);
     List<Object[]> list = sQLQuery.list();    //注意,得到的是一个里面放的是object数组的List集合
     for (Object[] object : list) {
System.out.println(Arrays.toString(object));     //在这里可以根据自己的需要,进行封装
     }   

  <3.2>采用addEntity方法   如果想返回的是list里是person对象 
      SQLQuery sQLQuery = session.createSQLQuery("select {person.*} from person"); //注意这里的语法{person.*}
sQLQuery.addEntity("person", Person.class);
sQLQuery.addScalar("id",Hibernate.INTEGER);
      sQLQuery.addScalar("name",Hibernate.STRING);
List<Person> list = sQLQuery.list(); 
for (Person object : list) {
System.out.println(object); 
}


          总结:不管是SQL还是HQL,当你查询出来的不是整个对象,而是其中的某些字段,或者说:当几个表关联查询的时候,取二个表中的某些字段,      
  返回的不是一个封装好的对象的List集合,那么这个List里到底是什么东西呢,这里分为两种情况
           <1>如果只有一个字段,list里每个元素都是Object
  <2>如果多于一个字段,list里每个元素都是Object[], 所以,这时候就需要自己去封装对象了,将object数组中的元素迭代出来,
     然后封装到一个对象里。


      三种查询方式优缺点
HQL功能最强大,适合各种情况,但是动态条件查询构造起来很不方便 
Criteria最适合动态条件查询,不太适合统计查询,QBE还不够强大,只适合相当简单的查询 
NativeSQL可以实现特定数据库的SQL,但是可移植性就牺牲了 

利用 Query接口的setMaxResults方法 表示一次从list里拿多少条记录  通常用于分页中
     
  6.pojo的3种状态


瞬态  持久化  托管(离线)


一个PO有三种状态:
1、未被持久化的VO
此时就是一个内存对象VO,由JVM管理生命周期
2、已被持久化的PO,并且在Session生命周期内
此时映射数据库数据,由数据库管理生命周期
3、曾被持久化过,但现在和Session已经detached了,以VO的身份在运行
这种和Session已经detached的PO还能够进入另一个Session,继续进行PO状态管理,此时它就成为PO的第二种状态了。这种PO实际上是跨了Session进行了状态维护的。在传统的JDO1.x中,PO只有前面两种状态,一个PO一旦脱离PM,就丧失了状态了,不再和数据库数据关联,成为一个纯粹的内存VO,它即使进入一个新的PM,也不能恢复它的状态了


POJO<Plain old java Object>, PO<Persistance Object>
         POJO<Plain old java Object>:一种纯粹的java,即private属性及对这个属性的get/set方法,不实现,不继承任何java框架的接口,类。
PO<Persistance Object>:持久对象,,
有时也被称为Data对象,对应数据库中的entity,可以简单认为一个PO对应数据库中的一条记录。
在hibernate持久化框架中与insert/delet操作密切相关。
PO中不应该包含任何对数据库的操作。
可以是由POJO发展而来
POJO(plain old java object)-------->PO(persistance Object)
POJO是沒有状态的,加了状态之后就变成了PO(POJO+xml就是PO);


  7 HIbernate主键详解
    1.Assigned
        Assigned方式由程序生成主键值,并且要在save()之前指定否则会抛出异常
       
        特点:主键的生成值完全由用户决定,与底层数据库无关。用户需要维护主键值,
            在调用session.save()之前要指定主键值。
           
        主键由外部程序负责生成,无需Hibernate参与。
    2.Hilo
        Hilo使用高低位算法生成主键,高低位算法使用一个高位值和一个低位值,
        然后把算法得到的两个值拼接起来作为数据库中的唯一主键。
       
        Hilo方式需要额外的数据库表和字段提供高位值来源。
       
        默认请况下使用的表是通过hi/lo 算法实现的主键生成机制,
        需要额外的数据库表保存主键生成历史状态。
    3.hibernate_unique_key
        默认字段叫作next_hi。next_hi必须有一条记录否则会出现错误。
       
        特点:需要额外的数据库表的支持,能保证同一个数据库中主键的唯一性,
        但不能保证多个数据库之间主键的唯一性。Hilo主键生成方式由Hibernate维护,
        所以Hilo方式与底层数据库无关,但不应该手动修改hi/lo算法使用的表的值,
        否则会引起主键重复的异常。
    4.Increment
        Increment方式对主键值采取自动增长的方式生成新的主键值,但要求底层数据库的支持Sequence。
        如Oracle,DB2等。需要在映射文件xxx.hbm.xml中加入Increment标志符的设置。
   
        特点:由Hibernate本身维护,适用于所有的数据库,不适合多进程并发更新数据库,
        适合单一进程访问数据库。不能用于群集环境。
        主键按数值顺序递增。此方式的实现机制为在当前应用实例中维持一个变量,以保存着当前的最大值,
        之后每次需要生成主键的时候将此值加1作为主键。
        这种方式可能产生的问题是:如果当前有多个实例访问同一个数据库,那么由于各个实例各自维护
        主键状态,不同实例可能生成同样的主键,从而造成主键重复异常。因此,如果同一数据库有多个
        实例访问,此方式必须避免使用。
    5.Identity
        Identity当时根据底层数据库,来支持自动增长,不同的数据库用不同的主键增长方式。
       
        特点:与底层数据库有关,要求数据库支持Identity,如MySQl中是auto_increment, SQL Server
        中是Identity,支持的数据库有MySql、SQL Server、DB2、Sybase和HypersonicSQL。
       
        Identity无需Hibernate和用户的干涉,使用较为方便,但不便于在不同的数据库之间移植程序。
        采用数据库提供的主键生成机制。如DB2、SQL Server、MySQL中的主键生成机制。
    6.Sequence
        Sequence需要底层数据库支持Sequence方式,例如Oracle数据库等
       
        特点:需要底层数据库的支持序列,支持序列的数据库有DB2、PostgreSql、Qracle、SAPDb等在不
        同数据库之间移植程序,特别从支持序列的数据库移植到不支持序列的数据库需要修改配置文件
       
        采用数据库提供的sequence 机制生成主键。如Oralce 中的Sequence。
        与hilo 类似,通过hi/lo 算法实现的主键生成机制,只是主键历史状态保存在Sequence中,
        适用于支持Sequence的数据库,如Oracle。
    7.Native
        Native主键生成方式会根据不同的底层数据库自动选择Identity、Sequence、Hilo主键生成方式
       
        特点:根据不同的底层数据库采用不同的主键生成方式。由于Hibernate会根据底层数据库采用
        不同的映射方式,因此便于程序移植,项目中如果用到多个数据库时,可以使用这种方式。
        由Hibernate根据底层数据库自行判断采用identity、hilo、sequence其中一种作为主键生成方式。
    8. uuid.hex
        UUID使用128位UUID算法生成主键,能够保证网络环境下的主键唯一性,也就能够保证在不同
        数据库及不同服务器下主键的唯一性。
       
        特点;能够保证数据库中的主键唯一性,生成的主键占用比较多的存贮空间
       
        由Hibernate基于128 位唯一值产生算法生成16 进制数值(编码后以长度32的字符串表示)作为主键。
    9.Foreign GUID
        Foreign用于一对一关系中。GUID主键生成方式使用了一种特殊算法,保证生成主键的唯一性,
        支持SQL Server和MySQL 
        使用外部表的字段作为主键。
       
        一般而言,利用uuid.hex方式生成主键将提供最好的性能和数据库平台适应性。
    10.uuid.string
        与uuid.hex 类似,只是生成的主键未进行编码(长度16)。在某些数据库中可能出现问题(如PostgreSQL)。

8 在Hibernate应用中如何处理批量更新和批量删除?---executeUpdate()方法支持
  Hibernate3.0对批量更新和批量删除提供了支持,能够直接执行批量更新或批量删除语句,无需把被更新或删除的对象先加载到内存中。以下是通过Hibernate3.0执行批量更新的程序代码: 
Session session = sessionFactory.openSession(); 
Transaction tx = session.beginTransaction(); 
String hqlUpdate = "update Customer set name = :newName where name = :oldName"; 
int updatedEntities = s.createQuery( hqlUpdate ) 
.setString( "newName", newName ) 
.setString( "oldName", oldName ) 
.executeUpdate(); 
tx.commit(); 
session.close(); 
以下是通过Hibernate3.0执行批量删除的程序代码: 
Session session = sessionFactory.openSession(); 
Transaction tx = session.beginTransaction(); 
String hqlDelete = "delete Customer where name = :oldName"; 
int deletedEntities = s.createQuery( hqlDelete ) 
.setString( "oldName", oldName ) 
.executeUpdate(); 
tx.commit(); 
session.close(); 


9  Hibernate 映射
    <1>基本映射
        基本属性映射

     <property name="PO属性名" column="数据库表列名" type="数据类型" />
      
   <2> 集合映射   知道是如何实现 加一个表利用主键关联

  1.List
<list name="list属性名" table="list对应表名">
<key column="list属性所在类的id(主键" not-null="true"></key>
<list-index column="list_order" />
<element type="list元素类型" column="list元素列名"></element>
</list>

2.Set
<set name="set属性名" table="set对应表名">
<key column="set属性所在类的id(主键" not-null="true"></key>
<element type="set元素类型" column="set元素列名"></element>
</set>

3.Map
<map name="map属性名" table="map对应表名">
<key column="map属性所在类的id(主键)" not-null="true"></key>
<map-key type="map的key的数据类型" column="map的key对应的列名"></map-key>
<element type="map的value的数据类型" column="map的value对应的列名"></element>
</map>

    <3>关联映射  重点掌握 一对多   级联保存,更新 以及删除
        单向映射和双向映射的区别:
单向:一方持有另一方的实例或集合  我中有你,你中没有我。也就是说:通过这个表可以查询到另外一个表数据,但是通过另外一个表,查询不到这个表的数据
双向:彼此持有对方的实例或集合    我中有你,你中也有我。也就是说:通过这个表可以查询到另外一个表的数据,通过另外一个表也可以查询到这个表的数据


  1对1
       <1> 外键关联实现
     单向: <many-to-one name="对象属性名" column="字段名(可不写则与属性名相同)" unique="true"/>
                      双向: <many-to-one name="对象属性名" column="字段名(可不写则与属性名相同)" unique="true"/>
            <one-to-one name="对象属性名" property-ref="另一对象属性名"/>
              
<2> 主键关联实现   一对一主键关联映射中,默认了cascade属性
     单向:
     <id name="id">
<generator class="foreign">
<param name="property">对象属性名</param>
</generator>
    </id>
     <one-to-one name="对象属性名" constrained="true"/>


     双向:
      
      <id name="id">
<generator class="foreign">
<param name="property">对象属性名</param>
</generator>
     </id>
     <one-to-one name="对象属性名" constrained="true"/>
 
           
   
   另一方
   <one-to-one name="对象属性名"/> 
    

1对多
               单向:


<set name="set属性名">
<key column="外键列名"/>
<one-to-many class="包名.类名"/>
</set>
               
      双向:
             
               <set name="set属性名">
<key column="外键列名"/>
<one-to-many class="包名.类名"/>
      </set>
 
      <many-to-one name="对象属性名" column="字段名(可不写则与属性名相同"/>

多对多  中间表

      单向:
       <set name="addresses" table="jointable">
<key column="personId"/>
<many-to-many column="addressId" class="Address"/>
</set>


双向  在多对多关联中 下面的两个方向配置字段一致 如果想不一致 则可以使用不一样的中间表名,不一致时候会没有默认值错误
   
                 <set name="set" table="jointable">
<key column="personid"/>
<many-to-many column="wifeid" class="com.Wife"/>
</set>


<set name="persons" inverse="true" table="jointable">
<key column="wifeid"/>
<many-to-many column="personid" class="Person"/>
</set>

        级联保存,更新 以及删除  在一对多演示   

级联删除分为两种删除
 <1>只把多方(子表)中的关联字段设置为null  当主表为pojo 也就是瞬时状态时
 <2>多方(子表)记录删除掉  当主表为po 也就是持久化状态时


<set name="set属性名" inverse="false" cascade="save-update,delete">
<key column="外键列名"/>
<one-to-many class="包名.类名"/>
</set>


      cascade(级联)与inverse(反转)的作用
   
     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不设置级联,
            在one-to-one和one-to-many中设置级联。
        说明:
            none:表示没有任何操作级联(默认值)
            All:表示所有的操作都级联
            Save-update:表示在save/update/save-update时产生级联
            Delete:表示删除时级联
            若要多个操作都产生级联则:可以用,分开如:save-update,delete
       
        Delete-orphan表示若在从对象中把对应的主对象的值修改为null则删除从对象。通常在one-one中使用。
        一般以many-to-one,many-to-many不设置级联,在one-to-one和one-to-many中设置save-update级联。
  
     Inverse表示:
        “是否放弃维护关联关系”(在Java里面个对象产生关联时,对数据库的影响),
        在one-to-many和many-to-many的集合定义中使用,inverse=“true”表示该对象不维护关联关系;
        该属性的值一般在使用有序集合时设置成false(注意hibernate的缺省值是false).
        One-to-many维护关联关系就是更新外键.many-to-many维护关联关系就是在中间表增减记录


    <4>继承映射:三种策略


  <1>每个类层次映射一个表         (表个数与类个数相同)
<hibernate-mapping>
<class name="test.Sup" table="sup">
<id name="id" column="id" type="java.lang.String">
<generator class="uuid.hex" />
</id>
<property name="name" column="name" type="java.lang.String" />


<joined-subclass name="test.Sub1" table="sub1">
      <key column="id" />
<property name="sex" type="java.lang.String" />
</joined-subclass>


<joined-subclass name="test.Sub2" table="sub2">
<key column="id" />
<property name="age" type="java.lang.String" />
</joined-subclass>
</class>
</hibernate-mapping>




<2>每个子类(具体类)映射一个表   (表个数与子类个数相同)
<hibernate-mapping>


<class name="test.Sup" table="sup" abstract="true">
<id name="id" column="id" type="java.lang.String">
<generator class="uuid.hex" />
</id>
<property name="name" column="name" type="java.lang.String" />


<union-subclass name="test.Sub1" table="sub1" >
<property name="sex" type="java.lang.String" />
</union-subclass>


<union-subclass  name="test.Sub2" table="sub2">
<property name="age" type="java.lang.Integer" />
</union-subclass>
</class>
</hibernate-mapping>




<3>所有类用一个表映射
<hibernate-mapping>
<class name="test.Sup" table="sup" discriminator-value="sup">
<id name="id" column="id" type="java.lang.String">
<generator class="uuid.hex" />
</id>


<discriminator column="personType" type="java.lang.String" /><!--在id属性标签后-->
<property name="name" column="name" type="java.lang.String" />


<subclass name="test.Sub1" discriminator-value="sub1">
<property name="sex" type="java.lang.String" />
</subclass>


<subclass name="test.Sub2" discriminator-value="sub2">
<property name="age" type="java.lang.Integer" />
</subclass>
</class>
</hibernate-mapping>
        三种方法的优缺点:
            1、每一个具体子类映射成单个数据库表,而抽象基类不参与映射。
                优点:
                数据操作实现简单,每个表中都包含自己所需要的具体子类的所有信息,减少了多表关联操作时的性能消耗。
                缺点:
                类的修改会导致相对应的表及其子类所对应表的更改。不支持多态查询。
                应用:
                适合在类层次结构上有一定数量的抽象类的情况下使用。
            2、将整个类层次映射为单个数据库表。
                这对于子类属性不多的情况非常有效。每个子类由识别列(discriminator column)区分。
                优点:
                实现简单,并支持多态。同时数据访问也比较简单,因为数据库表中包含了所有需要的信息。
                缺点:
                增加类层次中的耦合,类层次中任何类的属性的增加都有会导致表的变更。另外,对子类属性的修改错误将会影响到整个类的层次结构。当然也浪费了大量的数据库空间。表中引入区分子类的字段,子类的字段不能创建为空。
            3、继承关系中每个类均映射为一个数据库表
                优点:
                此时,与面向对象的概念是一致的,这种映射实现策略的最大好处就是关系模型完全标准化,关系模型和领域模型完全一致,易于修改基类和增加新的子类。
                缺点:
                数据库中存在大量的表,为细粒度级的数据模型,访问数据时将存在大量的关联表的操作,效率较低。


  《5》组件映射 
            在hibernate中,component是某个实体的逻辑组成部分,它与实体的根本区别是没有oid,
         component可以成为是值对象(DDD)


           采用component映射的好处:它实现了对象模型的细粒度划分,层次会更分明,复用率会更高
   
   <component name="contact(Javabean属性名)">
<property name="email"/>
<property name="address"/>
<property name="zipCode"/>
<property name="contactTel"/>
</component>


   <6> 组合映射 
         通常将复合主键相关的属性,单独放到一个类中
* 此类必须实现序列化接口
* 覆写hashcode和equals方法



   


   
12.  Hibernate 缓存机制


     12.1 Hibernate缓存概述


      缓存是介于物理数据源与应用程序之间,是数据库数据在内存中的存放临时copy的容器,
      其作用是为了减少应用程序对物理数据源访问的次数,从而提高了应用的运行性能。
Hibernate在进行读取数据的时候,根据缓存机制在相应的缓存中查询,
      如果在缓存中找到了需要的数据(我们把这称做“缓存命中"),则就直接把命中的数据作为结果加以利用,
      避免了建立数据库查询的性能损耗。
    
    简单介绍完Hibernate的缓存后,再结合Hibernate的获取数据方式来说明缓存的具体使用方式,
    在Hibernate中获取数据常用的方式主要有四种:Session.load、Session.get、Query.list、Query.iterator。 
    
                                                   是否延迟加载
            Session.load-------一级缓存-------二级缓存------------------------数据库


                                                            
            Session.get-------一级缓存-------二级缓存------------------------数据库


                                             
   Query.list---------查询缓存--------------数据库---------这里有可能调用Session.load(如果从数据库出来时id集合)


                    寻找id集合               根据id一个一个的load
   Query.iterator -----------------数据库---------------------------------session.load




         




2、Session.get 


在执行Session.get时,和Session.load不同的就是在当从缓存中获取不到时,直接从数据库中获取id对应的值。 








     12.2 Hibernate的缓存分为:
     
1、一级缓存(内置缓存)->session级别缓存,是Hibernate 的内置缓存    一级缓存在Hibernate中对应的即为session范围的缓存,也就是当session关闭时缓存即被清除,一级缓存在Hibernate中是不可配置的部分
                      测试一级缓存
       1 在同一个session中发出两次load或者是get查询 

不会发出sql,因为load使用缓存

2 在同一个session中发出两次iterate查询实体对象(查属性)
   2.1 查对象
    会发出查询id的sql,不会发出查询实体对象的sql,因为iterate使用缓存

   2.2查属性
     iterate查询普通属性,一级缓存不会缓存,所以发出sql
一级缓存是缓存实体对象的

3  开启两个session中发出load查询  
   
     会发出查询语句,session间不能共享一级缓存的数据
   因为它会伴随session的生命周期存在和消亡


4 在同一个session中先save,在发出load查询save过的数据
  //不会发出sql,因为save是使用缓存的
                             不会发出sql,因为save是使用缓存的
              
      2  二级缓存(应用级缓存):
         二级缓存也称进程级的缓存或SessionFactory级的缓存,二级缓存可以被所有的session共享
                  二级缓存的生命周期和SessionFactory的生命周期一致,SessionFactory可以管理二级缓存 ,二级缓存是缓存实体对象的


   什么样的数据适合存放到第二级缓存中?《人的登录信息》
1 很少被修改的数据
2 不是很重要的数据,允许出现偶尔并发的数据
3 不会被并发访问的数据
4 参考数据,指的是供应用参考的常量数据,它的实例数目有限,它的实例会被许多其类的实例引用,实例极少或者从来不会被修改。
不适合存放到第二级缓存的数据?
1 经常被修改的数据
2 财务数据,绝对不允许出现并发
3 与其他应用共享的数据。


     
                 
              .开启二级缓存的步骤:


二级缓存的配置和使用:

* 开启二级缓存,修改hibernate.cfg.xml文件
<property name="hibernate.cache.use_second_level_cache">true</property>
* 指定缓存产品提供商,修改hibernate.cfg.xml文件
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
* 指定那些实体类使用二级缓存(两种方法)
* 在映射文件中采用<cache>标签
                                  <cache usage="read-only"/>  
 usage="read-only"是“只读”缓存策略。这个<cache>标签只能放在<class>标签的内部,而且必须处在<id>标签的前面
  这个<cache>标签放在哪些<class>标签下面,就说明会多这些类的对象进行缓存 
* 在hibernate.cfg.xml文件中,采用<class-cache>标签
<class-cache class="com.bjsxt.hibernate.Classes" usage="read-only" />   注意,这个<class-cache>标签必须放在<mapping>标签的后面。




               测试二级缓存
    1 开启两个session,分别调用load或者get
                         //不会发出sql,因为开启了二级缓存,session是共享二级缓存的
                     2 开启两个session,分别调用load,在使用SessionFactory清除二级缓存
       //管理二级缓存
SessionFactory factory = HibernateUtils.getSessionFactory();
//factory.evict(Student.class);
factory.evict(Student.class, 1);


//会发出查询sql,因为二级缓存中的数据被清除了
                     3 开启两个session 一级缓存和二级缓存的交互
       //仅向二级缓存读数据,而不向二级缓存写数据
session.setCacheMode(CacheMode.GET);
//发出sql语句,因为session设置了CacheMode为GET,所以二级缓存中没有数据


//只向二级缓存写数据,而不从二级缓存读数据
session.setCacheMode(CacheMode.PUT);


//会发出查询sql,因为session将CacheMode设置成了PUT




        3、查询缓存:
       查询缓存的生命周期和session无关


query.list----> 数据放到 查询缓存(id)与二级缓存
 
查询缓存是针对普通属性结果集的缓存 

对实体对象的结果集只缓存id






如果采用的是select po.property这样的方式那么value为整个结果集,如采用的是from这样的方式那么value为获取的结果集中各po对象的主键ID

查询缓存的生命周期,当前关联的表发生修改,那么查询缓存生命周期结束


查询缓存的配置和使用:
* 在hibernate.cfg.xml文件中启用查询缓存,如:
<property name="hibernate.cache.use_query_cache">true</property>
* 在程序中必须手动启用查询缓存,如:
query.setCacheable(true);

                        //查询缓存只对query.list()起作用,query.iterate不起作用,也就是query.iterate不使用
//查询缓存




                         测试  


  1 开启查询缓存,关闭二级缓存 开启一个session,分别调用query.list
      没有发出查询sql,因为启用了查询缓存
                  2  开启查询缓存,关闭二级缓存 开启两个session,分别调用query.list
      不会发出查询sql,因为查询缓存的生命周期和session无关
                           3    开启查询缓存,关闭二级缓存 开启两个session,分别调用query.iterate
       查询缓存只对query.list()起作用,query.iterate不起作用,也就是query.iterate不使用
  4  关闭查询缓存,关闭二级缓存  开启两个session,分别调用query.list查询实体对象
      会发出查询sql,因为list默认每次都会发出查询sql


  5 开启查询缓存,关闭二级缓存 开启两个session,分别调用query.list查询实体对象
     会发出n条查询语句,因为开启了查询缓存,关闭了二级缓存,那么查询缓存会缓存实体对象的id
     所以hibernate会根据实体对象的id去查询相应的实体,如果缓存中不存在相应的
     实体那么将发出根据实体id查询的sql语句,否则不会发出sql使用缓存中的数据
                  6 开启查询缓存,开启二级缓存 开启两个session,分别调用query.list查询实体对象
     不会发出查询sql,因为开启了二级缓存和查询缓存,查询缓存缓存了实体对象的id列表
     hibernate会根据实体对象的id列表到二级缓存中取得相应的数据




Hibernate 延迟加载(lazy)


     重要的概念: 

   1、lazy的概念,指在需要的时候才发出sql
   2、lazy策略只是在session打开期间才是有效的
   3、Hibernate加载本身就是延迟加载,在*.hbm.xml配置文件中<class>里配置lazy="false"将其改
为非延迟加载。
   4、Hibernate属性的延迟加载,在*.hnm.xml配置文件中<property>里配置lazy="false"即可。


   注意:类的延迟加载并不影响属性的延迟加载;






  hibernate3.0中lazy有三个值,true,false,proxy,延迟加载
    Lazy的有效期:只有在session打开的时候才有效;session关闭后lazy就没效了。
    lazy策略可以用在:
        * <class>标签上:可以取值true/false
        * <property>标签上,可以取值true/false,这个特性需要类增强  ???  要对类增强器进行类增强org.hibernate.tool.instrument.InstrumentTask

bulid.xml代码 用ant运行 对类进行增强
      <?xml version="1.0" encoding="UTF-8"?>
<project name="hibernateSample" default="instrument" basedir=".">
<property name="lib.dir" value="./lib"/>
<property name="classes.dir" value="./bin"/>
<path id="lib.class.path">
 <fileset dir="${lib.dir}">
 <include name="**/*.jar"/>
 </fileset>
 </path>



<target name="instrument">
    <taskdef name="instrument"
classname="org.hibernate.tool.instrument.InstrumentTask">


<classpath path="${classes.dir}"/>
<classpath refid="lib.class.path"/>
    </taskdef>

    <instrument verbose="true">
<fileset dir="${classes.dir}/com">
  <include name="Person.class"/><!-- 此处是要增强的类-->
</fileset>
    </instrument>
 </target>


</project>










        * <set>/<list>等集合上,可以取值为true/false/extra
        * <one-to-one>/<many-to-one>等标签上,可以取值false/proxy/no-proxy
   


    类(Class)的延迟加载:
             
  


        * 设置<class>标签中的lazy="true",或是保持默认(即不配置lazy属性)
        * 如果lazy的属性值为true,那么在使用load方法加载数据时,只有确实用到数据的时候才会发出sql语句;
            这样有可能减少系统的开销。
   
    集合(collection)的延迟加载:可以取值true,false,extra
        * 保持集合上的lazy的默认值,此时的效果和lazy="extra"是基本一样的。
        * 设置集合上的lazy=extra,此时的效果和lazy属性的默认值是基本一样的。但是推荐使用这个属性值
            ,因为在统计时这种情况显得比较智能。当然延迟是有效果的。
        * 设置集合上的lazy=false
            true:默认取值,它的意思是只有在调用这个集合获取里面的元素对象时,才发出查询语句,加载其
            集合元素的数据
           
            false:取消懒加载特性,即在加载对象的同时,就发出第二条查询语句加载其关联集合的数据
           
            extra:一种比较聪明的懒加载策略,即调用集合的size/contains等方法的时候,hibernate并不会
            去加载整个集合的数据,而是发出一条聪明的SQL语句,以便获得需要的值,只有在真正需要用到这些集合元素
           
        对象数据的时候,才去发出查询语句加载所有对象的数据
      Hibernate单端关联懒加载策略:即在<one-to-one>/<many-to-one>标签上可以配置
        懒加载策略。可以取值为:false/proxy/no-proxy
            false:取消懒加载策略,即在加载对象的同时,发出查询语句,加载其关联对象
            proxy:这是hibernate对单端关联的默认懒加载策略,即只有在调用到其关联对象的方法的时候才真正发出查询语句
                查询其对象数据,其关联对象是代理类
            no-proxy:这种懒加载特性需要对类进行增强,使用no-proxy,其关联对象不是代理类
       
注意:在class标签上配置的lazy属性不会影响到关联对象!!!   
       


11 
         session.flush   


    session flush 方法主要做了2件事


    * 清理缓存


    * 执行sql


    session在什么情况下执行flush


    * 默认在事物提交时


    * 显示的调用flush


    * 在执行查询前 ,如iterate


            当程序执行完flush() 就已经向数据库发出了语句,但是我们是看不到的,这是由于数据库有一个隔离级别的问题。Mysql 默认重复读。


           


    
get和load的区别?
* get不支持lazy,load支持lazy
* 采用get加载数据,如果没有匹配的数据,返回null,而load则抛出异常org.hibernate.ObjectNotFoundException






Query的两个方法,list() 和 iterate() ,两个方法都是把结果集列出来, 但它们有以下区别
  
   1: 获取数据的方式不一样,list()会直接查数据库返回i结果集, iterate()会先到数据库中把id都取出来,
然后真正要遍历某个对象的时候先到缓存中找,如果找不到,以id为条件再发一条sql到数据库,
这样如果缓存中没有数据,则查询数据库的次数为n+1。
   2:query.list()用查询缓存,query.iterate不用
 


 12  hibernate 抓取策略 (   查询抓取  子查询抓取 连接抓取  批量抓取)
         连接抓取  fetch="join" 通过select语句使用外连接来加载其关联实体或集合 此时lazy会失效
查询抓取  fetch="select" 另外发送一条select语句抓取当前对象关联实体或集合
         子查询抓取 fetch="subselect" 另外发送一条select语句抓取在前面查询到的所有实对象的关联集体《list》合 通过子查询in完成
         批量抓取    batch-size设置
 
      1  单端代理的批量抓取  加载集合属性


          保持默认,同fetch="select",如:
<many-to-one name="classes" column="classesid" fetch="select"/>


fetch="select",另外发送一条select语句抓取当前对象关联实体或集合
      2 单端代理的批量抓取)  加载集合属性


设置fetch="join",如:
<many-to-one name="classes" column="classesid" fetch="join"/>


fetch="join",hibernate会通过select语句使用外连接来加载其关联实体或集合


此时lazy会失效
      3  集合代理的批量抓取  加载集合属性


保持默认,同fetch="select",如:
<set name="students" inverse="true" cascade="all" fetch="select">


fetch="select",另外发送一条select语句抓取当前对象关联实体或集合


     4  集合代理的批量抓取  加载集合属性


设置fetch="join",如:
<set name="students" inverse="true" cascade="all" fetch="join">


fetch="join",hibernate会通过select语句使用外连接来加载其关联实体或集合


此时lazy会失效
     5 集合代理的批量抓取  加载集合属性


设置fetch="subselect",如:
<set name="students" inverse="true" cascade="all" fetch="subselect">


fetch="subselect",另外发送一条select语句抓取在前面查询到的所有实体对象的关联集合
     如
List<Person> list =session.createQuery(" from Person").list();
for(Person p1:list)
   p1.getSet().iterator();
    
     6  batch-szie在<class>上的应用


batch-size属性,可以批量加载实体类,
<class name="Classes" table="t_classes" batch-size="3">


               由多方查询一方 一方配置batch-size
List<Address> list =session.createQuery(" from Address").list();
for (Iterator iter=list.iterator(); iter.hasNext();) {
Address  a = (Address)iter.next();
System.out.println( a.getLocation());
System.out.println( a.getP().getName());
}


     7  batch-szie在集合上的应用  加载集合属性
         batch-size属性,可以批量加载实体类,
        <set name="students" inverse="true" cascade="all" batch-size="5">




     
List<Person> list =session.createQuery(" from Person").list();
for(Person p1:list)
{
System.out.println( p1);
Iterator it=p1.getSet().iterator();
while(it.hasNext())
System.out.println(it.next()); 
}


 13 spring 整合hibernate
    
   可以先说说
Hibernate的原理及其步骤
 <1>读取并解析配置文件
    <2>读取并解析映射信息,创建SessionFactory
    <3>打开Session实例
    <4>创建事务Transaction
    <5>CRUD操作(执行数据操作)
    <6>提交事务
    <7>关闭Session实例


之后引入spring整合hibernate的几个方面 好处


1 基于依赖注入的SessionFactory管理机制
2 优秀的session的管理机制 spring提供每事务一次session的机制,大大提高了系统性能.spring对session的管理机制是透明的,无须在代码中操作session
3 统一的事务管理
4 统一的异常处理机制
5 HibernateTemplate 和 HibernateDaoSupport 支持类 




如何整
  Spring与Hibernate的整合
    <1>将原Hibernate的hibernate.cfg.xml文件中关于数据源的配置整合为   
        Spring的applicationContext.xml文件中的一个bean
        
<!--定义数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
        <property name="url"><value>jdbc:mysql://localhost:3306/j2ee</value></property>
        <property name="username"><value>root</value></property>
        <property name="password"><value>123456</value></property>
       </bean>








    <2>将Hibernate的核心SessionFactory接口的配置参数整合为Spring的  LocalSessionFactoryBean类   注入数据源
        applicationContext.xml文件中的一个bean


         <!--定义一个hibernate的SessionFactory-->
   <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource"><ref local="dataSource"/></property>  
<property name="mappingResources">
   <list>
<!--以下用来列出所有的PO映射文件-->
<value>com/Person.hbm.xml</value>
   </list>
</property>
<property name="hibernateProperties">
<props>
   <!--此处用来定义hibernate的SessionFactory的属性:不同数据库连接,启动时选择create,update,create-drop-->
   <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
   <prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
   </bean>




    <3>DAO类继承Spring的HibernateDaoSupport类,通过getHibernateTemplate()
        方法获得HibernateTemplate实例<要注入sessionFactory>,通过此实例进行CRUD操作,将
        SessionFactory,Session以及Transaction交由Spring框架负责管理。


     //HibernateDaoSupport本身提供setSessionFacotry(Session session)方法
//它会在创建该bean的时候自动设置SessionFactory,并将该SessionFactory给HibernateTemplate

public class MyTestDaoHibernate extends HibernateDaoSupport implements MyTestDao
{
   public Collection findAllMyTest()
{
return getHibernateTemplate().find("from MyTest");
}
}




    <4>在Spring的applicationContext.xml中配置DAO的bean   给HibernateTemplate注入sessionFactory


       <!--定义DAO Bean-->
<bean id="myTestDao" class="com.MyTestDaoImpl">
        <property name="sessionFactory"><ref local="sessionFactory"/></property>
      </bean>


     <5>整合事务HibernateTransactionManager
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
 <property name="sessionFactory">
  <ref local="sessionFactory" />
 </property>
</bean>




 Hibernate:注意Spring2.5已经不再支持Hibernate 3.1之前的版本了,也就是说2.1,3.0的版本也无法使用了。如果仍然要用的话,推荐使用Spring的2.0.6/2.0.7版本。




14 乐观锁与悲观锁
    
    乐观锁


大多数基于数据版本记录机制(version)实现,一般是在数据库表中加入一个version字段
读取数据时将版本号一同读出,之后更新数据时版本号加一,如果提交数据时版本号小于或等于数据表中
的版本号,则认为数据是过期的,否则给予更新




实现步骤
  <1> 在class标签上 optimistic-lock="version"  如  <class name="com.Person" table="person" optimistic-lock="version">
  <2> 在id属性后增加 <version name="version"/>
  <3> 给pojo类增加version属性 int类型 增加set get方法
  
  测试 修改数据库的数据 当数据没有变化时,版本号不会更新 数据变化时 版本号加1


  采用阻塞代码进行手动表中的version修改看效果  事务提交前阻塞
    session.update(p);
BufferedInputStream b = new BufferedInputStream(System.in);
b.read();
session.getTransaction().commit();
  


    
    悲观锁
    悲观锁的实现,通常依赖于数据库机制,在整个过程中将数据锁定,其它任何用户都不能读取或修改  利用LockMode.UPGRADE
   
     p=(Person)session.load(Person.class, 6,LockMode.UPGRADE);
   
  采用阻塞代码进行看效果  事务提交前阻塞
    session.update(inv);
BufferedInputStream b = new BufferedInputStream(System.in);
b.read();
session.getTransaction().commit();
  




15  Hibernate性能优化方案
         1.数据库设计优化
         2.Hibernate的hql优化
         3.API的正确使用
         4.缓存的配置管理(如:二级缓存中使用的技术是:EhCascade)
         5.关闭hql的打印输出(在hibernate.cfg.xml中不使用show sql)
         6.ID生成策略,延迟加载,关联优化

原创粉丝点击