自定义注解校验Oracle数据库字段设计为varchar2类型字段长度校验(LENGTHB函数值)

前言:

对于Oracle数据库varchar2类型的字段设计的字段,存储中文时一个汉字占3个字符,而数字、字母、符号占1个字符。导致接口校验字段内容长度时不方便,因此编写自定义注解用于校验这类字段的内容长度。

举个栗子:

类型说明
VARCHAR2(50 CHAR)按字符计数,可存储50个汉字
VARCHAR2(50)按字节计数,根据字符集不同,gbk可存25个汉字,utf8可存16个汉字
函数说明
lengthb(string)计算string所占的字节长度:返回字符串的长度,单位是字节
length(string)计算string所占的字符长度:返回字符串的长度,单位是字符

我们可以建表测试两种字段类型的区别:

create table tb_test_length(
    char1 varchar2(20),
    char2 varchar2(20 char)
);

所建表的长度均为20,不过char1最多只能存储6个汉字,而char2能存储最多20个汉字:

insert into tb_test_length values ('一二三四五六', '一二三四五六');
insert into tb_test_length values ('一二三四五六', '一二三四五六七');
insert into tb_test_length values ('一二三四五六七', '一二三四五六七'); -- 插入这条数据会报错

报错截图
使用lengthb函数查看成功插入数据char2的字节数:

select t.CHAR2, LENGTHB (t.CHAR2) from tb_test_length t where CHAR1 = '一二三四五六';

在这里插入图片描述


JSR-303定义的长度检查注解

注解说明
@Size(min=, max=)验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=, max=)Validates that the annotated string is between min and max included.

以上两个注解 @Size@Length均可用于String类型的字段长度校验,不过都是字符级别的长度检查。针对VARCHAR2(50)这种类型的数据表字段值的长度校验是有问题的。

于是我们写一个自定义注解 @ValiStringByte

import javax.validation.Constraint;
import java.lang.annotation.*;

@Constraint(validatedBy = {ValiStringByteValidator.class})
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValiStringByte {
	// 默认提示信息
    String message() default "{Character bytes are extremely long!}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
	
	// 最大字节数,可以自行调整
    int max() default 2147483647;
}

还要为 @ValiStringByte 注解实现校验逻辑,实现ConstraintValidator接口,重写isValid方法:

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class ValiStringByteValidator implements ConstraintValidator<ValiStringByte, String> {

    private int max = 0;

    public void initialize(ValiStringByte valiStringByte) {
        max = valiStringByte.max();
    }

    /**
     * 重写校验逻辑
     *
     * @param string
     * @param constraintValidatorContext
     * @return
     */
    @Override
    public boolean isValid(String string, ConstraintValidatorContext constraintValidatorContext) {
        if (null != string) {
            int total = count(string);
            return total <= max;
        } else {
            return true;
        }
    }

    /**
     * 统计参数的字节数
     *
     * @param str
     * @return
     */
    private static Integer count(String str) {
        int total = 0; // 字节总数

        if (null == str || str.equals("")) {
            return total;
        }

        for (int i = 0; i < str.length(); i++) {
            char tmp = str.charAt(i);
            if ((tmp >= 'A' && tmp <= 'Z') || (tmp >= 'a' && tmp <= 'z')) {
                total++;
            } else if ((tmp >= '0') && (tmp <= '9')) {
                total++;
            } else if (tmp == ' ') {
                total++;
            } else if (isChinese(tmp)) {
                total += 3;
            } else {
                total++;
            }
        }
        return total;
    }

    /**
     * 判断字符是否为汉字字符
     *
     * @param ch
     * @return
     */
    private static boolean isChinese(char ch) {
        Character.UnicodeBlock ub = Character.UnicodeBlock.of(ch);
        return ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
                || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
                || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS
                || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION;
    }
}

测试:

@Data
public class ParamBean {
	// 校验字段值字节数最长为20
    @ValiStringByte(max = 20)
    private String text;
}
@PostMapping(path = "/test")
public void test(@RequestBody @Validated ParamBean bean) {
    logger.info(bean.toString());
}

调用接口:


POST 127.0.0.1:8080/test  
param: {"text": "一二三四五六12" }     一共20个Byte

控制台正常打印:
在这里插入图片描述


POST 127.0.0.1:8080/test  
param: {"text": "一二三四五六123" }     一共21个Byte

控制台打印报错,说明能成功校验:
org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument at index 0 in method: public void TestController.test(ParamBean), with 1 error(s): [Field error in object ‘paramBean’ on field ‘text’: rejected value [一二三四五六123]; codes [ValiStringByte.paramBean.text,ValiStringByte.text,ValiStringByte.java.lang.String,ValiStringByte]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [paramBean.text,text]; arguments []; default message [text],20]; default message [{Character bytes are extremely long!}]] …

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值