hibernate是早一阵子学的,历史比较久远了,所以这篇文只能说是我笔记(还好本人是笔记狂,好记性不如烂笔头)上hibernate的知识要点和徐培成老师讲课中提到的一些实际应用中的小技巧和注意事项的摘取和整理(美其名曰“总结”)。
开卷语:
1、学习一个框架,看源码不如看doc文档,源码只是实现,doc是思想(ms很多人说过,学习一个框架重在学习它的思想,就是这个“思想”二字深奥。。我的感觉是,这个东西学起来不难,用起来不难,但是要用得游刃有余很难(多练多看也许某天能达到),能自己设计一个这样的框架更是难上加难(除了多练多看还得多想还不一定能达到这个境界)。。反正本人在可以预见的很漫长的一段时间内是不会具备这个能耐滴T_T)。
2、hibernate是一项数据持久化技术,封装了数据的访问细节,所以说体现了OO思想。
3、hibernate应用属于数据持久化层,而数据持久化层是从业务逻辑层分离出来的,为业务逻辑层提供面向对象的api。
4、完善的持久化层应该至少具备以下几点(下面是老师总结的,不过《深入浅出Hibernate》第一章也有提到,推荐一下,这是一本好书,虽然是2005年的):
(1)代码复用性高,可完成所有数据访问操作;
(2)如果有需要,能够支持多种数据库平台,而且更换数据库平台时,代码无需更改(参见上一条);
(3)具有相对独立性,当持久化层更换时(例如将hibernate换成toplink,这里体现了接口编程的好处),不会影响上层实现。
5、hibernate是和标准sql打交道的,但只是对jdbc做了轻量级封装,即只封装了jdbc的部分api,例如存储过程就没有做封装。
6、hibernate也被称为orm工具,“映射”是其思想要点,主要是由反射调用实现的--这个是指底层,而表现形式在我看来可能就是注解及配置文件。关于hibernate注解的具体使用,可以参见doc文档~
杂记(接上):
6、实体间的关系(UML):
(1)关联:类之间的引用关系,例如A{B,C},作图时使用实线箭头表示;
(2)依赖:类之间的访问关系,例如A{foo{new B;b.getXXX;}},作图时用虚线箭头表示;
(3)聚集:主要是从整体和部分的生命周期来看,是一种逻辑约束,当整体不存在时,其组成部分也随之消失。
--映射组成关系,区分值和实体。持久化类的属性分为两种:值(value)类型和实体(entity)类型。值类型没有OID,不能被单独被持久化,它的生命周期依赖于所属的持久化类的生命周期,例如组件类型(注释component)就是一种值类型;实体类型有OID,可以单独被持久化。最简单的区分方式是:看session的CURD方法能否直接对其操作,可以的话就是实体类型,不可以就是值类型。
(4)一般化:也说泛型或者继承关系,例如网上支付和现金支付继承于付款方式。
7、set类子元素的inverse属性
假设将order5所属客户改为customer2:
Customer c2 = (Customer)session.load(Customer.class,new Long(2));
Order o5 = (Order)session.load(Order.class,new Long(5));
o5.setCustomer(c2);
c2.getOrders().add(o5);
tx.commit();
-------------------------------------------------------
update orders set orderno=?, price=?, cid=? where id=?
update order set cid=? where id=?
虽然如果直接从库里更改的话只需要一条语句update语句,但是hibernate执行了两条更新语句。这是因为Hibernate会自动清理缓存中的所有持久化对象,按照持久化对象的改变来同步更新数据库,对于hibernate而言,“将order5所属客户改为customer2”中涉及的两个实体属性都有更改,因此执行了上述的两条更新语句。
重复执行多余的sql语句会影响java性能,解决这个问题的办法是把<set>元素的inverse属性设为true,该属性的默认值是false。
<set name=“orders” cascade=“save-update” inverse=“true”>
<key column=“CUSTOMER_ID” />
<one-to-many class=“mypack.Order” />
</set>
执行语句就只有一条了:update orders set orderno=?, price=?, cid=? where id=?
Customer和order的双向关联中,customer端的关联只是order端关联的镜像。这样当hibernate同时探测到持久化对象customer和order的状态均发生变化时,仅按照order对象状态更新数据库。
8、拆分表与投影查询
只查询若干字段的sql查询叫投影查询,例如select id, name from customers。
先说一下jdbc大对象在不同数据库里支持的字段类型:oracle(blob、clob),mysql(blob,longblob--最好用这个),sqlserver(image,text),db2(blob、?)。
相较于查询整个表的数据而言,投影查询也可以提高性能,但是实际开发中有很多还是将(有些)对象实体的某些属性(例如大对象数据)单独存放在另一张表中,例如图片,大文本数据等。因为如果某张表里有大对象数据,虽然查询的时候没有涉及到它,但是查询前数据库要扫描表,在扫描过程中,大对象字段还是这个元组中的一个单元,扫描过程中仍然有它,就有额外的开销,还是会影响性能。
还有一种情况,如果某些字段查询高度频繁,一般也单独存放在一张表中。最典型的例子,登陆所用到的name和password。
9、hibernate的检索级别:类级别和关联级别(下面是课件截图)。
类级别:立即检索,延迟检索。
关联级别:立即检索,延迟检索,迫切左外连接。
10、关于关联时的fetch~lazy
举例:user与address,one2one关联的时候,如果lazy属性设置为fetch=FetchType.LAZY,在session连接关闭后再取关联对象adderss
Address类:
@OneToOne(mappedBy="shippingAddress", cascade=CascadeType.ALL,fetch=FetchType.LAZY)
public User2 getUser2() {
return user2;
}
测试代码段:
User2 user2 = (User2) session.get(User2.class, tempId);
session.close();
assertNotNull(user2.getShippingAddress());
assertEquals("street", user2.getShippingAddress().getStreet());
hibernate3.2.5:编译可以通过,但是程序运行时会报错---默认属性值为EAGER;
hibernate3.3.2:编译时即报错---默认属性值为LAZY(一是提高性能,二是防止隐患,过多数据eager加载时,每开始一次事务hibernate都会把整个数据库而不仅仅只是某些表加载到内存,这样估计系统很可能会挂掉。。。猜的)(If you define too many non-lazy associations in your object model, Hibernate will fetch the entire database into memory in every transaction-->hibernate_reference.pdf-->p264);
道听途说:据闻业界一直有编译时错误和运行时错误地位之争,不知是否属实,不过从这个问题ms能看出点苗头。。。
查看了一下hibernate_reference(3.2.0)官方文档,fetching strategies的内容和hibernate_reference(3.3.2)差不多是一样的。。。可是上面那个是我运行的结果。。。(编译时报错暂时还没复现出来)
ps:hibernate还可以和lucene集成,详情请参见hibernate_annotations.pdf第五章~
内容还有很多很多很多很多。。。不写了(手都酸了)。。。大家自行google百度或者来传智播客亲自聆听吧:)