使用proxool连接sybase时处理事务的问题
2010-08-21 23:40
323 查看
问题描述
今天花了一天时间解决一个问题,好久没有这样钻研解决这样的问题了。
http://topic.csdn.net/t/20050608/16/4068615.html
问题跟这个帖子相同,就是使用proxool+sybase处理事务时,会报错。
先看看以下代码
public static void test2() throws Exception {
// Connection conn = getConnectionFromPool();
Connection conn = getConnection();
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
String sql = "select 1"
;
stmt.executeQuery(sql);
conn.commit();
try {
conn.setAutoCommit(true);
} catch (Exception e) {
e.printStackTrace();
}
stmt.close();
conn.close();
}
public static Connection getConnection() throws Exception {
Class.forName("com.sybase.jdbc3.jdbc.SybDriver"
);
String username = "sa"
;
String pwd = ""
;
String url = "jdbc:sybase:Tds:192.168.0.100:5000/test"
;
return DriverManager.getConnection(url, username, pwd);
}
static public Connection getConnectionFromPool() throws Exception {
Connection connection = null;
String alias = "test"
;
Class.forName("org.logicalcobwebs.proxool.ProxoolDriver"
);
if (connection == null) {
Properties info = new Properties();
info.setProperty("proxool.maximum-connection-count"
,
"20"
);
info.setProperty("user"
,
"sa"
);
info.setProperty("password"
,
""
);
info.put("proxool.maximum-active-time"
,
"60000"
);
String driverClass = "com.sybase.jdbc3.jdbc.SybDriver"
;
String driverUrl = "jdbc:sybase:Tds:192.168.0.100:5000/test"
;
String url = "proxool."
+ alias +
":"
+ driverClass +
":"
+ driverUrl;
connection = DriverManager.getConnection(url, info);
System.out.println(connection);
}
return connection;
}
在运行test2()函数时没有出现异常正常,这里是没有使用proxool,用原始的数据库连接方式。
将test2()第一行的注释去掉,将第二行注释掉,运行时就会报错
错误信息代码
Exception in thread
"main"
com.sybase.jdbc3.jdbc.SybSQLException: SET CHAINED command not allowed within multi-statement transaction.
at com.sybase.jdbc3.tds.Tds.processEed(Tds.java:2942
)
at com.sybase.jdbc3.tds.Tds.nextResult(Tds.java:2246
)
at com.sybase.jdbc3.jdbc.ResultGetter.nextResult(ResultGetter.java:69
)
at com.sybase.jdbc3.jdbc.SybStatement.nextResult(SybStatement.java:220
)
at com.sybase.jdbc3.jdbc.SybStatement.nextResult(SybStatement.java:203
)
at com.sybase.jdbc3.jdbc.SybStatement.updateLoop(SybStatement.java:1804
)
at com.sybase.jdbc3.jdbc.SybStatement.executeUpdate(SybStatement.java:1787
)
at com.sybase.jdbc3.jdbc.SybPreparedStatement.executeUpdate(SybPreparedStatement.java:116
)
at com.sybase.jdbc3.tds.Tds.setOption(Tds.java:1278
)
at com.sybase.jdbc3.jdbc.SybConnection.setAutoCommit(SybConnection.java:1011
)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39
)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25
)
at java.lang.reflect.Method.invoke(Method.java:585
)
at org.logicalcobwebs.proxool.WrappedConnection.invoke(WrappedConnection.java:208
)
at org.logicalcobwebs.proxool.WrappedConnection.intercept(WrappedConnection.java:98
)
at com.sybase.jdbcx.SybConnection$$EnhancerByProxool$$8df5facc.setAutoCommit(<genera
4000
ted>)
at TestConnectionTran.test(TestConnectionTran.java:34
)
at TestConnectionTran.main(TestConnectionTran.java:13
)
问题原因
s_phoenix大侠的解决办法是:
写道
自己解决了。通过拦截setAutoCommit和close方法,并适当做修改,解决了sybase的问题。
修改的是proxool,因为jconn没有源代码。修改hibernate又不成功,只好修改夹在其中的缓冲池了。
虽然我最后也是这样解决的,但是s_phoenix大侠没有告诉我怎么修改,我还是花了几个小时来调试proxool源代码。
首先说明下,sybase为什么会报这个错误
错误信息代码
SET CHAINED command not allowed within multi-statement transaction.
到google,baidu里面随便搜下,就有一堆。简单的一句话:处于事务环境下,调用 set chained
是会发生异常的,这一点手册上也非常明确的指出了。使用sybase的jconn驱动,调用setAutoCommit(false)事务开始,也就是
set chained
on,然后执行完相应的操作后commit或者rollback结束事务,这时需要setAutoCommit(true)“完壁归赵”,以便连接池回
收。但是在proxool中后面setAutoCommit(true)会报错,以上的错误。这个错误明显是事务没有结束又调用
setAutoCommit方法,但是明明事务已经结束了,原因何在?
查看源代码发现了问题,先看源代码org.logicalcobwebs.proxool.WrappedConnection:
Proxool代码代码
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
int argCount = args != null ? args.length : 0
;
Method concreteMethod = method;
if (proxyConnection != null && proxyConnection.getConnection() != null) {
concreteMethod = InvokerFacade.getConcreteMethod(proxyConnection.getConnection().getClass(), method);
}
try {
if (proxyConnection != null && proxyConnection.isReallyClosed()) {
// The user is trying to do something to this connection and it's been closed.
................
if (concreteMethod.getName().equals(IS_CLOSED_METHOD)) {
return result;
}
这块代码是动态代理执行Connection的所有方式时被调用的方法,可以看出,每次都会调用
proxyConnection.isReallyClosed()方法,这个方法里面调用的是Connection.isClose()方法.使用连接
池调用test2()函数,可以类比于以下方式:
非连接池代码
public static void test1() throws Exception {
Connection conn = getConnection();
conn.isClosed();
conn.setAutoCommit(false);
conn.isClosed();
conn.commit();
conn.isClosed();
conn.setAutoCommit(true);
}
运行test1报错根test2一样.问题就出现在这里.可以猜到conn.isClosed();又开启了一个新的事务,该事务没有结束就
conn.setAutoCommit(true);当然会报错.事实上,我通过抓报工具,发现conn.isClosed();会发送命令sp_mda
到数据库中,这个过程就可以看作是在数据库中执行以下命令:
Sybase代码
set chained on
go
select 1
go
commit
go
sp_mda 0
,
3
,
2
set chained off
go
问题
解决
问题原因找到了,怎么解决呢? s_phoenix大侠给了我提示,提前拦截setAutoCommit方法.
修改后的代码
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = null;
int argCount = args != null ? args.length : 0
;
Method concreteMethod = method;
if (proxyConnection != null && proxyConnection.getConnection() != null) {
concreteMethod = InvokerFacade.getConcreteMethod(proxyConnection
.getConnection().getClass(), method);
}
try {
if (concreteMethod.getName().equals("setAutoCommit"
)) {
result = concreteMethod.invoke(proxyConnection.getConnection(),
args);
return result;
}
} catch (Exception e) {
LOG.error("setAutoCommit error"
, e);
throw e;
}
try {
if (proxyConnection != null && proxyConnection.isReallyClosed()) {
// The user is trying to do something to this connection and
// it's been closed.
if (concreteMethod.getName().equals(IS_CLOSED_METHOD)) {
// That's cool. No problem checking as many times as you
// like.
简单的测试了下,没有问题.会不会对其他地方产生影响呢?我不知道,可能会发生很奇妙的结果,哈哈...还是得严格测试下.
修改后,还要注意一点,在commit后面要紧跟setAutoCommit(true),如果中间又调用connection其他方法,还是会报错的.
今天花了一天时间解决一个问题,好久没有这样钻研解决这样的问题了。
http://topic.csdn.net/t/20050608/16/4068615.html
问题跟这个帖子相同,就是使用proxool+sybase处理事务时,会报错。
先看看以下代码
public static void test2() throws Exception {
// Connection conn = getConnectionFromPool();
Connection conn = getConnection();
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
String sql = "select 1"
;
stmt.executeQuery(sql);
conn.commit();
try {
conn.setAutoCommit(true);
} catch (Exception e) {
e.printStackTrace();
}
stmt.close();
conn.close();
}
public static Connection getConnection() throws Exception {
Class.forName("com.sybase.jdbc3.jdbc.SybDriver"
);
String username = "sa"
;
String pwd = ""
;
String url = "jdbc:sybase:Tds:192.168.0.100:5000/test"
;
return DriverManager.getConnection(url, username, pwd);
}
static public Connection getConnectionFromPool() throws Exception {
Connection connection = null;
String alias = "test"
;
Class.forName("org.logicalcobwebs.proxool.ProxoolDriver"
);
if (connection == null) {
Properties info = new Properties();
info.setProperty("proxool.maximum-connection-count"
,
"20"
);
info.setProperty("user"
,
"sa"
);
info.setProperty("password"
,
""
);
info.put("proxool.maximum-active-time"
,
"60000"
);
String driverClass = "com.sybase.jdbc3.jdbc.SybDriver"
;
String driverUrl = "jdbc:sybase:Tds:192.168.0.100:5000/test"
;
String url = "proxool."
+ alias +
":"
+ driverClass +
":"
+ driverUrl;
connection = DriverManager.getConnection(url, info);
System.out.println(connection);
}
return connection;
}
在运行test2()函数时没有出现异常正常,这里是没有使用proxool,用原始的数据库连接方式。
将test2()第一行的注释去掉,将第二行注释掉,运行时就会报错
错误信息代码
Exception in thread
"main"
com.sybase.jdbc3.jdbc.SybSQLException: SET CHAINED command not allowed within multi-statement transaction.
at com.sybase.jdbc3.tds.Tds.processEed(Tds.java:2942
)
at com.sybase.jdbc3.tds.Tds.nextResult(Tds.java:2246
)
at com.sybase.jdbc3.jdbc.ResultGetter.nextResult(ResultGetter.java:69
)
at com.sybase.jdbc3.jdbc.SybStatement.nextResult(SybStatement.java:220
)
at com.sybase.jdbc3.jdbc.SybStatement.nextResult(SybStatement.java:203
)
at com.sybase.jdbc3.jdbc.SybStatement.updateLoop(SybStatement.java:1804
)
at com.sybase.jdbc3.jdbc.SybStatement.executeUpdate(SybStatement.java:1787
)
at com.sybase.jdbc3.jdbc.SybPreparedStatement.executeUpdate(SybPreparedStatement.java:116
)
at com.sybase.jdbc3.tds.Tds.setOption(Tds.java:1278
)
at com.sybase.jdbc3.jdbc.SybConnection.setAutoCommit(SybConnection.java:1011
)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39
)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25
)
at java.lang.reflect.Method.invoke(Method.java:585
)
at org.logicalcobwebs.proxool.WrappedConnection.invoke(WrappedConnection.java:208
)
at org.logicalcobwebs.proxool.WrappedConnection.intercept(WrappedConnection.java:98
)
at com.sybase.jdbcx.SybConnection$$EnhancerByProxool$$8df5facc.setAutoCommit(<genera
4000
ted>)
at TestConnectionTran.test(TestConnectionTran.java:34
)
at TestConnectionTran.main(TestConnectionTran.java:13
)
Exception in thread "main" com.sybase.jdbc3.jdbc.SybSQLException: SET CHAINED command not allowed within multi-statement transaction. at com.sybase.jdbc3.tds.Tds.processEed(Tds.java:2942) at com.sybase.jdbc3.tds.Tds.nextResult(Tds.java:2246) at com.sybase.jdbc3.jdbc.ResultGetter.nextResult(ResultGetter.java:69) at com.sybase.jdbc3.jdbc.SybStatement.nextResult(SybStatement.java:220) at com.sybase.jdbc3.jdbc.SybStatement.nextResult(SybStatement.java:203) at com.sybase.jdbc3.jdbc.SybStatement.updateLoop(SybStatement.java:1804) at com.sybase.jdbc3.jdbc.SybStatement.executeUpdate(SybStatement.java:1787) at com.sybase.jdbc3.jdbc.SybPreparedStatement.executeUpdate(SybPreparedStatement.java:116) at com.sybase.jdbc3.tds.Tds.setOption(Tds.java:1278) at com.sybase.jdbc3.jdbc.SybConnection.setAutoCommit(SybConnection.java:1011) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:585) at org.logicalcobwebs.proxool.WrappedConnection.invoke(WrappedConnection.java:208) at org.logicalcobwebs.proxool.WrappedConnection.intercept(WrappedConnection.java:98) at com.sybase.jdbcx.SybConnection$$EnhancerByProxool$$8df5facc.setAutoCommit(<generated>) at TestConnectionTran.test(TestConnectionTran.java:34) at TestConnectionTran.main(TestConnectionTran.java:13)
问题原因
s_phoenix大侠的解决办法是:
写道
自己解决了。通过拦截setAutoCommit和close方法,并适当做修改,解决了sybase的问题。
修改的是proxool,因为jconn没有源代码。修改hibernate又不成功,只好修改夹在其中的缓冲池了。
虽然我最后也是这样解决的,但是s_phoenix大侠没有告诉我怎么修改,我还是花了几个小时来调试proxool源代码。
首先说明下,sybase为什么会报这个错误
错误信息代码
SET CHAINED command not allowed within multi-statement transaction.
SET CHAINED command not allowed within multi-statement transaction.
到google,baidu里面随便搜下,就有一堆。简单的一句话:处于事务环境下,调用 set chained
是会发生异常的,这一点手册上也非常明确的指出了。使用sybase的jconn驱动,调用setAutoCommit(false)事务开始,也就是
set chained
on,然后执行完相应的操作后commit或者rollback结束事务,这时需要setAutoCommit(true)“完壁归赵”,以便连接池回
收。但是在proxool中后面setAutoCommit(true)会报错,以上的错误。这个错误明显是事务没有结束又调用
setAutoCommit方法,但是明明事务已经结束了,原因何在?
查看源代码发现了问题,先看源代码org.logicalcobwebs.proxool.WrappedConnection:
Proxool代码代码
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
int argCount = args != null ? args.length : 0
;
Method concreteMethod = method;
if (proxyConnection != null && proxyConnection.getConnection() != null) {
concreteMethod = InvokerFacade.getConcreteMethod(proxyConnection.getConnection().getClass(), method);
}
try {
if (proxyConnection != null && proxyConnection.isReallyClosed()) {
// The user is trying to do something to this connection and it's been closed.
................
if (concreteMethod.getName().equals(IS_CLOSED_METHOD)) {
return result;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; int argCount = args != null ? args.length : 0; Method concreteMethod = method; if (proxyConnection != null && proxyConnection.getConnection() != null) { concreteMethod = InvokerFacade.getConcreteMethod(proxyConnection.getConnection().getClass(), method); } try { if (proxyConnection != null && proxyConnection.isReallyClosed()) { // The user is trying to do something to this connection and it's been closed. ................ if (concreteMethod.getName().equals(IS_CLOSED_METHOD)) { return result; }
这块代码是动态代理执行Connection的所有方式时被调用的方法,可以看出,每次都会调用
proxyConnection.isReallyClosed()方法,这个方法里面调用的是Connection.isClose()方法.使用连接
池调用test2()函数,可以类比于以下方式:
非连接池代码
public static void test1() throws Exception {
Connection conn = getConnection();
conn.isClosed();
conn.setAutoCommit(false);
conn.isClosed();
conn.commit();
conn.isClosed();
conn.setAutoCommit(true);
}
public static void test1() throws Exception { Connection conn = getConnection(); conn.isClosed(); conn.setAutoCommit(false); conn.isClosed(); conn.commit(); conn.isClosed(); conn.setAutoCommit(true); }
运行test1报错根test2一样.问题就出现在这里.可以猜到conn.isClosed();又开启了一个新的事务,该事务没有结束就
conn.setAutoCommit(true);当然会报错.事实上,我通过抓报工具,发现conn.isClosed();会发送命令sp_mda
到数据库中,这个过程就可以看作是在数据库中执行以下命令:
Sybase代码
set chained on
go
select 1
go
commit
go
sp_mda 0
,
3
,
2
set chained off
go
set chained on go select 1 go commit go sp_mda 0,3,2 set chained off go
问题
解决
问题原因找到了,怎么解决呢? s_phoenix大侠给了我提示,提前拦截setAutoCommit方法.
修改后的代码
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = null;
int argCount = args != null ? args.length : 0
;
Method concreteMethod = method;
if (proxyConnection != null && proxyConnection.getConnection() != null) {
concreteMethod = InvokerFacade.getConcreteMethod(proxyConnection
.getConnection().getClass(), method);
}
try {
if (concreteMethod.getName().equals("setAutoCommit"
)) {
result = concreteMethod.invoke(proxyConnection.getConnection(),
args);
return result;
}
} catch (Exception e) {
LOG.error("setAutoCommit error"
, e);
throw e;
}
try {
if (proxyConnection != null && proxyConnection.isReallyClosed()) {
// The user is trying to do something to this connection and
// it's been closed.
if (concreteMethod.getName().equals(IS_CLOSED_METHOD)) {
// That's cool. No problem checking as many times as you
// like.
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; int argCount = args != null ? args.length : 0; Method concreteMethod = method; if (proxyConnection != null && proxyConnection.getConnection() != null) { concreteMethod = InvokerFacade.getConcreteMethod(proxyConnection .getConnection().getClass(), method); } try { if (concreteMethod.getName().equals("setAutoCommit")) { result = concreteMethod.invoke(proxyConnection.getConnection(), args); return result; } } catch (Exception e) { LOG.error("setAutoCommit error", e); throw e; } try { if (proxyConnection != null && proxyConnection.isReallyClosed()) { // The user is trying to do something to this connection and // it's been closed. if (concreteMethod.getName().equals(IS_CLOSED_METHOD)) { // That's cool. No problem checking as many times as you // like.
简单的测试了下,没有问题.会不会对其他地方产生影响呢?我不知道,可能会发生很奇妙的结果,哈哈...还是得严格测试下.
修改后,还要注意一点,在commit后面要紧跟setAutoCommit(true),如果中间又调用connection其他方法,还是会报错的.
相关文章推荐
- 使用select处理长连接的问题
- Power5连接使用DS8000遇到问题处理一例
- [置顶] Access数据库使用数据库加密,C#连接access数据库问题处理
- 解决问题记录(3)-事务处理出错:连接打开但是在fetching状态中。
- 关于Java中使用hessian-3.0.20调用远程服务连接出错异常try catch无法捕捉的问题处理
- Sping Data Redis 使用事务时,不关闭连接的问题
- spring 使用AbstractRoutingDataSource自定义动态数据源时的事务处理问题
- [转载]C#中使用ADO.NET连接SQL Server数据库,自动增长字段用作主键,处理事务时的基本方法
- 在存储过程中使用事务处理 sql server 删除多表操作一致性的问题
- [原创]C#中使用ADO.NET连接SQL Server数据库,自动增长字段用作主键,处理事务时的基本方法
- centos screen 命令使用处理SSH连接中断问题
- 使用jdbc 连接sybase 11 时出现的问题
- Android多线程操作sqlite(Sqlite解决database locked问题)(2)使用事务处理的效果
- 使用isql连接Sybase ASE数据库的常见错误及处理方式
- 如何使用专用管理连接进行问题诊断和处理 【摘自SQL Server在线帮助】
- WebSphere使用数据源连接数据库的事务问题
- ORA-14450: 试图访问已经在使用的事务处理临时表,根据网上文章提供的方法,问题是解决了
- Sping Data Redis 使用事务时,不关闭连接的问题
- 代码中进行事务处理 -多次新建连接导致的问题。
- TransactionScope使用(一)——简单的实现事务处理