今天就来聊聊我捣鼓 `uniqueResult` 这玩意儿的经历。这东西,听起来挺厉害,说是能直接拿唯一结果,省事儿。
起因是这样的:
那阵子我在做一个小功能,需要根据一个特定的条件去数据库里捞一条记录出来。比如说,根据用户的邮箱地址查用户信息。按理说,邮箱这玩意儿在系统里应该是唯一的嘛一个邮箱对应一个用户,没毛病。当时我就想,这不正好嘛直接用 `uniqueResult` ,一步到位,代码还显得干净。
动手实践过程:
说干就干。我先是配置好 Hibernate,拿到 `Session` 对象,这都是常规操作。然后就开始写 HQL 查询语句,类似 `from User where email = :email` 这种。写完之后,创建 `Query` 对象,设置参数,信心满满地调用 `*()`。
本地跑跑,测试几次,效果真不错!输入一个存在的邮箱,唰一下用户对象就回来;输入一个不存在的邮箱,返回的是 `null`,我也做判断处理。当时心里还挺美,觉得这方法挺好使。
踩坑时刻来:
高高兴兴把代码提交上去,部署到测试环境。结果没过多久,QA 同学就找过来,说系统偶尔会报错。我赶紧去看日志,好家伙,一个刺眼的 `NonUniqueResultException` 异常怼在我脸上。啥意思?就是说,它查出来不止一条记录!
我当时就懵,说好的唯一?赶紧去数据库里查。查半天,发现问题所在。因为一些历史遗留数据,或者某个不太严谨的导入操作,数据库里竟然真的存在几条邮箱地址重复的用户记录!这下 `uniqueResult` 不干,它就认死理,说好一条就必须是一条,多就直接抛异常撂挑子。
咋解决的?
没办法,`uniqueResult` 这老哥太严格,容不得一点沙子。面对这种数据可能不“干净”的情况,用它风险太高。我只好把代码改。
改动也简单,就是不用 `uniqueResult()` ,改成用 `list()` 方法。`list()` 就不管那么多,查出来几条就返回几条,放一个列表里给你。
- 拿到这个列表后,我先判断它是不是空的(`null` 或者 `isEmpty()`)。
- 如果不为空,我再判断列表的大小(`size()`)。
- 如果大小是 1,那正就是我想要的唯一结果,取出第一个元素就行。
- 如果大小大于 1,那就说明数据有问题,得赶紧记录日志,或者通知管理员去处理这些重复数据,至少程序不能在这里崩掉。
- 如果大小是 0,那就说明没查到,按查不到的逻辑处理。
事后
这回实践让我明白几件事:
第一,`uniqueResult` 确实方便,但前提是你得百分百确定你的查询条件在数据库里绝对只会匹配到零条或一条记录。最好是有数据库唯一约束来保证。
第二,如果对数据的唯一性没那么大把握,或者系统里存在一些“脏数据”的可能,那还是老老实实用 `list()` 查询,然后自己写逻辑来判断结果数量,这样更稳妥,程序的健壮性也更高。
第三,别忘,就算是预期只有一条,`uniqueResult` 也可能返回 `null`(就是没查到的情况)。所以用它的时候,必须要做非空判断,不然等着你的就是 `NullPointerException`。
`uniqueResult` 是个好东西,但得看场景用。不能因为它看起来简洁就无脑上,不然关键时刻可能就给你挖个坑。这就算是我对 `uniqueResult` 的一点实践记录和体会,分享给大家,希望能少走点弯路。