宝宝Hibernate学习总结

宝宝Hibernate学习总结

2020-08-08 10:37:47 浏览次数:

 宝宝 Hibernate 学习总结

 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、many-to-one 标签会在当前表中加字段即外键,而 one-to-one name="user"标签不会加字段,指示 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 可以应用的标签类型有 class、property、one-to-one、many-to-one、集合。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 的主配置文件(这个名字可以随便起):!DOCTYPE hibernate-configuration PUBLIC"-//Hibernate/Hibernate Configuration DTD 3.0//EN""!--配置与数据库连接

 有关的信息,并引入映射文件,此配置文件的属性都比较简单就不用再 38 了--hibernate-configuration session-factory property name="connection.driver_class"oracle.jdbc.driver.OracleDriver/property property name="connection.url"jdbc:oracle:thin:@localhost:1521:ORCL9I/property property name="connection.username"test/property property name="connection.password"test/property property name="dialect"org.hibernate.dialect.Oracle9Dialect/property!--下面这个属性配置成 update 后,则我们从新生成表时,没有改变的表将不会被drop(删了从建),只会新建有变动的表,而且这个属性设置后我们也不用hibernate 的生成表工具(SchemaExport)去执行创建表,而是当我们插入数据时,hibernate 会自动帮我们先创建表,再去插入数据。--property name="hbm2ddl.auto"update/property property name="show_sql"true/property mapping resource="many_to_many_shuangxiang/User.hbm.xml"/mapping resource="many_to_many_shuangxiang/Role.hbm.xml"//session-factory/hibernate-configuration 第二、列出 Hibernate 中比较复杂的多对多双向关联映射配置(如果我是项目经理,这些对象的所有关心都不会配置,呵呵)就以用户和角色为例:1、用户的映射文件如下:?xml version="1.0"?!DOCTYPE hibernate-mapping PUBLIC"-//Hibernate/Hibernate Mapping DTD 3.0//EN""!--加入这个 package 属性后,下面就不用写全包名了--hibernate-mapping package="many_to_many_shuangxiang"class name="User"table="t_user"id name="userId"generator class="sequence"!--这里 Id 生成策略配成了 oracle 的序列,下面 param 参数名固定为sequence,然后值 SEQ_USER 是你提前建好的序列名字--param name="sequence"SEQ_USER/param/generator/id property name="userName"/!--set 中的 table 会创建个中间表--set name="roles"table="user_role"!--下面这个 key 是中间表中的外键,引用当前表的主键--key column="userId"/!--many-to-many 中的 column 也是中间表的外键(中间表用的是复合主键),引用 class 属性中持久类 Role 对应表的主键--many-to-many class="Role"column="roleId"//set/class/hibernate-mapping 2、角色的映射文件如下:?xml version="1.0"?!DOCTYPE

 hibernate-mapping PUBLIC"-//Hibernate/Hibernate Mapping DTD 3.0//EN""!--加入这个 package 属性后,下面就不用写全包名了--hibernate-mapping package="many_to_many_shuangxiang"class name="Role"table="t_role"id name="roleId"!--这里 id 生成策略配成 uuid即 32 位字符,这个也比较常用,因为不会重复。--generator class="uuid"//id property name="roleName"/set name="users"table="user_role"key column="roleId"/many-to-many class="User"column="userId"//set/class/hibernate-mapping 第三、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 uorder 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 uwhere u.userId="uuid1"").uniqueResult();4、对象属性查询(直接用 iterate()方法):Iterator iterator=session.createQuery("select u.userName from User uwhere u.userId=?and u.userName=?").setString(0,"8a 88831127f5fd8d0127f5fd90b20003").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 uwhere 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 uwhere u.userName=:name").setParameter("name","宝宝").list();for(Iterator i=list.iterator();i.hasNext();){String userName=(String)i.next();System.out.println(userName);}★Session的获取方式:(1)采用 getCurrentSession()创建的 Session 会绑定到当前的线程中即同一线程的所有代码执行中可以用第一次创建的 Sesssion,而采用openSession()则不会。(2)采用 getCurrentSession()创建的 Session 在commit()或 rollback()后会自动关闭,而 openSession 则不会。★用getCurrentSession()方法创建一个与当前线程绑定的 Session 时需要在

 hibernate 配置文件中加如下配置:property name="hibernate.current_session_context_class"thread/property 好啦,不能在写了,刚搬了房子公交车很不方便,晚上 209 路居然 7 点半就是最后一趟了,昨天都意外打出租了,嘿嘿!

 特别声明:

 1 :资料来源于互联网,版权归属原作者 2 :资料内容属于网络意见,与本账号立场无关 3 :如有侵权,请告知,立即删除。

最新文章
热门文章
相关推荐