Oracle坑之-空字符串与NULL

本文探讨了Oracle数据库中空字符串('')被视为NULL的问题,并解释了如何通过转换为特殊值或使用IS NULL来解决这一问题。同时提供了多个测试案例及注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Oracle坑之-空字符串与NULL

空字符串与NULL

首先有如下代码

SELECT *
  FROM Pdc_DataDomain DD
INNER JOIN Pdc_DD_Table DDT ON DD.DataDomainID = DDT.DataDomainID AND DD.ApplicationDBID = '3e7c6764d73f4c7786c99e5b72eb6912' AND DDT.TableName <> '#' AND NOT EXISTS (SELECT * FROM pdc_Application_3e7c6764 A WHERE A.TableName = DDT.TableName AND A.FieldName = DDT.FieldName AND A.FieldType = DDT.FieldType AND nvl(A.FieldLength, '') = nvl(DDT.FieldLength, '') AND nvl(A.FieldScale, '') = nvl(DDT.FieldScale, '')) 

核心重点在这句AND nvl(A.FieldScale, '') = nvl(DDT.FieldScale, '')上,这句当A的FieldScale和DDT的FieldScale都为空的时候,会出现两者nvl后都返回'',但是Oracle的特性是''会被处理为null,而null不等于任何值,包括自己,,那么就over了,如果出现上面这种应该如何处理呢,很简单,转化的时候可以将其转化为一个程序中不会出现的值即可,如果程序逻辑没有不会出现的值,就只有使用isnull来判断了.

以下是参考资料:

-- oracle 将 空字符串即''当成null,测试脚本如下:

select nvl(null,-1) from dual; sele'','-1') from dual; select nvl(nvl(null,''),-1) from dual; select nvl(trim(' '),'-1') from dual;ct nvl( 

-- 但是要记住,null 与任何值做逻辑运算得结果都为 false,包括和null本身:

select nvl(max('1'),-1) from dual where null = ''; select nvl(max('1'),-1) from dual where null <> ''; select nvl(max('1'),-1) from dual where null = '-1'; select nvl(max('1'),-1) from dual where null <> '-1'; select nvl(max('1'),-1) from dual where null = null; select nvl(max('1'),-1) from dual where null <> null; 

-- 不过,用 is null 判断时,空字符串和 null 都 is null:

select nvl(max('1'),-1) from dual where '' is null; select nvl(max('1'),-1) from dual where null is null; 

-- 还要记住,null 和任何数值进行数学运算,结果都为 null:

select nvl(null + 0,-1) from dual; 

-- 不过,可以使用 || 将空字符串或 null 和字符串连接

select 'a' || null || 'b' from dual; 

-- 另,作为以下的 Oracle 内置的函数的参数时,结果也为 null:

select nvl(length(null),-1) from dual; select nvl(trim(null),-1) from dual; select nvl(ltrim(null),'-1') from dual; select nvl(rtrim(null),'-1') from dual; select nvl(rtrim(null,' '),'-1') from dual; select nvl(soundex(null),'-1') from dual; select nvl(SubStr(null,1),-1) from dual; select nvl(InStr(null,1),-1) from dual; select nvl(replace(null,'a','b'),'-1') from dual; select nvl(min(null),'-1') from dual; select nvl(max(null),'-1') from dual; select nvl(sum(null),'-1') from dual; select nvl(avg(null),'-1') from dual; select nvl(sum(null),'-1') from dual; 

-- 不过,作为以下的 Oracle 内置的函数的参数时,结果不为null:

select concat('a', null) from dual; select concat(null, 'a') from dual; select count(null) from dual; 

-- 其他函数用的时候,也可以使用以上的方法测试

来源: http://edgenhuang.iteye.com/blog/975567

<think>嗯,用户现在想了解在SSM框架中使用不同数据库(Oracle、MySQL、PostgreSQL)时插入空字符NULL的区别及注意事项。这个问题看起来涉及到不同数据库对值和空字符的处理方式,以及如何在SSM框架中正确配置以避免问题。首先,我需要回顾一下各个数据库在这方面的特性。 首先,用户提到的三个数据库:Oracle、MySQL、PostgreSQL。我需要分别分析它们对空字符NULL的处理。根据之前的知识,Oracle会把空字符当作NULL处理,而MySQL和PostgreSQL则区分空字符NULL。但需要确认这一点是否正确,特别是PostgreSQL的最新版本是否有变化。比如,我记得PostgreSQL确实允许空字符NULL共存,而Oracle确实在隐式转换中将空字符转为NULL。 接下来,需要考虑在SSM框架中,特别是MyBatis,如何处理这些差异。例如,当Java中的字段为空字符时,MyBatis在生成SQL语句时,是插入空字符还是NULL?这可能涉及到MyBatis的配置以及数据库驱动程序的设置。比如,MySQL的驱动可能有参数来控制是否将空字符转换为NULL,比如jdbc:mysql://...?emptyStringsConvertToZero=false。 然后,用户提到使用SSM框架,即Spring、Spring MVC、MyBatis。需要考虑到各个层中的处理,例如Controller层接收到的参数可能包含空字符,Service层处理业务逻辑,DAO层通过MyBatis数据库交互。此时,如何确保数据在不同层之间正确传递,并且最终生成正确的SQL语句是关键。 另外,用户提供的引用中有提到MyBatis-Plus不能解决所有问题,可能需要手动编写SQL,这时候需要特别注意SQL语句中空字符NULL的正确使用。例如,在Oracle中插入空字符会被转为NULL,那么在编写插入语句时,如果希望插入NULL,可以直接插入空字符,但若需要明确插入NULL,可能需要特别处理。 接下来,需要总结各个数据库的区别: 1. Oracle空字符视为NULL,插入空字符会存储为NULL,查询空字符需要使用IS NULL。 2. MySQL:严格模式下,空字符NULL是不同的,非严格模式下可能允许某些字段为空字符即使定义为NOT NULL。 3. PostgreSQL:明确区分空字符NULL,字段是否允许空字符NULL取决于表定义。 然后,注意事项方面,需要考虑字段的约束(如NOT NULL)、默认值、索引的影响。例如,在Oracle中,如果字段设置为NOT NULL,尝试插入空字符会报错,因为空字符被转为NULL,而NULL违反NOT NULL约束。 在SSM框架中的配置,比如MyBatis的映射文件中,如何处理空字符。例如,使用<if test="field != null">或者<if test="field != ''">来判断是否插入值,或者使用动态SQL标签来避免插入不必要的NULL空字符。 另外,前端传递的数据可能包含空字符,需要后端验证和处理,确保符合数据库的约束。例如,在Spring MVC中,可以使用@NotBlank或@NotNull注解来验证参数,避免将无效数据传递到DAO层。 还需要考虑数据库驱动的配置参数,例如MySQL的jdbc参数,如allowMultiQueries、zeroDateTimeBehavior等,是否影响空字符的处理。 可能还需要提到测试的重要性,尤其是在不同数据库环境下测试插入操作,确保行为一致。例如,使用JUnit测试用例,针对不同数据库运行,验证空字符NULL的处理是否符合预期。 最后,总结最佳实践,比如在设计表结构时明确字段是否允许NULL空字符,统一框架层的处理逻辑,使用数据库约束和校验注解,以及进行充分的测试。</think>--- ### SSM框架中插入空字符NULL的差异及注意事项(Oracle/MySQL/PostgreSQL) --- #### 一、核心差异对比 | **行为** | Oracle 12c+ | MySQL 8.0 | PostgreSQL 15 | |-------------------|--------------------------|--------------------------|--------------------------| | **空字符存储** | 自动转换为NULL | 存储为真实空字符 | 存储为真实空字符 | | **NULL比较** | `'' IS NULL`返回true | `'' IS NULL`返回false | `'' IS NULL`返回false | | **字段约束冲突** | NOT NULL字段不能插入 | NOT NULL字段允许空字符 | NOT NULL字段允许空字符 | --- #### 二、各数据库具体表现 1. **Oracle** - **特性**:所有空字符(`''`)会被隐式转换为NULL[^1] - **插入示例**: ```sql INSERT INTO users(name) VALUES ('') -- 实际存储为NULL ``` - **查询陷阱**: ```sql SELECT * FROM users WHERE name = '' -- 无结果返回(需用IS NULL) ``` 2. **MySQL** - **模式影响**: ```sql -- 严格模式下(默认开启): CREATE TABLE test (col VARCHAR(10) NOT NULL); INSERT INTO test VALUES('') -- 成功(空字符NULL) INSERT INTO test VALUES(NULL) -- 违反约束 ``` 3. **PostgreSQL** - **明确区分**: ```sql CREATE TABLE demo ( id SERIAL PRIMARY KEY, empty_str VARCHAR(10) CHECK(empty_str != '') -- 禁止空字符但允许NULL ) ``` --- #### 三、SSM框架处理要点 1. **MyBatis映射控制** - **动态SQL处理**: ```xml <insert id="insertUser"> INSERT INTO users <trim prefix="(" suffix=")" suffixOverrides=","> <if test="name != null">name,</if> <!-- 同时过滤NULL空字符 --> <if test="name != null and name != ''">name,</if> <!-- 精确控制 --> </trim> VALUES (...) </insert> ``` - **类型处理器**: ```java @Override public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) { ps.setString(i, "".equals(parameter) ? null : parameter); // Oracle专用处理 } ``` 2. **Spring数据校验** - **DTO层验证**: ```java public class UserDTO { @NotBlank(message = "名称不能为") // 禁止空字符NULL private String name; @Nullable // 允许NULL但禁止空字符(需自定义校验器) private String description; } ``` 3. **驱动配置差异** - **MySQL连接参数**: ```properties # 关闭空字符NULL(默认true) jdbc.url=jdbc:mysql://host/db?emptyStringsConvertToZero=false ``` - **Oracle特殊处理**: ```java // 插入前强制转换 if (StringUtils.isEmpty(entity.getRemark())) { entity.setRemark(null); // 避免Oracle空字符NULL冲突 } ``` --- #### 四、跨数据库兼容方案 1. **统一字段设计规范** - 重要业务字段使用`NOT NULL DEFAULT ''`(MySQL/PostgreSQL) - 禁用Oracle的`VARCHAR2(0)`特殊类型[^3] 2. **SSM框架层抽象** ```java public abstract class BaseDao { protected void preProcessEmptyString(BaseEntity entity) { // Oracle环境专用处理 if (isOracleDB()) { Field[] fields = entity.getClass().getDeclaredFields(); for (Field field : fields) { if (field.getType() == String.class) { field.setAccessible(true); String value = (String) field.get(entity); if ("".equals(value)) field.set(entity, null); } } } } } ``` 3. **测试策略** - 边界测试用例: ```java @Test public void testInsertEmptyString() { User user = new User(); user.setName(""); // 空字符 userMapper.insert(user); // 验证不同数据库的实际存储值 assertThat(user.getName()).isEqualTo(isOracle ? null : ""); } ``` --- #### 五、典型问题解决方案 **场景1:Oracle中需要存储真实空字符** ```sql -- 使用特殊占位符(需应用层转换) ALTER TABLE products ADD (remark VARCHAR2(10) DEFAULT '[[EMPTY]]'); -- Java层处理 public String getRemark() { return "[[EMPTY]]".equals(this.remark) ? "" : this.remark; } ``` **场景2:MySQL严格模式下的历史数据迁移** ```sql -- 临时关闭约束 SET SESSION sql_mode = ''; INSERT INTO archive SELECT * FROM old_table WHERE col = ''; SET SESSION sql_mode = 'STRICT_TRANS_TABLES'; ``` --- #### 六、最佳实践总结 1. **设计阶段**明确字段是否需要区分`''`和`NULL` 2. **框架层**实现数据库类型自适应的值处理 3. **持久层**使用MyBatis的`<choose>`处理多数据库差异 4. **验证环节**增加数据库类型识别标记(通过`jdbcTemplate.getMetaData()`) 5. **文档标注**在字段注释中注明特殊处理要求 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值