今天聊一聊开发中经常踩到的一些坑。
1、直接使用查询出的对象进行更新
案例:
ClassSch classSch = classSchService.get(classSchId);
if (classSch == null) {
throw new BizException("课次信息不存在");
}
classSch.setStatus(ClassSch.Status.END.getValue());// 已结束
classSch.setUpdater(ReqThreadLocal.getUserName());
classSch.setUpdateTime(DateUtils.getCurrentTime());
j = classSchService.update(classSch);
if (j <= 0) {
throw new BizException("更新课次状态失败,请稍后再试");
}
这样做会存在2个问题,
第一个问题:假如你的代码中有乐观锁,而你此次的操作是不需要乐观锁的,那么这次的更新很可能失败,因为你查询出来的数据很可能已经被别人更新。
第二个问题:假如别人已经修改了你查询出来的数据,而你使用查询出的数据对象更新的时候会把别人已经更新的数据还原。
最好的方法是每次更新数据的时候new一个对象,把你要更新的数据赋值到新的对象中。如果存在乐观锁还可以根据选择是否使用乐观锁。
2、主从库数据,刚刚插入的数据查询不到。
在我们的数据量变大的时候,我们经常会用到主从数据库,但是有时候会出现刚刚插入的数据再次查询无法查询到的问题。这是什么原因造成的呢,答案是,当插入和查询不是在一个事物中的时候,我们插入的数据被保存到了主库,而查询访问的是从库,数据从主库同步到从库需要一定的时间,所以会出现刚刚插入的数据查询不到的问题。
那么怎么解决这样的问题呢?
第一种方案:把插入和查询操作放在同一个事物中,这样数据库默认会继续从主库读。
第二种方案:在查询的sql前面加上 /FORCE_MASTER/。案例如下:
<select id="getByOrderNo" parameterType="java.lang.String" resultMap="BaseResultMap">
<if test="isFromMaster != null and isFromMaster == true">
/*FORCE_MASTER*/
</if>
select
<include refid="Base_Column_List"/>
from order_info
where order_no = #{orderNo,jdbcType=VARCHAR}
and is_del = 0
limit 1
</select>
3、事务中包含异步消息,异步消息中查询不到刚刚插入的数据
有时候我们会遇到插入和查询明明是在一个事务中,只是查询操作是异步消息而已,但是我们就是查询不到刚刚插入的数据,我们会感到很奇怪,明明在一个事务,不存在主从库的问题,到底是什么原因呢?
原因其实很简单,因为事务还没有提交,所以在异步的消息中查询不到刚刚插入的数据
4、JSON的toJSON方法会将Map中key的类型都转为String类型。
toJSON方法源码如下:
public static Object toJSON(Object javaObject, SerializeConfig config) {
if (javaObjectinstanceof Map) {
Map map = (Map) javaObject;
int size = map.size();
Map innerMap;
if (mapinstanceof LinkedHashMap) {
innerMap =new LinkedHashMap(size);
}else if (mapinstanceof TreeMap) {
innerMap =new TreeMap();
}else {
innerMap =new HashMap(size);
}
JSONObject json =new JSONObject(innerMap);
for (Map.Entry entry : map.entrySet()) {
Object key = entry.getKey();
String jsonKey = TypeUtils.castToString(key);
Object jsonValue =toJSON(entry.getValue());
json.put(jsonKey, jsonValue);
}
return json;
}
上面的代码中重点代码为下面这一行,在这一行我们能看到此方法把key的类型都转成了String类型
String jsonKey = TypeUtils.castToString(key);
易犯错误,因为toJSON方法会将Map中key的类型都转为String类型。所以以下的使用方法是错误的
private void execute(JSONObject data) {
Map userIdAndPhoneMap = (Map) data.get("userIdAndPhoneMap");// 获取参数
List pushMessageDetailList =new ArrayList<>();
for (Integer userId : userIdAndPhoneMap.keySet()) {
PushMessageDetail pushMessageDetail =new PushMessageDetail();
pushMessageDetail.setUserId(userId);
pushMessageDetailList.add(pushMessageDetail);
}
pushMessageDetailService.saveBatch(pushMessageDetailList);
}
在以上代码中for (Integer userId : userIdAndPhoneMap.keySet()) 的key值在map中会不存在,因为Integer类型被转为了String类型