Hibernate笔记

来源:互联网 发布:sql数据库管理工具 编辑:程序博客网 时间:2024/06/03 04:42

Hibernate框架的优势与适用场景

优点:

1. 它是纯面向对象的持久化技术,不用使用者会SQL,用纯面向对象的接口去操作数据。

2. Hibernate是一个ORM(Object Relation Mapping)持久化技术。

3. Hibernate它提供了数据缓存技术,而且不止一个缓存(一级缓存、二级缓存、查询缓存)。

4. Hibernate持久化技术可以跨数据库,它是通过方言来解决这个问题的。

缺点:

1. 性能问题,因为SQL是Hibernate生成的,我们无法控制SQL的生成,但数据库性能优化的一个非常重要的的手段就是通过优化SQL来完成的。比如:

select * from employee where id in (1, 2, 3);

Select * from employee where id = 1 or id = 2 or id = 3;

Select * from employee where id between 1 and 3;

Select * from employee where id = 1 union

Select * from employee where id = 2 union

Select * from employee where id = 3

适用场景:

1. Hibernate适用于中小型项目。

2. 不适用项目中存在大量的表与表之间特别复杂的关系。

3. 对性能要求很高的项目。


Hibernate的开发环境搭建

1. 把Hibernate的相关的jar包导入项目中的classpath.

           antlr-2.7.6.jar

           asm-1.5.3.jar

           asm-attrs-1.5.3.jar

           backport-util-concurrent.jar

           cglib-2.1_3.jar

           commons-collections-2.1.1.jar

           commons-logging-1.0.4.jar

           dom4j-1.6.1.jar

           ehcache-1.5.0.jar

           ejb3-persistence-1.0.1.GA.jar

           hibernate-3.2.6.ga.jar

           hibernate-annotations-3.3.1.GA.jar

           hibernate-commons-annotations-3.0.0.ga.jar

           jta-1.0.1B.jar

          mysql-connector-java-5.1.10-bin.jar

          mysql-connector-java-5.1.10.jar

2. Hibernate开发的三要素

      a)     配置文本件:配置的是数据库连接、配置Hibernate特性功能、配置ORM配置文件的路径. hibernate.cfg.xml

         1、数据库的链接信息

               <propertyname="hibernate.connection.username">root</property>

               <propertyname="hibernate.connection.password">123456</property>

               <propertyname="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>

               <propertyname="hibernate.connection.url">jdbc:mysql://localhost:3306/privatesecretary</hibernate>

          2、动态生成表

               <propertyname="hbm2ddl.auto">update</property>

                create:表示启动的时候先drop,再create (测试人员 准备标准测试数据)

                create-drop:也表示创建,只不过再系统关闭前执行一下drop (测试程序是否正确)

               update: 这个操作启动的时候会去检查schema是否一致,如果不一致会做scheme更新 (建表,更新表结构【只能加】)

                validate:启动时验证现有schema与你配置的hibernate是否一致,如果不一致就抛出异常,并不做更新

          3、引入映射文件

               <mappingresource="com/voole/vo/Role.hbm.xml" />

          4、输出SQL语句

               <property name="show_sql">true</property>

          5、格式化SQL语句

               <propertyname="format_sql">true</property>

          6、方言

               <propertyname="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

          6、关于二级缓存的配置

               <propertyname="cache.use_second_level_cache">true</property>

               <propertyname="cache.provider_class">

                    org.hibernate.cache.EhCacheProvider

               </property>

          7、关于查询缓存的配置

               <propertyname="cache.use_query_cache">true</property>

          8、统计机制

               <propertyname="hibernate.generate_statistics">true</property>

          9、从当前线程中产生session

               <propertyname="current_session_context_class">thread</property>

       b)     ORM配置文件:配置对象与表的映射关系 javabean的名称.hbm.xml.

          1. 如果java的属性名与表中的字段名不匹配时我们可以通过如下方式来解决:

              <property name=”name” column=”t_name”>

          2. Java属性的类型与表中字段的类型的映射,如果不指定类型映射那么Hibernate会用反射机制来自动根据一定的规则去匹配(参考 官方文档 92页)

          3. 配置表的一些约束非空约束  长度约束

               not-null="true"length="23"

          4. 主键生成策略

              在<id>元素中 通过 <generator class="生成策略"></generator> 指定数据表主键生成策略

              常用六种主键生成策略

                  1) increment

                       increment标识符生成器由 Hibernate以递增的方式为代理主键赋值

                       原理:select max(id), insert max(id)+1

                       *使用 increment创建数据表 没有主键自增长, 通过hibernate在程序内部完成自增

                       *好处跨平台 ,缺点 高并发访问时,可以出现主键冲突问题 

                  2) identity(无线程问题)---- Mysql

                       identity标识符生成器由底层数据库来负责生成标识符,它要求底层数据库把主键定义为自动增长字段类型

                       原理: 依赖数据库内部自增长,和hibernate无关

                       *创建数据表 `id` int(11) NOT NULLAUTO_INCREMENT

                       *优点,无需程序处理,数据库自己完成主键增长,缺点 Mysql支持 自增主键, oracle 不支持  

                  3) sequence --- Oracle

                       sequence 标识符生成器利用底层数据库提供的序列来生成标识符

                       原理: 依赖数据库序列支持 ,和hibernate程序无关

                       * Oracle支持序列, Mysql不支持序列

                  4) native

                       native标识符生成器依据底层数据库对自动生成标识符的支持能力,来选择使用 identity, sequence 或 hilo 标识符生成器.

                       * Mysql自动选择 identity,oracle 自动选择 sequence 

                  5) uuid

                       uuid的主键生成,采用String类型主键

                       随机生成32位字符串

                  6) assigned

                       assigned是自然主键生成策略 ----必须用户在程序中指定 (无法自动生成)

       c)      javaBean

            1. 在写javabean时一定要保证javabean中有oid(对应表中的主键)

            2. 尽写成员变量时不要用基本类型,要用对应的包装类型

            3. Javabean这个类不要用final去修饰,因为hibernate有个延迟加载的功能(动态代理cglib)。

            4. Javabean一定要有一个默认无参的构造方法。

3. 把log4j.properties放到classpath中

4. 编写Hibernate的持久化代码。

    publicvoid test(){       // 1.新增配置文件对象,此对象是用来读取Hibernate的配置文件,       //默认是放在classpath根据目录       //名字为hibernate.cfg.xml        Configuration config =new Configuration().configure();       // 2.通过configuration对象再创建SessionFactory对象        SessionFactorysessinFactory = config.buildSessionFactory();       // 3.通过SessionFactory对象创建Session        Session session =sessinFactory.openSession();       // 4.利用session开启事务        Transaction t =session.beginTransaction();       try {           // 5.创建一个Student对象并初始化            Student student =new Student();            student.setAge(18);            student.setName("张三");           // 6.调用session的save方法把数据存到数据库的表中            session.save(student);           // 7.提交事务            t.commit();        }catch (Exception e) {            e.printStackTrace();            t.rollback();        }finally{           // 8.释放资源            session.close();            sessinFactory.close();        }    }


Hibernate所涉及的主要类

Configuration

          l  它是专门读取Hibernate配置文件与映射文件的类,它是SessionFactory对象的工厂,这个类是单例的,在你的项目中这个类只能产生一个对象,这个类是线程安全的。

SessionFactory

          l  sessionFactory这个对象中存放了Hibernate配置信息、存放了映射配置信息、二级缓存,这个类的对象是重量级的(建立这个对象需要大量的资源,非常耗时),所这个对象不能随便去创建,这个类的对象是进程级别的,这个对象也是单例的,这个对象是产生session对象的工厂。

Session

          l  Session对象是专门用于操作数据库(CRUD),此对象中包含着Hibernate的一级缓存,这个对象是每次操作数据库都实例化的,这个对象不是线程安全的

Transaction

          l  Transaction对象是对事务进行操作的,例如:事务的提交、事务的回滚、事务的开始。

 

Hibernate中的Session有两种获取方式:

         1. 通过sessionFactory.openSession(); 每次调用此方法都创建一个新的session, 用此方法获取的session用完之后要调用session.close()方法释放资源。

         2. 通过sessionFactory.getCurrentSession(); 为每个线程创建一个新的session,用此方法获取的session用完之后,只要事务提交后那个session会自释放,不用我们显示调session.close();

         注意:要使用getCurrentSession()必须在配置文件中加以下配置:

               <propertyname="current_session_context_class">thread</property>


Hibernate的数据懒加载

org.hibernate.LazyInitializationException

如何解决这个问题呢?(三种解决方案)

            1. 在session没关闭之前,用Hibernate.initialize(懒加载的对象)进行显示初始化。(只能处理单个对象懒加载,不能处理集合)

            2. 改变查询策略,也就是说用立即加载load --> get  iterator -- list

               *设置为立即加载lazy=false (缺点,每次查询客户,都要查询订单,性能问题)

            3. OpenSessionInView (session不随事务关闭而关闭,将session生命周期延长到表现层View层)

                将Session开到表现层 --------- 存在性能问题,通过配置Filter,无需编程

                做法: 在struts2过滤器前,配置OpenSessionInViewFilter

HQL与SQL区别

    SQL是数据库查询语言,它面向是数据库中的表与表中的列。

    HQL是对象查询言,它面向的是类与类中的属性。

 

持久化对象的状态

  * transient瞬时态(临时态、自由态) : 不存在持久化标识OID,尚未与Hibernate Session关联对象,被认为处于瞬时态,失去引用将被JVM回收

    OID就是 对象中 与数据库主键 映射 属性 ,例如 Customer类 id 属性

  * persistent持久态 : 存在持久化标识OID,与当前session有关联,并且相关联的session没有关闭 ,并且事务未提交 

  * detached脱管态(离线态、游离态) : 存在持久化标识OID,但没有与当前session关联,脱管状态改变hibernate不能检测到

Session中一级缓存

案例一: 证明一级缓存是存在的

生成一条SQL语句,返回同一个对象 ,第一次查询生成SQL,查询对象,将对象放入一级缓存,第二次查询,直接从一级缓存获得

       

        Session session = HibernateUtils.getSession();        Transaction t =session.beginTransaction();           Student s1 =(Student)session.get(Student.class, 4);        Students2 =(Student)session.get(Student.class, 4);        Students3 =(Student)session.get(Student.class, 5);       //统计一级缓存中持久化对象的数量        System.out.println(session.getStatistics().getEntityCount());        session.evict(s1);        System.out.println(session.getStatistics().getEntityCount());        t.commit();


 

案例二 : 测试Hibernate快照 (深入理解一级缓存 内存结构原理 )

           hibernate向一级缓存放入数据时,同时保存快照数据(数据库备份),当修改一级缓存数据,在flush操作时,对比缓存和快照,

           如果不一致,自动更新 (将缓存的内容 同步到数据库, 更新快照)

           *  快照区使用,在Session保存一份 与数据库 相同的数据 ,在session的 flush时, 通过快照区 比较得知 一级缓存数据是否改变,如果改变执行对应操作 (update)

          * Hibernate中 持久态 对象具有自动更新数据库能力 (持久态对象 才保存在 Session中,才有快照 )

         

          Session session = HibernateUtils.getSession();          Transaction t =session.beginTransaction();          Student s1 =(Student)session.get(Student.class, 4);          s1.setAge(888);          t.commit(); 

           注意:使用Query查询它会只向一级缓存中放数据,不会在一级缓存中取数据。

Hibernate查询方式

         1. session.get  session.load 这种查询方式只能通过主键查询一条记录。

         2. HQL    通过HQL查询要用到Query这个类

         1. 单表查询            

             session.createQuery(“from Student”).list();

         2. 按条件查询         

             Query query = session.createQuery(“from Student where name = ?”);             query.setParameter(0, “zx”);             Query query1 = session.createQuery("from Student where name =:name");             query1.setParameter("name", "zx");

         3. 投影查询           

             Query query = session.createQuery("select name, age fromStudent");             List<Object[]>list = (List<Object[]>)query.list();                                for(Object[] item : list){                          System.out.println("姓名:" + item[0] + " 年龄" + item[1]);                  }             Query query =session.createQuery("select new Student(name, age) from Student");                  List<Student>list = (List<Student>)query.list();                               for(Student item : list){                           System.out.println("姓名:" + item.getName() + " 年龄" + item.getAge());                  }

             注意:在提供有参构造方法的同时别忘了再提供一个无参的构造方法

        4.  分组查询         

                  Query query = session.createQuery("select count(*) from Studentgroup by clazz");                         List<Long> list = query.list();                                 for (Long item : list){                           System.out.println("学生人数:" + item);                  }

        5. 连接查询(left join, right join, inner join)

                1. 迫切: 在连接关键字后面加fetch (推荐)

                2. 非迫切: 不加

                区别: 迫切的把查询结果封装成了主表对应的对象,通过主表的对象可以通过关联方法导航到子表的对象的数据。

                非迫切的是以object数组的形式返回的,数组中的第一元素是主表中的对象,第二元素是子表中的对象。

用本地SQL查询 

        Session session = HibernateUtils.getSession();        Transactiont = session.beginTransaction();        List<Object[]>list =session.createSQLQuery("select *from t_student").list();        t.commit();

Hibernate的二级缓存

Hibernate的二级缓存的生命周期是和SessionFactory一样的。二缓存默认是关闭,而且二级缓存不是Hibernate实现,需要第三方的缓存插件来支持

二级缓存的开启步骤:

在hibernate.cfg.xml文件中

         <!--             开启二级缓存          -->         <property name="cache.use_second_level_cache">true</property>               <propertyname="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>               <!--             开启hibernate的统计机制          -->          <propertyname="hibernate.generate_statistics">true</property> 

加入相关缓存的jar包