openxava / 文档 / 第十一章:用在集合的 @DefaultValueCalculator

课程: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

目录

第十一章:用在集合的 @DefaultValueCalculator
使用  @DefaultValueCalculator
总结
我们已经使用持久属性和计算属性在我们的应用程序中添加了业务逻辑,现在我们将对集合使用 @DefaultValueCalculator 注解。

使用 @DefaultValueCalculator

我们在细节里每行计算的方式并不是最好的。它至少有两个缺点。首先,用户可能希望改变某个产品的单价。其次,如果一个产品的价格发生变化,所有发票的金额也发生变化,这并不是我们想要的。
为了避免这些缺点,最好的方法就是在每行存储格该行的价格。接下来,我们在 Detail 类添加一个持久属性  pricePerUnit,并在使用 @DefaultValueCalculator 时,从 Product 中的价格去计算。以下可以看到我们所获得的效果:

business-logic_en020.png

计算初始值的逻辑将在 PricePerUnitCalculator 中,它只会从产品中读取价格。以下是计算器的代码:
package com.yourcompany.invoicing.calculators; // 在 calculators 包

import org.openxava.calculators.*;
import com.yourcompany.invoicing.model.*;
import lombok.*;
 
import static org.openxava.jpa.XPersistence.*; // 用于使用 getManager()
 
public class PricePerUnitCalculator implements ICalculator {
 
    @Getter @Setter
    int productNumber;
 
    public Object calculate() throws Exception {
        Product product = getManager() // XPersistence 的 getManager()
            .find(Product.class, productNumber); // 寻找产品
        return product.getPrice(); // 返回价格
    }
 
}
接下请,在 Detail 类里添加 pricePerUnit 属性 :
@DefaultValueCalculator(
    value=PricePerUnitCalculator.class, // 这个类会计算初始值
    properties=@PropertyValue(
        name="productNumber", // 计算器的 productNumber 属性由详细信息的 product.number 填充
        from="product.number")
)
@Money
BigDecimal pricePerUnit; // 常规的持久属性
这样子,当用户选择一个产品时,该价格字段将使用产品的价格填充,但由于它是持久属性,用户可以更改它。未来,如果产品的价格发生变化,在详细信息中的单价不会改变。
这代表您必须调整金额计算属性:
@Money
@Depends("pricePerUnit, quantity") // pricePerUnit 而不是 product.number
public BigDecimal getAmount() {
    if (pricePerUnit == null) return BigDecimal.ZERO; // pricePerUnit 而不是 product 和 product.getPrice()
    return new BigDecimal(quantity).multiply(pricePerUnit); // pricePerUnit 而不是 product.getPrice()
}
getAmount() 使用 pricePerUnit 作为源而不是 product.price。
最后,我们必须编辑 CommercialDocument 实体,以在列表显示新属性:
@ElementCollection
@ListProperties("product.number, product.description, quantity, pricePerUnit, amount") // 添加 pricePerUnit
Collection<Detail> details;
现在可以使用订单和发票模块,并试试详细信息里的新方法。


总结

在本章,我们学会了如何使用 @DefaultValueCalculator 注解来定义不同属性的值。

下载本课源代码

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