值对象校验
值对象是否可空、最大长度、最大值、最小值等有效性校验是业务处理过程中必须又繁琐的一项工作。
fxEAP基于元数据以及注解,实现以下两个层面的值对象有效性校验,能够极大减少校验值对象有效性的代码量:
- 基于元数据,在创建界面组件时,添加对其输入的有效性进行限制;
- 基于注解,供代码层面对值对象进行有效性校验,例如保存、更新等;
fxEAP中,以上两种情况均不需要编辑代码。
以在FormView创建一个字符串编辑器为例,在创建编辑器时,需要提供 最大长度、默认值、基于正则的校验规则 三个参数:
/**
* 构造一个StringField。可以设置字符串的最大长度、默认值
*
* @param property 实现IPropertyItem接口的属性
* @return StringField属性编辑器
*/
public static IPropertyEditor<?> createStringEditor(IFormPropertyItem property) {
return new AbstractPropertyEditor<String, RegularStringField>(property, new RegularStringField(
property.getMaxLength(), property.getDefaultValue(), property.getRegularExpression())) {
{
enableAutoSelectAll(getEditor());
}
@Override
protected StringProperty getObservableValue() {
return getEditor().textProperty();
}
@Override
public void setValue(String value) {
getEditor().setText(value);
}
};
}
以在TableView中创建一个字符串编辑为例,创建一个LRTextFieldTableCell:
} else if (DataTypeDefine.JAVA_TYPE_STRING.getJavaType().equalsIgnoreCase(java_type)) {
// String
col.setResizable(true);
col.setCellValueFactory(new PropertyValueFactory<>(property.getPropertyCode()));
if (property.getMetadataEditable()) {
col.setCellFactory(column -> new LRTextFieldTableCell(property));
}
}
在调用父类构造器时,传入了上述三个参数。
public class LRTextFieldTableCell<S, T> extends LRBaseTextFieldTableCell<ValueObject, String> {
public LRTextFieldTableCell(ITablePropertyItem propertyItem) {
super(propertyItem.getPropertyCode(), propertyItem.getMaxLength(), propertyItem.getDefaultValue(), propertyItem.getRegularExpression());
this.propertyItem = propertyItem;
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (!isNowFocused) {
commitEdit(textField.getText());
} else {
this.textField.setEditable(TableCellEditableUtils.checkFieldEditableByFormula(propertyItem, getTableView().getItems().get(getIndex())));
}
});
}
}
public StringField(Integer maxLength, final String defaultValue) {
super();
// 是否可编辑
editFlagProperty().addListener((observable, oldValue, newValue) -> setDisable(!newValue));
// 限制输入最大长度
textProperty().addListener((observableValue, oldValue, newValue) -> {
if (!checkValueLengthValid(newValue)) {
setText(oldValue);
}
});
// 设置最大允许长度
if (maxLength == null) {
maxLength = -1;
}
setMaxLength(maxLength);
if (StringUtils.isNotBlank(defaultValue)) {
// 设置默认值
setText(defaultValue);
}
}
protected boolean checkValueLengthValid(final String value) {
if (getMaxLength() != null && getMaxLength() > 0 && StringUtils.isNotBlank(getText()) && StringUtil.lenOfChinesString(value) > getMaxLength()) {
logger.error(String.format("非法的值:%s", value));
return Boolean.FALSE;
}
return Boolean.TRUE;
}
- 基于元数据自动构建,无需手工编写代码
- 平台已经针对已知不同的情况,提供了对应的界面组件
可以比较明显地看到,有3个必输字段。
提供两个工具类,区别为是否基于Spring提供的容器内MessageResource机制:
- com.lirong.eap.platform.pub.validation.BeanBasedValidatorUtils:容器外校验工具。
- com.lirong.eap.platform.pub.validation.SpringBasedValidatorUtils:容器内校验工具,fxEAP默认使用。
共有3个必输只输入2个,并进行保存操作:
/**
* 买入价 BigDecimal
*/
public static final String BUYING_RATE_PROPERTY_CODE = "buyingRate";
public static final String BUYING_RATE_PROPERTY_NAME = "买入价"; // $NON-NLS$
@PropertyName(BUYING_RATE_PROPERTY_NAME)
@NotNull(message = "{PLATFORM.SM.ExchangeDailyVO.BuyingRate.NotBlank}")
@DecimalMin("0.01")
@DecimalMax("9999999999.99")
private BigDecimal buyingRate;
buyingRate字段,不允许为空,限定了最小值、最大值。
为同时验证国际化机制是否有效,以下测试基于en_US环境进行。
@Data
class DemoBigDecimal {
/**
* 买入价 BigDecimal
*/
public static final String BUYING_RATE_PROPERTY_NAME = "买入价"; // $NON-NLS$
@PropertyName(BUYING_RATE_PROPERTY_NAME)
@NotNull(message = "{PLATFORM.SM.ExchangeDailyVO.BuyingRate.NotBlank}")
@DecimalMin("0.01")
@DecimalMax("9999999999.99")
private BigDecimal buyingRate;
}
@Test
public void testNotNull() {
DemoBigDecimal vo = new DemoBigDecimal();
final String strValidation = BeanBasedValidatorUtils.getInstance().validateEntity(vo);
if (StringUtils.isNotBlank(strValidation)) {
throw new RuntimeException(strValidation);
}
}
java.lang.RuntimeException:
null : DemoBigDecimal.buyingRate must be not blank;
@Test
public void testRange() {
DemoBigDecimal vo = new DemoBigDecimal();
vo.setBuyingRate(new BigDecimal("-1"));
final String strValidation = BeanBasedValidatorUtils.getInstance().validateEntity(vo);
if (StringUtils.isNotBlank(strValidation)) {
throw new RuntimeException(strValidation);
}
}
java.lang.RuntimeException:
-1 : DemoBigDecimal.must be greater than or equal to 0.01
package com.lirong.eap.test.validation;
import com.lirong.eap.platform.pub.validation.BeanBasedValidatorUtils;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.javers.core.metamodel.annotation.PropertyName;
import org.junit.BeforeClass;
import org.junit.Test;
import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.Locale;
/**
* <p>Title: LiRong Java Enterprise Application Platform</p>
* <p>Description: </p>
* Copyright: CorpRights lrJAP.com<br>
* Company: lrJAP.com<br>
*
* @author jianjun.yu
* @version 3.0.0-SNAPSHOT
* @date 2022-05-31
* @since 1.0.0-SNAPSHOT
*/
public class HelloBigDecimalValidation {
/**
* 测试测试时使用en_US语言环境
*/
@BeforeClass
public static void init() {
Locale.setDefault(Locale.US);
}
/**
* 非空测试
*/
@Test
public void testNotNull() {
DemoBigDecimal vo = new DemoBigDecimal();
final String strValidation = BeanBasedValidatorUtils.getInstance().validateEntity(vo);
if (StringUtils.isNotBlank(strValidation)) {
throw new RuntimeException(strValidation);
}
}
/**
* 值越界测试
*/
@Test
public void testRange() {
DemoBigDecimal vo = new DemoBigDecimal();
vo.setBuyingRate(new BigDecimal("-1"));
final String strValidation = BeanBasedValidatorUtils.getInstance().validateEntity(vo);
if (StringUtils.isNotBlank(strValidation)) {
throw new RuntimeException(strValidation);
}
}
}
@Data
class DemoBigDecimal {
/**
* 买入价 BigDecimal
*/
public static final String BUYING_RATE_PROPERTY_NAME = "买入价"; // $NON-NLS$
@PropertyName(BUYING_RATE_PROPERTY_NAME)
@NotNull(message = "{PLATFORM.SM.ExchangeDailyVO.BuyingRate.NotBlank}")
@DecimalMin("0.01")
@DecimalMax("9999999999.99")
private BigDecimal buyingRate;
}