概述
重构非强检器具类别CRUD
,实体间的关联较复杂,重构编辑时花了大量的时间。
非强检器具类别实体相关ER
图:
问题总结
需求描述
不确定度的增删改查,这个和新增时是一致的,可以直接参考。
可选测量范围单位:这个后台存储的是计量单位的集合,没有存储计量单位类别,所以需要在数据加载完成之后,根据计量单位的类别,设置当前参量类别的计量单位类别。
列出该计量单位类别下的所有计量单位,然后需要将该非强检类别选中的计量单位默认勾选。
绑定计量单位类别
原来的编辑代码中用到了大量的$timeout
。
看了半天没看明白,感觉设置类别时不需要延迟执行,把每个参量类别中计量单位集合的第一个计量单位的类别用于绑定(在增加时已经验证,所以可以保证选中所有的计量单位属于同一类别)。
设置默认选中
设置选中时,遇到了问题。
需要显示某计量单位类别下的所有计量单位。
非强检器具类别中能找到测量范围可选单位集合,根据计量单位能找到计量单位类别,但是该计量单位类别是不能含有所有计量单位的,如果有,就会造成循环,Json
转换时异常。
最初的想法是视图中将计量单位类别传给了指令,指令将数据补全之后再传回来。然后用$timeout
过一会等数据传回来之后再设置单位的选中。
后来一想,不就是补全计量单位的数据吗?为什么非要在指令里补全,直接在控制器中获取所有计量单位类别,然后再补全即可。
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
的地方,绝不用等号!
删除原实体关联
以下的思路有问题,仅供参考,切勿学习!!!应该是改变原集合。
编辑的时候,如果移除了某个不确定度,直接使用级联是删不掉的。
所以需要在保存之前删除掉所有的不确定度,然后再根据新增加的数据进行保存。
在仓库中声明相关删除不确定度的方法。
/**
* @author zhangxishuo on 2018/10/15
* 不确定度仓库
*/
public interface AccuracyUncertaintyRepository extends CrudRepository {
/**
* 根据参量类别删除实体
* @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());
}
更新时,后台发生异常。
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
。
我的update
方法调用该方法,但是我的update
不存在事务,所以抛出异常,“事务必须”异常。
事务的传播行为是方法之间调用时发生的,所以当某个方法传播级别设置为Never
,但是该方法并不被其他含有事务的方法所调用,所以仅仅是禁用事务,没有异常。
为方法添加事务,功能完成!
正确的编辑思想
删不掉的原因是因为后台编辑时直接设置了新的List
,然后Hibernate
采用持久化的方式作用该列表,然后保存,之前的还在。
应该是直接修改原List
。
// 移除已经被删除的不确定度
oldAccuracyUncertainties.removeIf((accuracyUncertainty) -> !newAccuracyUncertainties.contains(accuracyUncertainty));
// 添加新增的不确定度
for (AccuracyUncertainty accuracyUncertainty : newAccuracyUncertainties) {
if (!oldAccuracyUncertainties.contains(accuracyUncertainty)) {
oldAccuracyUncertainties.add(accuracyUncertainty);
}
}
用到了contains
,所以要重写equals
与hashCode
方法。
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);
}