目錄
下載地址:https://download.csdn.net/download/HNU_Csee_wjw/12493894
前言
本項目是基於java語言的用戶管理系統,選用 Servlet + JSP + MySQL + JDBCTempleat + Duird + BeanUtils + tomcat 技術完成,旨在更深入瞭解JavaWeb工程的MVC開發模式,僅供交流學習使用,在文末將給出完整代碼下載鏈接。具體效果如下:
將MySQL數據庫中存儲的信息展示在網頁端,實現分頁顯示,支持添加聯繫人,刪除單個/多個聯繫人,修改聯繫人信息,條件查詢的功能。內置管理員登陸操作,可供後續繼續開發使用。
本項目需要導入的jar包如下:
1 數據庫設計
創建user表格,存有id,name,gender,address,qq,email,username,password屬性,分別代表每一個用戶的id,姓名,性別,年齡,地址,qq號,郵箱,賬戶名及密碼,其中id爲主鍵,如下:
create table user( -- 創建表
id int primary key auto_increment,
name varchar(20) not null,
gender varchar(5),
age int,
address varchar(32),
qq varchar(20),
email varchar(50),
username varchar(50),
password varchar(50)
);
數據庫設計完畢之後,相應的把項目中對應的User類也創建出來,屬性爲以上一個,並提供所有屬性的getter和setter方法,類的toString方法。
package domin;
/**
* 對應數據庫表的實體類
*/
public class User {
private int id;
private String name;
private String gender;
private int age;
private String address;
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;
}
private String qq;
private String email;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getQq() {
return qq;
}
public void setQq(String qq) {
this.qq = qq;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
", address='" + address + '\'' +
", qq='" + qq + '\'' +
", email='" + email + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
2 查詢所有用戶
查詢所有用戶並顯示的設計邏輯如下圖所示:
當點擊主界面index.jsp中的查詢按鈕時,超鏈接跳轉到後臺 UserListServlet.java 程序,完成三個步驟:
- 調用service層的findAll(),返回保存有查詢到的所有用戶信息的List集合
- 將List集合存入request域中,令 key = "users"
- 轉發值list.jsp進行頁面展示
UserListServlet.java 對應核心代碼爲:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 調用UserService完成查詢
UserService service = new UserServiceImpl();
List<User> users = service.findAll();
// 將list存入request域
request.setAttribute("users", users);
// 轉發到list.jsp
request.getRequestDispatcher("/list.jsp").forward(request, response);
}
list.jsp頁面採用 jstl + el 語法的 foreach 語句遍歷request域中存儲的集合,生成表格展示:
<c:forEach items="${users}" var="user" varStatus="s">
<tr>
<td>${s.count}</td>
<td>${user.name}</td>
<td>${user.gender}</td>
<td>${user.age}</td>
<td>${user.address}</td>
<td>${user.qq}</td>
<td>${user.email}</td>
<td><a class="btn btn-default btn-sm" href="update.html">修改</a> <a class="btn btn-default btn-sm" href="">刪除</a></td>
</tr>
</c:forEach>
service層 UserServiceImpl.java 類定義findAll()方法以供UserListServlet.java 調用:
public class UserServiceImpl implements UserService{
private UserDao dao = new UserDaoImpl();
@Override
public List<User> findAll(){
// 調用Dao完成查詢
return dao.findAll();
}
}
該方法調用了dao層 UserDaoImpl.java 中操作數據庫查詢的findAll()方法:
public class UserDaoImpl implements UserDao{
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
@Override
public List<User> findAll(){
// 使用JDBC操作數據庫
String sql = "select * from user";
List<User> users = template.query(sql, new BeanPropertyRowMapper<User>(User.class));
return users;
}
}
操作數據庫使用了Spring框架對JDBC的封裝——JDBCTemplate對象template,來簡化java操作數據庫的開發,由於這裏要執行的是查詢操作,所以只需定義查詢所有用戶的sql語句並調用template的query方法即可。創建template時需要傳入一個連接池類DataSource的對象,可以使用阿里巴巴druid數據庫連接池技術來封裝JDBC的工具類,該工具類封裝了加載配置文件,初始化連接池對象,獲取連接池對象以及獲取連接Connection對象的功能,具體實現如下:
package util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import javax.xml.crypto.Data;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
* JDBC工具類 使用Durid連接池
*/
public class JDBCUtils {
private static DataSource ds ;
static {
try {
//1.加載配置文件
Properties pro = new Properties();
//使用ClassLoader加載配置文件,獲取字節輸入流
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
//2.初始化連接池對象
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 獲取連接池對象
*/
public static DataSource getDataSource(){
return ds;
}
/**
* 獲取連接Connection對象
*/
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
}
相應的druid配置文件爲:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///day17
username=root
password=root
# 初始化連接數量
initialSize=5
# 最大連接數
maxActive=10
# 最大等待時間
maxWait=3000
3 添加聯繫人功能
添加聯繫人的設計邏輯如下圖所示:
點擊添加聯繫人的按鈕之後,跳轉至後臺 AddUserServlet.java 程序,完成五個步驟:
- 設置編碼爲"utf-8"防止出現中文亂碼
- 獲取添加的新的聯繫人的所有數據
- 根據獲取的數據封裝聯繫人爲User類的對象
- 調用service層的add()方法完成添加
- 跳轉回userListServlet再次查詢所有聯繫人並展示
基於以上步驟,AddUserServlet.java 的主要代碼如下:
@WebServlet("/addUserServlet")
public class AddUserServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 設置編碼
request.setCharacterEncoding("utf-8");
// 獲取數據
Map<String, String[]> map = request.getParameterMap();
// 封裝對象
User user = new User();
try {
BeanUtils.populate(user, map);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
// 調用service保存
UserService service = new UserServiceImpl();
service.addUser(user);
// 跳轉到userListServlet
response.sendRedirect(request.getContextPath() + "/userListServlet");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
其中使用了Spring的BeanUtils.populate()方法簡化了User類的封裝,只需傳入初始化的空User類對象user和保存有該user對象的所有屬性的map,map又可以由request.getParameterMap()方法快速獲取。
添加聯繫人界面 add.jsp 需要爲表單設置action屬性,將其指向 addUserServlet
<form action="${pageContext.request.contextPath}/addUserServlet" method="post">
service層 UserServiceImpl.java 類定義add(User user)方法以供AddUserServlet.java 調用:
@Override
public void addUser(User user){
dao.add(user);
}
該方法調用了dao層 UserDaoImpl.java 中添加數據庫數據的add(User user)方法:
爲了保證動態操作數據庫,定義sql語句時將要寫數據的地方用 ? 佔位,使用template的update()方法時再動態傳入數據
@Override
public void add(User user){
String sql = "insert into user values(null,?,?,?,?,?,?,null,null)";
template.update(sql, user.getName(), user.getGender(), user.getAge(), user.getAddress(), user.getQq(), user.getEmail());
}
4 刪除用戶功能
刪除聯繫人的設計邏輯如下圖所示:
點擊刪除按鈕之後,跳轉到後臺 DelUserServlet.java 程序,完成三個步驟:
- 獲取待刪除用戶的id
- 調用service層的deleteUser(String id)方法完成刪除
- 跳轉回userListServlet再次查詢所有聯繫人並展示
基於以上步驟,DelUserServlet.java 的主要代碼如下:
@WebServlet("/delUserServlet")
public class delUserServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 獲取id
String id = request.getParameter("id");
// 調用service刪除
UserService service = new UserServiceImpl();
service.deleteUser(id);
// 跳轉到查詢所有servlet
response.sendRedirect(request.getContextPath() + "/userListServlet");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
service層 UserServiceImpl.java 類定義deleteUser(String id)方法以供DelUserServlet.java 調用:
由於數據庫存儲的id是int型數據,所以在這裏簡單做一個數據類型轉換,並利用java自動拆包特點
@Override
public void deleteUser(String id) {
dao.delete(Integer.parseInt(id));
}
該方法調用了dao層 UserDaoImpl.java 中刪除數據庫數據的delete(int id)方法:
@Override
public void delete(int id) {
String sql = "delete from user where id = ?";
template.update(sql, id);
}
修改頁面list.jsp,刪除操作最好由js控制彈出一個確認提示框再進行相關操作,所以先對按鈕增加一個js判斷,由於user的id屬性只能在foreach循環中獲取(局部變量),所以這裏js函數要傳一個參數:
<a class="btn btn-default btn-sm" href="javascript:deleteUser(${user.id});">刪除</a
相應的js代碼爲,跳轉鏈接要把id也傳過去:
function deleteUser(id) {
// 用戶確定操作提示
if (confirm("您確定要刪除嗎")){
location.href="${pageContext.request.contextPath}/delUserServlet?id="+id;
}
}
5 修改聯繫人功能
修改聯繫人的設計邏輯如下圖所示:
修改邏輯要分爲兩個部分進行:回顯、修改
5.1 回顯部分
點擊修改按鈕之後,首先進行回顯部分的操作,將選中的用戶信息事先展示到頁面的各個輸入框內,跳轉到後臺 FindUserServlet.java 程序,完成四個步驟:
- 獲取待修改用戶的id
- 調用service層的findUserById(String id)方法,查詢用戶信息
- 將User類對象存到request域內
- 轉發至修改頁面update.jsp並作展示
基於以上步驟,FindUserServlet.java 的主要代碼如下:
@WebServlet("/findUserServlet")
public class FindUserServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 獲取id
String id = request.getParameter("id");
// 調用service查詢
UserService service = new UserServiceImpl();
User user = service.findUserById(id);
// 將user存入request
request.setAttribute("user", user);
// 轉發到update.jsp
request.getRequestDispatcher("/update.jsp").forward(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
service層 UserServiceImpl.java 類定義findUserById(String id)方法以供FindUserServlet.java 調用:
@Override
public User findUserById(String id) {
return dao.findById(Integer.parseInt(id));
}
該方法調用了dao層 UserDaoImpl.java 中查詢數據庫數據的findById(int id)方法:
由於要返回一個User類對象,所以要使用template.queryForObject()方法
@Override
public User findById(int id) {
String sql = "select * from user where id = ?";
return template.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), id);
}
獲取到待修改的user後,將其各個原本的屬性值回顯至update.jsp頁面的文本輸入框內,每一個input輸入框的value屬性要用el語法設置爲待修改用戶的屬性${user.屬性名},如:
<input type="text" class="form-control" id="name" name="name" value="${user.name}" readonly="readonly" placeholder="請輸入姓名" />
5.2 修改部分
對於修改信息界面,需要添加一個隱藏域,用於存儲id,後續servlet操作需要根據id來進行
<input type="hidden" name="id" value="${user.id}">
點擊提交按鈕以後,跳轉到後臺 UpdateUserServlet.java 程序,完成四個步驟:
- 設置編碼爲 "utf-8",防止出現中文亂碼問題
- 獲取修改頁面提交的表單數據集合map
- 使用獲得的數據封裝User類對象
- 調用service層的updateUser(User user)方法完成修改
- 跳轉回userListServlet再次查詢所有聯繫人並展示
基於以上步驟,UpdateUserServlet.java 的主要代碼如下:
@WebServlet("/updateUserServlet")
public class UpdateUserServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
// 獲取map
Map<String, String[]> map = request.getParameterMap();
// 封裝對象
User user = new User();
try {
BeanUtils.populate(user, map);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
// 調用service
UserService service = new UserServiceImpl();
service.updateUser(user);
// 跳轉到查詢所有Servlet
response.sendRedirect(request.getContextPath()+"/userListServlet");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
service層 UserServiceImpl.java 類定義updateUser(User user)方法以供UpdateUserServlet.java 調用:
@Override
public void updateUser(User user) {
dao.update(user);
}
該方法調用了dao層 UserDaoImpl.java 中修改數據庫數據的update(User user)方法:
修改數據庫的sql邏輯和添加聯繫人的類似
@Override
public void update(User user) {
String sql = "update user set name=?, gender=?, age=?, address=?, qq=?, email=? where id=?";
template.update(sql, user.getName(), user.getGender(), user.getAge(), user.getAddress(), user.getQq(), user.getEmail(), user.getId());
}
6 刪除選中的聯繫人功能
刪除聯繫人的設計邏輯如下圖所示:
點擊刪除選中按鈕之後,跳轉到後臺 DelSelectServlet.java 程序,完成三個步驟:
- 獲取所有選中的用戶的id
- 調用service層的deleteUsers(String[] ids)方法,刪除所有用戶
- 跳轉回userListServlet再次查詢所有聯繫人並展示
基於以上步驟,DelSelectServlet.java 的主要代碼如下:
@WebServlet("/delSelectedServlet")
public class DelSelectedServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 獲取所有id
String[] ids = request.getParameterValues("uid");
// 調用service刪除
UserService service = new UserServiceImpl();
service.delSelectedUser(ids);
// 跳轉查詢所有的servlet
response.sendRedirect(request.getContextPath() + "/userListServlet");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
service層 UserServiceImpl.java 類定義delSelectedUser(String[] ids)方法以供DelSelectServlet.java 調用:
@Override
public void delSelectedUser(String[] ids) {
if (ids != null && ids.length > 0){
for (String id : ids) {
// 調用dao刪除
dao.delete(Integer.parseInt(id));
}
}
}
由於之前已經實現過dao層刪除單個用戶的操作,所以這裏只需循環遍歷id數組,將每一個id對應的單個用戶都刪除即可。
修改用戶頁面list.jsp,將用戶展示的table用表單form包裹起來,這樣就可以提交所有選中的行了。checkbox的value值要設置爲用戶的id。
<td><input type="checkbox" name="uid" value="${user.id}"></td>
這樣提交時才能把選中的用戶的id都提交過去
<form action="${pageContext.request.contextPath}/delSelectedServlet">
<table border="1" class="table table-bordered table-hover">
<tr class="success">
<th><input type="checkbox" id="firstCb"></th>
<th>編號</th>
<th>姓名</th>
<th>性別</th>
<th>年齡</th>
<th>籍貫</th>
<th>QQ</th>
<th>郵箱</th>
<th>操作</th>
</tr>
<c:forEach items="${users}" var="user" varStatus="s">
<tr>
<td><input type="checkbox" name="uid" value="${user.id}"></td>
<td>${s.count}</td>
<td>${user.name}</td>
<td>${user.gender}</td>
<td>${user.age}</td>
<td>${user.address}</td>
<td>${user.qq}</td>
<td>${user.email}</td>
<td><a class="btn btn-default btn-sm" href="${pageContext.request.contextPath}/findUserServlet?id=${user.id}">修改</a>
<a class="btn btn-default btn-sm" href="javascript:deleteUser(${user.id});">刪除</a></td>
</tr>
</c:forEach>
</table>
</form>
添加script代碼,實現手動確認功能
window.onload = function () {
// 給刪除選中按鈕添加單擊事件
document.getElementById("delSelected").onclick = function () {
// 表單提交
if (confirm("您確定要刪除選中條目嗎")){
// 判斷是否有選中的條目
var flag = false;
var cbs = document.getElementsByName("uid");
for (var i = 0; i < cbs.length; i ++){
if(cbs[i].checked){
flag = true;
break;
}
}
if (flag){
document.getElementById("form").submit();
}
}
}
}
接下來設置第一行的checkbox可以實現全選全不選,邏輯上只用保證所有的所有checkbox的checked屬性與第一行相同即可
window.onload = function () {
// 第一行checkbox實現全選
document.getElementById("firstCb").onclick = function () {
var cbs = document.getElementsByName("uid");
for (var i = 0; i < cbs.length; i ++){
// 設置所有checkbox的狀態與第一行相同
cbs[i].checked = this.checked;
}
}
}
7 分頁查詢功能
由於要實現分頁查詢,需要總記錄數、總頁碼數、每一頁的數據集合、當前的頁碼以及每一頁顯示的條目數等數據,所以最好用一個類的對象來保存這些數據,如下圖所示
於是設計PageBean類來保存這些數據,相應的生成getter、setter及toString方法
package domin;
import java.util.List;
/**
* 分頁對象
*/
public class PageBean<T> {
private int totalCount; // 總記錄數
private int totalPage; // 總頁碼
private List<T> list; // 每頁數據
private int currentPage;// 當前頁碼
private int rows; // 每頁顯示記錄數
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public int getTotalPage() {
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getRows() {
return rows;
}
public void setRows(int rows) {
this.rows = rows;
}
@Override
public String toString() {
return "PageBean{" +
"totalCount=" + totalCount +
", totalPage=" + totalPage +
", list=" + list +
", currentPage=" + currentPage +
", rows=" + rows +
'}';
}
}
實際上,服務器端只需接收到頁面傳過來的當前頁碼及每頁顯示的條目數即可得到所有的PageBean類的屬性,具體如上圖所示
實現分頁查詢並顯示的設計如下圖所示:
查詢用戶信息時,會自動調用後臺 FindUserByPageServlet.java 程序,完成以下四個步驟:
- 接受請求參數 currentPage, rows
- 調用service層的 findUserByPage(currentPage, rows) 方法獲取分頁查詢結果(PageBean類的對象)
- 將該對象存入request域中
- 轉發至頁面list.jsp展示
基於以上步驟,FindUserByPageServlet.java 的主要代碼如下:
@WebServlet("/findUserByPageServlet")
public class FindUserByPageServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 獲取參數
String currentPage = request.getParameter("currentPage");
String rows = request.getParameter("rows");
if (currentPage == null || "".equals(currentPage)){
currentPage = "1";
}
if (rows == null || "".equals(rows)){
rows = "5";
}
// 調用service查詢
UserService service = new UserServiceImpl();
PageBean<User> pb= service.findUserByPage(currentPage, rows);
// 將PageBean存入request
request.setAttribute("pb", pb);
// 轉發到list.jsp
request.getRequestDispatcher("/list.jsp").forward(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
service層 UserServiceImpl.java 類定義findUserByPage(String _currentPage, String _rows)方法以供FindUserByPageServlet.java 調用,在這裏要根據傳入的當前頁面和每頁顯示條目數來計算出其他所需要的值,具體分爲以下七個步驟:
- 創建PageBean對象pb
- 設置pb的currentPage和rows屬性
- 調用dao層的finTotalCount()方法查詢總記錄數totalCount
- 計算sql查詢的起始數據位置 start = (currentPage - 1) * rows
- 調用dao層的findByPage(int start, int rows)方法查詢用戶集合
- 計算總頁碼,totalCount % rows 並向上取整
- 返回pb
基於以上步驟,UserServiceImpl.java 中添加方法代碼如下:
@Override
public PageBean<User> findUserByPage(String _currentPage, String _rows) {
int currentPage = Integer.parseInt(_currentPage);
int rows = Integer.parseInt(_rows);
// 創建PageBean對象
PageBean<User> pb = new PageBean<User>();
// 查詢總記錄數
int totalCount = dao.finTotalCount();
pb.setTotalCount(totalCount);
// 計算總頁碼
int totalPage = (totalCount % rows) == 0 ? (totalCount/rows) : (totalCount/rows) + 1;
pb.setTotalPage(totalPage);
// 設置參數
if (currentPage > totalPage)
currentPage = totalPage;
if (currentPage <= 0)
currentPage = 1;
pb.setCurrentPage(currentPage);
pb.setRows(rows);
// 調用dao查詢List集合
int start = (currentPage - 1) * rows;
List<User> list = dao.findByPage(start, rows);
pb.setList(list);
return pb;
}
該方法調用了dao層 UserDaoImpl.java 中查詢數據庫總數據數的finTotalCount()方法:
@Override
public int finTotalCount() {
String sql = "select count(*) from user";
return template.queryForObject(sql, Integer.class);
}
還調用了dao層按照頁面可展示用戶數查詢的findByPage(int start, int rows)方法:
@Override
public List<User> findByPage(int start, int rows) {
String sql = "select * from user limit ? , ?";
return template.query(sql, new BeanPropertyRowMapper<>(User.class), start, rows);
}
改造顯示頁面list.jsp
首先將循環取出每一條用戶數據按行展示的jstl語句中foreach的items參數修改
items="${pb.list}"
動態顯示總記錄數
<span style="font-size: 25px; margin-left: 5px;">
共${pb.totalCount}條記錄,共${pb.totalPage}頁
</span>
動態修改導航欄個數,高亮當前頁面,並綁定servlet
<c:forEach begin="1" end="${pb.totalPage}" var="i">
<c:if test="${pb.currentPage == i}">
<li class="active"><a href="${pageContext.request.contextPath}/findUserByPageServlet?currentPage=${i}&rows=5">${i}</a></li>
</c:if>
<c:if test="${pb.currentPage != i}">
<li><a href="${pageContext.request.contextPath}/findUserByPageServlet?currentPage=${i}&rows=5">${i}</a></li>
</c:if>
</c:forEach>
添加導航欄向左符號《 和 向右符號 》功能
<c:if test="${pb.currentPage == 1}">
<li class="disabled">
</c:if>
<c:if test="${pb.currentPage != 1}">
<li>
</c:if>
<a href="${pageContext.request.contextPath}/findUserByPageServlet?currentPage=${pb.currentPage - 1}&rows=5" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<c:if test="${pb.currentPage == pb.totalPage}">
<li class="disabled">
</c:if>
<c:if test="${pb.currentPage != pb.totalPage}">
<li>
</c:if>
<a href="${pageContext.request.contextPath}/findUserByPageServlet?currentPage=${pb.currentPage + 1}&rows=5" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
完成分頁查詢後將 UpdateUserServlet.java 和 AddUserServlet.java 和 DelSelectedServlet.java 和delUserServlet.java 的重定向目標換成分頁查詢的servlet
8 複雜條件查詢
由於根據條件查詢所得的結果也要分頁展示,所以仍需藉助PageBean對象,如下圖所示:
當用戶點擊查詢按鈕時,會傳過來一個表單,這裏表單一共有姓名、地址、email三個屬性,但有可能用戶只輸入其中的幾個,所以sql語句要動態的生成,如上圖所示,如果用戶輸入了姓名和地址,那麼查詢總的條目數的模糊查詢sql語句變爲:
select count(*) from user where name like ? and address like ?;
同理,分頁查詢的sql語句爲:
select * from user where name like ? and address like ? limit ?, ?;
改造list.jsp
給查詢部分的表單添加action和method,action傳入分頁的servlet
<form class="form-inline" action="${pageContext.request.contextPath}/findUserByPageServlet" method="post">
注意表單中的input標籤必須加上name屬性,否則數據到不了服務器。
爲了查詢條件的回顯,將查詢輸入框的value進行設置
<input type="text" name="name" value="${condition.name[0]}" class="form-control" id="exampleInputName2">
<input type="text" name="address" value="${condition.address[0]}" class="form-control" id="exampleInputHome2">
<input type="text" name="email" value="${condition.email[0]}" class="form-control" id="exampleInputEmail2">
分頁導航在點擊左右按鈕或數字按鈕是,要把可能存在的條件查詢的條件name=${condition.name[0]}&address=${condition.address[0]}&email=${condition.eamil[0]} 也加上
<li><a href="${pageContext.request.contextPath}/findUserByPageServlet?currentPage=${i}&rows=5&name=${condition.name[0]}&address=${condition.address[0]}&email=${condition.eamil[0]}">${i}</a></li>
改造FindUserByPageServlet.java
添加獲取條件查詢的參數condition,condition如果爲空,則表示默認查詢,沒有設置查詢條件
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 獲取參數
String currentPage = request.getParameter("currentPage");
String rows = request.getParameter("rows");
if (currentPage == null || "".equals(currentPage)){
currentPage = "1";
}
if (rows == null || "".equals(rows)){
rows = "5";
}
// 獲取條件查詢的參數
Map<String, String[]> condition = request.getParameterMap();
// 調用service查詢
UserService service = new UserServiceImpl();
PageBean<User> pb= service.findUserByPage(currentPage, rows, condition);
// 將PageBean存入request
request.setAttribute("pb", pb);
request.setAttribute("condition", condition); // 儲存查詢條件,用於回顯
// 轉發到list.jsp
request.getRequestDispatcher("/list.jsp").forward(request, response);
}
改造UserServiceImpl.java
其中的findUserByPage方法添加一個參數map,表示條件查詢的條件,在方法裏面調用UserDaoImpl類的操作數據庫方法時,也多傳入一個map參數
@Override
public PageBean<User> findUserByPage(String _currentPage, String _rows, Map<String, String[]> condition) {
int currentPage = Integer.parseInt(_currentPage);
int rows = Integer.parseInt(_rows);
// 創建PageBean對象
PageBean<User> pb = new PageBean<User>();
// 查詢總記錄數
int totalCount = dao.finTotalCount(condition);
pb.setTotalCount(totalCount);
// 計算總頁碼
int totalPage = (totalCount % rows) == 0 ? (totalCount/rows) : (totalCount/rows) + 1;
pb.setTotalPage(totalPage);
// 設置參數
if (currentPage <= 0)
currentPage = 1;
if (currentPage > totalPage)
currentPage = totalPage;
pb.setCurrentPage(currentPage);
pb.setRows(rows);
// 調用dao查詢List集合
int start = (currentPage - 1) * rows;
List<User> list = dao.findByPage(start, rows, condition);
pb.setList(list);
return pb;
}
改造UserDaoImpl.java
動態生成sql語句,注意要排除分頁條件參數 "currentPage" 和 "rows"
@Override
public int finTotalCount(Map<String, String[]> condition) {
// 定義模板sql
String sql = "select count(*) from user where 1=1";
StringBuilder sb = new StringBuilder(sql);
// 遍歷map
Set<String> keySet = condition.keySet();
// 參數的集合
List<Object> params = new ArrayList<>();
for (String key : keySet) {
// 排除分頁條件參數
if ("currentPage".equals(key) || "rows".equals(key))
continue;
String value = condition.get(key)[0];
if (value != null && !"".equals(value)){
// 有值
sb.append(" and "+ key +" like ? ");
params.add("%" + value + "%"); // 存儲sql語句中 ? 的值
}
}
System.out.println(sb.toString());
System.out.println(params);
return template.queryForObject(sb.toString(), Integer.class,params.toArray());
}
findByPage(int start, int rows, Map condition)方法的實現類似於上面的實現,在這裏沒有將重複代碼提取出一個單獨的方法,大家可以自己完成一下
@Override
public List<User> findByPage(int start, int rows, Map<String, String[]> condition) {
String sql = "select * from user where 1 = 1 ";
StringBuilder sb = new StringBuilder(sql);
// 遍歷map
Set<String> keySet = condition.keySet();
// 參數的集合
List<Object> params = new ArrayList<>();
for (String key : keySet) {
// 排除分頁條件參數
if ("currentPage".equals(key) || "rows".equals(key))
continue;
String value = condition.get(key)[0];
if (value != null && !"".equals(value)){
// 有值
sb.append(" and "+ key +" like ? ");
params.add("%" + value + "%"); // 存儲sql語句中 ? 的值
}
}
// 添加分頁查詢
sb.append(" limit ?,? ");
// 添加分頁查詢參數值
params.add(start);
params.add(rows);
sql = sb.toString();
System.out.println(sql);
System.out.println(params);
return template.query(sql, new BeanPropertyRowMapper<>(User.class), params.toArray());
}
9 登錄功能
login.jsp頁面
將表單的action設置爲對應servlet程序的目錄
<form action="${pageContext.request.contextPath}/loginServlet" method="post">
驗證碼圖片的src設置爲對應servlet程序的目錄
<a href="javascript:refreshCode()">
<img src="${pageContext.request.contextPath}/checkCodeServlet" title="看不清點擊刷新" id="vcode"/>
</a>
在javascript中定義刷新驗證碼的函數
function refreshCode() {
// 獲取驗證碼圖片對象
var vcode = document.getElementById("vcode");
// 設置src屬性 加上時間戳
vcode.src = "${pageContext.request.contextPath}/checkCodeServlet?time=" + new Date().getTime();
}
驗證碼自動生成程序 CheckCodeServlet.java,網上有很多實例代碼,這裏只將其貼在這裏
package web.servlet;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 驗證碼
*/
@WebServlet("/checkCodeServlet")
public class CheckCodeServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
//服務器通知瀏覽器不要緩存
response.setHeader("pragma","no-cache");
response.setHeader("cache-control","no-cache");
response.setHeader("expires","0");
//在內存中創建一個長80,寬30的圖片,默認黑色背景
//參數一:長
//參數二:寬
//參數三:顏色
int width = 80;
int height = 30;
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
//獲取畫筆
Graphics g = image.getGraphics();
//設置畫筆顏色爲灰色
g.setColor(Color.GRAY);
//填充圖片
g.fillRect(0,0, width,height);
//產生4個隨機驗證碼,12Ey
String checkCode = getCheckCode();
//將驗證碼放入HttpSession中
request.getSession().setAttribute("CHECKCODE_SERVER",checkCode);
//設置畫筆顏色爲黃色
g.setColor(Color.YELLOW);
//設置字體的小大
g.setFont(new Font("黑體",Font.BOLD,24));
//向圖片上寫入驗證碼
g.drawString(checkCode,15,25);
//將內存中的圖片輸出到瀏覽器
//參數一:圖片對象
//參數二:圖片的格式,如PNG,JPG,GIF
//參數三:圖片輸出到哪裏去
ImageIO.write(image,"PNG",response.getOutputStream());
}
/**
* 產生4位隨機字符串
*/
private String getCheckCode() {
String base = "0123456789ABCDEFGabcdefg";
int size = base.length();
Random r = new Random();
StringBuffer sb = new StringBuffer();
for(int i=1;i<=4;i++){
//產生0到size-1的隨機值
int index = r.nextInt(size);
//在base字符串中獲取下標爲index的字符
char c = base.charAt(index);
//將c放入到StringBuffer中去
sb.append(c);
}
return sb.toString();
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request,response);
}
}
當用戶點擊登陸按鈕時,自動跳轉到後臺 LoginServlet.java 程序, 完成以下幾個步驟:
- 設置編碼爲"utf-8",防止中文亂碼問題
- 獲取用戶輸入的登陸數據map 及 輸入的驗證碼
- 驗證碼校驗
- 若校驗成功則根據 map 封裝用戶,並調用service層的login(User user)方法查詢是否登陸成功
- 若登陸成功則返回主頁 index.jsp,失敗返回登錄頁面 login.jsp
基於以上步驟,LoginServlet.java 程序的主要代碼如下:
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 設置編碼
request.setCharacterEncoding("utf-8");
// 獲取數據
String verifycode = request.getParameter("verifycode");
Map<String, String[]> map = request.getParameterMap();
// 封裝User對象
User user = new User();
try {
BeanUtils.populate(user, map);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
// 驗證碼校驗
HttpSession session = request.getSession();
String checkcode_server = (String) session.getAttribute("CHECKCODE_SERVER");
session.removeAttribute("CHECKCODE_SERVER");
if (! checkcode_server.equalsIgnoreCase(verifycode)){
request.setAttribute("login_msg", "驗證碼錯誤");
request.getRequestDispatcher("/login.jsp").forward(request, response);
return;
}
// 調用service查詢
UserService service = new UserServiceImpl();
User loginUser = service.login(user);
// 判斷是否成功
if (loginUser != null){
// 登陸成功
session.setAttribute("user", loginUser);
response.sendRedirect(request.getContextPath() + "/index.jsp");
}else {
request.setAttribute("login_msg", "用戶名或密碼錯誤");
request.getRequestDispatcher("/login.jsp").forward(request, response);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
service層 UserServiceImpl.java 類定義login(User user)方法以供 LoginServlet.java 調用:
@Override
public User login(User user){
return dao.findUserByUsernameAndPassword(user.getUsername(), user.getPassword());
}
該方法調用了dao層的findUserByUsernameAndPassword(String username, String password)判斷能否根據用戶名和密碼找到該用戶,如果無法找到即返回null
public User findUserByUsernameAndPassword(String username, String password){
try{
String sql = "select * from user where username = ? and password = ?";
User user = template.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), username, password);
return user;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
10 添加過濾器進行登錄驗證
爲了保證只有在登錄的條件下才可以訪問各個資源,需要添加 Filter 來進行驗證,驗證主要分爲以下兩步驟:
- 訪問的是否是與登錄相關的資源
- 是,則直接放行
- 否,則判斷其是否登錄
2. 判斷是否已經登陸(Session中是否有User)
- 是,則放行
- 否,則跳轉至登錄頁面
基於以上步驟,編寫Filter代碼如下:
package web.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* 登錄驗證的過濾器
*/
@WebFilter("/*")
public class LoginFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
// 強制轉換
HttpServletRequest request = (HttpServletRequest) req;
// 獲取資源請求路徑
String uri = request.getRequestURI();
// 判斷是否包含登錄相關資源路徑
// 要注意放行掉 css/js/圖片/驗證碼等
if (uri.contains("/login.jsp") || uri.contains("/loginServlet") || uri.contains("/css/") || uri.contains("/js/") || uri.contains("/fonts/") || uri.contains("/checkCodeServlet")){
chain.doFilter(req, resp);
}else {
// 驗證用戶是否登陸
Object user = request.getSession().getAttribute("user");
if (user != null){
// 登陸了,放行
chain.doFilter(req, resp);
}else {
request.setAttribute("login_msg", "您尚未登陸,請登錄");
request.getRequestDispatcher("login.jsp").forward(request, resp);
}
}
}
public void init(FilterConfig config) throws ServletException {
}
}
注意與登陸有關的資源不只是相應的.jsp頁面和servlet,還有相關的css,js,字體,驗證碼等資源。
11 過濾敏感詞彙
有些時候,用戶可能會輸入一些敏感詞彙,我們需要將其和諧掉,然而request沒有setAttribute的方法,所以可以使用代理的方式,創建一個新的request代理,增強網頁中getParameter()方法,如果做了敏感詞彙的河蟹,則放行新的request代理,否則,放行原先的request。
package web.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
/**
* 敏感詞彙過濾器
*/
@WebFilter("/*")
public class SensitiveWordsFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//1.創建代理對象,增強getParameter方法
ServletRequest proxy_req = (ServletRequest)Proxy.newProxyInstance(req.getClass().getClassLoader(), req.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//增強getParameter方法
//判斷是否是getParameter方法
if (method.getName().equals("getParameter")){
//增強返回值
//獲取返回值
String value = (String) method.invoke(req, args);
if (value != null){
for (String str : list) {
if (value.contains(str)){
value = value.replaceAll(str, "***");
}
}
}
return value;
}
return method.invoke(req, args);
}
});
// 注意要改成代理後的request
chain.doFilter(proxy_req, resp);
}
private List<String> list = new ArrayList<>(); // 敏感詞彙集合
public void init(FilterConfig config) throws ServletException {
try {
// 獲取文件真實路徑
ServletContext servletContext = config.getServletContext();
String realPath = servletContext.getRealPath("/WEB-INF/classes/敏感詞彙.txt");
// 讀取文件
BufferedReader br = new BufferedReader(new FileReader(realPath));
// 將文件的每一行數據添加到list中
String line = null;
while ((line = br.readLine()) != null){
list.add(line);
}
br.close();
System.out.println(list);
}catch (Exception e){
e.printStackTrace();
}
}
}