使用反射和对数据库表信息的查询来实现接口的自动校验

2020-02-27  本文已影响0人  和平菌

一、需求:
产品要求对所有接口增加校验,包括不能为空和长度校验,避免字段长度过长而插入数据库的时候报错。

不为空的校验我们可以通过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、表字段是下划线分割命名方式、实体是驼峰命名方式,需要转换来映射。

上一篇 下一篇

猜你喜欢

热点阅读