導讀
在項目開(kāi)發(fā)中如何進(jìn)行需求分析,如何規劃數據庫、數據庫建模以及怎樣將模式設計應用于具體的項目困擾了開(kāi)發(fā)人員。本文以一個(gè)在線(xiàn)課程項目為線(xiàn)索全景展現軟件開(kāi)發(fā)的各個(gè)步驟。
閱讀導航
|
導航 |
簡(jiǎn)介 |
| CoursesOnline簡(jiǎn)介 | 簡(jiǎn)要介紹CoursesOnline系統的設計目的 |
| 開(kāi)發(fā)環(huán)境 | 介紹開(kāi)發(fā)工具 |
| 需求分析 | CoursesOnline系統開(kāi)發(fā)初期的需求調研,數據庫建模、數據字典、數據表的規劃 |
| CoursesOnline系統設計 | 在系統設計中引入設計模式,如何選擇設計模式 |
| 創(chuàng )建數據庫 | 在Oracle9i中創(chuàng )建數據庫 |
| CoursesOnline的具體實(shí)現 | 用JBuilder9開(kāi)發(fā)CoursesOnline系統步驟 |
| 部分代碼 | 部分步驟代碼 |
| 附錄 | Jbuilder9、Oracle9i、Weblogic7的安裝配置;數據表SQL腳本 |
二、開(kāi)發(fā)環(huán)境
CoursesOnline使用Jbuilder9 + Oracle9i + WebLogic7的開(kāi)發(fā)環(huán)境。
因為J2EE是一種行業(yè)標準,所以采用哪種開(kāi)發(fā)環(huán)境的搭配并不是最重要的。目前其它常見(jiàn)開(kāi)發(fā)環(huán)境的搭配還有Eclipse + MySQL + Jboss(都是OpenSource),VJA + DB2 + WebSphere,等等。
Jbuilder9 + Oracle9i + WebLogic 7開(kāi)發(fā)環(huán)境的配置請參考附錄A。
三、CoursesOnline需求分析
3.1 CoursesOnline用例


| 序號 | 字段名 | 數據類(lèi)型 | 約束 | 備注 |
| 1 | ActorID | SmallInt | PK | Actor標識符 |
| 2 | UserName | Varchar(20) | Actor登錄賬號 | |
| 3 | Password | Char(8) | Actor登錄口令 | |
| 4 | ActorType | SmallInt | Actor類(lèi)型,0:系統管理員;1:老師;2:學(xué)生 |
| 序號 | 字段名 | 數據類(lèi)型 | 約束 | 備注 |
| 1 | ActorID | SmallInt | FK(Actor) | Actor標識符 |
| 2 | ActorName | Varchar(20) | Actor姓名 | |
| 3 | Phone | Varchar(16) | 電話(huà) | |
| 4 | Varchar(50) |
| 序號 | 字段名 | 數據類(lèi)型 | 約束 | 備注 |
| 1 | CoursesID | SmallInt | PK | 課程標識符 |
| 2 | CoursesName | Varchar(20) | 課程名稱(chēng) | |
| 3 | StartDate | Date | 課程開(kāi)始時(shí)間 | |
| 4 | EndDate | Date | 課程結束 | |
| 5 | ActorID | SmallInt | FK(Actor) | |
| 6 | RoomID | SmallInt | FK(Room) | 教室標識符 |
| 序號 | 字段名 | 數據類(lèi)型 | 約束 | 備注 |
| 1 | ActorID | SmallInt | FK(Actor) | Actor標識符(學(xué)生,ActorType=2) |
| 2 | CoursesID | SmallInt | FK(Courses) | 課程標識符 |
| 序號 | 字段名 | 數據類(lèi)型 | 約束 | 備注 |
| 1 | RoomID | SmallInt | PK | 教室標識符 |
| 2 | RoomName | Varchar(30) | 教室名稱(chēng) |
四、CoursesOnline系統設計
4.1 Design Pattern的選擇和思考
在系統設計上,Design Pattern的選擇是很重要的。因為正確的Design Pattern不僅在開(kāi)發(fā)階段可以讓開(kāi)發(fā)人員思路清晰得心應手,而且在維護階段也不至于讓維護人員抓狂。除此之外,對于系統的健壯和運行效率而言也起著(zhù)舉足輕重的作用。如果采用了錯誤的Design Pattern,那么對于系統來(lái)說(shuō)就像是在錯誤的時(shí)間錯誤的地點(diǎn)與錯誤的敵人打了一場(chǎng)錯誤的戰爭。
下面以CoursesOnline系統學(xué)生注冊時(shí)采用的兩種不同的處理方法為例來(lái)簡(jiǎn)單說(shuō)明選擇Design Pattern的重要性。
學(xué)生在Client提交注冊信息,包括登錄賬號,登錄口令,姓名,電話(huà)和Email五項內容。從數據庫ER圖中我們可以看到學(xué)生信息被分散在兩張表里,也就是說(shuō)服務(wù)器端有兩個(gè)Entity Bean來(lái)存取學(xué)生的注冊信息,一個(gè)為Actor,另一個(gè)為ActorInfo。
方法一:客戶(hù)端直接與Entity Bean溝通以完成工作



五、創(chuàng )建數據庫
先有雞再有蛋?還是先有蛋再有雞?
先有數據庫表再有Entity Bean?還是先有Entity Bean再有數據庫表?
這兩個(gè)問(wèn)題有異曲同工之妙。雞與蛋的問(wèn)題已經(jīng)討論幾千年了,哲學(xué)的Big Fans可能會(huì )爭的臉紅耳赤唾沫橫飛;偶們只是普通的程序員,誰(shuí)先誰(shuí)后的問(wèn)題還留給理論學(xué)家吧。
可能會(huì )有人先設計Entity Bean再建數據庫表,也有可能反其道而行。在CoursesOnline這個(gè)實(shí)驗性質(zhì)系統的開(kāi)發(fā)過(guò)程中,是先建數據庫表然后才有Entity Bean。
5.1 新建一個(gè)數據庫
Oracle可以在命令行模式下敲入dbca,或者直接在開(kāi)始菜單里找到并運行Database Configuration Assistant,然后根據向導的提示新建一個(gè)名為CoursesDB的數據庫。

六、CoursesOnline的具體實(shí)現
6.1 在Jbuilder 9中新建一個(gè)工程
啟動(dòng)Jbuilder 9,File->New Project,項目名為CoursesOnline,選擇路徑后,點(diǎn)擊finish









6.3.2 登錄流程的代碼
/*--------------------------------------------------------------------------------------*/
//1、用戶(hù)登錄界面 - login.jsp
| <!- 部分HTML代碼此處省略 -- > <form name="teaForm" method="post" action="<%=contextPath%>/login.do"> ?。紅able border="0" cellspacing="0" cellpadding="0"> ?。紅r> ?。紅d width="60"><font size="2">用戶(hù)類(lèi)型</font></td> ?。紅d> ?。糹nput type="radio" value="0" name="actortype" checked><font size="2">系統管理員 </font> ?。糹nput type="radio" value="1" name="actortype"><font size="2">學(xué)生 </font> ?。糹nput type="radio" value="2" name="actortype"><font size="2">老師 </font> ?。?td> ?。?tr> ?。紅r> ?。紅d width="60"><font size="2">用戶(hù)名</font></td> ?。紅d><input name="username" value="" size="12"> *</td> ?。?tr> ?。紅r> ?。紅d width="60"><font size="2">口令</font></td> ?。紅d><input type="password" name="password" value="" size="8" maxlength="8"> *</td> ?。?tr> ?。?table> ?。糱r> ?。紅able border="0" cellspacing="0" cellpadding="0"> ?。紅r> ?。紅d width="50"> </td> ?。紅d><input type="submit" value="登 錄"></td> ?。?tr> ?。?table> </form> |
/*--------------------------------------------------------------------------------------*/
//2、Struts程序
//2.1 LoginForm.java(存儲用戶(hù)在頁(yè)面上提交的用戶(hù)名、口令和用戶(hù)類(lèi)型)
| package com.chenxc.coursesonline.struts; /** * Title: LoginForm * Description: 存儲登錄信息 * Time: 2004-3-20 * Company: * Author: chenxc * version 1.0 */ import java.util.*; public class LoginForm extends org.apache.struts.action.ActionForm{ private String username; //用戶(hù)名 private String password; //口令 private int actortype; //用戶(hù)類(lèi)型 //用戶(hù)名 public void setUsername(String username) { this.username = username; } public String getUsername() { return username; } //口令 public void setPassword(String password) { this.password = password; } public String getPassword() { return password; } //用戶(hù)類(lèi)型 public void setActortype(int actortype) { this.actortype = actortype; } public int getActortype() { return actortype; } } |
//2.2 LoginAction.java(控制登錄流程的走向)
| package com.chenxc.coursesonline.struts; /** * Title: LoginAction * Description: 登錄驗證 * Time: 2004-3-20 * Company: * Author: chenxc * version 1.0 */ import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import java.util.*; import org.apache.struts.action.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.ServletException; import com.chenxc.coursesonline.struts.*; public class LoginAction extends org.apache.struts.action.Action{ public ActionForward execute(ActionMapping mapping,ActionForm actionForm,HttpServletRequest request,HttpServletResponse response) throws Exception { ActionErrors errors = new ActionErrors(); HttpSession session = request.getSession(); ActionForward forward = null; LoginForm loginForm = (LoginForm)actionForm; FacadeBean facadeBean = new FacadeBean(); if(facadeBean.actorlogin(loginForm.getUsername(),loginForm.getPassword(),loginForm.getActortype())) { forward = mapping.findForward("success"); return forward; } return (new ActionForward(mapping.getInput())); } } |
//2.3 FacadeBean.java(實(shí)現登錄驗證)
| package com.chenxc.coursesonline.struts; /** * Title: FacadeBean * Description: 負責與Session Bean溝通 * Time: 2004-3-20 * Company: * Author: chenxc * version 1.0 */ import com.chenxc.coursesonline.ejb20.*; import javax.naming.*; import java.util.Properties; import javax.rmi.PortableRemoteObject; import javax.ejb.CreateException; import java.rmi.RemoteException; public class FacadeBean extends Object { private static final String ERROR_NULL_REMOTE = "Remote interface reference is null. It must be created by calling one of the Home interface methods first."; private static final int MAX_OUTPUT_LINE_LENGTH = 100; private CoursesFacadeHome coursesFacadeHome = null; private CoursesFacade coursesFacade = null; //Construct the FacadeBean public FacadeBean() { initialize(); } public void initialize() { try { //get naming context Context context = getInitialContext(); //look up jndi name Object ref = context.lookup("CoursesFacade"); //look up jndi name and cast to Home interface coursesFacadeHome = (CoursesFacadeHome) PortableRemoteObject.narrow(ref,CoursesFacadeHome.class); } catch (Exception e) { e.printStackTrace(); } } private Context getInitialContext() throws Exception { String url = "t3://192.168.100.134:7001"; String user = null; String password = null; Properties properties = null; try { properties = new Properties(); properties.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory"); properties.put(Context.PROVIDER_URL, url); if (user != null) { properties.put(Context.SECURITY_PRINCIPAL, user); properties.put(Context.SECURITY_CREDENTIALS, password == null ? "" : password); } return new InitialContext(properties); } catch (Exception e) { throw e; } } //actorlogin public boolean actorlogin(String username, String password, int actortype) { try { coursesFacade = coursesFacadeHome.create(); if (coursesFacade.actorLogin(username, password, actortype)) { return true; } else { return false; } } catch (CreateException ex) { ex.printStackTrace(); } catch (RemoteException ex) { ex.printStackTrace(); } return false; } } |
/*--------------------------------------------------------------------------------------*/
//3、Session Bean程序
//3.1 SessionFacadeHome.java
| package com.chenxc.coursesonline.ejb20; import javax.ejb.*; import java.util.*; import java.rmi.*; public interface SessionFacadeHome extends javax.ejb.EJBHome { public SessionFacade create() throws CreateException, RemoteException; } |
//3.2 SessionFacade.java
| package com.chenxc.coursesonline.ejb20; import javax.ejb.*; import java.util.*; import java.rmi.*; public interface SessionFacade extends javax.ejb.EJBObject { public boolean actorLogin(String actor, String password, int actortype) throws RemoteException; } |
//3.2 SessionFacadeBean.java
| package com.chenxc.coursesonline.ejb20; import javax.ejb.*; import javax.naming.*; import java.rmi.RemoteException; public class SessionFacadeBean implements SessionBean { SessionContext sessionContext; ActorHome actorHome; Actor actor; public void ejbCreate() throws CreateException { try { Context ctx = new InitialContext(); actorHome = (ActorHome)ctx.lookup("Actor"); } catch (Exception ex) { throw new EJBException(ex); } } public void ejbRemove() { /**@todo Complete this method*/ } public void ejbActivate() { /**@todo Complete this method*/ } public void ejbPassivate() { /**@todo Complete this method*/ } public void setSessionContext(SessionContext sessionContext) { this.sessionContext = sessionContext; } public boolean actorLogin(String username, String password, int actortype) { try { actor = actorHome.findByName(username,actortype); if(actor.getPassword().equals(password)) { System.out.println(username + " 登錄成功!"); return true; } }catch(ObjectNotFoundException ex) { } catch(Exception ex) { ex.printStackTrace(); } return false; } } |
/*--------------------------------------------------------------------------------------*/
//4、為Entity Bean(Actor)新增一個(gè)findByName的Finder,如下圖

附錄A 開(kāi)發(fā)環(huán)境的配置
對于JBuilder、Oracle9i、WebLogic 7、WebLogic Domain的安裝配置除參考相關(guān)的安裝手冊外,可參考本欄目發(fā)表的《Jbuilder9+Oracle9i+Weblogic8.1安裝配置》
A. 配置Jbuilder 9
A.選擇Tools->Configure Servers配置Server信息






| CREATE TABLE Appointment (ActorID SMALLINT NOT NULL,CoursesID SMALLINT NOT NULL); ALTER TABLE Appointment ADD ( PRIMARY KEY (ActorID, CoursesID) ) ; CREATE TABLE Courses ( CoursesID SMALLINT NOT NULL, CoursesName VARCHAR(30) NULL, StartDate DATE NULL, EndDate DATE NULL, RoomID SMALLINT NULL, ActorID SMALLINT NULL ); ALTER TABLE Courses ADD ( PRIMARY KEY (CoursesID) ) ; CREATE TABLE ActorInfo ( ActorID SMALLINT NOT NULL, ActorName VARCHAR(30) NULL, Phone VARCHAR(16) NULL, Email VARCHAR(50) NULL ); ALTER TABLE ActorInfo ADD ( PRIMARY KEY (ActorID) ) ; CREATE TABLE Actor ( ActorID SMALLINT NOT NULL, UserName VARCHAR(20) NULL, Password CHAR(8) NULL, ActorType SMALLINT NULL ); ALTER TABLE Actor ADD ( PRIMARY KEY (ActorID) ) ; CREATE TABLE Room (RoomID SMALLINT NOT NULL,RoomName VARCHAR(20) NULL); ALTER TABLE Room ADD ( PRIMARY KEY (RoomID) ) ; ALTER TABLE Appointment ADD ( FOREIGN KEY (CoursesID) REFERENCES Courses ) ; ALTER TABLE Appointment ADD ( FOREIGN KEY (ActorID) REFERENCES Actor ) ; ALTER TABLE Courses ADD ( FOREIGN KEY (ActorID) REFERENCES Actor ) ; ALTER TABLE Courses ADD ( FOREIGN KEY (RoomID) REFERENCES Room ) ; ALTER TABLE ActorInfo ADD ( FOREIGN KEY (ActorID) REFERENCES Actor ) ; |
| insert into actor values(1,‘sysadmin‘,‘sysadmin‘,0); insert into actorinfo values(1,‘sysadmin‘,‘66668888‘,‘chenxc@263.net‘); |
| insert into actor values(2,‘rwx‘,‘rwx‘,1); insert into actorinfo values(2,‘任我行‘,‘77778888‘,‘rwx@courses.com.cn‘); |
| insert into actor values(3,‘dfbb‘,‘dfbb‘,1); insert into actorinfo values(3,‘東方不敗‘,‘88888888‘,‘dfbb@courses.com.cn‘); |
| insert into actor values(4,‘ybq‘,‘ybq‘,1); insert into actorinfo values(4,‘岳不群‘,‘99998888‘,‘ybq@courses.com.cn‘); |
| insert into room values(1,‘黑木崖教室1‘); insert into room values(2,‘黑木崖教室2‘); insert into room values(3,‘華山教室3‘); |
聯(lián)系客服