深入浅出hibernate摘录2

以下内容摘自深入浅出hibernate

在编写代码的时候,尽量将POJO的getter/setter方法设定为public,如果设定为private,protected,hibernate将无法对属性的存取进行优化,只能转而采用传统的反射机制进行操作(hibernate3中进行了局部改进),这将导致大量的性能开销(特别是在IBM JDK以及1.4之前的sun JDK版本中,反射带来的系统开销相当可观。)

级联关系设置为all。
级联(cascade)在Hibernate映射关系中是个非常重要的概念。它指的是当主控方执行操作时,关联对象(被动方)是否执行同一操作。如对主控对象调用save-update或delete方法时,是否同时对关联对象(被动方)进行save-update或delete。
如果设定为all,则代表无论主控方执行任何操作,都对其关联类进行同样的操作。


Hibernate 3 中,引入了Restrictions类作为Expression的替代(Expression类被设定为半废弃状态),Restrictions的使用方法与Expression一致(Hibernate3中,Expression实际上为Restrictions类的一个子类实现)。

示例查询:
Example类实现了Criteria接口,同样,它也可以用作Criteria的查询条件。Example的作用是:根据已有对象,查找属性与之相符的其他对象。

示例代码:

Criteria criteria = session.createCreateria(TUser.class);

TUser exampleUser = new TUser();
exampleUser.setName("Erica");
criteria.add(Example.create(exampleUser));

List list = criteria.list();

for(int i=0;i {
TUser user = (TUser) list.get(i);
System.out.println("User["+i+"]\t"+user.getName());
}

这里我们新建了一个TUser对象exampleUser,并作为范本,查询所有name属性与之相同的用户记录。

红色代码等价于:criteria.add(Expression.eq("name","Erica"));


DetachedCriteria 可以脱离session实例独立存在,这样,我们就可以将某些通用的criteria查询条件进行抽离,每次使用时再与当前session实例绑定以获得更好的代码重用效果。

HQL子句本身大小写无关,但是其中出现的类名和属性名必须注意大小写区分。

在hibernate2之前版本中,HQL仅用于数据查询。而在Hibernate3中,HQL具备了更加强大的功能。实体更新与删除就是其中的主要特性之一。

Hibernate中的HQL功能已日趋全面,同时也越来越接近传统的SQL语言。与sql不同的是,sql面向的是二维的结构化数据,而HQL则面向数据对象。在对象数据库尚不成熟的今天,通过面向对象的查询语言对关系型数据库进行访问,既满足了上层结构中面向对象设计的需求,也充分利用了现有技术平台,这也正是Hibernate的优势所在。

SQL Injection 是常见的系统攻击手段,这种攻击方式的目标即针对有SQL字符串拼装造成的漏洞。


参数绑定机制可以使得查询语法与具体参数数值相互独立。

fetch关键字只对inner join和left join有效。对于right join而言,由于作为关联对象容器的T_User对象可能为null,所以也就无法通过fetch关键字强制Hibernate进行集合填充操作。

HQL中,子查询必须出现在where子句中,且必须以一对圆括号包围。

Hibernate支持一下几种数据加载方式:
1,即时加载
当实体加载完成后,立即加载其关联数据。

2,延迟 加载
实体加载时,其关联数据并非即刻获取,而是等关联数据第一次被访问时在进行读取。

3,预先加载
预先加载时,实体及其关联对象同时读取,这与即时加载类似,不过实体及其关联数据是通过一条SQL语句(基于外连接)同时读取。

4,批量加载
对于即时加载和延迟加载,可以采用批量加载方式进行性能上的优化。

批量加载,简而言之,就是通过批量提交多个限定条件,一次完成多个数据的读取。如对于一下形式的sql:
Select from T_User where id=1;
Select from T_User where id=2;

我们可以将其整合成一条sql语句完成同样的功能:
Select from T_User where id=1 or id=2;

这就是所谓的批量加载机制。如果使用了批量加载机制,Hibernate在进行数据查询操作前,会自动在当前session中寻找是否还有其他同类型待加载的数据,如果有,则将其查询条件合并在当前select语句中一并提交,这样,通过一次数据库操作即完成了多个读取任务。

在实体配置的class节点中,我们可以通过

我们可以通过编码将一个Transient状态的对象手动的与库表记录形成关联,使其改造成一个Detached状态的对象,此时,我们手工构造的这个Detached对象与通过Session构造的Detached对象并没有什么区别。
不过,考虑到实际情况可能并非这么简单,Hibernate在判定对象处于Detached状态还是Transient状态时,有着更加复杂的机制。

判定一个对象是否处于Transient状态的条件:
1,首先,对象的id属性(如果此属性存在的话)是否为null
2,对应上面的示例,Hibernatne即根据此条件进行判定。
3,如果指定了id属性的unsaved-value,那么id属性是否等于unsaved-value。
4,如果配备了Version属性,version属性是否为null。
5,如果配备了Version属性,且为version指定了vunsaved-value,version属性值是否等于unsaved-value。
6,如果存在Interceptor,那么Interceptor.isUnsaved方法是否返回true。

VO与PO

有时候,为了方便,我们也将处于Transient和Detached状态的对象统称为值对象,而将处于Persistent状态的对象成为持久对象。
这时站在“实体对象是否被纳入Hibernate实体管理容器”的立场加以区分的,非管理的实体对象统称为VO,而被管理的实体对象成为PO.
在从VO和PO的角度重复一下上面的描述:
VO和PO的主要区别在于:
1,VO是相对独立的实体对象,处于非管理状态
2,PO是由Hibernate纳入其实体管理容器(Entity Map)的对象,它代表了与数据库中某条记录对应的Hibernate实体,PO的变化在事务提交时将反映到实际数据库中。
3,如果一个PO与其对应的session实例分离,那么此时,它又会变成一个VO。
由PO,VO的概念,有引申出一些系统层次设计方面的问题。如在传统的MVC架构中,位于model层的PO,是否允许被传递到其他层面。由于PO的更新最终将被映射到实际数据库中,如果PO在其他层面(如view层)发生了变动,那么可能会对Model层造成意想不到的破坏。
因此,一般而言,应该避免直接将PO传递到系统中的其他层面,一种解决方法是,通过构造一个新的VO,通过属性复制使其具备与PO相同的属性值,并以其为传输煤质(实际上,这个VO被用作Data Transfer Object,即所谓的DTO),将此VO传递给其他层面以实现必须的数据传送。

属性复制可以通过Apache Jakarta Commons Beanutils组件提供的属性批量复制工鞥,避免反复的get/set操作。

Collection在判断两个对象是否相等的时候,会首先调用对象的hashCode方法,如果hashCode相同的话,随即调用其equals方法,如果两次判断均为真,则认为对比的两个对象相等。


事务提交时,Hibernate会对session中的PO进行检测,判断那些发生了变化,并将发生变化的数据更新到数据库中。
这里就存在一个问题,Hibernate如何盘对一个数据对象是否发生了改变,或者说,Hibernate如何进行脏数据识别?
脏数据检查的一般策略大致有下面两种:
1,数据对象监控
数据对象监控的实现方式,大体上是通过拦截器对数据对象的设值方法(setter)进行拦截,拦截器的实现可以借助Dynamic Proxy或者CGLIB实现。一旦数据对象的设置方法被调用(通常也意味着数据对象的内容发生变化),则将其标志为“待更新”状态,之后在数据库操作时将其更新到对应的库表。

2,数据库版本对比
在持久层框架中维持数据对象的最近读取版本,当数据提交时将提交数据与此版本进行比对,如果发生变化则将其同步到数据库相应的库表。

在session中,保存了所有与当前session实例相关联的实体对象的当前实例和原始状态信息(即EntityEntry)。这两者以“key-value”的形式,保存在SessionImpl.entityEntries数据结构中。
SessionImpl.entityEntries是一个Map型的数据结构,其中每个项目(Entry)都包含了当前 与Session关联的一个实体对象实例及其原始信息。以实体对象为key,而以对应EntityEntry为value。session.flushEntities方法的工作,就是遍历entityEntries,并将其中的实体对象与其原始版本进行对比,判断对象状态是否更改。

类 IdentityHashMap
此类利用哈希表实现 Map 接口,比较键(和值)时使用引用相等性代替对象相等性。换句话说,在 IdentityHashMap 中,当且仅当 (k1==k2) 时,才认为两个键 k1 和 k2 相等(在正常 Map 实现(如 HashMap)中,当且仅当满足下列条件时才认为两个键 k1 和 k2 相等:(k1==null ? k2==null : e1.equals(e2)))。

对于save操作而言,如果对象已经与session向关联(即已经被加入session的实体容器中),则无需进行具体的操作。因为之后的session.flush过程中,Hibernate会对此实体容器中的对象进行遍历,查找出发生变化的实体,生成并执行相应的update语句。

缓存是数据库数据在内存中的临时容器,它包含了库表数据在内存中的临时拷贝,位于数据库与数据访问层之间。ORM在进行数据读取时,会根据其缓存管理策略,首先在缓存中查询,如果在缓存中发现所需数据(缓存命中),则直接以此数据作为查询结果加以利用,从而避免了数据库调用的性能开销。

只有在查询开始之前(也就是Hibernate生成SQL之前)设定加锁,才会真正通过数据库的锁机制进行加锁处理,否则,数据已经通过不包含for update子句的select SQL 加载进来,所谓数据库加锁也就无从谈起。

session在加载实体对象时,将经过哪些过程:
1,首先,我麽欧尼知道,hibernate中维护了两级缓存。第一级缓存由session实例维护,其中保持了session当前所有关联实体的数据,也称为内部缓存。而第二级缓存则存在与sessionFacotory层次,有当前所有由本sessionFacotory构造的session实例共享。
出于性能考虑,避免无谓的数据库访问,session在调用数据库查询功能之前,会现在缓存中进行查询。首先在第一级缓存,通过实体类型和id进行查找,如果第一级缓存查找命中,且数据状态合法,则直接返回。
2,之后,session会在当前“NonExists”记录中进行查找,如果“NonExists”记录中存在同样的查询条件,则返回null。“NonExists”记录了当前session实例在之前所有查询操作中,未能查询到有效数据的查询条件(相当与一个查询黑名单列表)。如此一来,如果session中一个无效的查询条件重复出现,即可迅速做出判断,从而获得最佳的性能表现。
3,对应load方法而言,如果内部缓存中未发现有效数据,则查询第二级缓存,如果第二级缓存命中,则返回。
4,如在缓存中未发现有效数据,则发起数据库查询操作,如经过查询未发现对应记录,则将此次查询的信息在“NonExists“中加以记录,并返回null。
5,根据映射配置和select SQL得到的resultSet,创建对应的数据对象。
6,将其数据对象纳入当前session实体管理容器(一级缓存)。
7,执行Interceptor.onLoad方法(如果有对应的interceptor)。
8,将数据对象纳入二级缓存。
9,如果数据对象实现了LifeCycle接口,则调用数据对象的onLoad方法。
10,返回数据对象。

你可能感兴趣的:(hibernate,Hibernate,数据结构,SQL,配置管理,项目管理)