我已使用@Transactional
readonly=true
註釋了我的服務方法。Spring:@Transactional with readonly = true not call conn.setReadOnly(true)
因爲那個spring/hibernate沒有調用jdbc連接驅動的setReadonly方法。我能做什麼?
由於我將使用主從複製,並且jdbc池使用連接上的readonly標誌將查詢路由到主節點或從節點。
我已使用@Transactional
readonly=true
註釋了我的服務方法。Spring:@Transactional with readonly = true not call conn.setReadOnly(true)
因爲那個spring/hibernate沒有調用jdbc連接驅動的setReadonly方法。我能做什麼?
由於我將使用主從複製,並且jdbc池使用連接上的readonly標誌將查詢路由到主節點或從節點。
首先,當您的PU事務模式爲RESOURCE_LOCAL時,應該只將readOnly標誌設置爲JDBC連接。如果是JTA,那麼您不得更改該設置,因爲您不會爲事務內的每個jdbc調用(JTA - 而不是Hibernate--將確保事務行爲)獲取相同的jdbc連接實例。當它是LOCAL時,Hibernate在第一次需要它時打開一個jdbc連接,並在事務處理期間保持它。
1 JPA
如果你使用JPA與Hibernate作爲供應商,您可以通過 添加此額外的行爲提供自己實現的JpaDialect的對EMF的定義。在使用Hibernate時,通常會注入一個HibernateJpaDialect。
JpaDialect接口有一個getJdbcConnection(em, readOnly)
方法,該方法返回實際JDBC連接上的句柄。事務開始時,JpaTransactionManager調用此方法。默認情況下,由於此JTA/RESOURCE_LOCAL對偶性,HibernateJpaDialect不會更改返回連接上的readOnly設置,但如果只運行本地事務,則可以執行此操作。
這裏就是這樣一個JpaDialect的的實現,實現了自己的目標:
ResourceLocalReadOnlyAwareHibernateJpaDialect
public class ResourceLocalReadOnlyAwareHibernateJpaDialect extends HibernateJpaDialect {
public ConnectionHandle getJdbcConnection(EntityManager entityManager, boolean readOnly) throws PersistenceException, SQLException {
Session session = getSession(entityManager);
return new HibernateReadOnlyAwareConnectionHandle(session, readOnly);
}
// this is similar to spring's HibernateJpaDialect own internal class,
// except for the readonly flags.
private static class HibernateReadOnlyAwareConnectionHandleimplements ConnectionHandle {
private final Session session;
private final boolean readOnly;
private static volatile Method connectionMethod;
public HibernateConnectionHandle(Session session, boolean readOnly) {
this.session = session;
this.readOnly = readOnly;
}
public Connection getConnection() {
try {
if (connectionMethod == null) {
// reflective lookup to bridge between Hibernate 3.x and 4.x
connectionMethod = this.session.getClass().getMethod("connection");
}
Connection con = (Connection) ReflectionUtils.invokeMethod(connectionMethod, this.session);
con.setReadOnly(this.readOnly);
return con;
} catch (NoSuchMethodException ex) {
throw new IllegalStateException("Cannot find connection() method on Hibernate session", ex);
}
}
public void releaseConnection(Connection con) { // #1
con.setReadOnly(false);
JdbcUtils.closeConnection(con);
}
}
}
注意1:重置只讀標誌設置爲false之前關閉連接(實際上不是一個真正的connection.close()
調用,但只是釋放連接池)。不太清楚是什麼觸發了這個方法的調用,但是它重置readOnly標誌與修改它的地方是一樣的。
2.純休眠
首先,確保HibernateTransactionManager.prepareConnection
仍是如此。
然後,我不知道該怎麼做。你必須調試到Spring的HibernateTransactionManager.isSameConnectionForEntireSession()
:如果方法返回true,connection.setReadOnly()將被調用,因此一切正常。
如果沒有,你可以改變Hibernate的connectionReleaseMode設置爲ON_CLOSE(Hibernate屬性hibernate.transaction.auto_close_session=true
,這是休眠3.1之前默認),或以始終返回true覆蓋HibernateTransactionManager.isSameConnectionForEntireSession()
(這被認爲是安全的問候HibernateTransactionManager的評論) 。兩者都是「高級調諧」,但應該是安全的AFAIK。實際上,我認爲應該將HibernateTransactionManager.isSameConnectionForEntireSession()
更改爲對ON_CLOSE 和 AFTER_TRANSACTION發佈模式返回true:對於HibernateTransactionManager,事務完成後無論如何都會發生清除,因此不會更改Hibernate行爲。
兩種解決方案值得探討,這裏提到的:http://www.dragishak.com/?p=307
TransactionSynchronizationManager.isCurrentTransactionReadOnly()
選擇不同的連接。這取決於您使用哪個連接池實現(例如,BoneCP,c3p0,不確定它是否支持DBCP)。請參閱上面鏈接的評論部分。
你確定這是最外面的註釋,即只讀'@ Transactional'不是從其他事務代碼調用的? – 2012-04-22 20:04:37
當您使用Hibernate作爲您的ORM供應商'@ Transactional'時,只會將刷新模式設置爲'MANUAL'。即它是「只讀」的,因爲它們從不調用EntityManager.flush()。如果你問我,這是一個非常殘忍的笑話,但它就是這樣。有關'@ Transactional'陷阱的更多信息,請參見[本頁](http://www.ibm.com/developerworks/java/library/j-ts1/index.html)。 – 2012-04-22 23:41:08
我有完全相同的問題與MySQL複製和hibernate.Did你找到任何解決方案? – 2012-06-04 12:52:09