注冊是本項目的第一步,首先要分析注冊的流程。
第一步:創建用戶相關類
** domain:User
** dao:UserDao
** service:UserService
** servlet:UserServlet
注:包名習慣以:cn.域名.項目名.相關類名:如:cn.baidu.bookstore.domain.User
第二步:用戶注冊流程如下圖
第三步:用戶激活功能如下圖
接下來就是代碼的設計
1、 建立相應的數據庫,並建立以下幾張表:
tb_user
tb_category
tb_book
tb_orders
tb_orderitem
表的架構如下
2、導包
數據庫用c3p0連接池鏈接,所以需要用到和c3p0相關的包
c3p0-0.9.2-pre1.jar,這是核心包,其依賴包為:mchange-commons-0.2.jar ,還有mysql-connector-java-5.1.28-bin.jar 驅動架包
當然還需要commons-dbutils這樣的工具包,方便我們設計;commons-beanutils-1.8.3.jar創建javabean的工具包,其依賴包commons-logging-1.1.1.jar
項目需要發送郵件激活注冊,與javamail相關的包有:mail.jar,activation.jar。同時用要用到ajax相關的包,由於包比較多,這里不再一一列出。
導入配置文件,c3p0配置文件如下:
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <c3p0-config> 3 <!-- 默認配置0~1 --> 4 <default-config> 5 <property name="jdbcUrl">jdbc:mysql://localhost:3306/數據庫名</property> 6 <property name="driverClass">com.mysql.jdbc.Driver</property> 7 <property name="user">root</property> 8 <property name="password">數據庫密碼</property> 9 10 <property name="acquireIncrement">3</property> 11 <property name="initialPoolSize">10</property> 12 <property name="minPoolSize">2</property> 13 <property name="maxPoolSize">10</property> 14 </default-config>> 15 </c3p0-config>
為了以后便於把郵件發送人修改,我統一把與郵件有關的設置在一個配置文件中。代碼如下:
host=smtp.qq.com //主機,采用qq郵箱的服務器 uname=郵箱(不帶后面的@qq.com) pwd=郵箱的密碼 from=郵箱的全部名稱 subject=\u8FD9\u662F\u6765\u81EAYouth\u7F51\u4E0A\u4E66\u57CE\u7684\u6FC0\u6D3B\u90AE\u4EF6 //主題,這里已經轉碼了 content=<a href\="http\://localhost/bookstore/UserServlet?method\=active&code\={0}">\u70B9\u51FB\u8FD9\u91CC\u5B8C\u6210\u6FC0\u6D3B</a> 激活鏈接
3、創建包。
關於注冊界面,需要創建如下包和類
com.youth.bookstore.user.domain---->User(javabean類),這是User的領域對象類,其中的字段必須要數據庫對應,包括uid,uname,password,email,code,state
com.youth.bookstore.user.Dao --->UserDao,這是User持久層
com.youth.bookstore.user.service --->UserService和UserException類,前者為業務層,后者為繼承了exception的異常類,用於拋出異常信息
com.youth.bookstore.user.web.servlet --->UserServlet,這是User的表述層
這是MVC設計模式,就是把表述層、業務層、數據層分開,這樣做的好處是有利於以后的移植,同時表述層值依賴於業務層,業務層值只依賴於持久層,持久層與數據庫打交道,極大的減少了各類之間的耦合度。
4、類的設計
1、UserDao類必須具備五個方法.
1、User findByUsername(String name) //通過用戶名查找用戶
2、User findByEmail(String name) //通過email查找用戶
3、void add(User user) //添加用戶
4、 User findByCode(String code) //通過激活碼來查找用戶
5、void updateState(String uid,boolean state) //更新用戶狀態,用戶已激活,則其state為true,,否則為false
2、UserService類的設計
1、void regist(User form) throws UserException //用戶注冊,如果用戶名以及郵箱已被注冊,則拋出UserException異常,否則在數據庫中添加注冊
UserException類為自己寫的類,繼承自Exception。
2、void active(String code) throws UserException //用戶激活,首先查看用戶的激活狀態,如果已經激活或激活碼不存在,則拋出異常
3、UserServlet類的設計
1、激活方法
2、注冊方法
每個類的具體代碼如下:
1、UserDao.java
package com.youth.bookstore.user.dao; import java.sql.SQLException; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import cn.itcast.jdbc.TxQueryRunner; import com.youth.bookstore.user.domain.User; /** * User持久層 * @author 11937 * */ public class UserDao { private QueryRunner qr = new TxQueryRunner(); /** * 按用戶名查詢 * @param username * @return */ public User findByUsername(String username){ try { String sql = "select * from tb_user where username=?"; return qr.query(sql, new BeanHandler<User>(User.class),username); } catch (SQLException e) { throw new RuntimeException(e); } } /** * 按用戶郵箱查詢 * @param email * @return */ public User findByEmail(String email){ try { String sql = "select * from tb_user where email=?"; return qr.query(sql, new BeanHandler<User>(User.class),email); } catch (SQLException e) { throw new RuntimeException(e); } } /** * 添加用戶 * @param user */ public void add(User user){ try { String sql = "insert into tb_user values(?,?,?,?,?,?)"; Object[] params = {user.getUid(),user.getUsername(), user.getPassword(),user.getEmail(), user.getCode(),user.isState()}; qr.update(sql, params); } catch (SQLException e) { throw new RuntimeException(e); } } /** * 按激活碼查詢 * @param code * @return */ public User findByCode(String code){ try { String sql = "select * from tb_user where code=?"; return qr.query(sql, new BeanHandler<User>(User.class),code); } catch (SQLException e) { throw new RuntimeException(e); } } /** * 更新用戶激活狀態信息 * @param uid * @param state */ public void updateState(String uid,boolean state){ try { String sql = "update tb_user set state=? where uid=?"; qr.update(sql, state,uid); } catch (SQLException e) { throw new RuntimeException(e); } } }
2、UserService.java
package com.youth.bookstore.user.service; import com.youth.bookstore.user.dao.UserDao; import com.youth.bookstore.user.domain.User; /** * User業務層 * @author 11937 * */ public class UserService { private UserDao userDao = new UserDao(); /** * 注冊功能 * @param user * @throws UserException */ public void regist(User form) throws UserException{ User user = userDao.findByUsername(form.getUsername()); if(user != null) throw new UserException("用戶名已被注冊"); user = userDao.findByEmail(form.getEmail()); if(user != null) throw new UserException("郵箱已被注冊"); userDao.add(form); } /* * 激活功能 */ public void active(String code) throws UserException{ /* * 1、使用code查詢數據庫 ,得到user */ User user = userDao.findByCode(code); /* * 2、校驗,如果user不存在,拋異常 */ if(user == null) throw new UserException("激活碼無效!"); /* * 3、再次校驗,用戶是否已經激活 */ if(user.isState()) throw new UserException("您已經激活過了,請直接登錄!"); /* * 4、激活成功 */ userDao.updateState(user.getUid(), true); } }
3、UserException.java
package com.youth.bookstore.user.service; public class UserException extends Exception{ public UserException() { super(); // TODO Auto-generated constructor stub } public UserException(String message) { super(message); } }
4、 UserServlet.java
package com.youth.bookstore.user.web.servlet; import java.io.IOException; import java.text.MessageFormat; import java.util.HashMap; import java.util.Map; import java.util.Properties; import javax.mail.MessagingException; import javax.mail.Session; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import cn.itcast.commons.CommonUtils; import cn.itcast.mail.Mail; import cn.itcast.mail.MailUtils; import cn.itcast.servlet.BaseServlet; import com.youth.bookstore.user.domain.User; import com.youth.bookstore.user.service.UserException; import com.youth.bookstore.user.service.UserService; /** * User表述層 * @author 11937 * */ public class UserServlet extends BaseServlet { private UserService userService = new UserService(); /** * 激活功能 * @param request * @param response * @return * @throws ServletException * @throws IOException */ public String active(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /* * 1、獲取參數激活碼 * 2、調用service方法激活 * > 保存異常信息到request域,轉發到msg.jsp * 3、保存成功信息到request域,轉發到msg.jsp */ String code = request.getParameter("code"); try { userService.active(code); request.setAttribute("msg", "恭喜,您已激活成功,請趕快登錄吧!"); } catch (UserException e) { request.setAttribute("msg", e.getMessage()); } return "f:/jsps/msg.jsp"; } /** * 注冊功能 * @param request * @param response * @return * @throws ServletException * @throws IOException */ public String regist(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /* * 1.封裝表單數據到form對象中 * 2.補全uid、code * 3、輸入校驗 * >保存錯誤信息、form到request域,轉發到regist.jsp * 4、調用service方法完成注冊 * >保存錯誤信息、form到request域,轉發到regist.jsp * 5、發郵件 * 6、保存成功信息轉發到regist.jsp */ //封裝表單數據 User form = CommonUtils.toBean(request.getParameterMap(), User.class); //補全 form.setUid(CommonUtils.uuid()); form.setCode(CommonUtils.uuid() + CommonUtils.uuid()); /*輸入校驗 * 1.創建一個map,用來封裝錯誤信息 */ Map<String,String> errors = new HashMap<String,String>(); String username = form.getUsername(); if(username == null || username.trim().isEmpty()){ errors.put("username", "用戶名不能為空"); }else if(username.length() < 3 || username.length() >10){ errors.put("username", "用戶名長度必須在3~10之間"); } String password = form.getPassword(); if(password == null || password.trim().isEmpty()){ errors.put("password", "密碼不能為空"); }else if(password.length() < 3 || password.length() >10){ errors.put("password", "密碼長度必須在3~10之間! "); } String email = form.getEmail(); if(email == null || email.trim().isEmpty()){ errors.put("email", "郵箱不能為空"); }else if(!email.matches("\\w+@\\w+\\.\\w+")){ errors.put("email", "Email格式錯誤 "); } /* * 3、判斷是否存在錯誤信息 */ if(errors.size() > 0){ //保存錯誤信息 //保存表單數據 //轉發至regist.jsp request.setAttribute("errors", errors); request.setAttribute("form", form); return "f:/jsps/user/regist.jsp"; } /* * 調用service的regist方法 */ try { userService.regist(form); } catch (UserException e) { //保存錯誤信息 //保存表單數據 //轉發至regist.jsp request.setAttribute("msg", e.getMessage()); request.setAttribute("form", form); return "f:/jsps/user/regist.jsp"; } /* * 說明userService執行成功 */ /* * 發送郵件 * 准備配置文件 */ //獲取配置文件內容 Properties props = new Properties(); props.load(this.getClass().getClassLoader(). getResourceAsStream("email_template.properties")); String host = props.getProperty("host"); //獲取主機 String uname = props.getProperty("uname"); //獲取用戶名 String pwd = props.getProperty("pwd"); String from = props.getProperty("from"); String to = form.getEmail(); //獲取發件人 String subject = props.getProperty("subject"); String content = props.getProperty("content"); //獲取郵件內容 //{0}為占位符的格式 //MessageFormat.format('{0}或者{1}出錯','name','password') content = MessageFormat.format(content,form.getCode()); //替換{0} Session session = MailUtils.createSession(host, uname, pwd); Mail mail = new Mail(from,to,subject,content); try{ MailUtils.send(session, mail); } catch(Exception e){ throw new RuntimeException(e); } /* 1、保存成功信息 * 2、轉發到msg.jsp */ request.setAttribute("msg", "恭喜,注冊成功!請馬上到郵箱激活 "); return "f:/jsps/msg.jsp"; } }
5、User.java
package com.youth.bookstore.user.domain; /** * User的領域對象 * * @author 11937 * */ public class User { /** * 對應數據庫 */ private String uid; // 主鍵 private String username; private String password; private String email; private String code; // 激活碼 private boolean state; // 激活狀態 public String getUid() { return uid; } public void setUid(String uid) { this.uid = uid; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public boolean isState() { return state; } public void setState(boolean state) { this.state = state; } public User(String uid, String username, String password, String email, String code, boolean state) { super(); this.uid = uid; this.username = username; this.password = password; this.email = email; this.code = code; this.state = state; } @Override public String toString() { return "User [uid=" + uid + ", username=" + username + ", password=" + password + ", email=" + email + ", code=" + code + ", state=" + state + "]"; } public User() { super(); } }
6、數據庫建立如下:
CREATE DATABASE bookstore; /*用戶表*/ CREATE TABLE tb_user( uid CHAR(32) PRIMARY KEY,/*主鍵*/ username VARCHAR(50) NOT NULL,/*用戶名*/ `password` VARCHAR(50) NOT NULL,/*密碼*/ email VARCHAR(50) NOT NULL,/*郵箱*/ `code` CHAR(64) NOT NULL,/*激活碼*/ state BOOLEAN/*用戶狀態,有兩種是否激活*/ ); SELECT * FROM tb_user; /*分類*/ CREATE TABLE category ( cid CHAR(32) PRIMARY KEY,/*主鍵*/ cname VARCHAR(100) NOT NULL/*分類名稱*/ ); INSERT INTO category(cid,cname) VALUES ('1','JavaSE'); INSERT INTO category(cid,cname) VALUES ('2','JavaEE'); INSERT INTO category(cid,cname) VALUES ('3','Javascript'); SELECT * FROM category; /*圖書表*/ CREATE TABLE book ( bid CHAR(32) PRIMARY KEY,/*主鍵*/ bname VARCHAR(100),/*圖書名*/ price DECIMAL(5,1),/*單價*/ author VARCHAR(20),/*作者*/ image VARCHAR(200),/*圖片*/ cid CHAR(32),/*所屬分類*/ FOREIGN KEY (cid) REFERENCES category(cid)/*建立主外鍵關系*/ ); INSERT INTO book VALUES ('1','Java編程思想(第4版)','75.6','qdmmy6','book_img/9317290-1_l.jpg','1'); INSERT INTO book VALUES ('2','Java核心技術卷1','68.5','qdmmy6','book_img/20285763-1_l.jpg','1'); INSERT INTO book VALUES ('3','Java就業培訓教程','39.9','張孝祥','book_img/8758723-1_l.jpg','1'); INSERT INTO book VALUES ('4','Head First java','47.5','(美)塞若','book_img/9265169-1_l.jpg','1'); INSERT INTO book VALUES ('5','JavaWeb開發詳解','83.3','孫鑫','book_img/22788412-1_l.jpg','2'); INSERT INTO book VALUES ('6','Struts2深入詳解','63.2','孫鑫','book_img/20385925-1_l.jpg','2'); INSERT INTO book VALUES ('7','精通Hibernate','30.0','孫衛琴','book_img/8991366-1_l.jpg','2'); INSERT INTO book VALUES ('8','精通Spring2.x','63.2','陳華雄','book_img/20029394-1_l.jpg','2'); INSERT INTO book VALUES ('9','Javascript權威指南','93.6','(美)弗蘭納根','book_img/22722790-1_l.jpg','3'); SELECT * FROM book; /*訂單表*/ CREATE TABLE orders ( oid CHAR(32) PRIMARY KEY,/*主鍵*/ ordertime DATETIME,/*訂單生成時間*/ total DECIMAL(10,0),/*訂單合計*/ state SMALLINT(1),/*訂單狀態:未付款、已付款但未發貨、已發貨但未確認收貨、收貨已結束*/ uid CHAR(32),/*訂單的主人*/ address VARCHAR(200),/*訂單的收貨地址*/ FOREIGN KEY (uid) REFERENCES tb_user(uid)/*建立主外鍵關系*/ ); SELECT * FROM orders; /*訂單項表*/ CREATE TABLE orderitem ( iid CHAR(32) PRIMARY KEY,/*主鍵*/ `count` INT,/*數量*/orderitem subtotal DECIMAL(10,0),/*小計*/ oid CHAR(32),/*所屬訂單*/ bid CHAR(32),/*訂單項所指的商品*/ FOREIGN KEY (oid) REFERENCES orders (oid),/*建立主外鍵關系*/ FOREIGN KEY (bid) REFERENCES book (bid)/*建立主外鍵關系*/ ); SELECT * FROM orderitem;
總結:在設計過程中,有一個問題我幾乎弄了一個下午,就是我在發送郵件的時候,郵箱服務器指定為stmp.qq.com時,用qq郵箱給其他郵箱發郵件時,如果發給對方的是qq郵箱,對方可以接收到郵件,但是無法打開設定的超鏈接,我查看了它的源碼,在相應位置處,確實是一個a標簽,同時鏈接也並有錯誤,單獨把鏈接復制過來去訪問,則可以成功。我用此qq郵箱給163郵箱發送郵件,163郵箱可以直接點開鏈接。后來我又用163郵箱給qq發送郵件,發現根本無法連接163服務器,我確認已經登錄網頁163郵箱,打開了了 POP3/SMTP/IMAP,但是依舊沒有解決此問題,希望看到這篇博客的博友,如果好的解決方案,請分享到下面,感激不盡。
寫於2015-12-18 21:22:16。