在选择 sequence.nextval 时,tomcat 环境中的 H2 数据库抛出 RuntimeException “unexpected code path”

Posted

技术标签:

【中文标题】在选择 sequence.nextval 时,tomcat 环境中的 H2 数据库抛出 RuntimeException “unexpected code path”【英文标题】:H2 database in tomcat environment throwing RuntimeException "unexpected code path" on select sequence.nextval 【发布时间】:2016-12-23 00:08:04 【问题描述】:

我在 tomcat 中使用 H2 数据库 h2-1.4.193 作为数据源。

这是我的server.xml 条目:

<Resource auth="Container" 
    driverClassName="org.h2.Driver" 
    logAbandoned="true" 
    maxIdle="10" 
    maxOpenPreparedStatements="5" 
    maxTotal="50" 
    maxWaitMillis="10000" 
    name="jdbc/shopfloor_local" 
    password="***" poolPreparedStatements="false" 
    removeAbandonedOnBorrow="true" removeAbandonedOnMaintenance="true"      
    removeAbandonedTimeout="180" scope="Shareable" testWhileIdle="true"
    timeBetweenEvictionRunsMillis="600000" type="javax.sql.DataSource"

    url="jdbc:h2:~/shopfloor;AUTO_SERVER=TRUE;TRACE_LEVEL_SYSTEM_OUT=3"

    username="SA"
    validationQuery="select  1"/>

我将此数据源用于 DataSourceRealm 和我的应用程序。在我的应用程序中,我对序列执行选择:

select aufgabenliste_seq.nextval

然后我得到这个错误:

2016-12-23 00:46:03 jdbc[3]: 
/**/conn3.setAutoCommit(false);
2016-12-23 00:46:03 jdbc[3]: 
/**/PreparedStatement prep8 = conn3.prepareStatement("select    aufgabenliste_seq.nextval");
2016-12-23 00:46:03 jdbc[3]: Plan       : calculate cost for plan    [SYSTEM_RANGE:0:org.h2.table.RangeTable@603d6c40]
2016-12-23 00:46:03 jdbc[3]: Plan       :   for table filter SYSTEM_RANGE:0:org.h2.table.RangeTable@603d6c40
2016-12-23 00:46:03 jdbc[3]: Table      :     potential plan item cost 1 index PUBLIC.RANGE_INDEX
2016-12-23 00:46:03 jdbc[3]: Plan       :   best plan item cost 1 index PUBLIC.RANGE_INDEX
2016-12-23 00:46:03 jdbc[3]: Plan       : plan cost 2
2016-12-23 00:46:03 jdbc[3]: /**/ResultSet rs9 = prep8.executeQuery();
2016-12-23 00:46:03 lock: 1 exclusive write lock requesting for SYS
2016-12-23 00:46:03 lock: 1 exclusive write lock added for SYS
2016-12-23 00:46:03 lock: 1 exclusive write lock unlock SYS
2016-12-23 00:46:03 lock: 1 exclusive write lock requesting for SYS
2016-12-23 00:46:03 lock: 1 exclusive write lock added for SYS
2016-12-23 00:46:03 jdbc[3]: exception
org.h2.jdbc.JdbcSQLException: Allgemeiner Fehler: "java.lang.RuntimeException: Unexpected code path"
General error: "java.lang.RuntimeException: Unexpected code path"; SQL statement:
select aufgabenliste_seq.nextval [50000-193]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
    at org.h2.message.DbException.get(DbException.java:168)
    at org.h2.message.DbException.convert(DbException.java:295)
    at org.h2.command.Command.executeQuery(Command.java:213)
    at org.h2.jdbc.JdbcPreparedStatement.executeQuery(JdbcPreparedStatement.java:110)
    at org.apache.tomcat.dbcp.dbcp2.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:82)
    at org.apache.tomcat.dbcp.dbcp2.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:82)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...
...
Caused by: java.lang.RuntimeException: Unexpected code path
    at org.h2.message.DbException.throwInternalError(DbException.java:242)
    at org.h2.message.DbException.throwInternalError(DbException.java:255)
    at org.h2.engine.Session.addLock(Session.java:842)
    at org.h2.mvstore.db.MVTable.doLock2(MVTable.java:254)
    at org.h2.mvstore.db.MVTable.doLock1(MVTable.java:202)
    at org.h2.mvstore.db.MVTable.lock(MVTable.java:167)
    at org.h2.engine.Database.lockMeta(Database.java:909)
...
...

我已经调试了代码。在方法addLock()中的Session类中抛出异常:

    public void addLock(Table table) 
        if (SysProperties.CHECK) 
            if (locks.contains(table)) 
                DbException.throwInternalError();
            
        
        locks.add(table);
    

要记录的table 对象包含表SYS

为什么在这种情况下请求锁定SYS 失败? 我应该为连接字符串提供额外的参数吗?

另见Google Groups

【问题讨论】:

看起来像双锁 sys.询问开发人员,很可能是 h2 中的错误。 是的,我正在联系开发者:groups.google.com/forum/#!topic/h2-database/x2XoFP1G2l8 【参考方案1】:

我发现这篇文章试图修复 Confluence 内部 h2 数据库,得到同样的错误,这对我有用。这是我所做的 gist on my GitHub 的 shell 脚本 - 你必须根据你的环境进行调整:

#!/bin/bash
# This script has to be run as your confluence user or root
# Create a backup of, and attempt to restore a confluence h2 internal database

# Go home.
cd ~

# Stop confluence
/opt/atlassian/confluence/bin/stop-confluence.sh
# Really though...
pgrep java | xargs kill

#create a good backup of your database files
mkdir -p ~/confluence-db-restore/originals
cp /var/atlassian/application-data/confluence/database/*.db ~/confluence-db-restore/originals

# create another good backup of your database files
tar -cvzf ~/confluence-db-restore/"$(date '+%Y-%m-%d')_h2db_backup.tar.gz" ~/confluence-db-restore/originals

# create a copy of your db to work from
cp ~/confluence-db-restore/originals/*.db ~/confluence-db-restore

# Create recovery SQL script
/opt/atlassian/confluence/jre/bin/java -cp \
/opt/atlassian/confluence/confluence/WEB-INF/lib/h2-*.jar \
org.h2.tools.Recover

# move working files and restore database
mkdir -p ~/confluence-db-restore/recovery
mv *.sql ~/confluence-db-restore/recovery
cd ~/confluence-db-restore/recovery
/opt/atlassian/confluence/jre/bin/java -cp \
/opt/atlassian/confluence/confluence/WEB-INF/lib/h2-*.jar \
org.h2.gools.RunScript -url jdbc:h2:./h2db -user sa -script *.sql


################################################################################
# IMPORTANT THAT YOU MOVE THE RECOVERED DATABASE IF THE RESTORE WAS SUCCESSFUL #
# Uncomment this line to copy the restored database over your existing         #
################################################################################

# cp -f ~/confluence-db-restore/recovery/*.db /var/atlassian/application-data/confluence/database/
# chown -R confluence:confluence /var/atlassian/application-data/confluence

systemctl restart confluence

【讨论】:

以上是关于在选择 sequence.nextval 时,tomcat 环境中的 H2 数据库抛出 RuntimeException “unexpected code path”的主要内容,如果未能解决你的问题,请参考以下文章

如何在JDBC中从SQL获取sequence.nextval?

Oracle:max(id)+1 和 sequence.nextval 之间的区别

代理键的 INSERT ALL INTO 和 Sequence.nextval

带有 rs.getInt() 或 getLong() 的 Oracle 12c 中的 Sequence.NEXTVAL 失败 - 那么它返回啥数据类型?

关于hibernate的hibernate_sequence.nextval

怎样在oracle中取出当前序列值