周末拿最近學習的知識 (JDBC, Servlet, JSP) 做一個小案例, 本來周末就應該整理筆記的, 但是打球也不能耽誤啊, 所以只好趕着在今天下班的時間, 做下記錄.
技術准備
-
Java 基礎知識, JavaEE 基礎
tomcat, servlet, jsp(EL + JSTL)
-
web前端的基礎知識
html, css, javascript基礎 + Jquery 基礎
-
關系型數據庫
使用 SpringJDBC 操作 mysql
-
開發工具
IDEA, JDK8.0
需求分析
- 使用 mysql 來存儲學生信息
- 頁面上完成學生數據的展示, 可進行學生信息的增刪改差, 然后支持上一頁, 下一頁操作, 以及學生信息查詢.
實現
一. 表結構設計
-
創建數據庫: student
將編碼設置為 utf-8, 便於存取中文
drop database if exists student; create database student default charset=utf8;
-
創建表: student
create table student( id int unsigned primary key auto_increment not null, student_id varchar(20) not null unique COMMENT '學號', name varchar(20) not null, gender varchar(5), birthday date, address varchar(50), qq varchar(20), email varchar(50) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
設置student_id的原因是因為學號可能會改變, 而主鍵 id 無法修改.
二. 創建JavaEE項目, 導入 jar 包
項目結構圖如下:
三. 准備前端頁面 (原型設計)
在項目中就是前端的 html 代碼, 這一步也非常的重要.
首頁以列表的形式展示學生信息
新增 和 修改信息頁面, 這兩個頁面應該長的很像
如果把新增和修改看成一個頁面, 那么一共就兩個頁面 ~
學生信息列表
添加學生頁面
編輯學生頁面
四. src目錄層次結構
代碼模塊的划分:
- 實體類: student (就一張學生表, 所以對應的實體類只有一個)
- 數據訪問對象: dao (操作學生對象的 StudentDao)
- 邏輯處理: Servlet (學生信息列表展示, 新增, 修改, 刪除學生信息)
- 工具類: JDBCDruidUtils (封裝了 SpringJDBC 對數據庫的操作)
- 單元測試: test
在 src 目錄下創建代碼的層次結構:
五. 具體代碼邏輯實現
student 實體類
package com.student.entity;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Student {
private int id;
private String studentId;
private String name;
private String gender;
private Date birthday;
private String address;
private String qq;
// setter and getter , 日期類型需要額外轉換下
public String getBirthday() {
SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd");
return formater.format(this.birthday);
}
public void setBirthday(String birthday) {
SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = formater.parse(birthday);
} catch (ParseException e) {
e.printStackTrace();
}
this.birthday = date;
}
}
JDBCDruidUtils 連接池:
package com.student.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
/**
* JDBCDruidUtils 工具類
*/
public class JDBCDruidUtils {
private static DataSource ds = null;
static {
try {
// 加載配置文件
Properties pro = new Properties();
// 獲取 src 路徑下的文件 --> ClassLoader
ClassLoader cl = JDBCDruidUtils.class.getClassLoader();
InputStream is = cl.getResourceAsStream("druid.properties");
pro.load(is);
// 通過工廠函數 獲取 數據庫連接池 (傳入配置文件)
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (Exception e) {
e.printStackTrace();
}
}
// 獲取連接池對象
public static DataSource getDataSource(){
return ds;
}
// 獲取連接對象
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
}
druid.properties 配置文件
driverClassName=com.mysql.jdbc.Driver
# useUnicode表示允許使用自定義的Unicode
url=jdbc:mysql://192.168.0.115:3306/JDBC?useUnicode=true&characterEncoding=utf8
username=username
password=password
initialSize=5
maxActive=10
maxWait=3000
studentDao
DAO (Data Access Object) 數據庫訪問, 就是對數據庫的操作進行封裝, 讓 servlet 里邊看不到 JDBC 的代碼
package com.student.dao;
import com.student.entity.Student;
import com.student.utils.JDBCDruidUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* student 對象相關的操作方法
*/
public class StudentDao {
// 獲取 JdbcTemplate 對象
private static JdbcTemplate template = new JdbcTemplate(JDBCDruidUtils.getDataSource());
/**
* 獲取 JdbcTemplate 對象, 將學生信息寫入到數據庫中
*/
public static int createStudent(Student student){
String createSql = "insert into student values(?,?,?,?,?,?,?,?);";
int num = StudentDao.template.update(createSql, student.getId(), student.getStudentId(), student.getName(),
student.getGender(), student.getBirthday(), student.getAddress(), student.getQq(), student.getEmail());
return num;
}
/**
* 通過id查詢學生
* @param id
* @return
*/
public static Student queryStudentById(String id){
String querySql = "select * from student where id = ?";
Student student = StudentDao.template.queryForObject(querySql, new BeanPropertyRowMapper<Student>(Student.class), id);
return student;
}
/**
* 更新 學生信息
* @param student
* @return
*/
public static int updateStudent(Student student){
String updateSql = "update student set gender = ?, birthday = ?, address = ?," +
"qq = ?, email = ? where id = ?;";
int num = StudentDao.template.update(updateSql, student.getGender(), student.getBirthday(), student.getAddress(),
student.getQq(), student.getEmail(), student.getId());
return num;
}
/**
* 刪除學生信息
* @param id
* @return
*/
public static int deleteStudentById(String id){
String deleteSql = "delete from student where id = ?";
int num = StudentDao.template.update(deleteSql, id);
return num;
}
}
學生列表展示
package com.student.servlet;
import com.student.utils.JDBCDruidUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@WebServlet("/list")
public class ReadList extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 不用獲取參數, 所以不用設置流的編碼
// 獲取 JdbcTemplate 對象
DataSource ds = JDBCDruidUtils.getDataSource();
JdbcTemplate template = new JdbcTemplate(ds);
// 查詢結果, 將結果集封裝為 ReadList 集合
// String querySql = "select * from student limit 0, 3";
String querySql = "select * from student";
java.util.List<Map<String, Object>> studentList = template.queryForList(querySql);
// 將學生對象列表存儲到 request 對象域中
request.setAttribute("studentList", studentList);
// 轉發到 list 頁面
request.getRequestDispatcher("/list.jsp").forward(request, response);
}
}
jsp 頁面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<!-- 使用Edge最新的瀏覽器的渲染方式 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- viewport視口:網頁可以根據設置的寬度自動進行適配,在瀏覽器的內部虛擬一個容器,容器的寬度與設備的寬度相同。
width: 默認寬度與設備的寬度相同
initial-scale: 初始的縮放比,為1:1 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 上述3個meta標簽*必須*放在最前面,任何其他內容都*必須*跟隨其后! -->
<title>學生信息管理系統</title>
<!-- 1. 導入CSS的全局樣式 -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- 2. jQuery導入,建議使用1.9以上的版本 -->
<script src="js/jquery-2.1.0.min.js"></script>
<!-- 3. 導入bootstrap的js文件 -->
<script src="js/bootstrap.min.js"></script>
<style type="text/css">
td, th {
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<h3 style="text-align: center">學生信息列表</h3>
<br>
<form id="form" action="${pageContext.request.contextPath}/delete" method="post">
<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>地址</th>
<th>QQ</th>
<th>郵箱</th>
<th>操作</th>
</tr>
<c:forEach items="${requestScope.studentList}" var="student" varStatus="s">
<tr>
<%--<td><input type="checkbox" name="uid" value="${student.id}"></td>--%>
<td>${s.count}</td>
<td>${student.student_id}</td>
<td>${student.name}</td>
<td>${student.gender}</td>
<td>${student.birthday}</td>
<td>${student.address}</td>
<td>${student.qq}</td>
<td>${student.email}</td>
<td><a class="btn btn-default btn-sm" href="${pageContext.request.contextPath}/update?id=${student.id}">修改</a>
<a class="btn btn-default btn-sm" href="javascript:deleteStudent(${student.id});">刪除</a></td>
</tr>
</c:forEach>
</table>
</form>
<div style="float: right;margin: 5px;">
<a class="btn btn-primary" href="${pageContext.request.contextPath}/create.jsp">添加聯系人</a>
</div>
</div>
<script>
function deleteStudent(id){
//用戶安全提示
if(confirm("您確定要刪除嗎?")){
//訪問路徑
location.href="${pageContext.request.contextPath}/delete?id="+id;
location.submit();
}
}
</script>
</body>
</html>
新增一條學生信息
先返回新增學生信息的 jsp 頁面, 然后用戶在鍵入信息之后, 提交表單到后端處理.
package com.student.servlet;
import com.student.dao.StudentDao;
import com.student.entity.Student;
import java.io.IOException;
import java.util.Map;
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("/create")
public class Create extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 獲取參數, 設置流的編碼
request.setCharacterEncoding("utf-8");
// 將參數封裝到 map 集合中 (使用JavaBeanUtils工具)
Map<String, String[]> map = request.getParameterMap();
Student student = StudentDao.mapToStudent(map);
// 獲取 JdbcTemplate 對象, 將學生信息寫入到數據庫中
int num = StudentDao.create(student);
// 重定向到 list 頁面
response.sendRedirect(request.getContextPath() + "/list");
}
}
jsp 頁面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<!-- 使用Edge最新的瀏覽器的渲染方式 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- viewport視口:網頁可以根據設置的寬度自動進行適配,在瀏覽器的內部虛擬一個容器,容器的寬度與設備的寬度相同。
width: 默認寬度與設備的寬度相同
initial-scale: 初始的縮放比,為1:1 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 上述3個meta標簽*必須*放在最前面,任何其他內容都*必須*跟隨其后! -->
<title>添加學生信息</title>
<!-- 1. 導入CSS的全局樣式 -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- 2. jQuery導入,建議使用1.9以上的版本 -->
<script src="js/jquery-2.1.0.min.js"></script>
<!-- 3. 導入bootstrap的js文件 -->
<script src="js/bootstrap.min.js"></script>
</head>
<body>
<div class="container" style="width: 400px;">
<h3 style="text-align: center">添加學生信息頁面</h3>
<br>
<form action="${pageContext.request.contextPath}/create" method="post">
<div class="form-group">
<label for="studentId">學號:</label>
<input type="text" class="form-control" id="studentId" name="studentId" placeholder="請輸入學號">
</div>
<div class="form-group">
<label for="name">姓名:</label>
<input type="text" class="form-control" id="name" name="name" placeholder="請輸入姓名">
</div>
<div class="form-group">
<label>性別:</label>
<input type="radio" name="gender" value="男" checked="checked"/>男
<input type="radio" name="gender" value="女"/>女
</div>
<div class="form-group">
<label for="birthday">生日:</label>
<input type="text" class="form-control" id="birthday" name="birthday" placeholder="請輸入生日">
</div>
<div class="form-group">
<label for="address">地址:</label>
<input type="text" class="form-control" id="address" name="address" placeholder="請輸入地址"/>
</div>
<div class="form-group">
<label for="qq">QQ:</label>
<input type="text" class="form-control" id="qq" name="qq" placeholder="請輸入QQ號碼"/>
</div>
<div class="form-group">
<label for="email">Email:</label>
<input type="text" class="form-control" id="email" name="email" placeholder="請輸入郵箱地址"/>
</div>
<div class="form-group" style="text-align: center">
<input class="btn btn-primary" type="submit" value="提交" />
<input class="btn btn-default" type="reset" value="重置" />
<input class="btn btn-default" type="button" value="返回" id="getBack"/>
</div>
</form>
</div>
<script>
//給返回按鈕添加單擊事件, 返回上一個頁面
document.getElementById("getBack").onclick = function(){
window.history.back(-1);
};
</script>
</body>
</html>
更新學生信息
get 方法中返回編輯學生信息的 jsp 頁面, 填充當前學生的信息
post 方法對用戶提交的 form 表單, 對學生信息進行更新
package com.student.servlet;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import com.student.dao.StudentDao;
import com.student.entity.Student;
import org.apache.commons.beanutils.BeanUtils;
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("/update")
public class Update extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 獲取參數, 設置流的編碼
request.setCharacterEncoding("utf-8");
// 將參數封裝到 map 集合中 (使用JavaBeanUtils工具)
Map<String, String[]> map = request.getParameterMap();
Student student = new Student();
try {
BeanUtils.populate(student, map);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
// 獲取 JdbcTemplate 對象, 將學生信息更新到數據庫中
int num = StudentDao.updateStudent(student);
System.out.println("更新成功, num: " + num);
// 重定向到 list 頁面
response.sendRedirect(request.getContextPath() + "/list");
}
// 返回帶有 student 信息的修改頁面
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 獲取student的 id
String id = request.getParameter("id");
Student student = StudentDao.queryStudentById(id);
// 將需要修改的學生信息放到 request 域對象中
request.setAttribute("student", student);
// 請求轉發到 update.jsp 頁面
request.getRequestDispatcher("/update.jsp").forward(request, response);
}
}
jsp頁面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>修改學生信息</title>
<link href="css/bootstrap.min.css" rel="stylesheet">
<script src="js/jquery-2.1.0.min.js"></script>
<script src="js/bootstrap.min.js"></script>
</head>
<body>
<div class="container" style="width: 400px;">
<h3 style="text-align: center;">修改學生信息</h3>
<br>
<form action="${pageContext.request.contextPath}/update" method="post">
<!-- 隱藏域 提交id-->
<input type="hidden" name="id" value="${requestScope.student.id}">
<div class="form-group">
<label for="studentId">學號:</label>
<input type="text" class="form-control" id="studentId" name="studentId" value="${requestScope.student.studentId}" readonly="readonly" placeholder="請輸入學號" />
</div>
<div class="form-group">
<label for="name">姓名:</label>
<input type="text" class="form-control" id="name" name="name" value="${requestScope.student.name}" readonly="readonly" placeholder="請輸入姓名" />
</div>
<div class="form-group">
<label>性別:</label>
<c:if test="${requestScope.student.gender == '男'}">
<input type="radio" name="gender" value="男" checked />男
<input type="radio" name="gender" value="女" />女
</c:if>
<c:if test="${requestScope.student.gender == '女'}">
<input type="radio" name="gender" value="男" />男
<input type="radio" name="gender" value="女" checked />女
</c:if>
<c:if test="${requestScope.student.gender == null}">
<input type="radio" name="gender" value="男" checked/>男
<input type="radio" name="gender" value="女" />女
</c:if>
</div>
<div class="form-group">
<label for="birthday">生日:</label>
<input type="text" class="form-control" value="${requestScope.student.birthday}" id="birthday" name="birthday" placeholder="請輸入生日" />
</div>
<div class="form-group">
<label for="address">地址:</label>
<input type="text" id="address" class="form-control" value="${requestScope.student.address}" name="address" placeholder="請輸入地址"/>
</div>
<div class="form-group">
<label for="qq">QQ:</label>
<input type="text" id="qq" class="form-control" value="${requestScope.student.qq}" name="qq" placeholder="請輸入QQ號碼"/>
</div>
<div class="form-group">
<label for="email">Email:</label>
<input type="text" id="email" class="form-control" value="${requestScope.student.email}" name="email" placeholder="請輸入郵箱地址"/>
</div>
<div class="form-group" style="text-align: center">
<input class="btn btn-primary" type="submit" value="提交" />
<%--<input class="btn btn-default" type="reset" value="重置" />--%>
<input class="btn btn-default" type="button" value="返回" id="getBack"/>
</div>
</form>
</div>
<script>
//給返回按鈕添加單擊事件, 返回上一個頁面
document.getElementById("getBack").onclick = function(){
window.history.back(-1);
};
</script>
</body>
</html>
刪除學生信息
寫到這里, 刪除的邏輯就非常簡單了, 需要注意的是, 刪除的時候, 需要給用戶彈框確認, 這部分涉及到前端 js 的一個知識點.
function deleteStudent(id){ //用戶安全提示 if(confirm("您確定要刪除嗎?")){ location.href="${pageContext.request.contextPath}/delete?id="+id; location.submit(); } }
package com.student.servlet;
import com.student.dao.StudentDao;
import com.student.entity.Student;
import java.io.IOException;
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("/delete")
public class Delete extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 獲取student的 id
String id = request.getParameter("id");
int num = StudentDao.deleteStudentById(id);
System.out.println("刪除學生信息, num: " + num);
// 重定向到 list 頁面
response.sendRedirect(request.getContextPath() + "/list");
}
}
案例總結
- 一共花了 6 個小時左右的時間, 后端的邏輯處理不算復雜, 知識點忘記了可以看下前邊學習的筆記, 總的來說還算順利.
- 前端的頁面布局是資料里邊找的模板, 然后改了一些, 能夠配合后端的 api 進行數據交互, 然后運用 jsp 中的 EL 表達式和 JSTL 標簽進行展示, 沒有涉及到一些很難的內容.
- 這是一個前后端不分離的案例, 和之前學習的 python flask 項目里邊的 jinja2 模板引擎非常的相似, 例如大胡子語法( {{}} ), 控制代碼塊( if else, for 循環 ), 過濾器, 模板繼承等內容, 有之前的項目經驗, 理解起來也比較的容易.
案例改進
寫到這里, 還是一個非常粗糙的案例, 可以優化的點有:
- 新增, 修改學生信息時對參數進行校驗,
- 按學號, 姓名, 性別進行檢索,
- 分頁查詢,
- 批量刪除,
- 登錄功能,
- 權限管理 (分管理員, 普通用戶等, 普通用戶沒有刪除功能)
后邊有時間再持續優化 ~ (也許就這么一說, haha)
最后, 附上github倉庫地址: https://github.com/kaichenkai/StudentManagement