例如有一个批量操作数据库的功能(例如数据导入),当某条数据导入出现数据异常后,希望不影响后面数据的导入,于是我们通常情况会想到用try,catch将异常捕后,继续后面的处理;
本来的处理导入的代码是放在Ctroller层的,一直没出现什么问题,数据库异常正常捕获。但是近两天看Ctroller层中的代码太多涉及到业务逻辑的处理,于是进行优化,将导入的处理方法移到Service层,问题就出现了,处理数据库异常捕获的try ,catch失效了,该报异常还是报异常,数据一有错误,导入完全失败。
调试代码过程中,发现虽然saveOrUpdate()后并没有发出SQL语句,也就是没有真正执行持久化操作,因此异常还没抛出,当然也就无法捕获了,于是想办法让hibernate立即执行SQL,于是在saveorUpdate()后,加了句baseDao.flush()方法,刷新session缓存,oK,问题解决。
SendRecord srd = null;
int failCount = 0;
SendRecord pRecord = null;
for (SendRecord record : imports) {
pRecord = findSendRecordIfExists(record);
if (pRecord == null) { //判断发行履历客户主表是否存在该客户
String tempCode = findMaxTmpSoldToCode();
record.setSoldToCode(tempCode);
srd = record;
} else {
srd = pRecord;
srd.setCapitalType(record.getCapitalType());
srd.setProfession(record.getProfession());
srd.setTelephone(record.getTelephone());
srd.setFax(record.getFax());
srd.setEmail(record.getEmail());
srd.setPostalCode(record.getPostalCode());
}
try {
baseDao.saveOrUpdate(srd);
baseDao.flush();
} catch (Exception e) {
failCount = failCount + srd.getSendRecordDetailList().size() + 1;
logger.error("发送履历导入过程中:[客户编号=" + srd.getSoldToCode() + "\t客户名称=" + srd.getCustomerNtvName()
+ " \t客户地址=" + srd.getNtvAddress() + "\t客户部门=" + srd.getDepartment() + "\t联系人="
+ srd.getAttention() + "]对应的【主记录】导入失败");
e.printStackTrace();
continue;
}
for (SendRecordDetail srdd : record.getSendRecordDetailList()) {
srdd.setSendRecord(srd);
srdd.setSendCode(constructSendCode());
try {
baseDao.saveOrUpdate(srdd);
baseDao.flush();
} catch (Exception e) {
failCount++;
logger.error("发送履历导入过程中:[客户编号=" + srd.getSoldToCode() + "客户名称=" + srd.getCustomerNtvName()
+ " 客户地址=" + srd.getNtvAddress() + "客户部门=" + srd.getDepartment() + "联系人="
+ srd.getAttention() + "]对应的【子记录】导入失败");
e.printStackTrace();
}
}
}
return failCount;
回想之所以在Ctroller层,没有发生这种情况是因为,service方法执行完成后,事务就已经提交了,而在service层方法内部为保证事务的原子性,都是同步提交的,到service方法结束后才执行,因此try,catch中无法捕获数据库异常了,为了让它立刻执行,只有通过flush()刷新缓存了。