一、前言(业务常见痛点)
开发中对接 SQL Server 数据库时,绝大多数人都会遇到一个经典坑:
明明前端输入字符数没超,入库却报字段超长、数据被截断,本地测试正常,服务器Linux环境必现异常。
核心原因:SQL Server 的 varchar 是字节限制,不是字符限制,且默认中文排序规则编码和Java默认编码不统一,导致前端、Java校验、数据库存储三方长度判定不一致。
本文基于最常用的数据库字段定义:
[YDJH] varchar(50) COLLATE Chinese_PRC_CI_AS NULL手把手讲透编码原理、字节占用规则、Java正确校验方案,彻底根治超长报错问题。
二、SQL Server 核心编码规则(必看)
2.1 Chinese_PRC_CI_AS 底层编码
SQL Server 中国大陆默认排序规则Chinese_PRC_CI_AS,对应的底层代码页为CP936(GBK编码),这是行业通用标准。
编码字节占用规则:
英文、数字、半角符号:1字节/个
中文汉字、全角符号:2字节/个
2.2 varchar(50) 真实存储容量
重点:varchar(n) 代表最大 n 字节,不是 n 个字符。
针对 varchar(50):
纯中文场景:最多存储25个汉字(50/2)
混合场景:汉字+英文/数字混合,按实际字节累加
超出50字节:数据库直接截断数据或抛出超长异常
2.3 新旧排序规则区别(拓展避坑)
Chinese_PRC_CI_AS(老版):纯 GBK 编码,无生僻字支持Chinese_PRC_90/100/130_CI_AS(新版):底层 GB18030,兼容GBK,支持生僻字(部分生僻字占4字节)
普通业务场景,两种规则用GBK校验完全够用。
2.4 区分 nvarchar 关键知识点
很多开发者混淆两类字段,这里明确区分:
varchar:非Unicode,GBK编码,按字节校验
nvarchar:Unicode编码(UTF-16LE),中英文均2字节,直接按字符长度 str.length() 校验
三、Java 校验常见致命错误
3.1 错误1:使用无参 getBytes()
// 绝对禁止!跨环境失效 byte[] bytes = str.getBytes();无参方法依赖操作系统默认编码:
Windows:默认GBK,侥幸正常
Linux/Mac/服务器:默认UTF-8(中文3字节/个)
后果:服务器端计算字节数远大于数据库实际字节数,出现明明没超长,却被Java误判拦截,或校验失效导致入库报错。
3.2 错误2:使用 StandardCharsets 常量校验
很多同学想使用JDK自带的StandardCharsets,但发现里面没有GBK常量!
StandardCharsets 仅支持:US_ASCII、ISO_8859_1、UTF-8、UTF-16系列,不包含GBK/GB18030。
严禁用 UTF-8 校验本字段:UTF-8中文3字节,和SQL Server GBK字节规则完全不匹配,100%误判。
四、Java 正确精准校验方案(可直接复用)
4.1 核心原理
必须手动指定GBK编码,让Java计算的字节数完全对齐 SQL Server DATALENGTH()结果。
4.2 完整工具类(长度校验+自动截断)
import java.nio.charset.Charset; import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.UnsupportedCharsetException; /** * SQL Server varchar(GBK) 字段长度校验工具 * 适配排序规则:Chinese_PRC_CI_AS */ public class SqlVarcharCheckUtil { /** * 获取字符串GBK字节长度(与数据库DATALENGTH完全一致) * @param str 待校验字符串 * @return GBK字节数 */ public static int getGbkByteLength(String str) { if (str == null || str.isEmpty()) { return 0; } try { Charset gbkCharset = Charset.forName("GBK"); return str.getBytes(gbkCharset).length; } catch (IllegalCharsetNameException | UnsupportedCharsetException e) { throw new RuntimeException("当前JDK环境不支持GBK编码,数据库字段校验失败", e); } } /** * 校验是否符合 varchar(50) 字节规范 * @param text 待校验文本 * @return true=合规,false=超长 */ public static boolean isVarchar50Valid(String text) { if (text == null) { return true; } return getGbkByteLength(text) <= 50; } /** * 自动截断至varchar(50)最大字节,避免入库报错 * @param val 原始文本 * @return 截断后合规文本 */ public static String trimToVarchar50(String val) { if (val == null || isVarchar50Valid(val)) { return val; } // 逐字符截断,保证GBK字节不超50 int index = 0; while (index < val.length() && getGbkByteLength(val.substring(0, index + 1)) <= 50) { index++; } return val.substring(0, index); } }4.3 数据库校验对照SQL
可通过以下SQL验证Java结果准确性,两者数值完全一致:
-- 查看字段实际占用字节数 SELECT DATALENGTH(YDJH) AS 实际字节数, LEN(YDJH) AS 字符数 FROM 表名DATALENGTH:真实存储字节数(和Java GBK计算结果一致)LEN:纯字符个数,无参考意义
五、全文核心总结(干货速记)
SQL Server varchar(50) + Chinese_PRC_CI_AS:GBK编码,最大50字节,最多25个纯汉字。
禁止使用无参 getBytes():跨环境编码不一致,必出BUG。
StandardCharsets无GBK:必须通过
Charset.forName("GBK")获取编码。varchar按字节校验,nvarchar按字符校验,切勿混淆。
服务器Linux环境必须指定GBK,否则UTF-8三字节中文会导致校验完全失效。
六、拓展建议
如果业务需要支持生僻字、全量中文兼容,可将编码替换为GB18030,仅需修改代码中编码名称,适配新版SQL Server排序规则,兼容性更强。