歡迎光臨
每天分享高質量文章

通向架構師的道路(第十八天)萬能框架 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檔案

 

 

      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”>

 

         

 

   

 

         

 

               

 

               

 

               

 

               

 

               

 

               

 

               

 

               

 

               

 

       

 

         

 

               

 

       

 

         

 

               

 

                       

 

                       

 

                       

 

                       

 

                       

 

                       

 

                       

 

                       

 

                       

 

                       

 

                       

 

                       

 

                       

 

               

 

       

 

         

 

               

 

               

 

       

 

我們來解讀這個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

 

 

                        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”>

 

         

                     

                                     

                                               

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

                                                    ]]>

                                   

                     

         

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

 

/span>

 

          “-//Apache Software Foundation//DTD Struts Configuration 1.3//EN”

 

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

 

 

       

 

               

 

       

 

       

 

       

 

               

 

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

 

                       

 

                       

 

                       

 

               

 

       

 

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

 

 

      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”>

 

         

 

                   

 

                             

 

                               

 

                                       

 

                                               

 

                                                SELECT student_no, student_name from t_student

 

                                                ]]>

 

                                       

 

                               

 

                               

 

                                       

 

                                               

 

                                                delete from t_student where student_no=?

 

                                                ]]>

 

                                       

 

                               

 

                                     

 

                                               

 

                                                         

 

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

 

                                                          ]]>

 

                                               

 

                                     

 

                             

 

                   

 

         

 

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

StudentDAOImpl.java

public List getAllStudent() throws Exception {

 

                   List stdList = new ArrayList();

 

                   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 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

系列


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

關註「ImportNew」,提升Java技能

贊(0)

分享創造快樂