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

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

作者: 和平菌 | 来源:发表于2020-02-27 11:55 被阅读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、表字段是下划线分割命名方式、实体是驼峰命名方式,需要转换来映射。

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/uzdthhtx.html