一、需求:
产品要求对所有接口增加校验,包括不能为空和长度校验,避免字段长度过长而插入数据库的时候报错。
不为空的校验我们可以通过validation-api的注解可以实现
@NotBlank(message = "X名称不能为空")
但是长度的校验用起来发现不是很理想,而且有一定的配置量,还存在数据库发生更改的时候程序也相应的做修改。
于是打算开发自动校验的工具。
二、思路:
1、根据SQL查询表信息。
SELECT column_name AS cname, data_type AS dtype, character_maximum_length AS cmax,column_comment AS ccomment
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = ?
我们可以得到字段名称、字段类型、字段最大长度、注释
2、通过反射获取字段的属性名称和值。
3、根据表配置对字段的属性值进行校验。
三、实现:
直接上代码
@Component
public class Validation {
private static Pattern humpPattern = Pattern.compile("[A-Z]");
@Autowired
JdbcTemplate jdbcTemplate;
/**
* 自动校验字段长度
* @param o
* @return
*/
public String autoSize(Object o){
if(o == null){
return null;
}
String table = getTableName(o);
if(table == null || table.length() == 0){
return null;
}
Map<String,FieldInfo> fieldInfos = getTableFieldsInfo(table);
if(fieldInfos == null || fieldInfos.size() == 0){
return null;
}
Field[] fields = o.getClass().getDeclaredFields();
if(fields == null || fields.length == 0){
return null;
}
for(Field field : fields){
String msg = checkField(field, fieldInfos, o);
if(msg != null){
return msg;
}
}
return null;
}
private String checkField(Field field, Map<String,FieldInfo> fieldInfos, Object o){
if(field == null){
return null;
}
try {
/**如果是varchar再做校验*/
String fieldTypeName = field.getType().getName();
if(!String.class.getName().equals(fieldTypeName)){
return null;
}
String fieldName = field.getName();
String tableName = camelTounderline(fieldName);
FieldInfo fieldInfo = fieldInfos.get(tableName.toLowerCase());
if(fieldInfo == null){
return null;
}
String formName = fieldName;
if(fieldInfo.getCcomment() != null && fieldInfo.getCcomment().length() > 0){
formName = fieldInfo.getCcomment();
}
field.setAccessible(true);
String fieldValue = null;
Object fieldValueObject = field.get(o);
if(fieldValueObject != null){
fieldValue = field.get(o).toString();
}
boolean isNullValue = fieldValue == null || fieldValue.length() == 0;
if(isNullValue){
return null;
}
int valueLen = fieldValue.length();
if(fieldInfo.getCmax() != null && //
valueLen > fieldInfo.getCmax()){//
return formName + "字段长度不得超过" + fieldInfo.getCmax();
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public static String camelTounderline(String str) {
Matcher matcher = humpPattern.matcher(str);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
}
matcher.appendTail(sb);
return sb.toString();
}
private Map<String,FieldInfo> getTableFieldsInfo(String tableName){
String sql = "SELECT column_name AS cname, data_type AS dtype, character_maximum_length AS cmax,is_nullable AS nullable, column_comment AS ccomment " +
"FROM INFORMATION_SCHEMA.COLUMNS " +
"WHERE table_name = ?";
RowMapper<FieldInfo> rowMapper = new BeanPropertyRowMapper<>(FieldInfo.class);
List<FieldInfo> fieldInfosList = jdbcTemplate.query(sql, new Object[]{tableName}, rowMapper);
if(fieldInfosList == null || fieldInfosList.size() == 0){
return null;
}
Map<String,FieldInfo> fieldInfos = new HashMap<>();
fieldInfosList.forEach(fi->{
String name = fi.getCname();
if(name != null && name.length() > 0){
fieldInfos.put(name.toLowerCase(), fi);
}
});
return fieldInfos;
}
private String getTableName(Object o){
if(o == null){
return null;
}
Annotation[] annotations = o.getClass().getAnnotations();
if(annotations == null || annotations.length == 0){
return null;
}
for(Annotation a: annotations){
if(a instanceof TableName){
return ((TableName) a).value();
}
}
return null;
}
}
四、使用时的注意点:
1、用到了一个注解来获取表名称
2、表字段是下划线分割命名方式、实体是驼峰命名方式,需要转换来映射。
网友评论