使用Long类型intValue()方法引发的错误
最近遇到一个问题,在用公司平台提供的save
方法保存数据后,主键值会发生变化,导致程序出错。而这个错误是偶发的,只是在压力测试并发时偶尔会出现。
- 场景如下:
Model model = new Model();
long id = IdGenerator.generateLongId();
model.setId(id);
model.setXxx(xxx);
doSomething();
//调用平台提供的保存方法
PlantformUtils.save(model);
//此方法中会用id去查询,但查不到数据
doOtherThings(id);
首先排查我自己程序中修改model.id的地方,通过日志分析没有发现异常,后来发现是在调用平台的save
方法后,model的id发生了变化,导致数据库中插入的model的id并不是我当初设置的值。
平台的save
方法中有一处判断,大概如下:
...
//取Model的主键对象,看是否为空,如果为空则重新设置
Object pkValue = getPKValue(model);
if(pkValue == null
|| (pkValue instanceof String && pkValue.toString().trim().length() == 0)
|| (pkValue instanceof Number && ((Number)pkValue).intValue() == 0) ){
//重新设置model的主键
setPKValue(model);
}
...
通过断点调试,发现有少数的pkValue不为空也不为0时进入了此if条件中,将能进入if中的pkValue与正确的pkValue作了对比,结果如下:
//不正确的
Object pkValue = 293344286336876544L;
System.out.println(((Number)pkValue).intValue());//0
//正确的
pkValue = 293347735933812736L;
System.out.println(((Number)pkValue).intValue());//738197504
java.long.Long中intValue的代码如下:
public int intValue() {
return (int)value;
}
- 原因:
Java中long类型占8个字节,int类型占4个字节,将long转为int时,是将long类型值先转为二进制,然后从低位到高位截32位:
Long:293344286336876544
对应的二进制为:10000010010001010110001001100000000000000000000000000000000
从低位到高位截取32位:00000000000000000000000000000000
Int:0
Long:293347735933812736
对应的二进制为:10000010010001011100011011000101100000000000000000000000000
从低位到高位截取32位:00101100000000000000000000000000
Int:738197504
也就是当Long类型数的低位4个字节每一位都为0时,它的intValue值就为0,所以才会在并发时出现此问题。