1

概述

重构非强检器具类别CRUD,实体间的关联较复杂,重构编辑时花了大量的时间。

非强检器具类别实体相关ER图:

clipboard.png

问题总结

需求描述

clipboard.png

不确定度的增删改查,这个和新增时是一致的,可以直接参考。

可选测量范围单位:这个后台存储的是计量单位的集合,没有存储计量单位类别,所以需要在数据加载完成之后,根据计量单位的类别,设置当前参量类别的计量单位类别。

列出该计量单位类别下的所有计量单位,然后需要将该非强检类别选中的计量单位默认勾选。

绑定计量单位类别

原来的编辑代码中用到了大量的$timeout

clipboard.png

看了半天没看明白,感觉设置类别时不需要延迟执行,把每个参量类别中计量单位集合的第一个计量单位的类别用于绑定(在增加时已经验证,所以可以保证选中所有的计量单位属于同一类别)。

clipboard.png

设置默认选中

设置选中时,遇到了问题。

clipboard.png

需要显示某计量单位类别下的所有计量单位。

非强检器具类别中能找到测量范围可选单位集合,根据计量单位能找到计量单位类别,但是该计量单位类别是不能含有所有计量单位的,如果有,就会造成循环,Json转换时异常。

clipboard.png

最初的想法是视图中将计量单位类别传给了指令,指令将数据补全之后再传回来。然后用$timeout过一会等数据传回来之后再设置单位的选中。

clipboard.png

后来一想,不就是补全计量单位的数据吗?为什么非要在指令里补全,直接在控制器中获取所有计量单位类别,然后再补全即可。

MeasurementUnitCategoryService.findAll(function(data) {
    // 传给视图
    self.categorys = data;

    // 设置主参量单位类别
    angular.forEach(self.categorys, function(category) {
        if ($scope.data.primaryParameterCategory._measurementUnitCategory.id === category.id) {
            // 设置主参量计量单位类别
            angular.copy(category, $scope.data.primaryParameterCategory._measurementUnitCategory);
            // 激活单位
            self.activeUnit($scope.data.primaryParameterCategory._measurementUnitCategory.measurementUnitSet, $scope.data.primaryParameterCategory.measureScaleUnitSet);
        }
    });

    // 设置参量单位类别
    angular.forEach($scope.data.parameterCategoryList, function(parameterCategory) {
        angular.forEach(self.categorys, function(category) {
            if (parameterCategory._measurementUnitCategory.id === category.id) {
                // 设置参量计量单位类别
                angular.copy(category, parameterCategory._measurementUnitCategory);
                // 激活单位
                self.activeUnit(parameterCategory._measurementUnitCategory.measurementUnitSet, parameterCategory.measureScaleUnitSet);
            }
        });
        angular.forEach(parameterCategory.additionalParameterCategoryList, function(additionalParameterCategory) {
            angular.forEach(self.categorys, function(category) {
                if (additionalParameterCategory._measurementUnitCategory.id === category.id) {
                    // 设置附加参量计量单位类别
                    angular.copy(category, additionalParameterCategory._measurementUnitCategory);
                    // 激活单位
                    self.activeUnit(additionalParameterCategory._measurementUnitCategory.measurementUnitSet, additionalParameterCategory.measureScaleUnitSet);
                }
            });
        });
    });
});

对象相关问题

又是对象引用引发的问题,一个选中变了,都跟着变了。

计量单位

// 设置主参量类别的计量单位类别
$scope.data.primaryParameterCategory._measurementUnitCategory = category;

...

// 设置参量计量单位类别
parameterCategory._measurementUnitCategory = category;

...

// 设置附加参量计量单位类别
additionalParameterCategory._measurementUnitCategory = category;

改用angular.copy,保证两个对象独立。以后 能用copy的地方,绝不用等号!

clipboard.png

删除原实体关联

以下的思路有问题,仅供参考,切勿学习!!!应该是改变原集合。

编辑的时候,如果移除了某个不确定度,直接使用级联是删不掉的。

所以需要在保存之前删除掉所有的不确定度,然后再根据新增加的数据进行保存。

在仓库中声明相关删除不确定度的方法。

/**
 * @author zhangxishuo on 2018/10/15
 * 不确定度仓库
 */
public interface AccuracyUncertaintyRepository extends CrudRepository<AccuracyUncertainty, Long> {
    /**
     * 根据参量类别删除实体
     * @param id
     */
    void deleteAllByParameterCategory_Id(Long id);
}
logger.debug("删除主参量类别不确定度");
accuracyUncertaintyRepository.deleteAllByParameterCategory_Id(oldNonMandatoryInstrumentCategory.getPrimaryParameterCategory().getId());

logger.debug("删除参量类别不确定度");
for (ParameterCategory parameterCategory : oldNonMandatoryInstrumentCategory.getParameterCategoryList()) {
    accuracyUncertaintyRepository.deleteAllByParameterCategory_Id(parameterCategory.getId());
}

更新时,后台发生异常。

clipboard.png

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' call; nested exception is javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread.

TransactionRequiredException,最核心的就是这个词。

我猜想Hibernate在实现deleteAllByParameterCategory_Id方法时,将该方法的事务级别设置为MANDATORY

clipboard.png

我的update方法调用该方法,但是我的update不存在事务,所以抛出异常,“事务必须”异常。

事务的传播行为是方法之间调用时发生的,所以当某个方法传播级别设置为Never,但是该方法并不被其他含有事务的方法所调用,所以仅仅是禁用事务,没有异常。

为方法添加事务,功能完成!

clipboard.png

正确的编辑思想

删不掉的原因是因为后台编辑时直接设置了新的List,然后Hibernate采用持久化的方式作用该列表,然后保存,之前的还在。

应该是直接修改原List

// 移除已经被删除的不确定度
oldAccuracyUncertainties.removeIf((accuracyUncertainty) -> !newAccuracyUncertainties.contains(accuracyUncertainty));
// 添加新增的不确定度
for (AccuracyUncertainty accuracyUncertainty : newAccuracyUncertainties) {
    if (!oldAccuracyUncertainties.contains(accuracyUncertainty)) {
        oldAccuracyUncertainties.add(accuracyUncertainty);
    }
}

用到了contains,所以要重写equalshashCode方法。

equals用于判断相等,hashCode用于计算哈希值,这两个方法通常一起重写。两个对象相等,hashCode一定相等,hashCode相等,但不一定是同一对象。

不确定度:

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    AccuracyUncertainty that = (AccuracyUncertainty) o;
    return Objects.equals(minAccuracyValue, that.minAccuracyValue) &&
            Objects.equals(maxAccuracyValue, that.maxAccuracyValue) &&
            Objects.equals(minAccuracyUnit, that.minAccuracyUnit) &&
            Objects.equals(maxAccuracyUnit, that.maxAccuracyUnit);
}

@Override
public int hashCode() {

    return Objects.hash(minAccuracyValue, maxAccuracyValue, minAccuracyUnit, maxAccuracyUnit);
}

计量单位:

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof MeasurementUnit)) return false;
    MeasurementUnit that = (MeasurementUnit) o;
    return Objects.equals(id, that.id);
}

@Override
public int hashCode() {

    return Objects.hash(id);
}

张喜硕
2.1k 声望423 粉丝

浅梦辄止,书墨未浓。