最近在用spring项目做接口开发 数据库用的是Hibernate4.0。 在做业务的时候有一个非常奇怪的错误,
[ERROR][SqlExceptionHelper] ORA-00942: 表或视图不存在
[2021-03-26 12:07:55,753][ERROR][BatchingBatch] HHH000315: Exception executing batch [could not execute batch]
[2021-03-26 12:07:55,754][ERROR][BaseDao] oracle error query: sql: select nvl(b.xmlx,0) as XMLX,b.FYGB as FYGB,a.FYDJ as YLDJ,1 as YLSL ,1 as ZFBL,a.FYDJ as HJJE,1 as DZBL from GY_YLMX a,GY_YLSF b where a.JGID=:JGID and a.FYXH=:FYXH and a.FYXH=b.FYXH,parameters:{FYXH=13764, JGID=4}
org.hibernate.HibernateException: org.hibernate.exception.SQLGrammarException: could not execute batch
错误是由于我在公共调用方法里面捕获了错误信息,把错误时候的日志打印出来
public List<Map<String, Object>> doSqlQuery(String sql,Map<String, Object> parameters)
throws PersistentDataOperationException{
Session session =getCurrentSession();
List<Map<String, Object>> l =null;
try {
l=getList(sql, session, parameters);
} catch (HibernateException e) {
logger.error("oracle error query:"+" sql:"+sql+",parameters:"+parameters,e);
throw new PersistentDataOperationException(e);
}
return l;
}
注意这里:
catch (HibernateException e) {
logger.error("oracle error query:"+" sql:"+sql+",parameters:"+parameters,e);
throw new PersistentDataOperationException(e);
}
重点来了:GY_YLMX a,GY_YLSF b 这两张表在数据库里面都有着的,一开始以为是多数据源切换的问题,还停留在老的数据源那边 但是把表换成另外一个数据源里面的表依旧报这个错,说明不是。于是又怀疑是上面的save保存问题,由于对Hibernate的save保存机制不熟悉但是上面日志没报错说是save那几张表导致的错误(如果有错应该都是捕捉到了) 。 查的怀疑人生了。
后面把日志级别从error调成debug,让更多的日志打印出来。发现报错还真在save(Object)这里。 为了方便理解,我把部分代码贴上来:
phiszzDao.save(ghmx);
phiszzDao.saveObj(jzls);
phiszzDao.saveObj(bcjl);//注意这个bcjl表在当前数据源中的表是不存在的
phiszzDao.saveObj(brzd);
phiszzDao.saveObj(yj01);
StringBuffer querySFXM = new StringBuffer(" select nvl(b.xmlx,0) as XMLX,b.FYGB as FYGB,a.FYDJ as YLDJ,1 as YLSL ,1 as ZFBL,a.FYDJ as HJJE,1 as DZBL ")
.append(" from GY_YLMX a,GY_YLSF b " )
.append(" where a.JGID=:JGID and a.FYXH=:FYXH and a.FYXH=b.FYXH");
Map<String, Object> FYXXMap = phiszzDao.doMapQuery(querySFXM.toString(), fyxxparameters);
在debug日志里面显示的是phiszzDao.saveObj(bcjl); 这里报表或视图不存在,但是在日志级别为error由于没有输出详细信息报错的问题是在queryDFXM这条语句这里。
思考:
1、为什么save(bcjl) 报错后还可以走下去
2、为什么会在queryMap这个查询方法里面才会抛出。
为了便于理解 我把查询方法贴在下面:
/**
* 查询单一返回主键
* @param sql
* @param parameters
* @return
* @throws PersistentDataOperationException
*/
public Map<String, Object> doMapQuery(String sql,Map<String, Object> parameters)
throws PersistentDataOperationException{
Session session =getCurrentSession();
Map<String, Object> l =null;
try {
l=doLoad(sql, session, parameters);
} catch (HibernateException e) {
logger.error("oracle error query:"+" sql:"+sql+",parameters:"+parameters,e);
throw new PersistentDataOperationException(e);
}
return l;
}
/**
* 查询单一主键 返回
* @param sql
* @param session
* @param parameters
* @return
* @throws HibernateException
*/
public Map<String, Object> doLoad(String sql,Session session ,Map<String, Object> parameters)
throws HibernateException{
Map<String, Object> m=null;
try {
SQLQuery query = session.createSQLQuery(sql);
if (parameters != null && !parameters.isEmpty()) {
for (String key : parameters.keySet()) {
if (key.equals("first")) {
query.setFirstResult((Integer) parameters.get(key));
} else if (key.equals("max")) {
query.setMaxResults((Integer) parameters.get(key));
} else {
setParameter(query, key, parameters.get(key));
}
}
}
if (sql.indexOf(" as ") > 0) {
query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
}
m=(Map<String, Object>) query.uniqueResult();
session.flush();
} catch (HibernateException e) {
throw new HibernateException(e);
}
return m;
}
可能细心的朋友已经看出猫腻了,在query里面多了一个session.flush()方法。
那session的flush方法去做了什么呢,跟上面的save报错为什么提示在查询这里有没有关系呢?由于这个方法是事务控制 ,flush方法的主要作用就是清理缓存,强制数据库与Hibernate缓存同步,以保证数据的一致性。它 的主要动作就是向数据库发送一系列的sql语句,并执行这些sql语句,但是不会向数据库提交。
注意上面的加粗字体:这时候才会真正去执行sql 由于执行的时候 会把上面的save方法都先预先执行一遍(注意这里只是执行 还并未向数据库发起提交!)如果有错误就会抛出错误,所以就解释的通上面为什么会在查询 doSqlQuery方法是提示表或视图不存在!!!其实说的是save 里面的bcjl这张表不存在而并非是GY_YLMX a,GY_YLSF b 这两张表不存在。
那什么时候去提交呢?由于上面方法是事务控制的,要到整个方法走完才会提交到数据库。而commit方法则会首先调用flush方法,然后提交 事务。