hibernate总结

Hibernate总结

1.什么是Hibernate?

          首先,hibernate是数据持久层的一个轻量级框架。数据持久层的框架有很多比如:iBATIS,myBatis,Nhibernate,Siena等等。

          并且Hibernate是一个开源的orm(object relations mapping)框架,提供了查询获取数据的方法,用面向对象的思想来操作数据库,节省了我们开发处理数据的时间。

2.那使用Hibernate的优点呢?

          1.使用简介的hql语句(Hibernate query language)。可以不使用传统的insert,update等sql语句。比如insert一个对象,原来的做法是:insert into 表名称 alue(值1,值2,值3,……),而现在的做法是:save(对象)。

          2.使用or映射。对象到关系数据库之间的映射。是从对象的角度操作数据库,再次体现了面向对象思想。原来的实体抽取方法:首先有了表,然后表映射实体对象。而现在Hibernate做法是:直接由对象映射到表。

          3.没有侵入性,移植性比较好。什么是没有侵入性?就是Hibernate采用了pojo对象。所谓的pojo对象就是没有继承Hibernate类或实现Hibernate接口。这样的话,此类就是一个普通的Java类,所以移植性比较好。  

         4.支持透明持久化。透明是针对上层而言的。三层架构的理念是上层对下层的依赖,只是依赖接口不依赖具体实现。而Hibernate中的透明是指对业务逻辑层提供了一个接口session,而其他的都封装隐藏。持久化是指把内存中的数据存放到磁盘上的文件中。

3.当然一个事物,不可能十全十美,即使如此优秀的Hibernate也有自己的弱点。比如:若是大量数据批量操作。则不适合使用Hibernate。并且一个持久化对象不能映射到多张表中。

4.Hibernate中核心5个接口

       1.Configuration接口:负责配置及启动Hibernate,用来创建sessionFactory

       2.SessionFactory接口:一个SessionFactory对应一个数据源存储,也就是一个数据库对应一个SessionFactory。SessionFactory用来创建Session对象。并且SessionFactory是线程安全的,可以由多个线程访问SessionFactory共享。

       3.Session接口:这个接口是Hibernate中常用的接口,主要用于对数据的操作(增删改查)。而这个Session对象不是线程安全的。不能共享。

       4.Query接口:用于数据库的查询对象。

       5.Transaction接口:Hibernate事务接口。它封装了底层的事务操作,比如JTA(;Java transcation architecture)所有的数据操作,比如增删改查都写在事务中。

1.创建普通的Java项目。

               因为hibernate是一个轻量级的框架,不像servlet,还必须需要tomcat的支持,Hibernate只要jdk支持即可。

       2.引入jar包。

               可以在项目中直接引入jar包,在:项目--->属性--->然后如下图:

              另一种办法就是引入库,相当于一个文件夹,把所有的jar包放到自己新建的文件夹中。在:窗体-->选项-->然后如下图:

 

      3.提供Hibernate的配置文件。hibernate.cfg.xml文件。完成相应的配置

  1. <hibernate-configuration>  
  2.   
  3. <session-factory>  
  4.   
  5. <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>  
  6.   
  7. <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate_first</property>  
  8.   
  9. <property name="hibernate.connection.username">root</property>  
  10. 10.   

11. <property name="hibernate.connection.password">bjpowernode</property>  

  1. 12.   

13. <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>  

  1. 14.   

15. </session-factory>  

  1. 16.   

17. </hibernate-configuration>  


 

      在这里连接MySQL数据库,解释一下上面的标签。按照顺序来依次解释:第一个是连接mySql的驱动;第二个是连接的url;url后面的hibernate_first是数据库名字;第三个是和第四个分别是用户名和密码。第五个是方言。因为 hibernate对数据库封装,对不同的数据库翻译成不同的形式,比如drp中的分页,若是使用Oracle数据库,则翻译成sql语句三层嵌套。若是使用mySql数据库,则翻译成limit语句。

    4.建立实体User类:

  1. package com.bjpowernode.hibernate;  
  2.   
  3. import java.util.Date;  
  4.   
  5. public class User {  
  6.   
  7.     private String id;  
  8.       
  9.     private String name;  
  10. 10.       
  11. 11.     private String password;  
  12. 12.       
  13. 13.     private Date createTime;  
  14. 14.       
  15. 15.     private Date expireTime;  
  16. 16.   
  17. 17.     public String getId() {  
  18. 18.         return id;  
  19. 19.     }  
  20. 20.   
  21. 21.     public void setId(String id) {  
  22. 22.         this.id = id;  
  23. 23.     }  
  24. 24.   
  25. 25.     public String getName() {  
  26. 26.         return name;  
  27. 27.     }  
  28. 28.   
  29. 29.     public void setName(String name) {  
  30. 30.         this.name = name;  
  31. 31.     }  
  32. 32.   
  33. 33.     public String getPassword() {  
  34. 34.         return password;  
  35. 35.     }  
  36. 36.   
  37. 37.     public void setPassword(String password) {  
  38. 38.         this.password = password;  
  39. 39.     }  
  40. 40.   
  41. 41.     public Date getCreateTime() {  
  42. 42.         return createTime;  
  43. 43.     }  
  44. 44.   
  45. 45.     public void setCreateTime(Date createTime) {  
  46. 46.         this.createTime = createTime;  
  47. 47.     }  
  48. 48.   
  49. 49.     public Date getExpireTime() {  
  50. 50.         return expireTime;  
  51. 51.     }  
  52. 52.   
  53. 53.     public void setExpireTime(Date expireTime) {  
  54. 54.         this.expireTime = expireTime;  
  55. 55.     }  

56. }  


 

    5.建立User.hbm.xml,此文件用来完成对象与数据库表的字段的映射。也就是实体类的那些字段需要映射到数据库表中呢。 

  1. <?xml version="1.0"?>  
  2. <!DOCTYPE hibernate-mapping PUBLIC   
  3.     "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
  4.     "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
  5. <hibernate-mapping>  
  6.     <class name="com.bjpowernode.hibernate.User">  
  7.         <id name="id">  
  8.             <generator class="uuid"/>  
  9.         </id>  
  10. 10.         <property name="name"/>  
  11. 11.         <property name="password"/>  
  12. 12.         <property name="createTime"/>  
  13. 13.         <property name="expireTime"/>  
  14. 14.     </class>  

15. </hibernate-mapping>  


 

6.我们也映射完毕了,但是hibernate怎么知道我们映射完了呢,以及如何映射的呢?这就需要我们把我们自己的映射文件告诉hibernate,即:在hibernate.cfg.xml配置我们的映射文件。

  1. <mapping resource="com/bjpowernode/hibernate/User.hbm.xml"/>  

 

    7.生成数据库表。大家也看到了我们上述还没有新建数据表呢,在第三步我们只是新建了数据库而已。按照我们普通的做法,我们应该新建数据表啊,否则实体存放何处啊。这个别急,数据库表这个肯定是需要有的,这个毋庸置疑,但是这个可不像我们原来需要自己亲自动手建立哦,现在hibernate需要帮我们实现哦,如何实现嗯,hibernate会根据配置文件hibernate.cfg.xml和我们的映射文件User.hbm.xml会自动给我们生成相应的表,并且这个表的名字也给我们取好:默认是User。那如何生成表呢?

  1. //默认读取hibernate.cfg.xml文件  
  2.     Configuration cfg = new Configuration().configure();  
  3.       
  4.     SchemaExport export = new SchemaExport(cfg);  
  5.     export.create(truetrue);  


 

 8.那我们就开始进行操作啦,我们添加一个用户对象,看看hibernate是如何添加的呢?跟我们以前的做法有什么不同呢?

  1. public class Client {  
  2.   
  3.     public static void main(String[] args) {  
  4.           
  5.         //读取hibernate.cfg.xml文件  
  6.         Configuration cfg = new Configuration().configure();  
  7.           
  8.         //建立SessionFactory  
  9.         SessionFactory factory = cfg.buildSessionFactory();  
  10. 10.           
  11. 11.         //取得session  
  12. 12.         Session session = null;  
  13. 13.         try {  
  14. 14.             session = factory.openSession();  
  15. 15.             //开启事务  
  16. 16.             session.beginTransaction();  
  17. 17.             User user = new User();  
  18. 18.             user.setName("张三");  
  19. 19.             user.setPassword("123");  
  20. 20.             user.setCreateTime(new Date());  
  21. 21.             user.setExpireTime(new Date());  
  22. 22.               
  23. 23.             //保存User对象  
  24. 24.             session.save(user);  
  25. 25.               
  26. 26.             //提交事务  
  27. 27.             session.getTransaction().commit();  
  28. 28.         }catch(Exception e) {  
  29. 29.             e.printStackTrace();  
  30. 30.             //回滚事务  
  31. 31.             session.getTransaction().rollback();  
  32. 32.         }finally {  
  33. 33.             if (session != null) {  
  34. 34.                 if (session.isOpen()) {  
  35. 35.                     //关闭session  
  36. 36.                     session.close();  
  37. 37.                 }  
  38. 38.             }  
  39. 39.         }  
  40. 40.     }  

41. }  


 

 第八步,我们可以看到,没有我们熟悉的insert into表的sql语句了,那怎么添加进去的呢,到底添加了没?让我真实滴告诉你,确实添加进去了,不信的,可以自己尝试哦,这也是hibernate的优点,对jdbc封装的彻底,减少了我们对数据的操作时间哈。

 那我们看一下hibernate中整体的内容:

 

 hibernate框架中主键的生成策略有三种方式:

 

               1,数据库负责生成主键(代理主键)

 

                     a,native:表示由设置的方言决定采用什么数据库生成主键方式,例如:在MySQL中会采用自增长的方式,主键字段必须都是整形类型;在Oracle数据库中,会采用序列的增长方式。

 

                    b,sequence:表示采用数据库的序列生成主键,适用于Oracle,DB2数据库中。

 

                    c,identity:表示采用自增长的主键生成方式,适用于MySQL,SQL Server中。

 

                2,Hibernate框架负责生成主键值(代理主键):

 

                    a,increment:表示由框架本身提供计数器,累加数据,获取主键。

 

                     b,uuid:由框架根据参数(IP地址,JVM虚拟机启动时间,系统时间,计数器等)生成32位16进制的数字字符串。

 

               3,用户提供主键值(自然主键):

 

                     Assigned:业务(自己)提供主键。

           

          当然这里常用的是native,uuid和Assigned三值。在设置POJO类与表映射时,进行主键设置,标签为<id>,在其中的<generator>标签中进行设置,例如:

[html] view plain copy

 print?

  1. <!--   
  2.     id标签用来映射主键字段  
  3.     name属性表示类的属性  
  4.     column属性表示表的字段  
  5.  -->  
  6. <id name="usercode" column="usercode" length="32" type="java.lang.String">  
  7.     <!-- 主键生成策略 -->  
  8.     <generator class="assigned"/>  
  9. </id>  

Hibernate,对三种状态下的POJO的增删改查操作:

 

瞬时状态

持久化状态

游离状态

增(Save)

可以

不可以(没必要)

不可以(没必要)

改(Update)

不可以

修改后会自动更新,不需手动

可以

删(delete)

不可以

可以

可以

不可以

可以

可以

 

            1,保存(Save):            

            通过session.save(user);就可以保存数据了,但是这里想提一下,主键的生成策略不同,框架发送sql语句的时间是不同的:

                  a,native:在调用save方法时发送insert语句。

                  b,uuid主键生成策略和assigned主键生成策略:在提交事务时发送insert语句。        

            这是因为主键的生成时机不同,由于native是在数据库中生成的,所以发送的比较早。

     

            2,更新(Update):

             这里想说一下对游离对象的更新:

               

 

uuid

assigned

native

存在记录

发送upate语句

会查询判断,再更新

发送update语句

记录不存在

发送语句,剖异常

会查询判断,进行插入操作

发送语句,剖异常

 

          3,删除(delete):

           这里只要提供主键,可以根据主键id删除,只要id存在即可。

 

           4,查询:

             a,主键查询:

                     get:返回结果可能是:持久化对象或null,所以需要对结果进行非空判断。它利用了缓存,是立即查询。

                //get方法查询如果成功,那么返回的对象状态是持久化状态

Objectobj = session.get(User.class, "admin");

 

                  load:返回结果可能是:持久化对象或cglib代理对象或异常,利用缓存,默认为延迟加载。

              

                //load方法第一查询结果存放到缓存中,支持延迟加载,效率更高,但是主要

Objectobj = session.load(User.class, "admin");

 

             b,普通查询(面向对象查询):后边会介绍

                  

                1,Query:HQL (HibernateQuery Language),HQL语言是对SQL语言的封装,是面向对象的查询语言。例如:SQL : select * from t_user(表名) whereusername(字段)="tom"

                              HQL : from User(类名) where username(属性)="tom"

 

[java] view plain copy

 print?

  1. //分页查询  
  2. String hql = "from User u "; //面向对象查询   HQL!!!!!  
  3. Query query = session.createQuery(hql);  
  4. int pageno = 3 ;  
  5. int pagesize = 2 ;  
  6. int index = (pageno - 1) * pagesize ;  
  7.   
  8. query.setFirstResult(index);//某页的第一个下标  
  9. query.setMaxResults(pagesize);  //页数的大小   
  10. 10.   

11. //条件查询  

12. String hql = "from User u where u.usercode=? and u.userpswd=?"; //面向对象查询   HQL!!!!!  

13. Query query = session.createQuery(hql);  

14. query.setString(0, "admin"); //索引从0开始  

15. query.setString(1, "admin");  

 


           2,   Criteria:将所有的操作都以面向对象的方式进行完成。

 

[java] view plain copy

 print?

  1. //分页查询            

[java] view plain copy

 print?

  1.                      Criteria cra = session.createCriteria(User.class);       
  2. cra.setFirstResult(0); //开始索引  
  3. cra.setMaxResults(2); //每页数量  
  4.   
  5. //排序  
  6. Criteria cra = session.createCriteria(User.class);        
  7. cra.addOrder(Order.desc("username"));  
  8.   
  9. //条件查询  

10. Criteria cra = session.createCriteria(User.class);        

11. cra.add(Restrictions.eq("username", "aaa"));  

12. cra.add(Restrictions.eq("usercode", "aaa"));  

一对多,多对一关系映射,这里拿学生和班级进行简单演示:

 

             1,学生的类和对应的映射文件的编写:

 

[java] view plain copy

 print?

  1. private int sid ;  
  2. private String sname ;  
  3.   
  4. private Classes classes ; //,引入班级对象,多对一  

 

[html] view plain copy

 print?

  1. <class name="com.ljh.hibernate.pojo.Student" table="t_student" lazy="false">  
  2.     <id name="sid" column="sid">  
  3.         <generator class="native"/>  
  4.     </id>  
  5.     <property name="sname" column="sname" type="java.lang.String" length="20" not-null="true"/>         
  6.       
  7.     <!--   
  8.         表示对象的关系:多对一  
  9.         name 表示当前类的关系对象  
  10. 10.         column 表示数据库中外键字段(也是描述数据关系)  
  11. 11.         class 表示name属性值的类型  
  12. 12.         cascade 级联  
  13. 13.             主动方所做的操作(insert,update,delete),被动方也跟着做相同的操作。  
  14. 14.             取值:save-update、delete 、all  
  15. 15.             save-update : 保存或更新当前对象时,级联保存或更新关联对象  
  16. 16.             delete : 删除当前对象时,级联删除关联对象  
  17. 17.             all : 包含save,update,delete三种操作。  
  18. 18.             对于多对一的场合,级联不能使用delete和all,否则会违背数据关系完整性。  
  19. 19.         lazy : 延迟加载   
  20. 20.             延迟初始化对象信息,等使用对象时再查询数据库。   
  21. 21.             false : 禁用延迟加载  
  22. 22.             proxy : 使用延迟加载(默认值),采用cglib代理完成延迟加载的扩展功能。  
  23. 23.             no-proxy :  不使用代理,完成延迟加载 。可以使用第三方字节码增强工具。  
  24. 24.         fetch : 数据抓取策略  :根据主动方,查询被动方时所采用的查询方式。    
  25. 25.             fetch="select" 默认值,会采用多条语句的方式查找,往往会延迟加载数据  
  26. 26.             fetch="join"  默认会采用左连接查询数据,不会延迟加载数据。  
  27. 27.                 not-null 如果取值为true,那么框架采用内连接查询数据。  
  28. 28.      -->  
  29. 29.     <many-to-one name="classes" column="cid" cascade="save-update" lazy="no-proxy" fetch="join" not-null="true" class="com.ljh.hibernate.pojo.Classes"></many-to-one>  

30. </class>  




             2,班级的实体类和对应的映射文件:

 

[java] view plain copy

 print?

  1. private int cid ;  
  2. private String cname ;  
  3.   
  4. private Set<Student> studentSet = new HashSet<Student>(); //引入学生类的集合,一对多  

 

[html] view plain copy

 print?

  1. <class name="com.ljh.hibernate.pojo.Classes" table="t_classes" >  
  2.     <id name="cid" column="cid">  
  3.         <generator class="native"/>  
  4.     </id>  
  5.     <property name="cname" column="cname" type="java.lang.String" length="20" not-null="true"/>     
  6.       
  7.     <!-- 声明   一对多映射  
  8.         lazy : 延迟加载  
  9.             false : 禁用延迟加载  
  10. 10.             true : 延迟加载(默认值)  
  11. 11.             extra : 支持延迟加载的。(推荐)  
  12. 12.                 当获取集合自身信息时,可以发送高效的查询语句。  
  13. 13.                 例如:只希望获取集合的长度,而不需要获取集合中数据的信息,框架会通过函数执行查询进行计算集合长度。。  
  14. 14.       
  15. 15.         fetch : 数据抓取策略  
  16. 16.                 查询关联对象的数据时,所采用的查询方式。  
  17. 17.             join : 通过一条连接语句进行立即查询。(延迟加载不起作用)  
  18. 18.             select : 通过多条查询语句进行查询。  
  19. 19.             subselect : 通过子查询语句进行立即查询。  (不推荐使用)(默认情况下和select取值结果相同)  
  20. 20.           
  21. 21.         not-null="true" 对于一对多的查询,即使设置了not-null语句,依然采用左连接查询。  
  22. 22.           
  23. 23.         Inverse="true",表示控制反转,由对方也就是学生方来进行管理外键。因为外键在学生    
  24. 24.      -->  
  25. 25.     <set name="studentSet" cascade="all" inverse="true" fetch="subselect">  
  26. 26.         <key column="cid" not-null="true"></key>  
  27. 27.         <one-to-many class="com.bjpowernode.hibernate.pojo.Student"/>  
  28. 28.     </set>  

29. </class>  


    二,自关联:就是在自己的类进行关联自己,例如父菜单与子菜单的关系,对应的实体类,和映射文件

 

[java] view plain copy

 print?

  1. private int mid ;           private String name ;  
  2.           
  3.         private Set<Menu> menuSet = new HashSet<Menu>(); //父菜单与子菜单的关系为:一对多  
  4.           
  5.         private Menu pmenu ; //子菜单和父菜单的关系为:多对一  

 

[html] view plain copy

 print?

  1. <class name="com.ljh.hibernate.pojo.Menu" table="t_menu" >  
  2.     <id name="mid" column="mid">  
  3.         <generator class="native"/>  
  4.     </id>  
  5.     <property name="name" column="name" type="java.lang.String" length="20" not-null="true"/>   
  6.       
  7.     <many-to-one name="pmenu" column="m_id" cascade="save-update"></many-to-one>  
  8.       
  9.     <!--  
  10. 10.         自关联表的设计:外键字段不能为非空。 
  11. 11.      -->  
  12. 12.     <set name="menuSet" cascade="all" inverse="true">  
  13. 13.         <key column="m_id"></key>  
  14. 14.         <one-to-many class="com.ljh.hibernate.pojo.Menu"/>  
  15. 15.     </set>  

16. </class>  



      一对一关系映射:

                   1,假如是主键一对一用来映射:也就是说被动方的主键是来自于主动方的主键,也可以将之称之为外键:

                类之间相互添加彼此的应用。

                映射文件中主动方,增加一对一标签:

[html] view plain copy

 print?

  1. <one-to-one name="userinfo" cascade="all" class="com.ljh.hibernate.pojo.UserInfo"></one-to-one>  
  2.   
  3.               被动方,主键又是外键,也添加一对一的映射标签:  
  4.     <id name="uid" column="uid">  
  5.         <generator class="foreign">  
  6.             <param name="property">user</param>  
  7.         </generator>  
  8.     </id>  
  9.       
  10. 10.     <!--   
  11. 11.         描述一对一关系关系映射  
  12. 12.         constrained="true" : 表示强制要求一对一使用外键关联。增加外键约束。  
  13. 13.      -->  
  14. 14.     <one-to-one name="user" constrained="true"  class="com.ljh.hibernate.pojo.User"></one-to-one>  



                 2,使用外键约束,其实是多对一的特殊情况,例如学生对班级,

                     类添加彼此的应用。

                     映射文件中学生端主动端,添加外键进行约束,添加多对一标签,

[html] view plain copy

 print?

  1. <!--   
  2.     unique : 唯一约束  
  3.       
  4.         如果外键含有unique约束,那么表示主动方和被动方的关系为一对一。  
  5.           
  6.         对于一对一映射来讲,可以设置级联关系为delete 和 all  
  7.           
  8.         根据主动方查询被动方关联数据,是支持延迟加载的。  
  9.  -->  

10. <many-to-one name="classes" column="cid" unique="true" cascade="all" class="com.ljh.hibernate.pojo.Classes"></many-to-one>  

  1. 11.   
  2. 12.                班级端的映射文件:添加一对一的标签:  

13. <one-to-one name="student" cascade="all" property-ref="classes" class="com.ljh.hibernate.pojo.Student"></one-to-one>  



     多对多的关系

例如学生对课程的对应,在数据库中会生成第三张表进行维护:

                   在各自的类中引入对方的set集合,表示多对多。

                   

学生端:

[html] view plain copy

 print?

  1. <set name="courseSet" table="t_student_course" cascade="save-update">  
  2.     <key column="sid"></key>  
  3.     <many-to-many class="com.ljh.hibernate.pojo.Course" column="cid"></many-to-many>  
  4.   
  5. ;/set>  



课程端:

[html] view plain copy

 print?

  1. <!--   
  2.     对于多对多来讲,级联只能设置cascade="save-update"是合理的  
  3.     inverse="true" 让集合一端去维护中间表数据。任意一端都可以。  
  4.  -->  
  5.   
  6. <set name="studentSet" table="t_student_course"  cascade="save-update"  inverse="true">  
  7.     <key column="cid"></key>  
  8.     <many-to-many class="com.ljh.hibernate.pojo.Student" column="sid"></many-to-many>  
  9. </set>  

 



          联合主键的映射

               出现了这种联合键时,需要我们为其定义一个联合主键的类(实现Serializable接口),类中声明多个字段的属性,表示联合主键字段。

               在配置映射文件时,主键的配置利用联合主键的标签即可:

[html] view plain copy

 print?

  1. composite-id name="id">              <key-property name="title"></key-property>  
  2.                 <key-property name="author"></key-property>  
  3.             </composite-id>     

 

一级缓存(Session缓存):

 

             Session缓存表示将查询结果放置到Session的临时存储空间(一级缓存中)。Hibernate框架默认支持一级缓存的。一级缓存的范围较小,一旦Session关闭,那么缓存失效。我们使用框架的各种方法,例如:get,load,save,update,delete等都支持一级缓存的。

 

        二级缓存(SessionFactory缓存):

 

                1,概述:二级缓存其实就是将查询的数据放置在SessionFactory临时存储空间中,因为一个SessionFactory可以创建多个Session对象,所以范围比Session缓存的要大,多个Session可以共享二级缓存的数据。当然了二级缓存也不能存储大量的数据,这个要根据我们电脑配置进行设置。

 

                2,如何设置使用二级缓存呢?Hibernate默认是不支持二级缓存的。

 

                 a,首先在hibernate.cfg.xml配置文件中添加属性标签,启用二级缓存:

<!--启用二级缓存 -->

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

 

b,二级缓存需要使用额外的第三方组件:ehcache。需要我们拷入对应的jar包,以及将对应的ehcache.xml存放到src目录下。在这个配置文件中,我们可以设置二级缓存的大小等。

 

c,让框架识别添加入的ehcache缓存组件,在hibernate.cfg.xml配置文件中添加属性标签:

<!--让框架识别ehcache缓存组件 -->

<propertyname="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

 

d,设置需要缓存的映射类,这里只是将一些查询操作比较频繁的类指定即可,哪些不经常操作的数据,是没有必要利用缓存的。这里例如:

<!-- 将指定的类存放到二级缓存中,其中read-only是指放入缓存的数据是只读的-->

<class-cache usage="read-only" class="com.ljh.hibernate.pojo.Student"/>

 

              3,缓存的操作数据原理:

                   查询数据时,会首先从一级缓存中取数据,如果取上,则直接使用,否则到二级缓存中取,如果取到则直接使用,否则,就会发送语句查询数据库。这样利用一级和二级缓存会提高访问效率。

 

            当然使用二级缓存要慎重,要考虑数据的操作频繁性,服务器的硬件配置,数据的安全性等等方面,这样才能很好的利用二级缓存,提高我们操作数据的效率,否则使用不当就会带来不必要的麻烦。

 

 

     Lazy(懒加载)

因为感觉Lazy也用到了缓存,可能放到这里不太合理吧。在前边我们已经设计到了lazy了,这里简单总结一下。

 

           1,lazy(Hibernate的延迟加载功能):表示查询当前对象或关联对象数据时,不真正访问数据库,当使用对象非主键属性时,才真正发送查询语句,访问数据库。由于在某些情况下,查的数据在后续流程到可能用不上,如果做查询处理就多余了,所以延迟加载功能可以提高性能,合理使用即可。当然了Hibernate框架是通过Cglib代理来完成延迟加载功能的扩展的。

 

          2,用到lazy地方总结,这里只是对类和映射的地方简单总结了一下,在实际运用中我们还要学会恰当的选择:

标签

起作用到什么地方

取值解释

<class>

针对load方法

true  :  延迟加载  (默认)

false :         禁用延迟加载

<set>

针对于一对多,多对多

true  : 延迟加载 (默认)

false : 禁用延迟加载

extra :        支持延迟加载,在访问集合自身信息时可以发送高效的查询语句。(推荐)        

<many-to-one>

针对于多对一,一对一

false : 禁用延迟加载

proxy : 延迟加载  (默认)

no-proxy : 支持延迟加载,不使用cglbi代理的方式完成延迟加载。通过第三方的字节码增强工具。

<one-to-one>

针对一对一

和<many-to-one>标签一样,这里需要注意:        

一对一的主键关联映射,根据主动方(不含外键)查询被动方(既是主键,又是外键)时不支持延迟加载的。

 

          3,遇到的问题:lazy,延迟加载功能是运用的一级缓存,也就是利用的是session的内存,一般情况下,我们在用完session后会进行关闭,但是关闭后我们就无法进行延迟查询数据了,也就是说,延迟加载功能就将失效,剖出异常:No Sesssion,所以这是需要我们解决的。

  

          4.NoSession异常的解决:

 

              a,解决原理:我们可以在dao层不进行session的关闭,通过前边学习的AOP编程思想,添加过滤器,在视图层进行数据取完毕后进行关闭session,这样就可以解决了NoSession异常了。但是怎么获取呢?这里又想到了前边学习的ThreadLocal,我们可以将Session对象放到Threadlocal对象中,和线程进行绑定,到视图层再通过ThreadLocal进行获取关闭即可。

 

            b,解决方案,AOP编程思想,想必都想到了spring框架了吧,的确,Spring框架提供了OpenSessionInViewFilter过滤器解决NoSession异常,这里我们只需要配置一下即可。在web.xml中进行此过滤器的配置即可:

 

[html] view plain copy

 print?

  1. <!-- 过滤器需要配置在Struts2框架过滤器前面,否则不起作用。 -->  
  2. <filter>  
  3.     <filter-name>OpenSessionInViewFilter</filter-name>  
  4.     <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>  
  5. </filter>  
  6. <filter-mapping>  
  7.     <filter-name>OpenSessionInViewFilter</filter-name>  
  8.     <url-pattern>/*</url-pattern>  
  9. </filter-mapping>   


           c,配置的顺序问题,应该将openSessionInViewFilter配置到Struts2的流程前边,这是因为Filter执行顺序是从上向下的,而且Struts2框架的执行流程基本上包含了整个项目顺序的全部流程,MVC流程框架么,就体现在这里了。配置到Struts2框架的流程后边的过滤器是起不到作用的。

 

 

持久化对象的三种状态。

分别为:瞬时状态(Transient),持久化状态(Persistent),离线状态(Detached)。三种状态下的对象的生命周期如下:

 

三种状态的区别是:瞬时状态的对象:没有被session管理,在数据库没有;持久化状态的对象:被session管理,在数据库存在,当属性发生改变,在清理缓存时,会自动和数据库同步;离线状态:没有被session管理,但是在数据库中存在。

5.测试工具Juit。

测试类需要继承TestCase,编写单元测试方法,方法名称必须为test开头,方法没有参数没有返回值,采用public修饰。其中在测试中,查询对象时,使用get或者load两种方法进行加载,这种方法的区别:get不支持延迟加载,而load默认情况下是支持延迟加载。并且get查询对象不存在时,返回null;而load查询对象不存在时,则抛出ObjectNotFoundException异常。

6.悲观锁和乐观锁解释。

悲观锁为了解决并发性,跟操作系统中的进程中添加锁的概念一样。就是在整个过程中在事务提交之前或回滚之前,其他的进程是无法访问这个资源的。悲观锁的实现方式有两种:一种使用数据库中的独占锁;另一种是在数据库添加一个锁的字段。hibernate中声明锁如下:

Account account = (Account)session.get(Account.class, 1, LockMode.UPGRADE);而net.sf.hibernate.LockMode类表示锁模式,当取值LockMode.UPGRADE时,则表示使用悲观锁for update;而乐观锁是为了解决版本冲突的问题。就是在数据库中添加version字段,每次更新时,则把自己的version与数据库中的version进行比较,若是版本相比较低,则不允许进行修改更新。

7.H ibernate中的缓存机制。

缓存是什么呢?缓存是应用程序和数据库之间的内存的一片区域。主要的目的是:为了减少对数据库读取的时间。当查询数据时,首先在缓存中查询,若存在,则直接取出,若不存在,然后再向数据库中查询。所以应该把经常访问数据库的数据放到缓存中,至于缓存中的数据如何不断的置换,这也需要涉及一种淘汰数据的算法

谈到这个hibernate中的缓存,你想到了什么呢?刚才叙述缓存时,是否感觉很熟悉,感觉从哪也听过似的。嗯呢,是呢,是很熟悉,写着写着就很熟悉,这个刚才的缓存以及缓存的置换算法就和计算机组成中的cache类似。

好吧,来回到我们hibernate中的缓存。

hibernate中的缓存可以分为两种:一级缓存,也称session缓存;二级缓存,是由sessionFactory管理。

那一级缓存和二级缓存有什么区别呢?区别的关键关于:缓存的生命周期,也就是缓存的范围不同。

那首先介绍一下缓存的生命周期,也就是缓存的范围。

1.事务缓存,每个事务都有自己的缓存,当事务结束,则缓存的生命周期同样结束,正如上篇博客中我们提到,对数据库的操作,增删改查都是放到事务中的,和事务保持同步,若是事务提交完毕,一般是不允许是再次对数据库进行操作。所以session是属于事务缓存的。

2.应用缓存,一个应用程序中的缓存,也就是应用程序中的所有事务的缓存。只有当应用程序结束时,此时的缓存的额生命周期结束。二级缓存就是应用缓存。

3.集群缓存,被一台机器或多台机器的进程共享。

这下明白了一级缓存和二级缓存的区别了吧。那一级缓存和二级缓存的共同点是:都是缓存实体属性,

二级缓存一般情况都是由第三方插件实现的。第三方插件如:

EHCache,JbossCache(是由Jboss开源组织提供的),osCache(open symphony),swarmCache。前三种对hibernate中的查询缓存是支持的,后一种是不支持hibernate查询缓存。

那什么是hibernate查询缓存呢?

查询缓存是用来缓存普通属性的,对于实体对象而言,是缓存实体对象的id。

8.hql查询。

  hibernate query language。hql查询中关键字不区分大小写,但是类和属性都是区分大小写的。

  1.简单属性查询。

  单一属性查询,返回属性结果集列表,元素类型和实体类的相应的类型一致。

[java] view plain copy

 print?

  1. List students = session.createQuery("select name from Student").list();  
  2.   
  3. for (Iterator iter=students.iterator(); iter.hasNext();) {  
  4.   
  5. String name = (String)iter.next();  
  6.   
  7. System.out.println(name);  
  8.   
  9. }  


 

 //返回结果集属性列表,元素类型和实体类中的属性类型一致

多个属性查询,多个属性查询返回数组对象,对象数组的长度取决于属性的个数,对象数组中的元素类型与实体类中属性一致。

[java] view plain copy

 print?

  1. List students = session.createQuery("select id, name from Student").list();  
  2.   
  3. for (Iterator iter=students.iterator(); iter.hasNext();) {  
  4.   
  5. Object[] obj = (Object[])iter.next();  
  6.   
  7. System.out.println(obj[0] + ", " + obj[1]);  
  8.   
  9. }  


 

2.实体对象查询

List students = session.createQuery("from Student").list();

当然这种hql语句,可以使用别名,as可以省去,如:from Student as s,若是使用select关键字,则必须使用别名。如:select s from Student as s.但是不支持select * from Student格式。

查询中使用list和Iterate区别:

list查询是直接运行查询的结果,所以只有一句sql语句。而iterate方法则有可能会产生N+1条sql语句。这是怎么回事呢?要理解N+1条语句,首先得弄明白iterate是如何执行查询的?

首先发出一条查询对象ID的语句,然后根据对象的ID到缓存(缓存的概念上篇博客已经提到)中查找,若是存在查询出此对象的其他的属性,否则会发出N条语句,此时的N语句,是刚才第一次查询的记录条数。这种现象就是N+1sql语句。

其中list是默认情况下都发出sql语句,查询出的结果会放到缓存中,但是它不会利用缓存,即使放进去,下次执行时,仍然继续发出sql语句。

而:iterate默认情况下会利用缓存,若是缓存中有则不会发出N+1条语句。

3.条件查询。

这种方式就是传入参数,使用参数占位符“?”。也可以使用“:参数名”

Java代码如下:

[java] view plain copy

 print?

  1. List students = session.createQuery("select s.id, s.name from Student s where s.name like ?")  
  2.   
  3. .setParameter(0, "%0%")  
  4.   
  5. .list();  
  6.   
  7. List students = session.createQuery("select s.id, s.name from Student s where s.name like :myname")  
  8.   
  9. .setParameter("myname",  "%0%")  
  10. 10.   

11. .list();  


 4.使用原生sql语句。

     和咱们原先写入的sql语句一样。在此不介绍了。

 5.外置命名查询。

      这个听起来有点晦涩,怎么理解呢?其实通俗的说就是把hql语句写在外面,写在映射文件中。使用标签:

   

[html] view plain copy

 print?

  1. <query name="queryStudent">  
  2.   
  3. <![CDATA[ 
  4.  
  5. select s from Student s where s.id <? 
  6.  
  7. ]]>  
  8.   
  9. </query>  


 

    那在程序中是如何使用此标签的呢?使用session.getNameQuery(),并进行赋值,代码如下:

[java] view plain copy

 print?

  1. List students = session.getNamedQuery("queryStudent")  
  2.   
  3. .setParameter(0, 10)  
  4.   
  5. .list();  

 

 6.查询过滤器。

    这个是什么意思呢?过滤器大家很熟悉吧,不熟悉的可以参考我的以前博客<>.原来我们接触过编码过滤器,编码过滤器就是为了避免当时每个页面需要设置编码格式而提出的。这个查询过滤器其实也是这个意思。若是代码都需要某一句sql语句的话,可以考虑使用它。这样可以避免每次都写查询语句。

    使用如下:首先在映射文件中配置标签:

[html] view plain copy

 print?

  1. <filter-def name="testFilter">  
  2.   
  3. <filter-param type="integer" name="myid"/>  
  4.   
  5. </filter-def>  

 

  然后程序中如下使用并进行赋值:

[java] view plain copy

 print?

  1. session.enableFilter("testFilter")  
  2.   
  3. .setParameter("myid", 10);  


7.分页查询。

    分页查询,这个肯定不陌生,因为在做drp项目时,做的最多的是分页,当时使用Oracle数据库,分页查询涉及到三层嵌套。直接传入的参数为:每页的大小(记录数),页号。

     Hibernate中给我们已经封装好了,只要设置开始的页号以及每页的大小即可,不用亲自动手写嵌套的sql语句。

     代码如下:

[java] view plain copy

 print?

  1. List students = session.createQuery("from Student")  
  2.   
  3. .setFirstResult(1)  
  4.   
  5. .setMaxResults(2)  
  6.   
  7. .list();  

 

8.对象导航查询。

这个什么意思呢?这个只要是用于一个类的属性是另一个类的引用。比如:student类中有一个classes属性。其中的classes也是一个类Class的引用。

当我们查询的时候可以这样使用:

[java] view plain copy

 print?

  1. List students = session.createQuery("from Student s where s.classes.name like '%t%'")  
  2.   
  3. .list();  


相当于:s.getClasses.getName(),直接使用get后面的属性,然后首字母小写。

 这种语法,是不是很熟悉?想想我们在哪是不是也用过?想起来了吗?估计你猜出来啦,呵呵,是JSTL(jsp standard tag library)中。若是想进一步了解,可以参考我的博客哈,当时是转载滴貌似。

9.连接查询。

    连接分为:内连接和外连接,其中外连接分为左连接,右连接,完全连接。这个跟数据库中的左右连接其实是一样的。我们通俗解释一下:

    左连接:以左边为准,右边即使没哟匹配的,也要把这条记录查询出来,此时没有匹配的右边以null填充。

    右连接:以右边为准,左边即使没有匹配的,也要把这条记录查询出来,此时没有匹配的左边以null填充。

   完全连接:只要一方存在即可。

   内连接:必须两方都存在才可以查询提取此记录。

10.统计查询。

     其实就是查询count的记录数。其中查询出来的额count是long类型。

11.DML风格的操作。

     DML?其实DML=Data Manipulate Language(数据操作语言),举个例子:

  

[java] view plain copy

 print?

  1. session.createQuery("update Student s set s.name=? where s.id<?")  
  2.   
  3. .setParameter(0, "张三")  
  4.   
  5. .setParameter(1, 2)  
  6.   
  7. .executeUpdate();   

           

假若原来的名字是:李四,更新完数据库后变成王斌,若是我们此时取出数据,其姓名是李四还是王斌?按照道理应该是王斌,但是结果确实李四,若不信,可以自己去实践一下。

这个原因,是因为更新了数据库,但是缓存中没有更新,才会造成这种数据库和缓存不同步的问题。

所以,我们应该尽量不使用这种形式。扬其长避其短嘛。

 

posted on 2017-03-28 13:12  寻觅未知  阅读(223)  评论(0编辑  收藏  举报

导航