※异常信息 环境 MyBatis Oracle11.2c Terasoluna Batch
Caused by: org.springframework.jdbc.UncategorizedSQLException:
### Error updating database. Cause: java.sql.SQLException: ORA-00604: 再帰SQLレベル1でエラーが発生しました。
ORA-01000: 最大オープン・カーソル数を超えました。
ORA-00604: 再帰SQLレベル1でエラーが発生しました。
ORA-01000: 最大オープン・カーソル数を超えました。
※调试
cmd
sqlplus
sys as sysdba
$password
##第一条sql 显示现在使用中的cursor数 和 最大允许cursor数
##第二条sql 显示现在使用中的cursor 的用户 sid sql文内容
##spool logFile; xxxx ; spool off;
spool C:\Users\nttdata\Desktop\sqlseq.txt;
select max(a.value) as highest_open_cur, p.value as max_open_cur
from v$sesstat a, v$statname b, v$parameter p
where a.statistic# = b.statistic#
and b.name = 'opened cursors current'
and p.name= 'open_cursors'
group by p.value;
select c.user_name, c.sid, sql.sql_text
from v$open_cursor c, v$sql sql
where c.sql_id=sql.sql_id;
spool off;
※原因
最后发现是在sqlmap中使用了 ${constant},
而值是每次取到sequence的nextVal然后拼接xml配置文件中的固定值,所以都不相同。
$是直接拼入sql语句中的内容。一般使用#{value}。
当每次都不同时,生成的statement就不同。
在sqlsession关闭前,这样的statement超过最大值就会报 ORA-01000
※解决
将不变的内容用${constant}拼接,变化的内容用#{value}
这样#{value}在statement中是一个?,每次执行的都一样,不会新加statement。问题解决。
-------------------------------------------------------------------------------------
※异常
同时在调查这个问题的时候发现了另一个隐藏的问题
select sequenceName.nextval from dual
但将结果写入文件时,值除了第一条记录并没有采番。
当将结果插入db时,每次值都是新的。
查看log发现写文件的情况下,sql并未执行。
修改前
[2017/09/07 18:36:51] [main] [j.c.n.d.e.b.MappingBLogic] [DEBUG] read record [data={aaa=576}]
[2017/09/07 18:36:51] [main] [j.c.n.d.o.h.UnifiedCodeConvertHandler] [DEBUG] unified code convert execute [recordNo=576, column=aaa, value=576]
[2017/09/07 18:36:51] [main] [j.c.n.d.o.h.UnifiedCodeConvertHandler] [DEBUG] unified code convert success [recordNo=576, srcName=aaa, srcValue=576, destName=newseq, destValue=4209, replace=false]
修改后 第一条以后的数据,多了sql执行的参数和结果
[2017/09/07 18:37:24] [main] [j.c.n.d.e.b.MappingBLogic] [DEBUG] read record [data={aaa=622}]
[2017/09/07 18:37:24] [main] [j.c.n.d.o.h.UnifiedCodeConvertHandler] [DEBUG] unified code convert execute [recordNo=622, column=aaa, value=622]
[2017/09/07 18:37:24] [main] [j.c.n.d.e.d.Q.select ] [DEBUG] ==> Parameters: abcd(String)
[2017/09/07 18:37:24] [main] [j.c.n.d.e.d.Q.select ] [DEBUG] <== Total: 0
[2017/09/07 18:37:24] [main] [j.c.n.d.e.d.Q.select ] [DEBUG] ==> Parameters:
[2017/09/07 18:37:24] [main] [j.c.n.d.e.d.Q.select ] [DEBUG] <== Total: 1
[2017/09/07 18:37:24] [main] [j.c.n.d.o.h.UnifiedCodeConvertHandler] [DEBUG] unified code convert success [recordNo=622, srcName=aaa, srcValue=622, destName=newseq, destValue=4831, replace=false]
※原因
http://www.mybatis.org/mybatis-3/zh/java-api.html
清理 Session 级的缓存
void clearCache()
SqlSession 实例有一个本地缓存在执行 update,commit,rollback 和 close 时被清理。要 明确地关闭它(获取打算做更多的工作) ,你可以调用 clearCache()。
http://www.mybatis.org/mybatis-3/java-api.html
中文只说一句话,英文就解释很清楚,还是要看英文~~~
本地缓存
Mybatis使用两种缓存,本地缓存和二级缓存。
每当创建一个新的session,Mybatis会创建一个本地缓存并将它绑定到这个session。
在这个session中执行的任何查询会被缓存到本地缓存中,所以当后面使用相同的查询语句和参数时,不会去查询DB。
本地缓存在update,commit,rollback和close时被清空。
本地缓存默认在整个session生存周期中使用。本地缓存是用来解决循环引用和加快嵌套查询,所以他不能被完全禁用。
但是你可以设置本地缓存只在语句执行中使用,localCacheScope=STATEMENT.
mybatis-config.xml
<configuration>
<settings>
<setting name="localCacheScope" value="STATEMENT" />
</settings>
</configuration>
注意当localCacheScope被设置为SESSION时,Mybatis会返回缓存中的对象引用。对返回值比如list的修改都会影响本地缓存中的值
并影响session生命周期中从缓存中返回的值。所以,最佳实践是不要修改Mybatis的返回值。
你也可以手动清空本地缓存,用sqlSession的下面方法。
void clearCache()
Local Cache
MyBatis uses two caches: a local cache and a second level cache.
Each time a new session is created MyBatis creates a local cache and attaches it to the session. Any query executed within the session will be stored in the local cache so further executions of the same query with the same input parameters will not hit the database. The local cache is cleared upon update, commit, rollback and close.
By default local cache data is used for the whole session duration. This cache is needed to resolve circular references and to speed up repeated nested queries, so it can never be completely disabled but you can configure the local cache to be used just for the duration of an statement execution by setting localCacheScope=STATEMENT.
Note that when the localCacheScope is set to SESSION, MyBatis returns references to the same objects which are stored in the local cache. Any modification of returned object (lists etc.) influences the local cache contents and subsequently the values which are returned from the cache in the lifetime of the session. Therefore, as best practice, do not to modify the objects returned by MyBatis.
You can clear the local cache at any time calling:
void clearCache()
※解决
没有去改配置。
我使用了手动清空的方法。
注入一个sqlSession
当要取sequence的时候,先清空缓存。
private SqlSession sqlSession;
sqlSession.clearCache();
实验了下配置为STATEMENT效果相同