歡迎您光臨本站 註冊首頁

Spring的事務

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0

  今天對 spring 的 AOP 事務有了一個新的認識,所以趕緊把今天的學習記下來,希望在今後的學習中能夠起到一些作用,也能對今天的認識做一次總結.

  1.spring 分享

  先看一段代碼:

  Connection conn = Conn.getConnection();

  conn.setAutoCommit(false);

  ……..

  ……...

  conn.rollback();

  conn.commit();

  資料庫的事務是針對 Connection 的.

  接著再看一段代碼:( spring 中事務的一段學習代碼,這段代碼是把 spring 和 hibernate 結合在一起的,增加了理解上的難度,我的出發點一開始不要 hibernate ,就光用 jdbc 來進行資料庫事務,但是沒有其他好的代碼,就這樣吧)

  public Long addLineItem(Long orderId, LineItem lineItem){

  log("OrderListDAOHibernate.addLineItem : Start...");

  OrderList orderList = (OrderList) getHibernateTemplate().load(OrderList.class, orderId);

  lineItem.setOrderList(orderList);

  getHibernateTemplate().saveOrUpdate(lineItem);

  getHibernateTemplate().saveOrUpdate(orderList);

  log("OrderListDAOHibernate.addLineItem : Ending...");

  return lineItem.getId();

  }

  在這個代碼的配置文件中,把 addLineItem 做為一個切入點,進行事務,也就是說,在 addLineItem 的外面,再包上一層事務的外殼.

  但是這個時候,問題出來了,事務是針對 Connection 的,而上面的兩個連續的 HibernateTemplate 執行的 saveOrUpdate 中的 Connection 必須是一致才能用事務, spring 怎麼做到這一點的呢?(這個問題也就是在找 spring 的事務例子前,我想的 spring 中用 jdbc 來進行事務,怎麼樣讓 Connection 保持一致呢?但是沒有 jdbc 的例子,只有整合 hibernate 或者 ibatis 的例子,但是,我想,原理是一樣的吧.)

  解決問題的思路: HibernateTemplate 中的 Connection 必定一致.那麼就從 HibernateTemplate 入手.

  看 spring 的源代碼,既然是 Hibernate ,那麼,就沒有 Connection 給你看,只有 Session ,由 Session 來管理 Connection ,那麼用事務來控制的話,這個 Session 必定在所有該事務中是一致的.於是在 HibernateTemplate 中找到:

  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());

  }

  }

  看來在 SessionFactoryUtils 裡面,接著在 SessionFactoryUtils.getSession 中找:

  這個方法太長了,太複雜了,從簡,發現了非常關鍵的一點:

  SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

  假如 sessionHolder 不等於空,說明,在事務中有這樣一個還沒有 commit 的 session ,那麼就返回這個 session ,假如等於空,新建一個 session ,並且在事務里加入這個 session .這段代碼的意思大概是這樣,太繁雜了,只能猜,也肯定是如此.

  再看 getHibernateTemplate() 方法來自繼承 HibernateDaoSupport ,看了電子書《 spring-reference 》的第九章「 Dao 支持」, Dao 的支持類可以有好多,如: JdbcDaoSupport , HibernateDaoSupport , JdoDaoSupport 等等.

  既然前面一開始就是從 jdbc 的 spring 事務控制引起的,那麼看到了同樣的 HibernateDaoSupport---JdbcDaoSupport ,那麼 JdbcDaoSupport 也應該有 getJdbcTemplate() 這個方法,並且返回 JdbcTemplate 這個類.

  果然如此.

  於是剖析 JdbcTemplate 是不是和 HibernateTemplate 一樣.果然一樣.

  注意到:

  Connection con = DataSourceUtils.getConnection(getDataSource());

  Connection 是從 DataSourceUtils.getConnection() 來的,繼續跟蹤 DataSourceUtils.getConnection() .

  找到:

  ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);

  和 Hibernate 中的一模一樣,沒有了 session 的封裝,條理在 jdbc 中更加清晰了.

  至此, spring 的事務控制 已經全部搞定.

  2.Spring 事務管理的配置

  看了上面同事學習 spring 的筆記后自己也覺得有新的理解,從什麼地方說起呢?就從 spring 的事務配置說起吧.那麼我們看看 contextConfig.xml 吧.

  <bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">

  <property name="dataSource">

  <ref bean="dataSource" />

  </property>

  <property name="mappingResources">

  <list>

  <value>mf/org/user/User.hbm.xml</value>

  </list>

  </property>

  </bean>

  <bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">

  <property name="sessionFactory">

  <ref local="sessionFactory" />

  </property>

  </bean>

  <bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">

  <property name="transactionManager">

  <ref bean="transactionManager" />

  </property>

  <property name="transactionAttributes">

  <props>

  <prop key="save*">PROPAGATION_REQUIRED,-Exception</prop>

  <prop key="remove*">PROPAGATION_REQUIRED,-Exception </prop>

  <prop key="update*">PROPAGATION_REQUIRED,-Exception </prop>

  <prop key="incress*">PROPAGATION_REQUIRED,-Exception </prop>

  <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>

  </props>

  </property>

  </bean>

  <bean id="userManager" parent="txProxyTemplate">

  <property name="target" ref="userManagerTarget" />

  </bean>

  <bean id="userManagerTarget"

  class=" mf.org.hb.user.service.impl.UserManagerImpl">

  <property name="userDAO" ref="userDAO" />

  </bean>

  <bean id="userDAO" class="mf.org.hb.user.dao.hibernate.UserDAOHibernate">

  <property name="sessionFactory" ref="sessionFactory" />

  </bean>

  以上就是一個完整的 spring 配置,是不是很熟悉呢,這裡是用的 Appfuse 的框架,呵呵.有那麼點味道吧.

  我們看看

  <bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">

  <property name="sessionFactory">

  <ref local="sessionFactory" />

  </property>

  </bean>

  這一個 bean 讓 spring 為我們注入了什麼呢?事務,對!我們把 hibernate 的事務注入到了 spring 的 IOC 容器之中了.然後我們再看看:

  <bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">

  <property name="transactionManager">

  <ref bean="transactionManager" />

  </property>

  <property name="transactionAttributes">

  <props>

  <prop key="save*">PROPAGATION_REQUIRED,-Exception</prop>

  <prop key="remove*">PROPAGATION_REQUIRED,-Exception </prop>

  <prop key="update*">PROPAGATION_REQUIRED,-Exception </prop>

  <prop key="incress*">PROPAGATION_REQUIRED,-Exception </prop>

  <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>

  </props>

  </property>

  </bean>

  這個 bean 又是讓 spring 為我們注入了了什麼呢?事務代理,對了!我們把事務的代理交給一個 txProxyTemplate 的去做了,這樣的好處我待會再說,現在我們看看下面的一些配置信息.

  <prop key="save*">PROPAGATION_REQUIRED,-Exception</prop>

  <prop key="remove*">PROPAGATION_REQUIRED,-Exception </prop>

  <prop key="update*">PROPAGATION_REQUIRED,-Exception </prop>

  <prop key="incress*">PROPAGATION_REQUIRED,-Exception </prop>

  <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>

  這裡就是事務處理時如果遇到異常信息,或者其他的原因時我們要求 spring 把當前的事務回滾了,這樣才能不至於在資料庫中產生垃圾啊.我們規定所有的 save,remove,update,incress 這樣的方法開頭的在出現一些問題后把事務給回滾了,看看我們寫的: PROPAGATION_REQUIRED,-Exception .

  有人就會說 PROPAGATION_REQUIRED 就可以回滾事務啊,為什麼加上 ,-Exception 呢?其實我以前也時這樣想的,但這是不完全正確的,當然我們在處理一個事務時只要有一個 PROPAGATION_REQUIRED 就可以了,但是當我們的業務邏輯中要求我們在一個事務代理中開啟兩個事務,這兩個事務表面上沒有聯繫,但是實際中又有很多聯繫的,比如我們上傳附件和提交文檔,這樣兩個操作我們可以分開,他們不是往一個表裡插入數據,我們又不希望這兩個操作寫在一個 service 里,這樣我們要是有一個業務只要上傳附件呢?那樣我們是不是又要再寫一個方法啊!所以在開啟兩個事務時如果有一個拋出異常了,我們就要把上一個提交的事務回滾了,這樣做我們就要用的 -Exception 了,這樣就完全滿足我們的要求了,我也試過如果我寫的是 PROPAGATION_REQUIRED,-SQLException 時,這樣我們只會在出現 SQLException 時事務回顧,出現其他的異常事務就不回滾了,好在 spring 可以讓我們寫如異常的基類就可以做到捕獲任何異常,這樣我們就寫 -Exception 好了.特殊情況在特殊處理吧.通用情況下我們還是這樣的.

  我們再看看:

  <bean id="userManager" parent="txProxyTemplate">

  <property name="target" ref="userManagerTarget" />

  </bean>

  <bean id="userManagerTarget"

  class="mf.org.hb.user.service.impl.UserManagerImpl">

  <property name="userDAO" ref="userDAO" />

  </bean>

  <bean id="userDAO" class="mf.org.hb.user.dao.hibernate.UserDAOHibernate">

  <property name="sessionFactory" ref="sessionFactory" />

  </bean>

  當然我們也可以寫成:

  <bean id="userManager" parent="txProxyTemplate">

  <property name="target">

  <bean class="mf.org.hb.user.service.impl.UserManagerImpl">

  <property name="userDAO">

  <ref bean="userDao"/>

  </property>

  </bean>

  </property>

  </bean>

  <bean id="userDAO" class="mf.org.hb.user.dao.hibernate.UserDAOHibernate">

  <property name="sessionFactory" ref="sessionFactory" />

  </bean>

  這下我們解除以前的疑惑, parent="txProxyTemplate" 知道我們為什麼在上面先寫了 txProxyTemplate 的 bean 了吧,這樣我們就沒有必要再寫一編了.是不是很方便? spring 的這些技巧還不只這些呢.這樣我們就可以輕鬆利用以上這三個注入的類去做我們的邏輯了.

  Spring 就是要我們注入實現類,然後使用介面操作,這樣耦合性就不是那麼強了,這也體現了 Spring 的工廠模式.而 AOP 的 manager 又象我們熟知的代理模式吧 !

  3.注意要點

  在寫配置的時候注意各個 Manager 和 DAO 之間的關係,以及 <ref= 」」 > 之間的關係,清晰裡面的關係才能更好的配置


[火星人 ] Spring的事務已經有816次圍觀

http://coctec.com/docs/java/show-post-60133.html