持久化模式,第 1 部分: 现代 ORM 工具的策略和最佳实践
2010-04-02 00:00:00 来源:WEB开发网实现的细节
Hibernate 有一个 EmptyInterceptor 类,它为 Interceptor 接口中的十几个回调方法提供了空的实现。通过这个类添加审计信息是非常好的方式。在清单 7 中的实现中,只有两个方法与审计相关:onSave(当把新对象刷新到数据库时调用这个方法)和 onFlushDirty(当 Hibernate 把更新过的(脏)对象刷新到数据库时调用这个方法):
清单 7. 扩展 EmptyInterceptor
public class AuditInterceptor extends EmptyInterceptor {
@Override
public boolean onSave(Object entity, Serializable id, Object[] currentState,
String[] propertyNames, Type[] types) {
...
}
@Override
public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState,
Object[] previousState, String[] propertyNames, Type[] types) {
...
}
}
这两个事件都在 Hibernate 已经决定要保存对象的哪些属性以及那些属性的值之后调用。传递给这些方法的参数包括属性名的 String 数组和属性值的 Object 数组。因为 Hibernate 已经决定了要保存的值,所以直接更新对象不会取得想要的效果。实际上,更新对象不会对最终发送到数据库的值产生影响。所以,实际上需要更新属性值数组中的元素。尽管这种更新数组的方式有点儿笨拙,但是实现仍然相当简单明了。只需循环遍历属性名数组,寻找审计字段。找到这些字段之后,用对应的索引更新值数组。还有最后一点细节需要注意:这些回调方法返回一个布尔值。如果修改了对象的状态,方法就需要返回 true。如果没有进行修改,就应该返回 false。清单 8 中的示例代码显示这一逻辑:
清单 8. 更新审计字段的 Interceptor 回调方法
for(int i = 0; i < propertyNames.length; i++) {
if(propertyNames[i].equals("createdOn")) {
currentState[i] = new Date();
updated = true;
}
if(propertyNames[i].equals("createdBy")) {
currentState[i] = username;
updated = true;
}
}
最后一步是帮助强制使用统一的审计字段名称,并确保数据库实体具有这些字段。Auditable 接口是实现这个目标的最容易、最好的方法,但是它看上去有点儿奇怪。尽管为这些字段提供了 getter 和 setter 方法,但是审计代码中实际上不使用这些方法。但是,通过让实体实现 Auditable 接口,可以显著减少在开发持久化类时需要的代码量。
第 1 部分结束语
本文主要关注使用 Hibernate 特性在领域模型上应用基本的面向对象原则。与所有模式和最佳实践一样,您应该根据自己的环境评估这里描述的解决方案,并相应地进行调整。泛型 DAO 能够提供很强的灵活性。不同的数据库结构、技术和业务需求具有不同的通用功能,可以把这些功能转移到泛型 DAO 的通用代码中。不同的应用程序可能需要不同的审计信息(ATM 机领域实体事务的审计信息显然与职员地址的审计信息不一样)。无论处于什么上下文,最佳实践是相同的。
本系列的 第 2 部分 进一步讨论构造数据模型方面的最佳实践,包括利用 Hibernate 的多态性、泛型 DAO 的其他有用特性以及数据模型的性能调优。
本文示例源代码或素材下载
更多精彩
赞助商链接