(點選上方公眾號,可快速關註)
來源:袁鳴凱,
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技能