Spring Boot 集成 MyBatis 时因注解误用导致删除操作返回空指针的问题分析
在使用 Spring Boot 结合 MyBatis 进行数据库操作时,一个常见的细节错误可能导致意想不到的结果。本文将分析一个真实场景:数据成功删除,但接口却返回空指针异常。
问题现象
调用删除接口后,数据库记录已被成功移除,但服务端抛出 NullPointerException,而非预期的受影响行数。
错误复现
以下展示问题代码的关键部分:
Controller 层
@RestController
@RequestMapping("/items")
public class ItemController {
@Autowired
private ItemService itemService;
@DeleteMapping("/{itemId}")
public ResponseEntity<String> removeItem(@PathVariable Integer itemId) {
int affectedRows = itemService.removeById(itemId);
return ResponseEntity.ok("删除成功,影响行数:" + affectedRows);
}
}
Service 层
public interface ItemService {
int removeById(Integer id);
}
@Service
public class ItemServiceImpl implements ItemService {
@Autowired
private ItemMapper itemMapper;
@Override
public int removeById(Integer id) {
return itemMapper.removeById(id);
}
}
Mapper 层(问题所在)
@Mapper
public interface ItemMapper {
// 错误:使用了 @Select 注解执行 DELETE 语句
@Select("DELETE FROM t_item WHERE item_id = #{id}")
Integer removeById(int id);
}
根因分析
MyBatis 的注解具有语义区分:@Select 用于查询操作,预期返回结果集;@Delete 用于删除操作,预期返回受影响行数。当使用 @Select 执行 DELETE 语句时,MyBatis 底层仍按查询逻辑处理,尝试将结果映射为结果集而非更新计数,导致返回 null,进而引发空指针。
修正方案
将 @Select 替换为正确的 @Delete 注解:
@Mapper
public interface ItemMapper {
@Delete("DELETE FROM t_item WHERE item_id = #{id}")
int removeById(int id);
}
验证结果
修正后重新调用接口,返回预期结果:
删除成功,影响行数:1
扩展建议
为避免类似问题,建议:
- 严格遵循 SQL 操作与 MyBatis 注解的对应关系:
@Select、@Insert、@Update、@Delete - 在 Mapper 接口中使用基本类型
int而非包装类型Integer作为返回值,可在编译层面减少空值风险 - 考虑采用 XML 配置方式替代注解,尤其对于复杂 SQL,便于统一管理和审查
- 启用 IDE 的 SQL 语法检查插件,对注解中的 SQL 语句进行静态分析
XML 方式参考实现
@Mapper
public interface ItemMapper {
int removeById(int id);
}
<?xml version="1.0" encoding="UTF-8"? >
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.ItemMapper">
<delete id="removeById" parameterType="int">
DELETE FROM t_item WHERE item_id = #{id}
</delete>
</mapper>