當前位置:
首頁 > 知識 > 通向架構師的道路(第十八天)萬能框架 Spring ( 一 )(下)

通向架構師的道路(第十八天)萬能框架 Spring ( 一 )(下)

(點擊

上方公眾號

,可快速關注)




來源:袁鳴凱,


blog.csdn.net/lifetragedy/article/details/8096762




了解完了applicationContext.xml文件內容後我們繼續看下去:




jdbc.properties文件







jdbc.driverClassName=oracle.jdbc.OracleDriver


jdbc.databaseURL=jdbc:oracle:thin:@localhost:1521:ymkorcl


jdbc.username=alpha


jdbc.password=ENC(W1BJSjx6+1O1z3ArmojmaQG+r80ty3zX)




如何把這個jdbc.password後的值進行加密呢?我們來看:




Jasypt加密解密步驟一


首先你要下載最新版的jasypt,目前是1.9,除了把這三個jar文件






Jasypt加密解密步驟二


打開一個command窗口輸入如下的命令,假設我們的jdbc.password後的值為:password_1,要把這個password_1用PBEWITHMD5ANDDES加密,我們輸入如下的命令:







把OUTPUT這段複製下來後放入我們的properties 文件內,並用ENC()包括起來,這樣我們的spring就會在我們的J2EE容器啟動時碰到指定的properties文件中如果含有ENC()括起來的東西,去自動執行相當於如下的解密命令了:







而這邊的password就是你在環境變數中設定的:APP_ENCRYPTION_PASSWORD的值。



datasource.xml文件





<?xml version="1.0" encoding="UTF-8"?>


 


<beans xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"


 


      xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"


 


          xmlns:context="http://www.springframework.org/schema/context" xmlns="http://www.springframework.org/schema/beans"


 


          xsi:schemaLocation="


 


       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd


 


       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-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/context


 


 


http://www.springframework.org/schema/context/spring-context-3.0.xsd">


 


          <bean p:dataSource-ref="dataSource" />


 


    <!-- configure data base connection pool by using C3P0 -->


 


          <bean id="dataSource" destroy-method="close">


 


                <property name="driverClass" value="${jdbc.driverClassName}" />


 


                <property name="jdbcUrl" value="${jdbc.databaseURL}" />


 


                <property name="user" value="${jdbc.username}" />


 


                <property name="password" value="${jdbc.password}" />


 


                <property name="initialPoolSize" value="10" />


 


                <property name="minPoolSize" value="10" />


 


                <property name="maxPoolSize" value="15" />


 


                <property name="acquireIncrement" value="1" />


 


                <property name="maxIdleTime" value="5" />


 


        </bean>


 


          <bean id="transactionManager">


 


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


 


        </bean>


 


          <tx:advice id="txAdvice" transaction-manager="transactionManager">


 


                <tx:attributes>


 


                        <tx:method name="submit*" propagation="REQUIRED" rollback-for="java.lang.Exception" />


 


                        <tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception" />


 


                        <tx:method name="del*" propagation="REQUIRED" rollback-for="java.lang.Exception" />


 


                        <tx:method name="upd*" propagation="REQUIRED" rollback-for="java.lang.Exception" />


 


                        <tx:method name="save*" propagation="REQUIRED" rollback-for="java.lang.Exception" />


 


                        <tx:method name="query*" read-only="true" />


 


                        <tx:method name="find*" read-only="true" />


 


                        <tx:method name="get*" read-only="true" />


 


                        <tx:method name="view*" read-only="true" />


 


                        <tx:method name="search*" read-only="true" />


 


                        <tx:method name="check*" read-only="true" />


 


                        <tx:method name="is*" read-only="true" />


 


                        <tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception" />


 


                </tx:attributes>


 


        </tx:advice>


 


          <aop:config>


 


                <aop:pointcut id="serviceMethod" expression="execution(* org.sky.ssh1.alpha.service.impl.*.*(..))"/>


 


                <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" />


 


        </aop:config>


 


</beans>




我們來解讀這個datasource.xml文件吧,很簡單。




1)  該工程不使用任何容器內設的jdbcconnection pool的jndi也不使用jdbc直連,而是自帶一個叫c3p0的開源免費connection pool,因此你需要把







這個jar拷入工程的WEB-INF/lib目錄下,並且要把它拷入tomcat的lib目錄下。




1)  該工程使用jdbctemplate來調用我們的sql


2)  該工程使用聲明式事務管理




所謂聲明式事務就是「容器事務管理」,這就是在遠古時學習過ejb2.x的人的好處了,因為的遠古的 ejb2.0時就已經說了容器事務管理的好處了,就是你的service方法如果拋出指定的exception,那麼容器會自動rollback你這個service中所有的操作,如果在到達service結尾處還是沒有指定的exception拋出,那麼該service內執行的所有資料庫相關將自動被commit(筆者記得這種方法的使用,那已經是11年前的事了已經是,當時是P都看不懂什麼叫「聲明式」)。




還有一種事務叫「編程式事務」,即你自己在代碼裏手工在try{}塊的最後調用tran.commit,在catch{}塊中手工調用tran.rollback。當然,難免漏commit,忘rollback,所以聲明式事務的好處也體現了出來了。




3)  對於所有的「org.sky.ssh1.alpha.service.impl」這個包下所有的以:




is,check,select,query,get,search開頭的public方法,以只讀的方式即不啟用事務的方式來進行資料庫調用




對於所有的「org.sky.ssh1.alpha.service.impl「這個包下的所有的以:




upd,del,add,submit,save開頭的public方法全部進行事務調用,如果碰到拋出




java.lang.Exception或者繼承自java.lang.Exception的異常自動進行rollback。




看到這兒,我們明白了,網上一直說的:




事務要切在service方法上;


資料庫調用必須套在service方法內;


的真正意思了.




3.3 login的例子




我們先用一個簡單的login例子來使用我們的框架吧,先來看login例子的流程,很簡單。







相關的sql也很簡單:





SELECT count(1) from t_login where login_id=? and login_pwd=?




如何該sql返回0,代表不存在該用戶或者是用戶名/密碼輸出了,如果返回為1則代表登錄成功.




3.3.1 讓我們的sql變得可配置




我們在做工程時經常面臨這樣的一個問題,就是我們要麼把我們的sql寫成我們的class文件里和我們的代碼混寫,好一點的人喜歡聲明成constants變數(這個還好一點),但是這兩種方法都需要我們重編譯我們的工程,我們有沒有一種方法直接把我們的sql就寫成外部的xml文件里,然後在工程布署後我們可以經常修改(比如說長的SQL語句需要調優,這個如果改在代碼里工作量不得了,引起的牽連問題也會很多)。當然現在我們有了spring,我們可以這麼做,我們聲明一個loginDAO.xml文件,把SQL通過外部注入進loginDAO的相關方法。




3.3.2工程的結構安排







3.3.3 LoginDAO模塊


LoginDAO有LoginDAO介面與LoginDAOImpl實現類兩個類組成:




loginDAO.xml





<?xml version="1.0" encoding="UTF-8"?>


 


<beans xmlns="http://www.springframework.org/schema/beans"


 


                        xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"


 


                        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"


 


                        xmlns:context="http://www.springframework.org/schema/context"


 


                        xsi:schemaLocation="


 


       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd


 


       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-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/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">


 


          <bean id="loginDAO">


                      <property name="sql">


                                      <value>


                                                <![CDATA[


                                                             SELECT count(1) from t_login where login_id=? and login_pwd=?


                                                    ]]>


                                   </value>


                      </property>


          </bean>


</beans>




LoginDAO.java




package org.sky.ssh1.alpha.dao;


 


 



public interface LoginDAO {


                        public boolean login(String loginId, String loginPwd) throws Exception;


}




LoginDAOImpl.java





package org.sky.ssh1.alpha.dao.impl;


 


import org.sky.ssh1.alpha.dao.LoginDAO;


import org.springframework.stereotype.Repository;


import org.springframework.beans.factory.annotation.Autowired;


import org.springframework.jdbc.core.JdbcTemplate;


import javax.sql.*;


 


@Repository


 


public class LoginDAOImpl implements LoginDAO {


 


  


 


                        @Autowired


 


                        private DataSource dataSource;


 


                        @Autowired


 


                        private JdbcTemplate jdbcTemplate;


 


                        private String sql = "";


 


  


 


                        public void setSql(String sql) {


 


                                                this.sql = sql;


 


                        }


 


  


 


                        public boolean login(String loginId, String loginPwd) throws Exception {


 


                                                boolean answer = false;


 


                                                int recordCount = 0;


 


                                                recordCount = jdbcTemplate.queryForInt(sql, loginId, loginPwd);


 


                                                if (recordCount == 1) {


                                                                        answer = true;


                                                }


                                                return answer;


                        }


}


 


注意類上方的「@Repository「,代表該類作為一個spring bean由spring進行管理(即可將其注入到其它類中去)


 


3.3.4 LoginService模塊




一個Service模塊由Service介面與ServiceImpl實現類組成


 


LoginService.java





package org.sky.ssh1.alpha.service;


 


public interface LoginService {


        public boolean login(String loginId, String loginPwd) throws Exception;


}




LoginServiceImpl.java





package org.sky.ssh1.alpha.service.impl;


 


import javax.annotation.Resource;


 


import org.apache.commons.logging.Log;


 


import org.apache.commons.logging.LogFactory;


 


import org.sky.ssh1.alpha.dao.LoginDAO;


 


import org.springframework.stereotype.Service;


 


@Service


 


public class LoginServiceImpl implements org.sky.ssh1.alpha.service.LoginService {


 


        private Log logger = LogFactory.getLog(this.getClass());


        @Resource


       private LoginDAO loginDAO;


 


        public boolean login(String loginId, String loginPwd) throws Exception {


                boolean answer = false;


                try {


                        answer = loginDAO.login(loginId, loginPwd);


                } catch (Exception e) {


                        logger.error("login error:" + e.getMessage(), e);


                }


                return answer;


        }


}




注意兩個加粗處的使用,一個是聲明該類為一個Service類(要被事務切),一個是如何用註解的方式引用另一個dao類。




然後我們再來看Login的Struts模塊




3.3.5 Login相關的Controller




一個controller有兩部分組成:




struts-config.xml文件與action相關class。




WEB-INF/struts-config/login.xml





<?xml version="1.0" encoding="ISO-8859-1" ?>


 


<!DOCTYPE struts-config PUBLIC


 


          "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"


 


          "http://struts.apache.org/dtds/struts-config_1_3.dtd">


 


<struts-config>


 


        <form-beans>


 


                <form-bean name="loginForm" type="org.sky.ssh1.alpha.login.form.LoginForm" />


 


        </form-beans>


 


        <global-forwards />


 


        <action-mappings>


 


                <action path="/login" name="loginForm" scope="request"


 


                        parameter="method" input="/jsp/login/login.jsp">


 


                        <forward name="login_init" path="/jsp/login/login.jsp" />


 


                        <forward name="login_fail" path="/login.do" />


 


                        <forward name="login_success" path="/index.do" />


 


                </action>


 


        </action-mappings>


 


</struts-config>




LoginAction.java





package org.sky.ssh1.alpha.login.action;


 


import org.apache.struts.actions.DispatchAction; 


import javax.annotation.Resource;


import javax.servlet.http.HttpServletRequest;


import javax.servlet.http.HttpServletResponse;


import javax.servlet.http.HttpSession;


import org.apache.commons.logging.Log;


import org.apache.commons.logging.LogFactory;


import org.apache.struts.action.ActionForm;


 


import org.apache.struts.action.ActionForward;


 


import org.apache.struts.action.ActionMapping;


 


import org.sky.ssh1.alpha.service.LoginService;


 


import org.sky.ssh1.alpha.student.form.StudentForm;


 


import org.springframework.stereotype.Controller;


 


  


 


@Controller("/login")


 


public class LoginAction extends DispatchAction {


 


          protected final Log logger = LogFactory.getLog(getClass());


 


          @Resource


 


        LoginService loginService;


 


          public ActionForward submit(ActionMapping mapping, ActionForm form, HttpServletRequest request,


 


                             HttpServletResponse response) throws Exception {


 


                   String loginId = "";


 


                   String loginPwd = "";


 


                   try {


 


                             loginId = (String) request.getParameter("loginId");


 


                             loginPwd = (String) request.getParameter("loginPwd");


 


                             if (loginService.login(loginId, loginPwd)) {


 


                                      return new ActionForward("/index.do", true);


 


                             } else {


 


                                      request.setAttribute("loginCode", "101");


 


                                      return new ActionForward("/jsp/login/login.jsp", false);


 


                             }


 


                   } catch (Exception e) {


 


                             logger.error("UserLogin Exception:" + e.getMessage(), e);


 


                             return mapping.findForward("error");


 


                   }


 


          }


 


          public ActionForward unspecified(ActionMapping mapping, ActionForm form, HttpServletRequest request,


 


                             HttpServletResponse response) throws Exception {


 


                   try {


 


                             StudentForm stdForm = new StudentForm();


 


                             request.setAttribute("stdForm", stdForm);


 


                   } catch (Exception e) {


 


                             logger.error("UserLogin Exception:" + e.getMessage(), e);


 


                             return mapping.findForward("error");


 


                   }


 


                   return null;


 


          }


 


}




注意:




@Controller(「/login」)的使用,該註解將這個LoginAction委託給了spring進行管理了,這邊的路徑名必須和你在struts-config相關配置文件里的action的mapping名完全相等。




@Resource




LoginServiceloginService;




的使用,代表把service相關的功能注入給了LoginAction類。




登錄失敗效果:







登錄成功效果:







3.4 如何處理一個DAO對應多個SQL語句




有時,我們一個DAO方法除了select、get方法還會有del,add,upd等public方法,我們不可能為了每個publich方法再單獨去聲明一個*DAO.xml文件對吧,這樣做的話就會造成xml文件泛濫,那麼我們可以在xml文件中使用如下的技巧,如studentDAO類:




studentDAO.xml





<?xml version="1.0" encoding="UTF-8"?>


 


<beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p"


 


      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"


 


          xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"


 


          xsi:schemaLocation="


 


       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd


 


       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-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/context


 


 


http://www.springframework.org/schema/context/spring-context-3.0.xsd">


 


          <bean id="studentDAO">


 


                   <property name="sql">


 


                             <map>


 


                                <entry key="getAllStudent">


 


                                        <value>


 


                                                <![CDATA[


 


                                                SELECT student_no, student_name from t_student


 


                                                ]]>


 


                                        </value>


 


                                </entry>


 


                                <entry key="delStudent">


 


                                        <value>


 


                                                <![CDATA[


 


                                                delete from t_student where student_no=?


 


                                                ]]>


 


                                        </value>


 


                                </entry>


 


                                      <entry key="addStudent">


 


                                                <value>


 


                                                          <![CDATA[


 


                                                          insert into t_student(student_no, student_name)values(seq_student_no.nextval,?)


 


                                                          ]]>


 


                                                </value>


 


                                      </entry>


 


                             </map>


 


                   </property>


 


          </bean>


 


</beans>




那麼我們在使用時就可以如




StudentDAOImpl.java





public List<StudentDBO> getAllStudent() throws Exception {


 


                   List<StudentDBO> stdList = new ArrayList<StudentDBO>();


 


                   stdList = jdbcTemplate.query((String) sql.get("getAllStudent"), new Object[] {}, stdItemRowMapper());


 


                   return stdList;


 


}


 


public void addStudent(final String stdName) throws Exception {


 


                   jdbcTemplate.update((String) sql.get("addStudent"), new PreparedStatementSetter() {


 


                             public void setValues(PreparedStatement ps) throws SQLException {


 


                                      ps.setString(1, stdName);


 


                             }


 


                   });


 


}


 


public void delStudent(final String stdNo) throws Exception {


 


                   jdbcTemplate.update((String) sql.get("delStudent"), new PreparedStatementSetter() {


 


                             public void setValues(PreparedStatement ps) throws SQLException {


 


                                      ps.setString(1, stdNo);


 


                             }


 


                   });


 


}




看到沒有,加粗部分對於「一個dao如何對應多個 sql的使用」技巧。




3.5 驗證我們的聲明式事務




我們前面說了,只要我們使用表達式內指定的service的public方法拋出一個java.lang.Exception,容器就會為我們自動回滾該事務嗎?




即一個service方法內,如果調用了一連串的dao,如果沒有任何exception拋出則commit,如果有exception拋出則自動rollback該service的public方法中的所有資料庫操作。




我們來看一個例子。




StudentService中的delStudent方法





package org.sky.ssh1.alpha.service;


 


import java.util.List;


import org.sky.ssh1.alpha.dbo.StudentDBO;


import org.sky.ssh1.alpha.student.form.StudentForm;


 


 


public interface StudentService {


 


                public List<StudentForm> getAllStudent() throws Exception;


                public void addStudent(String stdName) throws Exception;


                public void delStudent(String[] stdNo) throws Exception;


 


}






StudentServiceImpl實現類片段





public void delStudent(String[] stdNo) throws Exception {


 


        for (String s : stdNo) {


 


                studentDAO.delStudent(s);


 


                throw new Exception("force system to throw a exception");


        }

 


}




該方法接受一個String數組,循環調用相關的dao方法來刪除從頁面選擇的student。




我們在for循環下方故意拋出一個exception,來看效果.




這是原來的數據:







下面是相關的頁面顯示:







我們選擇Student_No=12和Student_No=13(是蠻13的)的兩個學生,進行刪除。




通過時候關的service方法內的邏輯我們可以得知,Student_No=12的刪除dao調用是成功的,而到了刪除的dao要調用Student_No=13時會遭遇一個強制拋錯,於是頁面出錯,按照聲明式事務的理論,這兩個dao在一個service的public方法中被調用,因此一旦這個service方法拋錯,這個service中所有的dao操作將會被容器自動回滾,那我們來看:




選擇Student_No=12和Student_No=13,點刪除按鈕







頁面出錯了:







後台拋:







查看資料庫發覺記錄依然在(13的人真是難刪,呵呵),說明我們的事務的聲明是成功的.







結束今天的教程.




四 相關資料庫表結構




4.1 t_login表







4.2 t_student表







4.3 seq_student_no序列





CREATESEQUENCE  "ALPHA"."SEQ_STUDENT_NO" MINVALUE1MAXVALUE9999999999999999999INCREMENTBY




系列






  • 通向架構師的道路(第一天)之 Apache 整合 Tomcat



  • 通向架構師的道路(第二天)之 apache tomcat https 應用



  • 通向架構師的道路(第三天)之 apache 性能調優



  • 通向架構師的道路(第四天)之 Tomcat 性能調優



  • 通向架構師的道路(第五天)之 tomcat 集群 – 群貓亂舞



  • 通向架構師的道路(第六天)之漫談基於資料庫的許可權系統的設計



  • 通向架構師的道路(第七天)之漫談使用 ThreadLocal 改進你的層次的劃分



  • 通向架構師的道路(第八天)之 Weblogic 與 Apache 的整合與調優



  • 通向架構師的道路(第九天)之 Weblogic 的集群與配置



  • 通向架構師的道路 ( 第十天 ) 之 Axis2 Web Service ( 一 )



  • 通向架構師的道路 ( 第十一天 ) 之 Axis2 Web Service ( 二 )



  • 通向架構師的道路 ( 第十二天 ) 之 Axis2 Web Service ( 三 )



  • 通向架構師的道路 ( 第十三天 ) Axis2 Web Service 安全初步



  • 通向架構師的道路 ( 第十四天 ) Axis2 Web Service 安全之 rampart



  • 通向架構師的道路 ( 第十五天 ) IBM Websphere 的安裝與優化



  • 通向架構師的道路 ( 第十六天 ) IBM Websphere 與 IBM HttpServer 的集成



  • 通向架構師的道路 ( 第十七天 ) IBM Websphere 集群探秘 – WASND




看完本文有收穫?請轉發分享給更多人


關注「ImportNew」,提升Java技能


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 ImportNew 的精彩文章:

Spring Converter 入門之字元串轉化為枚舉
什麼時候使用 CountDownLatch

TAG:ImportNew |