1.JPA annotation中的默认值
- 表名与字段名都尽量与Entity对象相同则无需重复配置
- 在sessinFactory中使用ImprovedNamingStrategy,即可将LOGIN_NAME 这种带下划线的列名自动映射为loginName的属性。
- 在多对多关联关系中,@JoinTable的默认名为"主表名_子表名"
2.二级缓存
并不是所有对象都适合二级缓存,King的大实话:
- 更新大大小于读取的。
- 非重要性的.如CMS的数据,财务数据就不要了。
- 独立的.与遗留系统共同维护同一个表的就不要了。
最适合的,是那种Reference表,数据量又少,与之相连的类又多,如区域表,角色表。
四种隔离级别:
- Transactional 只用于JTA环境。
- Read-write 提交读(本事务只可以看到另一事务已提交的数据。不可重复读。)。
- Nonstrict-read-write 完全无并发访问保证,用于极少更新的表,或脏数据无所谓的表。
- Read-only 如题。只执行create,不update的数据。集群环境最爱。
一般在Read-write与Nonstrict-read-write 中选择,默认就还是用Read-write吧。
一般选用Ehcache作为二级缓存,并使用它的RMI分布式方案,同步发送Invalid信息实现集群同步。
Cache有三种,一般只采用前两种:
- 二级缓存:缓存对象及属性,建议使用。
- 集合缓存:缓存对象关联集合的ID,建议使用
- 查询缓存 : 缓存查询结果类型及ID,不建议使用。因为是否采用缓存中的查询结果,是靠比较查询涉及的表,每个表的最后更新时间来确定。每个表的最后更新时间在集群中需要实时同步,很是麻烦。
3.equals() and hashCode()
在《Hibernate in Action 2>第9章有详细介绍。Hibernate在自己Session范围内,能够保证对同一个数据库id,永远只返回同一个Java对象。只有在你在把从POST参数组装的VO或者从前一个session取得的Detached Object,与另一个代表同一个数据库对象的PO或VO放进同一个hash-base collections(如Set)中时,Java的Set才会让你郁闷一把,Set会认为这是两个对象。所以说这是hash-base collections的固有问题,Hibernate只是把它再次暴露而已。在Hibernate One Session per Request的模式下,情况还是会发生的。比如从页面参数里取得OrderItem对象,放进从数据库查出来的Order->OrderItems Set里。如果你的对象不需要放入Set(比如作为另一个对象的子对象),或者自己会在模式上避免把两个代表同一个数据库对象的对象放入同一Set中,你可以无需重载。
如果一定要重载,有三种策略
- 以ID列作比较,这贴近数据库ID比较,但Hibernate在持久化之前是没有ID值的。New Items的时候会很麻烦。
- 以除ID列和Collesions列(因为collctions代表另一个表了)的外所有列进行比较,比较贴近Java本身的比较,但这个也有问题,比如剩下的列里没有好的unique组合等。
- 以业务主键做比较,这是King唯一相对满意的方式,但就苦了程序员要自己精心挑选Unique的Business Key,变体力劳动为智力劳动。
4. 四种Fetch策略
按顺序从懒到积极的四种策略。
- Lazy:一般人好像只会用这种策略。查询一批Order,只返回Order实例,当访问某个Order的items时,查询某个Order的Items。这种策略会产生著名的N+1查询,比如查询所有Order,再查询它们的Items时,会为每个Order的Items发出一条查询语句。(注意,只查一条Order,然后遍历它的所有item不会N+1)
- Batch:查询一批Order,只返回Order实例,当访问某个Order的items时,批量查询一定数量Order的Items(如10个Order)。如果需要遍历所有Order的Items,需要N/10+1条语句。当然采用这种模式的前提是认为需要一些,但不是全部的Order的Items.
- SubSelect:依然在查询一批Order时,只返回Order实例,当访问某个Order的items时,查询所有Order的Items,需要两条语句。适合在不一定需要Items,如果需要就全需要的情况。
- Eager:只有一条语句,查询时返回Order与Items实例。适合一定全需要Items的情况。
看来Lazy,SubSelect,Eager的情况都很清晰。Batch介于Lazy与SubSelect之间,用得好时也不错。可惜不能在session级动态改变annotation,一设就是全局的了。
5. 级联
1. 如果在一个新建对象中插入一个新建的子对象,持久化新对象时持久化新子对象: @ManyToMany(cascade ={ CascadeType.PERSIST,CascadeType.MERGE })
2. 删除关联的子对象: @ManyToMany(cascade ={ CascadeType.REMOVE })
3. 1+2: @ManyToMany(cascade ={ CascadeType.ALL })
4. 删除一个旧对象时,如果它的子对象已经不被其他对象引用时删除,这个是Hibernate特有
@org.hibernate.annotations.Cascade(
value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN
)
5.@ManyToMany 级联删除JoinTable中间表的数据
如果是删除ManyToMany中的主对象,会自动删除中间表数据
如果是删除子对象(没有定义@JoinTable,只定义了mappedBy的对象),不会自动删除中间表,需要执行如下代码:
public void delete(Long id) {
Role role = this.get(id);
for (User user : role.getUsers()) {
user.getRoles().remove(role);
}
super.delete(role);
}
6.杂记
集合类对象的排序:
需要集合是可排序的,比如List,LinkedHashSet 等,注意Set与Map时,如果属性要显式初始化,要写成private Set<Role> roles = new LinkedHashSet<Role>()。如果是HashSet就依然是无序。
用@OrderBy("publishDate desc,price") 规定顺序,默认是ASC 升序,在sql中会插入相关语句.
flush的操作步骤:
- all entity insertions, in the same order the corresponding objects were saved using Session.save()
- all entity updates
- all collection deletions
- all collection element deletions, updates and insertions
- all collection insertions
- all entity deletions, in the same order the corresponding objects were deleted using Session.delete()
Replicating方法:
session2.replicate(cat, ReplicationMode.LATEST_VERSION); 将session1的cat对象复制到session2,如果session2中已存在cat,使用其中一种策略。
批量插入与更新时:
必须定期执行session.flush(),以减少lvl 1缓存的大小。
Hibernate filters:
<class name="myClass" ...>
...
<filter name="myFilter" condition=":myFilterParam > MY_FILTERED_COLUMN"/>
</class>
session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");
但不支持按id retrive。
Fillter Collctions:
过滤对象的关联Collection,使得lazy load的user.getRoles() 不会返回所有的关联对象,可以按条件返回,分页返回。
StatelessSession:
无persistence context,因此无lvl1,2级缓存和query缓存;无自动状态检查与保存,需显式调用session.update();不获取关联对象;不调用event/inceptor。