Hibernate学习

来源:互联网 发布:淘宝网是马云的吗 编辑:程序博客网 时间:2024/06/14 18:15

Hibernate学习

经过一段时间的学习和应用JDPC,逐渐感受到了用JDPC写代码的一些缺点:无法直接面向对象,代码冗余,特别是当属性很多时,用JDPC开发效率级低,需要大量的重复工作。

为了使操作直接面向对象进行,接触学习了Hibernate,hibernate是一个开发源代码的对象关系映射框架,对JDPC进行了非常轻量级的对象封装,简化了JDPC繁琐的编码,将对象和数据库中的表建立起了对应关系,直接面向对象。

Hibernate是一个优秀的java持久化层解决方案,是当今主流的对象—关系映射(ORM)工具,ORM是持久化的一种解决方案主要是把对象模型和关系型数据库关系模型映射起来,并且使用元数据对这些映射进行描述。

一、准备hibernate:

1、  搭建项目框架,引用外部jar架包;2、编写hibernate配置文件,建立主配置文件hibernate.cfg.xml,测试连接;3、实体映射文件,使实体类和数据库中的表联系到一起。主要有三种映射关系:类和表的映射,属性和主键的映射,属性和普通字段的映射。实体映射文件需要添加到主配置文件中并进行测试 。

二、数据库操作(7个步骤):

1、  配置Configuration

2、  创建SessionFactorySessionFactory

3、  打开Session

4、  开始事务

5、  持久化操作 :save/update/delete/find

6、  提交事务

7、  关闭Session

三、hibernate的五大接口

1、Configuration接口,对象用来配置和引导

2、SessionFactory接口,重量级接口,只用执行一次就行,创建代价太高

3、Session接口,拥有操作持久化对象的一系列API,可用用于管理,也称为“持久化管理器”

4、Transaction(事务)接口是对实际事务实现的一个抽象

5、Quary接口,实现查询功能

四、Hibernate中的对象有三种状态: 瞬时状态 (Transient),持久状态 (Persistent)和游离状态(Detached)

1、 瞬时状态 (Transient):由 new 创建,如: Student stu = new Student(); 

瞬时对象特点: 

(1) 不和 Session 实例关联 ;(2) 在数据库中没有和瞬时对象关联的记录

2、 持久状态 (Persistent) :

持久的实例在数据库中有对应的记录,并拥有一个持久化标识 (identifier). 

持久对象总是与 Session 和 Transaction 相关联,在一个 Session 中,对持久对象的改变不会马上对数据库进行变更,而必须在 Transaction 终止,也就是执行 commit() 之后,才在数据库中真正运行 SQL 进行变更,持久对象的状态才会与数据库进行同步。在同步之前的持久对象称为脏 (dirty) 对象,处于Session的缓存区中。

瞬时对象转为持久对象: 

(1) 通过 Session 的 save() 和 saveOrUpdate() 方法把一个瞬时对象与数据库相关联,这个瞬时对象就成为持久化对象。 

(2) 使用 fine(),get(),load() 和 iterater() 待方法查询到的数据对象,将成为持久化对象。 持久化对象的特点: 

(1) 和 Session 实例关联 

(2) 在数据库中有和持久对象关联的记录 

 

3、 脱管状态 (Detached) :与持久对象关联的 Session 被关闭后,对象就变为脱管对象。对脱管对象的引用依然有效,对象可继续被修改。 

脱管对象特点: 

(1) 本质上和瞬时对象相同 

(2) 只是比爱瞬时对象多了一个数据库记录标识值 id. 持久对象转为脱管对象: 

当执行 close() 或 clear(),evict() 之后,持久对象会变为脱管对象。 瞬时对象转为持久对象: 

通过 Session 的 update(),saveOrUpdate() 和 lock() 等方法,把脱管对象变为持久对象。

 

五、实体的关联关系

主要讲多对一关联(many-to-one)、一对多(one-to-many)、多对多(many-to-many)

1、多对一关联(many-to-one):在映射表中的体现

<!--多对一-->

<many-to-onename="type" class="entity.Type">

<column name="type_id"></column>

</many-to-one>

2、  一对多(one-to-many):

<set name="records">

       <key column="book_id"></key>

       <one-to-many class="entity.Record"/>

       </set>

3、  多对多(many-to-many)实际上是两个一对多:

<!-- 联合主键 -->

    <composite-id>

    <!--同时表达这是外键-->

    <key-many-to-one name="books"column="book_id" class="entity.Book"></key-many-to-one>

    <key-many-to-one name="stu"column="stu_id" class="entity.Student"></key-many-to-one>

    </composite-id>

六、Hibernate 支持两种主要的查询方式:HQL(Hibernate Query Languge,Hibernate 查询语言)查询,是一种面向对象的查询语言,其中没有表和字段的概念,只有类、对象和属性的概念,HQL 是应用较为广泛的方式

Criteria 查询又称为“对象查询”,它用面向对象的方式将构造查询的过程做了封装

1、 使用HQL需要四步

(1)得到Session

(2)编写HQL语句

(3)创建Query对象:Query接口是HQL 查询接口。它提供了各种的查询功能

(4)执行查询,得到结果

HQL语法和SQL语法基本相同,需要将SQL中的表名改为类名,列名改为属性

实例:

public void hqlText(){

       String hqlString = "from Student s where lower(s.name) like '%z%'";

       Session session = HibernateUtil.currentSession();

       Query query =session.createQuery(hqlString);

       List<Student>student = query.list();

       System.out.println(student);

    }

2、 Criteria查询,通过Criteria查询能实现HQL的查询方式,不过相对而言,HQL查询是基于字符串的,因此相对而言更加灵活。语法和HQL的查询方式类似,只是限制条件有区别。

实例说明:

    @Test

    public void test1() {

       Session session =HibernateUtil.currentSession();

       // Criteria查询

       Criteria criteria = session.createCriteria(Book.class);

       //添加限制条件,查询价格在30~40之间的图书

       criteria.add(Restrictions.gt("price",30d));

       criteria.add(Restrictions.lt("price",40d));

       List<Book> list = criteria.list();

       System.out.println(list);

    }

    /**

    *   Criteria接口的setMaxResults()方法可以限定查询回来的笔数,

    *   如果配合setFirstResult()设定查询的起始位置的话,就可以实现简单的分页。

    */

    @Test

    public void test2() {

       Session session =HibernateUtil.currentSession();

       Criteria criteria =session.createCriteria(Book.class);

       //criteria.addOrder(Order.desc("price"));倒序从大到小

       criteria.addOrder(Order.asc("price"));//从小到大

       criteria.setMaxResults(2);

       List<Book> list = criteria.list();

       System.out.println(list);

    }

七、加载计划以及延迟加载

为了加以区别,我们称第一层次的查询目标为“主对象”。要关注的是主对象及其关联对象的加载计划和加载策略,即数据何时被加载以及数据被怎样加载的问题。

涉及到加载计划(何时加载)的关注点有以下几个:

1、主对象的关联对象何时被加载

2、主对象的关联集合何时被加载

3、主对象本身何时被加载

4、主对象的属性何时被加载

Hibernate都有默认的加载计划。具体来说:

1、关联对象和关联集合的默认加载计划是:延迟加载,即加载主对象时它们不会被立即加载,而是直到使用这些对象和集合时才发送SQL语句、获取数据、初始化对象和集合。

例子:

public void lazyTest3() {

     Session session= HibernateUtil.currentSession();

     Book book =(Book) session.load(Book.class, 2);//并未触发查询,book是个代理对象

     System.out.println("============华丽的分割线=============");

     System.out.println(book);//触发代理对象的初始化——触发查询语句的发生

  }

2、主对象本身是否延迟加载取决于使用的是load()方法还是其它方法,load()方法是延迟的,而get()方法或其它方法是立即的。

 

3、主对象的属性默认是被立即加载的。

 

同样的我们也可以改变默认加载计划:

1、  通过修改对应类的实体映射文件,改变默认的加载计划。

关联对象立即加载

<many-to-onename="type"  lazy=”false”class="entity.Type">

<column name="type_id"></column>

</many-to-one>

关联集合立即加载

<set name="records"lazy=”false”>

       <key column="book_id"></key>

       <one-to-many class="entity.Record"/>

       </set>

但是这样配置之后,不管是否需要关联数据,Hibernate都会加载,这样会造成大量的内存浪费,所以比建议这么做

2、  采用编程方式在特定情况下进行立即加载(也称热加载)

public void forceInitTest() {

              Session session= HibernateUtil.currentSession();

              Type type =(Type) session.get(Type.class, 1);

              System.out.println(type.getName());

              Hibernate.initialize(type.getBooks());//强制初始化代理对象,立即加载

             

              System.out.println("=========================");

              HibernateUtil.closeCurrentSession();

              Set<Book>books = type.getBooks();

              System.out.println(books);

 

 

 

延迟加载仅在session范围内有效——持久对象上才能有延迟加载

托管状态对象上获取关联的对象或者集合会出现以下错误:

       org.hibernate.LazyInitializationException:failed to lazily initialize a collection of role: entity.Type.books, could notinitialize proxy - no Session

 

延时加载怎么解决?

       1、改变默认策略?

因为这样配置之后,不管是否需要关联数据,Hibernate都会加载,这样会造成大量的内存浪费,所以比建议这么做

       2、暂时不关闭session

       3、用程序强制初始化代理对象,Hibernate.initialize(tp.getBooks());//强制初始化关联集合

八、Hibernate高速缓存(这一部分不是很明白,先记下来,有时间再看)

1、一级缓存:Hibernate一级缓存的生命周期跟Session的生命周期一样,所以也可以将Hibernate一级缓存称为Session缓存。Hibernate一级缓存是一个强制的高速缓存。

get()和load()方法会去一级缓存中查找获取数据(又称命中数据),并且将查询结果放置在一级缓存中。list()方法会将查询结果放置在一级缓存中,但是它不会去一级缓存中查找获取数据,原因是list()方法不是通过id加载的。

iterate方法

Iterator<Seeker> iter = session.createQuery(“from**").iterate();

该语句只把ID的值放到迭代器中,当遍历的时候,会根据ID的值再去数据库中查。并且该语句会产生N+1次查询。

实例:

       @Test//iterate是典型的N+1查询,唯一的好处是会做缓存命中

       public void cache2() {

              Session session= HibernateUtil.currentSession();

              List<Book>books = session.createQuery("from Book").list();

             

              Iterator<Book>bookiIterator = session.createQuery("from Book").iterate();

              System.out.println("========华丽的分割线==============");

              while(bookiIterator.hasNext()){

                     Bookbook = bookiIterator.next();

                     System.out.println("========华丽的分割线2==============");

                     System.out.println(book);

              }

       }

 

在数据量比较大的情况下,管理一级缓存的做法一般都是在一定数量的更新或者保存等操作之后,使用flush()和clear()方法将数据同步到数据库并清除一级缓存,从而避免一次性大量实体数据的缓存导致内存溢出。

3、  二级缓存是一个可选的缓存插件,它由SessionFactory负责管理,所以也常将二级缓存称为SessionFactory缓存。Hibernate的二级缓存策略,是针对于id查询的缓存策略,对于条件查询则毫无作用。

二级缓存的使用条件:

(1)、很少被修改的数据

(2)、不是很重要的数据

(3)、不会被开发访问的数据

 

原创粉丝点击