Java Split():轻视limit参数带来的Bug

Java Split():轻视limit参数带来的Bug

编码文章call10242025-02-01 4:12:4014A+A-

前两天在排查个bug,问题的根节倒是不复杂,但整个过程也是小有收获。

埋雷

数据库实体中有这么个字段:

@Convert(converter = MapConverter.class)
private Map links;

MapConverter的实现

public class MapConverter implements AttributeConverter, String> {
    @Override
    public String convertToDatabaseColumn(Map strMap) {
        return strMap == null ? null : strMap.toString();
    }

    @Override
    public Map convertToEntityAttribute(String s) {
		// 空值处理
        try {
            Map map = new IdentityHashMap<>();
            // DB的列存的是字符串,形如 {key1=val1, key2=val2}, 所以这里去掉首尾的大括号,然后切分
            String string = s.substring(1, s.length() - 1);
            String[] list = string.split(",");

            for (String str : list) {
                String[] con = str.trim().split("=");
                map.put(con[0], con[1]);
            }
            return map;
        } catch (Exception e) {
            ...
        }
		...
    }
}

在这个ORM模型中,MapConverter定义了将对象中的Map结构如何存储到数据库的列中,以及如何从列解析到对象中。

踩雷

在生产环境,发现存入:

strMap.put("toutiao", "https://www.toutiao.com?key1=val&key2=val2%key3=val3")
strMap.put("xiaomi", "https://www.mi.com")

读取出来的toutiao链接仅为"https://www.toutiao.com?key1"

而且在存入DB接口,返回结果是正常的;查询结果就是不正常,而且更新其他字段后,发现DB中存的也仅仅是"{toutiao=https://www.toutiao.com?key1, ...}".

在排除了缓存等原因之后,将目光聚焦到MapConverter上,但是:convertToDatabaseColumn方法,不可能存在截断处理,那为什么DB中存的是不完整的链接了?

难不成convertToEntityAttribute这个负责读取DB内容进行解析映射的方法能兴风作浪?

屏气读到:

String[] con = str.trim().split("=");

会发现,split没有填入任何参数,这意味着切分所用的pattern将被尽可能多的执行,所以它取con[1]只能取到"https://www.toutiao.com?key1",这能解释了读取的问题

但是它如何影响DB中的存储结果了?

因为用的是ORM嘛

当你更新时,先查询这个对象,这个时候,会用到"convertToEntityAttribute", 它已经背弃你的初衷,返回了一个错误的结果;然后更新实体对象,持久化到DB中,这时DB的结果自然也是错误的。

清雷

那如何解决了?

造轮子这种事情还是要谨慎的,如果可以交给Jackson去转换,可以大大降低这种各种边际情况的发生吧

那非要自己切分了?split的limit的请了解一下:

split其实就是用正则表达式去匹配提取,它的另一个参数limit常常会被忽视,但确实非常重要的参数。

public String[] split(String regex, int limit)
regex?-- 这是在限定的正则表达式。
limit?-- 这个控制模式施加的数的次数,因此,会影响所得到的数组的长度

总的来说:

  • 如果 n < 0,那么模式将被应用尽可能多的次数,而且数组可以是任何长度。
  • 如果 n = 0,那么模式将被应用尽可能多的次数,数组可以是任何长度,并且结尾空字符串将被丢弃。
  • 如果 n > 0,则模式将被最多应用 n - 1 次,数组的长度将不会大于 n,而且数组的最后一项将包含所有超出最后匹配的定界符的输入。

应用在本处, 上述可改成如下(记得在放进map的时候,加上NPE判断等)

String[] con = str.trim().split("=", 2);

点击这里复制本文地址 以上内容由文彬编程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

文彬编程网 © All Rights Reserved.  蜀ICP备2024111239号-4