分析:很明顯,要修改用戶密碼我們還是需要和數據庫交互,那么就還是前面我們寫登陸功能的代碼編寫步驟 —— DAO層、service層、servlet層,前端頁面直接使用現成的,但是注意servlet中使用的地址和servlet的地址映射注意和前端頁面保持一致
為什么要按照DAO層、service層、servlet層,JSP頁面的順序來編寫呢?
原因在上圖展示的很清楚,開發JSP需要填寫servlet在服務器上的映射路徑,開發servlet需要調用service中的方法完成業務邏輯,開發service需要調用Dao中對數據庫的操作來操作數據庫,而只有Dao中使用的JDBC我們是數據庫廠商實現了的,所以我們可以直接使用;所以為了開發的完整性,我們就應該從Dao開始-->service-->servlet-->JSP
分析實現步驟/模塊功能划分(很重要)
只有我們先想好了怎么做,然后再去編寫代碼才會快,且有條不紊,切忌看完要求之后馬上開始寫代碼
1.導入前端素材
2.Dao接口
package com.thhh.dao.user;
import com.thhh.pojo.User;
import java.sql.Connection;
public interface UserDao {
/**
* 得到要進行登陸的用戶
* @param conn:數據庫連接對象
* @param userCode:通過用戶的用戶名userCode查詢用戶數據
* @return
*/
public User getLoginUserInfo(Connection conn,String userCode);
/**
* 修改用戶密碼
* @param conn:數據庫連接對象
* @param id:修改密碼的用戶的ID
* @param newPwd:新密碼
* @return:影響行數
*/
public int updatePwd(Connection conn,String newPwd,int id);
}
只需要看方法2
3.Dao接口實現
package com.thhh.dao.user;
import com.thhh.dao.BaseDao;
import com.thhh.pojo.User;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class UserDaoImpl implements UserDao{
//1、獲取要進行登陸的用戶對象
@Override
public User getLoginUserInfo(Connection conn, String userCode) {
PreparedStatement pstmt = null;
ResultSet rs = null;
User user = null;
if (conn!=null){
String sql = "SELECT * FROM smbms_user WHERE userCode = ?";
Object[] params = {userCode};
rs = BaseDao.executeQuery(sql,params,conn,pstmt,rs);//調用項目搭建階段准備的公共查詢方法
try {
while (rs.next()){
user = new User();
user.setId(rs.getInt("id"));
user.setUserCode(rs.getString("userCode"));
user.setUserName(rs.getString("userName"));
user.setUserPassword(rs.getString("userPassword"));
user.setGender(rs.getInt("gender"));
user.setBirthday(rs.getDate("birthday"));
user.setPhone(rs.getString("phone"));
user.setAddress(rs.getString("address"));
user.setUserRole(rs.getInt("userRole"));
user.setCreatedBy(rs.getInt("createdBy"));
user.setCreationDate(rs.getTimestamp("creationDate"));
user.setModifyBy(rs.getInt("modifyBy"));
user.setModifyDate(rs.getTimestamp("modifyDate"));user.setId(rs.getInt("id"));
user.setUserCode(rs.getString("userCode"));
user.setUserName(rs.getString("userName"));
user.setUserPassword(rs.getString("userPassword"));
user.setGender(rs.getInt("gender"));
user.setBirthday(rs.getDate("birthday"));
user.setPhone(rs.getString("phone"));
user.setAddress(rs.getString("address"));
user.setUserRole(rs.getInt("userRole"));
user.setCreatedBy(rs.getInt("createdBy"));
user.setCreationDate(rs.getTimestamp("creationDate"));
user.setModifyBy(rs.getInt("modifyBy"));
user.setModifyDate(rs.getTimestamp("modifyDate"));
}
//關閉資源
BaseDao.close(null,pstmt,rs);//因為數據庫的連接可能不只是這一個操作,所以我們不應該做完一件事就把數據庫連接對象銷毀,所以conn處傳的null
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
return user;
}
//2、修改用戶密碼
@Override
public int updatePwd(Connection conn, String newPwd, int id) {
PreparedStatement pstmt = null;
int rs = 0;
User user = null;
if (conn!=null){
String sql = "UPDATE smbms_user SET userPassword = ? WHERE id = ?";
Object[] params = {newPwd,id};//按照sql語句的占位符的順序來傳遞數據,使用的時候需要注意
rs = BaseDao.executeUpdate(sql,params,conn,pstmt);
BaseDao.close(null,pstmt,null);//把這次使用的sql語句發送器關掉,連接不要關,service還可能有其他用
}
return rs;
}
}
只需要看方法2
4.service接口
package com.thhh.service.user;
import com.thhh.pojo.User;
import java.sql.Connection;
public interface UserService {
/**
* 1、獲取登陸用戶對象,對用戶登陸身份進行驗證
* @param userCode:用戶賬號
* @param userPassword:用戶密碼,注意,密碼判斷我們在service層進行;
* 在Dao層只是簡單的操作數據庫,沒有其他的邏輯代碼;在servlet層中只是接收和轉發請求以及控制視圖跳轉
* 而對於業務層(service)就是用來實現業務邏輯代碼的
* @return
*/
public User login(String userCode,String userPassword);
/**
* 2、根據用戶ID修改用戶密碼
* @param newPwd:新密碼
* @param id:用戶ID
* @return
*/
public boolean updatePwd(String newPwd, int id);
}
只看方法2
5.service接口實現
package com.thhh.service.user;
/**
* 業務層主要就是編寫業務代碼,在編寫業務代碼的時候經常會調用數據庫
* 所以在業務層中需要使用到我們一開始編寫好的DAO的代碼
*/
import com.thhh.dao.BaseDao;
import com.thhh.dao.user.UserDao;
import com.thhh.dao.user.UserDaoImpl;
import com.thhh.pojo.User;
import java.sql.Connection;
public class UserServiceImpl implements UserService{
private UserDao userDao;//業務層需要使用Dao,所以直接將Dao作為一個成員變量來使用
public UserServiceImpl() {
this.userDao = new UserDaoImpl();//在業務層被實例化的時候就讓它得到Dao對象,后面就可以直接去用
}
/**
* 1、判斷登陸用戶的用戶名+密碼是否合法,並將用戶對象返回
* @param userCode:用戶賬號
* @param userPassword:用戶密碼,注意,密碼判斷我們在service層進行;
* 在Dao層只是簡單的操作數據庫,沒有其他的邏輯代碼;在servlet層中只是接收和轉發請求以及控制視圖跳轉
* 而對於業務層(service)就是用來實現業務邏輯代碼的
* @return
*/
@Override
public User login(String userCode, String userPassword) {
Connection conn = null;
User user = null;
User error = null;
conn = BaseDao.getConnection();//獲取數據庫連接對象
//通過業務層調用Dao層
user = userDao.getLoginUserInfo(conn,userCode);//調用userDao中的獲取用戶信息的方法
BaseDao.close(conn,null,null);
if (user.getUserPassword().equals(userPassword)){
return user;
}
return error;
}
/**
* 2、通過已經登陸用戶的ID修改新密碼,並將數據庫中受影響的行數返回
* @param newPwd:新密碼
* @param id:用戶ID
* @return
*/
@Override
public boolean updatePwd(String newPwd, int id) {
Connection conn = null;
int rs = 0;
boolean flag = false;
conn = BaseDao.getConnection();//獲取數據庫連接對象
//通過業務層調用Dao層
if (userDao.updatePwd(conn,newPwd,id)>0){//數據庫修改成功
flag = true;
}
BaseDao.close(conn,null,null);
return flag;
}
}
只看方法2
6.servlet編寫
package com.thhh.servlet.user;
import com.mysql.jdbc.StringUtils;
import com.thhh.pojo.User;
import com.thhh.service.user.UserService;
import com.thhh.service.user.UserServiceImpl;
import com.thhh.utils.Constants;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//實現servlet復用
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
boolean flag = false;
Object user = req.getSession().getAttribute(Constants.USER_SESSION);
String newpassword = req.getParameter("newpassword");
if (user!=null && !StringUtils.isNullOrEmpty(newpassword)){//獲取到了這個用戶對象且獲取到的新密碼不為空
UserService userService = new UserServiceImpl();
flag = userService.updatePwd(newpassword,((User)user).getId());//servlet調用業務層
if (flag){//修改成功
req.setAttribute("message","密碼修改成功!請使用新密碼重新登陸");
//移除用戶的session,利用過濾器阻止用戶再進行操作,直接跳轉error.jsp頁面
req.getSession().removeAttribute(Constants.USER_SESSION);
}else{
req.setAttribute("message","密碼修改失敗");
}
}else {
//用戶可以進行密碼修改,則user一定不是null,所以跳入這個分支的原因一定是newpassword = NULL
req.setAttribute("message","密碼設置有誤,請重新輸入!");
}
//無論是修改成功還是失敗,都重定向到密碼修改頁面,就是在刷新頁面,否則我們設置在req中的message屬性不會被前端讀到
req.getRequestDispatcher("pwdmodify.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
7.注冊servlet
<!--注冊用戶修改密碼的servlet-->
<servlet>
<servlet-name>UserServlet</servlet-name>
<servlet-class>com.thhh.servlet.user.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/jsp/user.do</url-pattern>
</servlet-mapping>
8.測試
bug1:
//1、在編寫servlet的時候,要判斷前端傳過來的新密碼是否為空,這里ZB用了一個工具類,但是這個工具類是isNullOrEmpty,即它的作用判斷"是空",所以使用的時候注意在前面加上一個"!"
//或者我們就是要常見的方法:newpassword!=null&&newpassword.length!=0
if (user!=null && !StringUtils.isNullOrEmpty(newpassword))
bug2:
//2、在編寫BaseDao即基本公共數據庫操作方法的時候,設置PreparedStatement對象中sql占位符的值時要注意
//PreparedStatement的占位符index從1開始,而數組的下標從0開始,所以我們使用的i=1,但是要注意控制循環次數的時候使用的是params.length,所以我們需要取"=",否則數組中的參數是取不完的,取不完就會出現SQL錯誤
for (int i=1;i<= params.length;i++){//循環遍歷參數數組,並將參數設入SQL中
pstmt.setObject(i,params[i-1]);//注意:數組的index從0開始,而PreparedStatement中設置占位符的值的index從1開始
}
bug3:
//前端頁面上,編寫的時候要求輸入舊密碼,但是實際測試的時候輸入舊密碼有BUG,我們直接不使用輸入舊密碼,使用新密碼+重復新密碼輸入框來修改密碼
//但是前端使用的JS控制了提交表單的按鈕,即需要3個輸入框輸入都滿足要求的時候才能提交表單數據,所以我們需要把判斷舊密碼輸入框的判斷語句注釋了
//這樣才能只通過新密碼+重復新密碼實現密碼修改
saveBtn.on("click",function(){
oldpassword.blur();
newpassword.blur();
rnewpassword.blur();
// oldpassword.attr("validateStatus") == "true"
// &&
if( newpassword.attr("validateStatus") == "true"
&& rnewpassword.attr("validateStatus") == "true"){
if(confirm("確定要修改密碼?")){
$("#userForm").submit();
}
}
});
9.優化servlet代碼,實現servlet復用
通過測試,功能完全相同,且修改密碼正確!