前言
在后端开发中,特别是当我们使用Spring Boot配合MySQL等关系型数据库时,经常会遇到一个看似微小但实则令人纠结的问题: "当用户的某个字符串字段没有填写时,数据库里到底该存
NULL还是存空字符串''?" 今天我们就结合Spring Boot项目实例,来聊聊这个话题。
一、 先搞清楚:它们到底有什么区别?
在决定用谁之前,我们必须先通过一个通俗的例子来看看这两者的本质区别。 假设我们有一张用户表 user,其中有一个字段是 nickname(昵称)。
- NULL :代表"未知"或"不存在"。
- 意思是:"我不知道这个用户的昵称是什么,或者用户压根没填。"
- 它是一个占位符,表示数据的缺失。
- 空字符串
'':代表"确定的空值"。- 意思是:"我知道这个用户的昵称是什么,它就是一个空的,长度为0的字符串。"
- 它是一个具体的值 。 生活中的类比:
- NULL 就像是一个还没拆封的问卷,关于"姓名"那一栏是一片空白,你不知道对方是忘了写还是不想写。
- 空字符串 就像对方把问卷交上来了,但在"姓名"那一栏画了一个横线"------",明确告诉你"我不想填"。
二、 结合Spring Boot实战:谁更"省心"?
作为开发者,我们不仅关心理论,更关心写代码时会不会给自己挖坑。让我们看看在Spring Boot项目中,这两种选择会带来什么不同。
1. 避免 NullPointerException 的噩梦
如果你的数据库存的是 NULL,在Java代码中处理不当,极易引发空指针异常。 场景: 判断用户的昵称是否为空。 如果存的是 NULL:
java
// 从数据库查出来的 user 对象
User user = userMapper.selectById(1L);
// 这里的 nickname 可能是 null
if (user.getNickname().isEmpty()) {
// ...
// 如果 nickname 是 null,调用 .isEmpty() 直接报错!NullPointerException!
}
为了安全,你不得不写出这样防御性的代码:
java
// 丑陋的防御性代码
if (user.getNickname() != null && user.getNickname().isEmpty()) {
// ...
}
// 或者使用工具类
if (StringUtils.isEmpty(user.getNickname())) {
// ...
}
如果存的是空字符串 '':
java
// 数据库查出来的 nickname 是 ""
if (user.getNickname().isEmpty()) {
// 安全!不会报错,逻辑正常执行
System.out.println("昵称为空");
}
结论: 使用空字符串 '',可以减少代码中大量的 null 检查,让代码更简洁,减少报错风险。
2. MyBatis 查询的"坑"
在使用 MyBatis-Plus 或 XML 写查询条件时,NULL 和空字符串的处理方式完全不同。 场景: 查询所有没有设置昵称的用户。 如果存的是 NULL: SQL语句必须写成:
sql
SELECT * FROM user WHERE nickname IS NULL;
如果你写成 WHERE nickname = '',你一条数据也查不到。而且在 MyBatis XML 中,判断 NULL 需要格外小心:
xml
<if test="nickname != null">
AND nickname = #{nickname}
</if>
<!-- 如果要查空的,通常还得单独处理 -->
如果存的是空字符串 '': SQL语句非常统一:
sql
SELECT * FROM user WHERE nickname = '';
这就和你查询普通数据(比如 WHERE nickname = '张三')的逻辑完全一致,不仅符合直觉,而且在拼接动态 SQL 时更加方便。
3. 前端交互与 JSON 序列化
在 Spring Boot 中,我们通常使用 Jackson 进行 JSON 序列化返回给前端。 如果存的是 NULL: 返回的 JSON 可能是这样的:
json
{
"id": 1,
"nickname": null
}
前端同学可能会跑来问你:"为什么这个字段是 null?我还要多写一层判断,能不能给我个空字符串?"或者,如果配置了 @JsonInclude(JsonInclude.Include.NON_NULL),这个字段直接就在 JSON 里消失了,前端还得处理字段丢失的情况。 如果存的是空字符串 '': 返回的 JSON 是这样的:
json
{
"id": 1,
"nickname": ""
}
前端接收到的数据类型永远是 String,不需要做 null 兼容,处理起来非常顺滑。
三、 什么时候应该坚持使用 NULL?
看了上面的例子,你可能会觉得:"既然空字符串这么好用,那是不是要把所有的 varchar 都设为默认空字符串?" 非也! 在以下场景中,NULL 才是正解。
1. 需要区分"没填"和"填了空的"
假设有一个 remark(备注)字段。
- 用户提交了表单,但备注栏没写东西 -> 存
''。 - 系统自动生成的数据,根本就没有备注这个概念 -> 存
NULL。 如果你需要在业务上严格区分"用户主动清空了备注"和"系统还没录入备注",那么NULL是必须的。
2. 索引与统计函数
虽然 MySQL 现在的版本优化了很多,但在某些老旧版本或特定数据库中,索引列如果存储大量空字符串 '',可能会导致索引效率问题。而 NULL 值通常不被计入索引(取决于数据库实现)。 另外,在使用 COUNT() 函数时:
COUNT(nickname):如果全是 NULL,结果是 0;空字符串则会被计入统计。 这会直接影响你的报表统计逻辑。
四、 总结与最佳实践建议
说了这么多,到底该怎么选?这里给出一个简单粗暴的建议,适用于绝大多数 Spring Boot 业务开发:
推荐方案:默认使用空字符串 ''
对于绝大多数 varchar 、text 类型的业务字段(如姓名、地址、描述、电话等):
- 数据库默认值设为
''(空字符串)。 - 字段定义设为
NOT NULL。
理由:
- 代码更健壮 :告别烦人的
NullPointerException。 - SQL 更简单 :统一使用
=和!=查询,不需要区分IS NULL。 - 前后端交互更友好:类型统一,前端少报错。
结语
技术选型没有绝对的对错,只有适不适合。 但为了团队协作的效率和代码的可维护性,"varchar 字段默认空字符串,NOT NULL" 确实能在多数情况下少踩些坑。