通向架構師的道路(第十八天)萬能框架 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技能
※Spring Converter 入門之字元串轉化為枚舉
※什麼時候使用 CountDownLatch
TAG:ImportNew |