O/R工具出現之後,簡化了許多復雜的信息持久化的開發。Spring應用開發者可以通過 Spring提供的O/R方案更方便的使用各種持久化工具,比如Hibernate;下面我們就 Spring+Hibernate中的Spring實現做一個簡單的剖析。
Spring對Hinberanate的配置是通過LocalSessionFactoryBean來完成的,這是一個工 廠Bean的實現,在基類AbstractSessionFactoryBean中:
Java代碼
/**
* 這是FactoryBean需要實現的接口方法,直接取得當前的sessionFactory的 值
*/
public Object getObject() {
return this.sessionFactory;
}
這個值在afterPropertySet中定義:
Java代碼
public void afterPropertiesSet() throws Exception {
//這個buildSessionFactory是通過配置信息得到SessionFactory的地方
SessionFactory rawSf = buildSessionFactory();
//這裡使用了Proxy方法插入對getCurrentSession的攔截,得到和事務相關 的session
this.sessionFactory = wrapSessionFactoryIfNecessary(rawSf);
}
我們先看看SessionFactory是怎樣創建的,這個方法很長,包含了創建Hibernate的 SessionFactory的詳盡步驟:
Java代碼
protected SessionFactory buildSessionFactory() throws Exception {
SessionFactory sf = null;
// Create Configuration instance.
Configuration config = newConfiguration();
//這裡配置數據源,事務管理器,LobHander到Holder中,這個Holder是一個 ThreadLocal變量,這樣這些資源就和線程綁定了
if (this.dataSource != null) {
// Make given DataSource available for SessionFactory configuration.
configTimeDataSourceHolder.set(this.dataSource);
}
if (this.jtaTransactionManager != null) {
// Make Spring-provided JTA TransactionManager available.
configTimeTransactionManagerHolder.set (this.jtaTransactionManager);
}
if (this.lobHandler != null) {
// Make given LobHandler available for SessionFactory configuration.
// Do early because because mapping resource might refer to custom types.
configTimeLobHandlerHolder.set(this.lobHandler);
}
//這裡是使用Hibernate的各個屬性的配置,這裡使用了Configuration類來 抽象這些數據
try {
// Set connection release mode "on_close" as default.
// This was the case for Hibernate 3.0; Hibernate 3.1 changed
// it to "auto" (i.e. "after_statement" or "after_transaction").
// However, for Spring's resource management (in particular for
// HibernateTransactionManager), "on_close" is the better default.
config.setProperty(Environment.RELEASE_CONNECTIONS, ConnectionReleaseMode.ON_CLOSE.toString());
if (!isExposeTransactionAwareSessionFactory()) {
// Not exposing a SessionFactory proxy with transaction- aware
// getCurrentSession() method -> set Hibernate 3.1 CurrentSessionContext
// implementation instead, providing the Spring-managed Session that way.
// Can be overridden by a custom value for corresponding Hibernate property.
config.setProperty (Environment.CURRENT_SESSION_CONTEXT_CLASS,
"org.springframework.orm.hibernate3.SpringSessionContext");
}
if (this.entityInterceptor != null) {
// Set given entity interceptor at SessionFactory level.
config.setInterceptor(this.entityInterceptor);
}
if (this.namingStrategy != null) {
// Pass given naming strategy to Hibernate Configuration.
config.setNamingStrategy(this.namingStrategy);
}
if (this.typeDefinitions != null) {
// Register specified Hibernate type definitions.
Mappings mappings = config.createMappings();
for (int i = 0; i < this.typeDefinitions.length; i++) {
TypeDefinitionBean typeDef = this.typeDefinitions [i];
mappings.addTypeDef(typeDef.getTypeName(), typeDef.getTypeClass(), typeDef.getParameters());
}
}
if (this.filterDefinitions != null) {
// Register specified Hibernate FilterDefinitions.
for (int i = 0; i < this.filterDefinitions.length; i++) {
config.addFilterDefinition(this.filterDefinitions [i]);
}
}
if (this.configLocations != null) {
for (int i = 0; i < this.configLocations.length; i++) {
// Load Hibernate configuration from given location.
config.configure(this.configLocations[i].getURL());
}
}
if (this.hibernateProperties != null) {
// Add given Hibernate properties to Configuration.
config.addProperties(this.hibernateProperties);
}
if (this.dataSource != null) {
boolean actuallyTransactionAware =
(this.useTransactionAwareDataSource || this.dataSource instanceof TransactionAwareDataSourceProxy);
// Set Spring-provided DataSource as Hibernate ConnectionProvider.
config.setProperty(Environment.CONNECTION_PROVIDER,
actuallyTransactionAware ?
TransactionAwareDataSourceConnectionProvider.class.getName() :
LocalDataSourceConnectionProvider.class.getName ());
}
if (this.jtaTransactionManager != null) {
// Set Spring-provided JTA TransactionManager as Hibernate property.
config.setProperty(
Environment.TRANSACTION_MANAGER_STRATEGY, LocalTransactionManagerLookup.class.getName());
}
if (this.mappingLocations != null) {
// Register given Hibernate mapping definitions, contained in resource files.
for (int i = 0; i < this.mappingLocations.length; i++) {
config.addInputStream(this.mappingLocations [i].getInputStream());
}
}
if (this.cacheableMappingLocations != null) {
// Register given cacheable Hibernate mapping definitions, read from the file system.
for (int i = 0; i < this.cacheableMappingLocations.length; i++) {
config.addCacheableFile(this.cacheableMappingLocations [i].getFile());
}
}
if (this.mappingJarLocations != null) {
// Register given Hibernate mapping definitions, contained in jar files.
for (int i = 0; i < this.mappingJarLocations.length; i++) {
Resource resource = this.mappingJarLocations[i];
config.addJar(resource.getFile());
}
}
if (this.mappingDirectoryLocations != null) {
// Register all Hibernate mapping definitions in the given directories.
for (int i = 0; i < this.mappingDirectoryLocations.length; i++) {
File file = this.mappingDirectoryLocations[i].getFile ();
if (!file.isDirectory()) {
throw new IllegalArgumentException(
"Mapping directory location [" + this.mappingDirectoryLocations[i] +
"] does not denote a directory");
}
config.addDirectory(file);
}
}
if (this.entityCacheStrategies != null) {
// Register cache strategies for mapped entities.
for (Enumeration classNames = this.entityCacheStrategies.propertyNames(); classNames.hasMoreElements();) {
String className = (String) classNames.nextElement ();
String[] strategyAndRegion =
StringUtils.commaDelimitedListToStringArray (this.entityCacheStrategies.getProperty(className));
if (strategyAndRegion.length > 1) {
config.setCacheConcurrencyStrategy(className, strategyAndRegion[0], strategyAndRegion[1]);
}
else if (strategyAndRegion.length > 0) {
config.setCacheConcurrencyStrategy(className, strategyAndRegion[0]);
}
}
}
if (this.collectionCacheStrategies != null) {
// Register cache strategies for mapped collections.
for (Enumeration collRoles = this.collectionCacheStrategies.propertyNames(); collRoles.hasMoreElements();) {
String collRole = (String) collRoles.nextElement();
String[] strategyAndRegion =
StringUtils.commaDelimitedListToStringArray (this.collectionCacheStrategies.getProperty(collRole));
if (strategyAndRegion.length > 1) {
config.setCollectionCacheConcurrencyStrategy (collRole, strategyAndRegion[0], strategyAndRegion[1]);
}
else if (strategyAndRegion.length > 0) {
config.setCollectionCacheConcurrencyStrategy (collRole, strategyAndRegion[0]);
}
}
}
if (this.eventListeners != null) {
// Register specified Hibernate event listeners.
for (Iterator it = this.eventListeners.entrySet().iterator (); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
Assert.isTrue(entry.getKey() instanceof String, "Event listener key needs to be of type String");
String listenerType = (String) entry.getKey();
Object listenerObject = entry.getValue();
if (listenerObject instanceof Collection) {
Collection listeners = (Collection) listenerObject;
EventListeners listenerRegistry = config.getEventListeners();
Object[] listenerArray =
(Object[]) Array.newInstance (listenerRegistry.getListenerClassFor(listenerType), listeners.size());
listenerArray = listeners.toArray (listenerArray);
config.setListeners(listenerType, listenerArray);
}
else {
config.setListener(listenerType, listenerObject);
}
}
}
// Perform custom post-processing in subclasses.
postProcessConfiguration(config);
// 這裡是根據Configuration配置創建SessionFactory的地方
logger.info("Building new Hibernate SessionFactory");
this.configuration = config;
sf = newSessionFactory(config);
}
//最後把和線程綁定的資源清空
finally {
if (this.dataSource != null) {
// Reset DataSource holder.
configTimeDataSourceHolder.set(null);
}
if (this.jtaTransactionManager != null) {
// Reset TransactionManager holder.
configTimeTransactionManagerHolder.set(null);
}
if (this.lobHandler != null) {
// Reset LobHandler holder.
configTimeLobHandlerHolder.set(null);
}
}
// Execute schema update if requested.
if (this.schemaUpdate) {
updateDatabaseSchema();
}
return sf;
}
而直接調用org.hibernate.cfg.Configuration來得到需要的SessionFactory:
Java代碼
protected SessionFactory newSessionFactory(Configuration config) throws HibernateException {
return config.buildSessionFactory();
}
所以我們這裡看到LocalSessionFactory大致起到的一個讀取資源配置然後生成 SessionFactory的作用;當然這裡在得到 SessionFactory之後,還需要對session的事務 管理作一些處理 - 使用了一個Proxy模式對getCurrentSession方法進行了攔截;
Java代碼
//這裡先根據當前的SessionFactory的類型得到Proxy,然後插入Spring定義好 的getCurrentSession攔截器
protected SessionFactory getTransactionAwareSessionFactoryProxy (SessionFactory target) {
Class sfInterface = SessionFactory.class;
if (target instanceof SessionFactoryImplementor) {
sfInterface = SessionFactoryImplementor.class;
}
return (SessionFactory) Proxy.newProxyInstance (sfInterface.getClassLoader(),
new Class[] {sfInterface}, new TransactionAwareInvocationHandler(target));
}
攔截器的實現如下:
Java代碼
private static class TransactionAwareInvocationHandler implements InvocationHandler {
private final SessionFactory target;
public TransactionAwareInvocationHandler(SessionFactory target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Invocation on SessionFactory/SessionFactoryImplementor interface coming in...
// 這裡對getCurrentSession方法進行攔截,得到一個和當前事務綁定 的session交給用戶
if (method.getName().equals("getCurrentSession")) {
// Handle getCurrentSession method: return transactional Session, if any.
try {
return SessionFactoryUtils.doGetSession ((SessionFactory) proxy, false);
}
catch (IllegalStateException ex) {
throw new HibernateException(ex.getMessage());
}
}
else if (method.getName().equals("equals")) {
// Only consider equal when proxies are identical.
return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
}
else if (method.getName().equals("hashCode")) {
// Use hashCode of SessionFactory proxy.
return new Integer(hashCode());
}
// 這裡是需要運行的SessionFactory的目標方法
try {
return method.invoke(this.target, args);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
我們看看getCurrentSession的實現,在SessionFactoryUtils中:
Java代碼
private static Session doGetSession(
SessionFactory sessionFactory, Interceptor entityInterceptor,
SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate)
throws HibernateException, IllegalStateException {
Assert.notNull(sessionFactory, "No SessionFactory specified");
//這個TransactionSynchronizationManager的Resource是一個ThreadLocal 變量,sessionFactory是一個單例,但ThreadLocal是和線程綁定的
//這樣就實現了Hiberante中常用的通過ThreadLocal的session管理機制
SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
if (sessionHolder != null && !sessionHolder.isEmpty()) {
// pre-bound Hibernate Session
Session session = null;
if (TransactionSynchronizationManager.isSynchronizationActive() &&
sessionHolder.doesNotHoldNonDefaultSession()) {
// Spring transaction management is active ->
// register pre-bound Session with it for transactional flushing.
session = sessionHolder.getValidatedSession();
if (session != null && ! sessionHolder.isSynchronizedWithTransaction()) {
logger.debug("Registering Spring transaction synchronization for existing Hibernate Session");
TransactionSynchronizationManager.registerSynchronization(
new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false));
sessionHolder.setSynchronizedWithTransaction(true);
// Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
// with FlushMode.NEVER, which needs to allow flushing within the transaction.
FlushMode flushMode = session.getFlushMode();
if (flushMode.lessThan(FlushMode.COMMIT) &&
! TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
session.setFlushMode(FlushMode.AUTO);
sessionHolder.setPreviousFlushMode(flushMode);
}
}
}
else {
// No Spring transaction management active -> try JTA transaction synchronization.
session = getJtaSynchronizedSession(sessionHolder, sessionFactory, jdbcExceptionTranslator);
}
if (session != null) {
return session;
}
}
//這裡直接打開一個Session
logger.debug("Opening Hibernate Session");
Session session = (entityInterceptor != null ?
sessionFactory.openSession(entityInterceptor) : sessionFactory.openSession());
// Use same Session for further Hibernate actions within the transaction.
// Thread object will get removed by synchronization at transaction completion.
// 把新打開的Session放到SessionHolder,然後放到ThreadLocal裡面去和線 程綁定起來,這個ThreadLocal是在 TransactionSynchronizationManager中配置好的, 可以根據sessionFactory來索取
// 同時根據事務處理的狀態來配置session的屬性,比如把FlushMode設置為 Never,同時把session和事務處理關聯起來
if (TransactionSynchronizationManager.isSynchronizationActive()) {
// We're within a Spring-managed transaction, possibly from JtaTransactionManager.
logger.debug("Registering Spring transaction synchronization for new Hibernate Session");
SessionHolder holderToUse = sessionHolder;
if (holderToUse == null) {
holderToUse = new SessionHolder(session);
}
else {
holderToUse.addSession(session);
}
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
session.setFlushMode(FlushMode.NEVER);
}
TransactionSynchronizationManager.registerSynchronization(
new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != sessionHolder) {
TransactionSynchronizationManager.bindResource (sessionFactory, holderToUse);
}
}
else {
// No Spring transaction management active -> try JTA transaction synchronization.
registerJtaSynchronization(session, sessionFactory, jdbcExceptionTranslator, sessionHolder);
}
// Check whether we are allowed to return the Session.
if (!allowCreate && !isSessionTransactional(session, sessionFactory)) {
closeSession(session);
throw new IllegalStateException("No Hibernate Session bound to thread, " +
"and configuration does not allow creation of non-transactional one here");
}
return session;
}
這裡就是在Spring中為使用Hiberante的SessionFactory以及Session做的准備工作, 在這個基礎上,用戶可以通過使用 HibernateTemplate來使用Hibernate的O/R功能,和以 前看到的一樣這是一個execute的回調:
Java代碼
public Object execute(HibernateCallback action, boolean exposeNativeSession) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
//這裡得到配置好的Hibernate的Session
Session session = getSession();
boolean existingTransaction = SessionFactoryUtils.isSessionTransactional(session, getSessionFactory());
if (existingTransaction) {
logger.debug("Found thread-bound Session for HibernateTemplate");
}
FlushMode previousFlushMode = null;
try {
previousFlushMode = applyFlushMode(session, existingTransaction);
enableFilters(session);
Session sessionToExpose = (exposeNativeSession ? session : createSessionProxy(session));
//這裡是回調的入口
Object result = action.doInHibernate(sessionToExpose);
flushIfNecessary(session, existingTransaction);
return result;
}
catch (HibernateException ex) {
throw convertHibernateAccessException(ex);
}
catch (SQLException ex) {
throw convertJdbcAccessException(ex);
}
catch (RuntimeException ex) {
// Callback code threw application exception...
throw ex;
}
finally {
//如果這個調用的方法在一個事務當中,
if (existingTransaction) {
logger.debug("Not closing pre-bound Hibernate Session after HibernateTemplate");
disableFilters(session);
if (previousFlushMode != null) {
session.setFlushMode(previousFlushMode);
}
} //否則把Session關閉
else {
// Never use deferred close for an explicitly new Session.
if (isAlwaysUseNewSession()) {
SessionFactoryUtils.closeSession(session);
}
else {
SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());
}
}
}
}
我們看看怎樣得到對應的Session的,仍然使用了SessionFactoryUtils的方法 doGetSession:
Java代碼
protected Session getSession() {
if (isAlwaysUseNewSession()) {
return SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor());
}
else if (!isAllowCreate()) {
return SessionFactoryUtils.getSession(getSessionFactory(), false);
}
else {
return SessionFactoryUtils.getSession(
getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator());
}
}
這樣我們就可以和其他的Template那樣使用Hibernate的基本功能了,使用的時候 Spring已經為我們對Session的獲取和關閉,事務處理的綁定做好了封裝 - 從這個角度看 也大大方便了用戶的使用。