使用JPA而不是Hibernate,我们将失去什么?
JPA相对于Hibernate,丢失了很多宝贵的东西,不支持面向对象的Criterion查询,使得我们很多基本的功能无法简单实现。如,通过 对象的属性值实现查询,我们将不得不去写JPQL,通过对字符串的操作来实现功能。一个很大的问题是,对字符串的操作是比较不安全的,如 removeOrderBy的方式实现里,我们就通过找到"order by",然后去掉后面部分来实现;但是,如果order和by之间再多一个空格,就会出现bug。当然,你也可以写一些严格的方法来避免这种问 题,hibernate的Criterion实现里也一定是这样实现的,但这样就增加了我们开发的复杂度,重复了hibernate已经实现了的工作。
JPA为什么不提供Criterion查询?
JPA是一种规范,是为了统一ORM当前混乱的局面而产生的。但是当前除了hibernate,其他ORM框架是没有提供类似于Criterion的查询方式的,所以规范里不能提出这种Criterion这种标准的,那样的话会使得大部分ORM框架无法实现JPA规范。
JPA不支持类似saveOrUpdate的方法
@org.hibernate.annotations.Index
hibernate为字段定义索引,这样通过工具读取映射文件(或annotation标注的映射元数据)生成ddl文件时,就可以自动为该字段创建索引。
在使用JPA时,我们可以同时定义这个索引,它虽然不被JPA的其他实现识别,但不会影响(JPA使用其他实现时)程序的正常运行。
如:
@org.hibernate.annotations.Index(name = "name"
)
@javax.persistence.Column(length = 50, nullable = false
)
public
String
getName() {
return
name;
}
这样,在使用hibernate-tools生成ddl时,会自动为name字段创建一个名称为"name"的索引。
JPA不支持有序集合的映射
在使用hibernate时,我们经常会使用List来映射一些有序集合。
一个典型的应用场景是菜单,由于菜单是有序的,我们会为每个菜单维护一个类似于position的字段,来区分同一个菜单下子菜单的排序。
使用hibernate时,我们可以通过将children这个属性映射为List,并指定其排序字段来实现:
@OneToMany(mappedBy = "parent"
)
@org.hibernate.annotations.IndexColumn(name = "position"
)
public
List<Menu> getChildren() {
return
children;
}
这样,我们就不用自己来维护菜单的排序字段position了,要实现其顺序,我们只需要更改菜单在children这个集合的先后顺序就OK了。
JPA标准中还不支持这种映射,目前我们使用hibernate的实现,这样映射是没有问题的,但是我们应该记住,这样是不符合JPA标准的,切换到另外一种实现这段代码是存在问题的。
Hibernate.initialize(Object o)
在dao里,我们可能需要手动去加载一个延迟加载的对象或属性或集合,这在hibernate里可以调用 Hibernate.initialize(Object o)方法来实现,而在JPA里好像还不能实现这个功能,我们目前的替代方案是,在同一个事务里手动去调用要加载对象的一个get方法,来触发这个加载过 程。
JPA1.0不支持二级缓存!!!
JPA1.0不支持二级缓存,表现在:
- 规范没有对二级缓存的支持
不过还是可以使用JPA具体实现对二级缓存的支持的
- JPA1.0规范的接口中没有对二级缓存的支持
为了解释这个问题,首先我们需要了解的是hibernate中Query接口对二级缓存的支持。
要知道,hibernate的Query接口提供的一些方法,如list,是不会去访问二级缓存的,而是直接生成sql去访问数据库。如果我们一味调用list方法,那么二级缓存的开启将形同虚设,并且会降低系统性能(因为二级缓存基本是个累赘)。
为了有效hit二级缓存,hibernate的Query接口提供了iterator方法,其实现是,先生成一条sql去数据库查询所有满足条件的实体的 id,然后根据得到的id去二级缓存查找对应的对象;如果没有找到对应的对象,则去数据库根据id去加载这个对象,并将加载到的对象缓存起来。(这样产生 一个问题,iterator在第一次调用时无法避免n+1次查询问题)
但是,JPA1.0规范中的Query接口并没有提供类似于iterator的接口,而只提供了类似于list的接口getResultList。这导致我们使用JPA1.0的接口时,将无法有效使用二级缓存。也就是说,我们只要调用Query.getResultList()接口去查询数据(这也是我们执行jpql的唯一途径),它都会去数据库进行查询,而不会去访问二级缓存。
目前要解决这个问题,只能在dao的接口里直接使用hibernate提供的Query接口,而不是JPA的接口。而这样做无疑不是我们使用JPA的初衷。
- JPA1.0不支持查询缓存
在hibernate里,查询缓存的概念在于,使用一条hql进行查询后,可以将查询结果缓存起来,下次使用相同的hql进行查询时,可以重复使用查询结果。(目前只了解其概念,没有实践过)
JPA1.0不支持查询缓存。
不过据了解,hibernate的查询缓存并不被推荐使用,推荐大量使用的是二级缓存。所以,这个也无伤大雅。
JPA2.0规范将支持Criterion查询。