人的缺点:宝宝 Hibernate学习总结

来源:百度文库 编辑:中财网 时间:2024/04/28 23:26:36
Hibernate是一个开放源代码的ORM(对象关系映射)框架,它对JDBC进行了轻量级的封装,Java程序员可以使用面向对象的编程思维来操纵数据库,它通过对象属性和数据库表字段之间的映射关系,将对象持久化到数据库中,可以说Hibernate就是将数据从对象形式转换成表字段后存入数据库的一种框架。hibernate移植非常好,因为它用到了方言配置,可以根据不同的数据库自动发出不同的sql。


下面是我感觉比较重要的方面就列出来了:
首先是搭建环境:
    1、我建的是个Java项目,第一步先引入HIbernate的jar包:hibernate3.jar这是核心包,还有lib下的所有包。
 
    2、我用oracle做测试,所以在引入oracle的驱动包,classes12.jar。
 
   3、在从源码包中拷贝一个hibernate.cfg.xml(放到src下,而且这个文件的配置可以参考etc/hibernate.properties      这个文件)这是Hibernate的默认配置文件名字,和User.hbm.xml(此文件跟你的pojo放在一块)映射配置文件。
这样Hibernate的环境基本上就搭建好了。

4、hibernate可以根据映射文件和pojo自动创建数据库表(用SchemaExport这个类)。

5、hibernate默认事务是手动的,所以我们必须手动获取事务,开启和关闭它。

6、在hibernate中持久化对象有三个状态,这个面试时可能会问到:
 (1)transient瞬时态:在数据库中没有与之匹配的数据,一般就是只new出了这个对象,并且在session缓存中也没有即此对象没                有纳入session的管理,此状态的对象不能直接存入数据库(会抛出TransientObjectException)。
 (2)persistent持久态:在数据库中有与之匹配的数据,并且纳入了session的管理(一般就是执行了save、update、load、get                  后的对象),在提交事务时(清理session缓存时)会和数据库同步更新,持久态对象才可以存入数据                 库。
 (3)detached游离态:在数据库中有与之匹配的数据,但没有纳入session缓存的管理。
 
7、(1)    讨论下hibernate的增(save)、删(delete)、改(update)、查(get,load)。
       增没什么说的,关于删除和修改时我们完全可以new出一个对象进行操作,只要给这个对象的标示赋值为库中存在的对象就行了,这个过程应该是这样的,当我们new出对象后并设置了相应的id(标示),然后执行session.delete()或者session.update()后,此对象在session缓存中就有了一份,再当我们commit提交
       事务时(清理缓存),数据库中有同样id标示的对象就会与session缓存中的持久态对象进行同步更新,所以这样更新和删除完全可以实现,但是我们一般不能这么做,   最好是先查出来在去更新或者删除,比如:当我们要更新一个User对象时,没有先查出来而是用new的方法并给这个新对象设置了一个与库中有对应ID的形式去更新,那么更新完后,如果没有赋值的属性都会变成null,这个可不是我们想要的结果哦。
   
    (2)在说说get和load这两个单一对象的查询方法:
        <1>get方法不支持lazy延迟加载,即一执行到get方法立刻发出sql语句,而load有延迟加载,即           load方法执行后,并不会执行查询sql只是返回了一个代理对象,只有等真正用到了这个对象才会发出查询       语句。
            <2>当get方法查找的对象不存在则返回null,而load会抛出异常。
              共同点:load和get只能通过主键标示加载实体对象。

8、 数据库的隔离级别决定了只发出插入sql而没有提交事务后是否能select看到数据。其实只要发出了插入的sql语句,即使你       没有提交事务,库里面就已经有了数据,只是数据库的默认隔离级别不会让你看到数据,这时你就可以回滚事务,取消        数据插入,而且这种隔离级别还可以设置的。

9、在hibernate中,持久态对象不能引用瞬时态对象。(插入有关联关系的对象时可能会遇到这种异常)。

10、hiberntate级联(cascade)只对增、删、改有作用,与查询没关系,默认不会级联即值为none。

11、在hibernate中对象之间的映射关系通常都配成双向的,比如双向一对一外键关联映射(这也是一对一中最常用的)。

12、一对一主键关联默认就有级联关系,其他关联关系都没有这种默认设置。

13、标签会在当前表中加字段即外键,而标签不会加字段,
    指示hibernate如何加载关联对象,它默认就去另一张表中找主键与当前表主键相等的数据。

14、在主键生成器中uuid和native比较常用,它们的区别是:
    (1)uuid在执行完save后只会把此对象放在session缓存中,并为其赋id值(是一个32位字符串),它只有事务提交时       也就是清理了缓存时才会发出sql。
    (2)native在执行完save后就立刻发出插入sql语句,即此时库中已经有了此对象数据,而且native是根据本地数据库    进行自增的,比如你用的是oracle,那它就会通过序列进行自增。

15、hibernate中的flush方法可以清理session缓存并执行相应的sql,我们可以手动执行,但提交事务前也会被自动执行的。

16、hibernate中的双向多对一和一对多是同一个概念,而且一对多和多对一都是在多的一端加个外键来指向一的一端,一对多单     向会发出多余的update语句(所以一般都配成双向 )。

17、hibernate一般在持久类中用到集合映射时都用Set(放入的对象不可重复)很少用List,而且还不能用Hashset具体类去声明     ,必须用接口Set去声明     ,因为hibernate对Set进行了扩展。

18、hibernate映射文件的单双向都是针对加载而言的。

19、hibernate支持lazy延迟加载(只有真正使用该对象时才会创建,即发出相应的sql语句),默认lazy都是true而且Session不    能关,否则lazy就失效了,如果你在Session关闭后在把延迟加载的对象输出给页面就会抛出异常即特别是用load(它支持lazy)   进行加载时Session关闭后在返回所查询的对象时就会抛此异常   org.hibernate.LazyInitializationException,但用get()加   载时就不存在这问题,因为lazy的生命周期和Session是同步的,这个面试可能会问到,解决方法就是不关Session,直到查询   的内容已经返回给页面在关闭,Spring框架就提供了一个Filter(过滤器)实现了此功能即从请求访问一直到响应返回后在关   闭Session。hibernate的延迟加载实现用的是第三方库cglib.jar,这是个动态代理类库,跟JDK的动态代理是不一样的,JDK的   动态代理只能代理实现了一定的接口的类。hibernate的lazy可以应用的标签类型有、    、集合。

20、get和load两个方法在同一个session中都会用到session缓存(也就是hibernate一级缓存)在执行这两个方法后都会给        session缓存中放一份,即第二次查询同一对象时不会在发出sql,只会从一级缓存中直接读取数据,一级缓存只缓存实体对象    ,不缓存普通属性,而且一级缓存的生命周期和Session的生命周期是一致的,不同的Session之间不能共享Session缓存中的数       据。

21、session.save()时也用到了一级缓存,即添加完后会给session缓存中放一份对象。

22、一级缓存不能控制,包括设置容量、或者取消,只能通过session.clear()等方法清空它。一级缓存是session缓存即同一个     session共享,二级缓存是进程级的缓存即SessionFactory级缓存(它的生命周期和SessionFactory一致),不同session之间    是可以共享的,它们的共同点是都缓存对象,不缓存普通属性。Hibernate的二级缓存集成了第三方库,比如:ehcache,而且    使用时必须配置(如:配置用哪种二级缓存,哪些实体类使用二级缓存)。
    二级缓存的简单示例:
    比如当二级缓存没配置时(默认是开启的)——>我们用load或者get查询时先会去一级缓存中查,没有找到再发出SQL。
    当二级缓存开启并配置后——>查询时先去一级缓存,如果没有在去二级缓存找,再没有就发出相应的SQL.。
    当二级缓存开启并配置后,load和get查询默认后会给一级缓存和二级缓存中都放一份(可以配置不往二级缓存中放),二级缓存可以被SessionFactory去直接管理,比如:清空。
  Hibernate还有一种缓存叫查询缓存(Hibernate默认是关闭的)此缓存对Query.iterate()不起作用,它是缓存普通属性和对象id标示的,它的生命周期不可控制(与session的生命周期没有关系的),利用率不高,此缓存使用时必须配置。
   忠告:Hibernate用不好会发出N多条sql语句,效率会大大降低。

23、用Query或者save操作大量实体对象时给Session缓存中都会放一份,这样会影响效率,很可能内存溢出,所以大量数据操作不     适合用hibernate。

24、每次执行list()默认都会像数据库发出sql语句,但它会一次性把集合中的所有元素都查出来,它只会为一级缓存中放数据,     但不会从缓存中取,而iterate()方法会先从缓存中取,如果缓存中没有就会很可能发生N+1问题。
    N+1问题:1—>首先发出一条查询对象id列表的sql。N—>根据id的列表一个一个去一级缓存中查找,如果没有就每个id发出一条sql,即N个id就发出N条查询语句。

25、hibernate 还支持HQL语句外置,即把HQL语句放到任何一个的hbm.xml映射文件中。

26、hibernate 还支持查询过滤器,即在相应的hbm.xml映射配置文件中配置条件后,在执行action中的HQL查询时就会自动加上     映射文件中配置的条件。

27、hibernate 也带left join和right join,左连接就是左边表全部显示,右边为空的就显示null。右连接正好相反。

28、HQL也支持DML风格语句,即用HQL的update、delete等,这个不建议使用,关键是我也没用过,嘿嘿。

29、hibernate的抓取策略默认就是fetch="select",意思是用到关联对象时,会再发一条select查询语句。如果手动设置        fetch="join"抓取策略,Hibernate会通过一条select语句使用外连接直接查出实体对象和与其关联的对象或者集合(即使你    不使用那些相关联的对象,照样会一次性查出来,影响效率)。
    Hibernate还有其他抓取策略,等用到了再去看吧!
   
30、hibernate 还可以配置批量查询、更新,即在配置文件中可以配置一个最大返回记录数,一般可以设为50,意思就是当你一次     性查询10000条数据时,底层数据库都是50条50条的给你返回,这个功能需要特定的数据库支持才行。

31、Hibernate的映射配置文件还是比较复杂,但它有注解配置,这个现在用的也非常多,个人也比较喜欢这种配置方式。


32、最后再说下Hibernate还集成了一个测试工具类,junit这是个第三方库,只要我们写的类继承了TestCase,然后
    方法必须是公共的返回值为空,且方法名以小写test开头,不能带参数。(类名无限制,但推荐最好以大写的Test结尾),这样就可以很方便的进行测试了。


下面贴出一些测试用例代码:
第一、hibernate.cfg.cml是Hibernate的主配置文件(这个名字可以随便起):

    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">



   
        oracle.jdbc.driver.OracleDriver
        jdbc:oracle:thin:@localhost:1521:ORCL9I
        test
        test
        org.hibernate.dialect.Oracle9Dialect
       
        update
        true
       
       
       
   




第二、列出Hibernate中比较复杂的多对多双向关联映射配置(如果我是项目经理,这些对象的所有关心都不会配置,呵呵)

      就以用户和角色为例:
      1、用户的映射文件如下:

    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
   

   
       
           
       

       
       
       
       
                       
           
           
       

   



       2、角色的映射文件如下:
      
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
   

   
       
           
       

       
       
           
           
       

   





第三、Hibernate根据持久化类(pojo+映射文件)去创建对应的数据库表:

public static void main(String[] args) {
        //加载hibernate的配置文件
        //Configuration cfg = new Configuration();这样会默认读取的是properties文件;
        Configuration cfg = new Configuration().configure();//调用了这个方法后才会默认去读取hibernate.cfg.xml配置文件。
        SchemaExport export = new SchemaExport(cfg);//用这个类就可以生成数据库表。
        export.create(true,true);//此方法生成表。
    }


第四、SessionFactory是个重量级对象,所以最好在整个系统就只创建一次,再给Session设计个单态模式:
public class SessionUtil {
    private static SessionFactory factory;
    private static Session session;
    static{
        Configuration cfg = new Configuration().configure();
        factory = cfg.buildSessionFactory();
        session = factory.openSession();
    }   
    public static Session getSession(){
        return session;
    }   
    public static void closeSession(Session session){
        if(session != null) session.close();
    }
}


第五、Hibernate 的增、删、改、查:

增——>session.save(user); 
删——>session.delete(user),这个通常是先查出来
改——>session.update(user),同样是先查出来,此时可以不调用update(),set()修改完属性后直接提交事务就行了。
查——>session.load(user.class,id) , session.get(user.class,id)个人比较喜欢用这种方式。;



第六、列出常用的HQL查询语句:

1、简单实体列表查询:
//里面传的是HQL查询语言,这里的User类跟此类没在同一个包下,hibernate会默认找到User所在的包。
Query query =  session.createQuery("from User");
List list = query.list();

2、Hibernate分页查询:
//里面传的是HQL查询语言,查询的直接是对象集合。
Query query =  session.createQuery("from User u order by u.userName");           
query.setFirstResult(0);//设置从库中第一条数据开始查询,即传0,在数据库中是从0开始的。           
query.setMaxResults(2);    //设置每次查询的记录数为2,即查询的对象个数。
List list = query.list();


3、单个实体查询:
User user = (User)session.createQuery("from User as u where u.userId='uuid1'").uniqueResult();

4、对象属性查询(直接用iterate()方法):
Iterator iterator = session.createQuery("select u.userName from User u where u.userId=? and u.userName=?")
                        .setString(0,"8a88831127f5fd8d0127f5fd90b20003")
                        .setString(1,"马文涛")//这两个参数就可以从页面传过来
                        .iterate();
            while(iterator.hasNext()){
                String userName = (String)iterator.next();
                System.out.println(userName);
            }

5、使用where、like、分组、过滤、排序、内置函数进行条件查询
List list = session.createQuery("select count(u),u.userName from User u where u.userName " +
                    "like '%文%' group by u.userName having count(u)>2 order by u.userName").list();
            for(Iterator i = list.iterator();i.hasNext();){
                Object[] o = (Object[])i.next();
                long count = (Long)o[0];//这个count()返回的类型是long,不能用int接受。
                String name = (String)o[1];
                System.out.println(name+"它的个数为:"+count);
            }:
/*这里的List返回的是一个对象数组(Object[]),数组长度就是select返回的属性类型个数,
数组元素中的类型就跟对应属性的类型一致。*/


6、用hibernate提供的内部函数包含(in)查询:
List list = session.createQuery("select userName from User where userName in(:name)")
            .setParameterList("name",new Object[]{"宝宝","涛哥"})
            .list();

7、用hibernate对象导航查询,用点进行导航(u.userName)如果有其他属性都可以点。
List list = session.createQuery("select u.userName from User u where u.userName=:name")
                        .setParameter("name","宝宝")
                        .list();
            for(Iterator i = list.iterator();i.hasNext();){
                String userName = (String)i.next();
                System.out.println(userName);
            }



好啦,不能在写了,刚搬了房子公交车很不方便,晚上209路居然7点半就是最后一趟了,昨天都意外打出租了,嘿嘿!