有一个项目,是用Hibernate开发的部署在WebSphere Application Server 5.1下,总是会出现数据库记录被死锁的情况,即使是一个简单的更新动作,都有可能造成。
这个问题,困扰我许久,检查过所有代码,事物一定要提交或者回滚,Session及时释放,怀疑过是否是因为是部署在集群环境,但好像那些措施总于事无补,百思不得其解。
就差没有使用容器提供的事物了。
突然间想到去各自的官网,查查有没有人碰到过类似的信息,于是在 http://www.hibernate.org 网站找到了Hibernate in WAS 5.x - fixes possible deadlock problem,其中提到要定制一个 ConnectionProvider,名之为 WSDataSourceConnectionProvider,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
package com.unmi.db; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; import org.apache.log4j.Logger; import org.hibernate.HibernateException; import org.hibernate.cfg.Environment; import org.hibernate.connection.ConnectionProvider; import org.hibernate.util.NamingHelper; import com.ibm.websphere.rsadapter.JDBCConnectionSpec; import com.ibm.websphere.rsadapter.WSCallHelper; import com.ibm.websphere.rsadapter.WSDataSource; import com.ibm.websphere.rsadapter.WSRRAFactory; import com.ibm.ws.rsadapter.jdbc.WSJdbcConnection; import com.ibm.ws.rsadapter.jdbc.WSJdbcUtil; /** * ConnectionProvider implementation that implements WSDataSource specific * Transaction Isolation level settings for the connection. Modified from the * original DatasourceConnectionProvider class by Lari Hotari * */ public class WSDataSourceConnectionProvider implements ConnectionProvider { public boolean supportsAggressiveRelease() { return false; } private WSDataSource ds; private String user; private String pass; private Integer isolation; private static final Logger log = Logger .getLogger(WSDataSourceConnectionProvider.class); public void configure(Properties props) throws HibernateException { String jndiName = props.getProperty(Environment.DATASOURCE); if (jndiName == null) { String msg = "datasource JNDI name was not specified by property " + Environment.DATASOURCE; log.fatal(msg); throw new HibernateException(msg); } user = props.getProperty(Environment.USER); pass = props.getProperty(Environment.PASS); String i = props.getProperty(Environment.ISOLATION); if (i == null) { isolation = null; } else { isolation = new Integer(i); log.info("JDBC isolation level: " + Environment.isolationLevelToString(isolation.intValue())); } try { ds = (WSDataSource) NamingHelper.getInitialContext(props).lookup( jndiName); } catch (Exception e) { log.fatal("Could not find datasource: " + jndiName, e); throw new HibernateException("Could not find datasource", e); } if (ds == null) throw new HibernateException("Could not find datasource: " + jndiName); log.info("Using datasource: " + jndiName); } public Connection getConnection() throws SQLException { if (log.isDebugEnabled()) { log.debug("getConnection()"); } Connection connection = ds.getConnection(createJDBCConnectionSpec()); if (log.isDebugEnabled()) { if (connection != null) { log.debug("Returning connection, System.identityHashCode=" + System.identityHashCode(connection)); log.debug("Connection=" + connection); log.debug("Shareable=" + WSCallHelper.isShareable(connection)); log.debug("Class=" + connection.getClass()); Connection nativeConnection = (Connection) WSJdbcUtil .getNativeConnection((WSJdbcConnection) connection); log.debug("Native connection=" + nativeConnection); log.debug("Native connection, System.identityHashCode=" + System.identityHashCode(nativeConnection) + ", class=" + nativeConnection.getClass()); } else { log.debug("Returning null"); } } return connection; } public void closeConnection(Connection conn) throws SQLException { conn.close(); } public void close() { } protected JDBCConnectionSpec createJDBCConnectionSpec() { JDBCConnectionSpec connSpec = WSRRAFactory.createJDBCConnectionSpec(); // 设置事称隔离级别 if (isolation != null) { connSpec.setTransactionIsolation(isolation); } if (user != null || pass != null) { connSpec.setUserName(user); connSpec.setPassword(pass); } return connSpec; } } |
以上代码中类 WSRRAFactory、JDBCConnectionSpec 用到了 $WAS_HOME/lib/rsaexternal.jar;类WSJdbcConnection、WSJdbcUtil 用到了 $WAS_HOME/lib/rsadapterspi.jar,所以需要在编译时 $WAS_HOME/lib 中两那两个包引用进来。
同时,在 hibernate.cfg.xml 中要有如下配置,记得在 WAS 还需配置相应的连接池 jdbc/unmiDs。
1 2 3 4 5 6 7 8 9 |
<property name="connection.datasource">jdbc/unmiDs</property> <property name="dialect">org.hibernate.dialect.OracleDialect</property> <property name="hibernate.connection.provider_class"> com.unmi.db.WSDataSourceConnectionProvider </property> <property name="connection.username">user</property> <property name="connection.password">password</property> |
文中又有链接提到 Sharing connections in WebSphere Application Server V5
大致说到的是,在WAS环境中Hibernate应用程序运行时可能一个事物使用的并非是同一个链接,无形中搞出个分布式事物来吧,具体情况和解决办法还有待细究,希望这个就是我想要的结果。
OK,明天再来分析上面那两篇文章吧,期望找到一个治本的解决方案。
好像与现今使用的C3P0连接池也有关系,有建议说换成最近新版的
文中 c3p0 problem 也有提到把 maxStatements 和 maxStatementsPerConnection 设置为零
c3p0.maxStatements=0
c3p0.maxStatementsPerConnection=0
本文链接 https://yanbin.blog/hibernate-was-dead-lock/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
关注