Spring的事務管理難點剖析:應用分層的迷惑

Web、Service及DAO三層劃分就像西方國家的立法、行政、司法三權分立同樣被奉爲金 科玉律,甚至有的開發人員認爲若是要使用Spring的事務 管理就必定要先進行三層的劃分。這個看似荒唐的論調在開發人員中很有市場。更有甚者,認爲每層必須先定義一個接口,而後再定義一個實現類。其結果是:一個 很簡單的功能,也至少須要3個接口和3個類,再加上視圖層的JSP和JS等,打牌均可以圍上兩桌了,這種誤解害人不淺。
   對將「面向接口編程」 奉爲圭臬,認爲放之四海而皆準的論調,筆者深不覺得然。是的,「面向接口編程」是Martin Fowler、Rod Johnson這些大師提倡的行事原則。若是拿這條原則去開發框架和產品,怎麼強調都不爲過。可是,對於咱們通常的開發人員來講,作的最多的是普通工程項 目,每每只是一些對數據庫增、刪、查、改的功能。此時,「面向接口編程」除了帶來更多的類文件外,看不到更多其餘的好處。
java

  Spring框架所提供的各類好處(如AOP、註解加強、註解MVC等)的惟一前提就是讓POJO的類變成一個受Spring容器管理的Bean,除此以 外沒有其餘任何的要求。下面的實例用一個POJO完成全部的功能,既是Controller,又是Service,仍是DAO:mysql

package com.baobaotao.mixlayer; 02   
03 import org.springframework.beans.factory.annotation.Autowired; 
04 import org.springframework.jdbc.core.JdbcTemplate; 
05 import org.springframework.stereotype.Controller; 
06 import org.springframework.web.bind.annotation.RequestMapping; 
07   
08 //①將POJO類經過註解變成Spring MVC的Controller 
09 @Controller
10 public class MixLayerUserService { 
11   
12     //②自動注入JdbcTemplate 
13     @Autowired
14     private JdbcTemplate jdbcTemplate; 
15   
16     //③經過Spring MVC註解映射成爲處理HTTP請求的函數,同時做爲一個擁有事務性的方法 
17     @RequestMapping("/logon.do") 
18     @Transactional
19     public String logon(String userName,String password){ 
20         if(isRightUser(userName,password)){ 
21             String sql = "UPDATE t_user u SET u.score = u.score + ? WHERE user_name =?"; 
22             jdbcTemplate.update(sql,20,userName); 
23             return "success"; 
24         }else{ 
25             return "fail"; 
26         } 
27     } 
28       
29     private boolean isRightUser(String userName,String password){ 
30         //do sth 
31         return true; 
32     } 
33 }

經過@Controller註解將MixLayerUserService變成Web層的Controller,同時也是Service層的服務類。此 外,因爲直接使用JdbcTemplate訪問數據,因此MixLayerUserService仍是一個DAO。來看一下對應的Spring配置文件:web

<?xml version="1.0" encoding="UTF-8" ?> 02 <beans xmlns="http://www.springframework.org/schema/beans"
03        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
04        xmlns:context="http://www.springframework.org/schema/context"
05        xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
06        xmlns:tx="http://www.springframework.org/schema/tx"
07        
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
08     http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-3.0.xsd 
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
http://www.springframework.org/schema/tx
 http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> 
09     … 
10     <!--①事務管理配置-> 
11     <bean id="transactionManager" 
12           class="org.springframework.jdbc.datasource.DataSourceTransactionManager" 
13           p:dataSource-ref="dataSource"/> 
14     <tx:annotation-driven/> 
15   
16           
17     <!--②啓動Spring MVC的註解功能-->
18     <bean class="org.springframework.web.servlet.mvc.annotation. 
19                   AnnotationMethodHandlerAdapter"/> 
20     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
21           p:prefix="/WEB-INF/jsp/" p:suffix=".jsp"/> 
22 </beans>

在①處,經過事務註解驅動使MixLayerUserService的logon()工做於事務環境下,②處配置了Spring MVC的一些基本設施。要使程序可以運行起來還必須進行web.xml的相關配置:spring

<?xml version="1.0" encoding="UTF-8"?> 02 <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
03     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
04     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee  
05     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> 
06     <context-param> 
07         <param-name>contextConfigLocation</param-name> 
08         <param-value>classpath:com/baobaotao/mixlayer/applicationContext.xml</param-value> 
09     </context-param> 
10     <context-param> 
11         <param-name>log4jConfigLocation</param-name> 
12         <param-value>/WEB-INF/classes/log4j.properties</param-value> 
13     </context-param> 
14   
15     <listener> 
16         <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> 
17     </listener> 
18     <listener> 
19         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
20     </listener> 
21   
22     <servlet> 
23         <servlet-name>user</servlet-name> 
24         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
25         <init-param> 
26             <param-name>contextConfigLocation</param-name> 
27             <param-value>classpath:com/baobaotao/mixlayer/applicationContext.xml</param-value> 
28         </init-param> 
29         <load-on-startup>1</load-on-startup> 
30     </servlet> 
31     <servlet-mapping> 
32         <servlet-name>user</servlet-name> 
33         <url-pattern>*.do</url-pattern> 
34     </servlet-mapping> 
35 </web-app>

這個配置文件很簡單,惟一須要注意的是DispatcherServlet的配置。默認狀況下Spring MVC根據Servlet的名字查找WEB-INF下的<servletName>-servlet.xml做爲Spring MVC的配置文件,在此,咱們經過contextConfigLocation參數顯式指定Spring MVC配置文件的確切位置。
   將org.springframework.jdbc及org.springframework.transaction的日誌級別設置爲DEBUG,啓 動項目,並訪問http://localhost:8088/chapter10/logon.do?userName=tom應 用,MixLayerUserService#logon方法將做出響應,查看後臺輸出日誌,以下所示:sql

引用
Returning cached instance of singleton bean 'transactionManager'
  Creating new transaction with name [com.baobaotao.mixlayer.MixLayerUserService.logon]:   
    PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
(DataSourceTransactionManager.java:204) - Acquired Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost , MySQL-AB JDBC Driver] for JDBC transaction
(DataSourceTransactionManager.java:221) - Switching JDBC Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost , MySQL-AB JDBC Driver] to manual commit
(JdbcTemplate.java:810) - Executing prepared SQL update
(JdbcTemplate.java:569) - Executing prepared SQL statement [UPDATE t_user u SET u.score = u.score + ? WHERE user_name =?]
(JdbcTemplate.java:819) - SQL update affected 0 rows
(AbstractPlatformTransactionManager.java:752) - Initiating transaction commit
(DataSourceTransactionManager.java:264) - Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost , MySQL-AB JDBC Driver]數據庫

   日誌中紅色部分說明了MixLayerUserService#logon方法已經正確運行在事務上下文中。編程

相關文章
相關標籤/搜索