前两天在排查个bug,问题的根节倒是不复杂,但整个过程也是小有收获。
埋雷
数据库实体中有这么个字段:
@Convert(converter = MapConverter.class)
private Map links;
MapConverter的实现
public class MapConverter implements AttributeConverter
在这个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);