openxava / 文档 / 第十五章:多用户时默认值的计算

课程:1. 入门教学 | 2. 基本域模型(上) | 3. 基本域模型(下) | 4. 优化用户界面 | 5. 敏捷开发 | 6. 映射式超类继承 | 7. 实体继承 | 8. 视图继承(View) | 9. Java 属性 | 10. 计算属性 | 11. 用在集合的 @DefaultValueCalculator | 12. @Calculation 和集合总计 | 13. 从外部文件的 @DefaultValueCalculator | 14. 手动更改 schema | 15. 多用户时默认值的计算 | 16. 同步持久属性和计算属性 | 17. 从数据库中的逻辑 | 18. 使用 @EntityValidator 进行验证 | 19. 验证替代方案 | 20. 删除时验证 | 21. 自定义 Bean Validation 注解 | 22. 在验证中调用 REST 服务 | 23. 注解中的属性 | 24. 改进标准行为 | 25. 行为与业务逻辑 | 26. 参照与集合 | A. Architecture & philosophy | B. Java Persistence API | C. Annotations | D. Automated testing

目录

第十五章:多用户时默认值的计算
JPA 回调方法
多用户時默认值的计算
总结
在上一章,我们学到如何手动修改数据库的方法,以便在不删除表的情况下修改数据。现在我们将看在多用户环境下如何定义默认值计算。

JPA 回调方法

将业务逻辑添加到模型的另一种有用方法是使用 JPA 回调方法。一个回调方法会在实体生命周期的某个特定时刻中作为持久对象调用。也就是说,您可以指定一些实体的保存、读取、删除或修改的逻辑。
在本章,我们将看到 JPA 回调方法的一些实际应用。

多用户时默认值的计算

到目前为止,我们一直在使用 @DefaultValueCalculator 计算发票和订单的序号。该序号会在用户点击新建发票或订单时计算默认值。因此,当多个用户同时点击“新建”按钮时,他们都会得到相同的数字。在多用户环境下不该是这样,生成序号最好的方法是在保存时生成它。
我们将使用 JPA 回调方法来实现序号的生成。 JPA 允许您将一个类的任何方法标记在其生命周期的任何时候执行。我们指定它在保存 CommercialDocument 之前计算。顺便改进计算方法,让订单和发票有不同编号计算。
在 CommercialDocument 实体中添加 calculateNumber() 方法:
@PrePersist // 在第一次保存对象之前执行
private void calculateNumber() throws Exception {
    Query query = XPersistence.getManager()
        .createQuery("select max(i.number) from " +
        getClass().getSimpleName() + // 這樣它对 Invoice 和 Order 都有效
        " i where i.year = :year");
    query.setParameter("year", year);
    Integer lastNumber = (Integer) query.getSingleResult();
    this.number = lastNumber == null ? 1 : lastNumber + 1;
}
此代码与 NextNumberForYearCalculator 的代码相同,差别是使用 getClass().getSimpleName() 而不是“CommercialDocument”。 getSimpleName() 方法返回不带包的类的名称,也就是实体名称。订单为“invoice”,发票为“Order”。因此,我们可以为 Order 和 Invoice 获得不同的编号。
JPA 规范声明您不应在回调方法中使用 JPA API。因此,从严格的 JPA 角度来看,上述方法是不合法的。但是,Hibernate(OpenXava 默认使用的 JPA 实现)允许您在 @PrePersist 中使用它。而由于 JPA 是进行此计算最简单的方法,因此我们使用它。
现在您可以从项目中删除 NextNumberForYearCalculator 类,并修改 CommercialDocument 的 number 属性以避免使用它:
@Column(length=6)
// @DefaultValueCalculator(value=NextNumberForYearCalculator.class, // 删除这几行
//     properties=@PropertyValue(name="year")
// )
@ReadOnly // 用户无法修改
int number;
要注意的是,除了删除 @DefaultValueCalculator 之外,我们还添加了 @ReadOnly 注解。这意味着用户不能输入或修改号码。这才是正确的方法,因为数字是在保存对象时生成的,因此用户键入的值始终会被覆盖。
现在您可以试试发票或订单模块,会看到该序号为空且不可编辑,而当您保存文档时,会计算序号并跟年份一起以消息显示。

总结

在本章中,我们了解到如何定义默认值,在应用程序用于多用户或大规模访问的环境时,这将非常有用。在接下来的课程中,我们将看其它添加业务逻辑的方法,以及如何同步属性。

下载本课源代码

对这节课有什么问题吗? 前往论譠 一切都顺利吗? 前往第十六章