場景:客戶使用原生 JDBC 事務(Connection 控制),我方服務基於 Spring 聲明式事務(Hibernate Session),雙方操作同一數據庫,需納入同一全局事務。本文提供無需 JTA、無需修改客戶代碼的輕量級整合方案:從 Hibernate Session 提取 Connection 傳給客戶,統一由我方程序化事務管理。
TransactionSynchronizationManager.bindResource(sessionFactory, holder):將 Session 綁定當前線程,讓 HibernateTemplate 能感知到。session.flush():確保 Hibernate 緩存同步到數據庫,客戶端後續 JDBC 操作可見。public class TransactionTemplate {
private FlushMode flushMode = FlushMode.ALWAYS;
public Object execute(TransactionCallback callback) {
// 1. 獲取 SessionFactory
SessionFactory sessionFactory = (SessionFactory) Framework.getEngine()
.getContainer().getComponent("sessionFactory");
// 2. 創建 Session 並綁定線程
Session session = getSession(sessionFactory);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
// 3. 取得 JDBC Connection 傳給回調
Connection conn = session.connection();
Transaction transaction = null;
Object result = null;
try {
// 4. 開啟 JDBC 事務
transaction = session.beginTransaction();
try {
result = callback.doInTransaction(conn); // 執行客戶+我方邏輯
} catch (RuntimeException | Error ex) {
doRollback(session, transaction);
throw ex;
}
// 5. 提交事務
transaction.commit();
} catch (WorkflowException e) {
logger.error("數據庫操作失敗,事務回滾也失敗!");
throw e;
} catch (RuntimeException | Error ex) {
logger.error("數據庫操作失敗,事務被回滾!");
throw ex;
} finally {
// 6. 清理:解綁、關閉 Session
TransactionSynchronizationManager.unbindResource(sessionFactory);
doClose(session);
}
return result;
}
protected Session getSession(SessionFactory sf) {
Session session = SessionFactoryUtils.getSession(sf, true);
if (flushMode != null) session.setFlushMode(flushMode);
return session;
}
private void doRollback(Session session, Transaction tx) {
try { tx.rollback(); } catch (Exception e) { throw new WorkflowException("回滾失敗"); }
finally { session.clear(); }
}
private void doClose(Session session) {
try { session.close(); } catch (Exception e) { throw new WorkflowException("關閉失敗"); }
}
// getter/setter...
}
public interface TransactionCallback {
Object doInTransaction(Connection conn);
}
public void methodA() {
TransactionTemplate template = new TransactionTemplate();
template.execute(new TransactionCallback() {
public Object doInTransaction(Connection conn) {
// 客戶代碼使用傳入的 conn
client.method1("1");
// 我方服務無聲明式事務,複用同一 Session/Connection
ourService.method2();
client.method3("l");
return null;
}
});
}
session.flush() 時機:在回調返回前、事務提交前調用,確保 Hibernate 寫入對後續 JDBC 可見