Web學生管理系統

學生管理系統Web項目實現與總結

經過了兩個階段的學習與嘗試,最終實現了Web端的學生管理系統。
爲什麼要說是兩個階段呢?

  1. 實際上在暑假學習Web的時候做了一個初版的學生管理系統,但是在最後一個傳值 的過程中,遇到了一個我以爲不能解決的問題,就因爲這個問題搗鼓了一天沒搗鼓出來,最終決定放棄這個項目。
  2. 直到我繼續往下學習的時候覺得這個學生管理系統應該還是得嘗試一下,於是最後一次嘗試中,解決了問題,初版的學生管理系統的問題在開學的2周後解決,在昨天完成了其他的所有功能並且修改了一下BUG

項目做了很久,遇到的問題很多,在此做一個項目實現流程與項目個人總結來記錄一下學習過程。接下來進入正題,爲了分析這個項目,我分爲五大塊:

  1. 項目的層次結構
  2. 項目的邏輯流程
  3. 項目的代碼實現
  4. 項目的運行效果
  5. 項目的問題總結
  6. 項目的感想與展望

1、項目的層次結構

項目結構

1、Dao層中包含了兩個接口,分別是StudentDao與UserDao,其下爲它們的實現類
2、Model層中PageBean爲分頁實現所需要的數據存儲類,Account爲登錄功能所需要的用戶數據類,Student爲顯示數據所需要的學生信息存儲類
3、Service層爲業務邏輯層,目的是爲了確保避免Dao層與用戶的直接接觸,類似一個Dao的代理
4、Servlet層的功能主要實現了JSP頁面與後端的數據交互並實現頁面的跳轉功能
5、Utils層的功能主要是爲了使用數據庫連接池以及判斷JSP頁面傳遞的值是否爲空

JSP結構

N、這裏六個JSP頁面中student1與student2分別爲單頁與分頁顯示的頁面(其餘略)

注意:由於Eclipse的問題,所以導致圖1沒有明顯的層次感,具體看包名分清

2、項目的邏輯流程

邏輯流程圖

3、項目的代碼實現
Dao層實現類

1、StudentDaoImpl

package Dao.DaoImpl;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import Dao.StudentDao;
import Model.PageBean;
import Model.Student;
import Utils.JDBCUtil_c3p0;
import Utils.StringUtil;
/**
 * StudentDaoImpl
 * @author Hillain
 */
public class StudentDaoImpl implements StudentDao{
	QueryRunner qr = JDBCUtil_c3p0.getQR();
	@Override
	public List<Student> findAll() {
		List<Student> list = null;
		try {
			list = qr.query("select * from stu",new BeanListHandler<Student>(Student.class));
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return list;
	}

	@Override
	public void insert(Student stu) {
		try {
			qr.update("insert into stu values (null,?,?,?,?,?,?,?,?)",
					stu.getSchool(),stu.getDepartment(),stu.getBanji(),stu.getName(),
					stu.getAge(),stu.getSex(),stu.getLocation(),stu.getRt());
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void delete(int id) {
		try {
			qr.update("delete from stu where id = ?",id);
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	@Override
	public Student findOne(int id) {
		Student stu = new Student();
		try {
			 stu = qr.query("select * from stu where id = ?", new BeanHandler<Student>(Student.class) ,id);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return stu;
	}

	@Override
	public void update(Student stu) {
		try {
			qr.update("update stu set school=?,department=?,banji=?,name=?,age=?,sex=?,location=?,rt=? where id=?", 
					stu.getSchool(),stu.getDepartment(),stu.getBanji(),stu.getName(),stu.getAge(),
					stu.getSex(),stu.getLocation(),stu.getRt(),stu.getId());
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	@Override
	public List<Student> search(String name, String sex) {
		String sql = "select * from stu where 1=1";		//先寫一個sql語句,後面的1=1是爲了方便加內容
		List<String> list = new ArrayList<String>();	//用於查詢參數的list
		List<Student> students = null;
		if(!StringUtil.isEmpty(name)){
			sql += " and name like ?";
			list.add("%"+name+"%");	//模糊查詢
		}
		if(!StringUtil.isEmpty(sex)){
			sql += " and sex = ?";
			list.add(sex);
		}
		try {
			students = qr.query(sql, new BeanListHandler<Student>(Student.class),list.toArray());
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return  students;
	}

	@Override
	public List<Student> findStudentByPage(int currentPage) {
		List<Student> students = new ArrayList<Student>();
		try {
			students = qr.query("select * from stu limit ? OFFSET ?",new BeanListHandler<Student>(Student.class), Page_Size,(currentPage-1)*Page_Size);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return students;
	}

	@Override
	public int findCount() {
		Long count = null;
		try {
			count = (Long)qr.query("select COUNT(*) from stu", new ScalarHandler());
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return count.intValue();
	}
}

2、UserDaoImpl

package Dao.DaoImpl;
import java.sql.SQLException;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import Dao.UserDao;
import Model.Account;
import Model.Student;
import Utils.JDBCUtil_c3p0;
/**
 * StudentDaoImpl
 * @author Hillain
 */
public class UserDaoImpl implements	UserDao{
	@Override
	public boolean login(String username, String password) {
		QueryRunner qr = JDBCUtil_c3p0.getQR();
		List<Account> list = null;
		try {
			list = qr.query("select * from admin where username = ? and password = ?", 
					new BeanListHandler<Account>(Account.class),username,password);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return !list.isEmpty();
	}
	
}

Model層

1、Account

package Model;

public class Account {
	private int id = -1;
	private String username = null;
	private String password = null;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	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;
	}
	@Override
	public String toString() {
		return "Account [id=" + id + ", username=" + username + ", password=" + password + "]";
	}
}

2、PageBean

package Model;

import java.util.List;

public class PageBean<T> {
	private String name;
	private String sex;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	private int method;//查看是哪種查詢
	public int getMethod() {
		return method;
	}
	public void setMethod(int method) {
		this.method = method;
	}
	private int currentPage;//當前頁數
	private int totalPage;//總頁數
	public int getCurrentPage() {
		return currentPage;
	}
	@Override
	public String toString() {
		return "PageBean [currentPage=" + currentPage + ", totalPage=" + totalPage + ", currentSize=" + currentSize
				+ ", totalSize=" + totalSize + ", list=" + list + "]";
	}
	public void setCurrentPage(int currentPage) {
		this.currentPage = currentPage;
	}
	public int getTotalPage() {
		return totalPage;
	}
	public void setTotalPage(int totalPage) {
		this.totalPage = totalPage;
	}
	public int getCurrentSize() {
		return currentSize;
	}
	public void setCurrentSize(int currentSize) {
		this.currentSize = currentSize;
	}
	public int getTotalSize() {
		return totalSize;
	}
	public void setTotalSize(int totalSize) {
		this.totalSize = totalSize;
	}
	public List<T> getList() {
		return list;
	}
	public void setList(List<T> list) {
		this.list = list;
	}
	private int currentSize;//當前個數
	private int totalSize;//總個數
	private List<T> list;//集合
}

3、Student

package Model;

import java.text.SimpleDateFormat;
import java.util.Date;
/**
 * Student數據存儲對象
 * @author Hillain
 */
public class Student {
	public Student(int id,String school, String department, String banji, String name, int age, String sex,
			String location, Date rt) {
		super();
		this.id = id;
		this.school = school;
		this.department = department;
		this.banji = banji;
		this.name = name;
		this.age = age;
		this.sex = sex;
		this.location = location;
		this.rt = rt;
	}
	public Student(String school, String department, String banji, String name, int age, String sex,
			String location, Date rt) {
		super();
		this.school = school;
		this.department = department;
		this.banji = banji;
		this.name = name;
		this.age = age;
		this.sex = sex;
		this.location = location;
		this.rt = rt;
	}
	public Student(){}
	int id = -1;
	String school = null;
	String department = null;
	String banji = null;
	String name = null;
	int age = -1;
	String sex = null;
	String location = null;
	Date rt = null;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	public String getLocation() {
		return location;
	}
	public void setLocation(String location) {
		this.location = location;
	}
	public Date getRt() {
		return rt;
	}
	public void setRt(Date rt) {
		this.rt = rt;
	}
	public String getSchool() {
		return school;
	}
	public void setSchool(String school) {
		this.school = school;
	}
	public String getDepartment() {
		return department;
	}
	public void setDepartment(String department) {
		this.department = department;
	}
	public String getBanji() {
		return banji;
	}
	public void setBanji(String banji) {
		this.banji = banji;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "Student [id=" + id + ", school=" + school + ", department=" + department + ", banji=" + banji
				+ ", name=" + name + ", age=" + age + ", sex=" + sex + ", location=" + location + ", rt=" + rt + "]";
	}
}

Service層實現類

1、SystemServiceImpl

package Service.ServiceImpl;

import java.util.ArrayList;
import java.util.List;

import Dao.StudentDao;
import Dao.DaoImpl.StudentDaoImpl;
import Dao.DaoImpl.UserDaoImpl;
import Model.PageBean;
import Model.Student;
import Service.SystemService;
/**
 * 具體的業務處理
 * @author Hillain
 * @param <T>
 *
 */
public class SystemServiceImpl<T> implements SystemService{
	StudentDao stuDao = new StudentDaoImpl();
	@Override
	public List<Student> findAll() {
		return stuDao.findAll();
	}

	@Override
	public boolean login(String username, String password) {
		return new UserDaoImpl().login(username, password);
	}

	@Override
	public void insert(Student stu) {
		stuDao.insert(stu);
	}

	@Override
	public void delete(int id) {
		stuDao.delete(id);
	}

	@Override
	public Student findOne(int id) {
		Student stu = stuDao.findOne(id);
		return stu;
	}

	@Override
	public void update(Student stu) {
		stuDao.update(stu);
	}

	@Override
	public List<Student> search(String name, String sex) {
		return stuDao.search(name,sex);
	}

	@Override
	public PageBean<T> findStudentByPage(int currentPage) {
		PageBean<Student> pb = new PageBean<Student>();
		StudentDao stu = new StudentDaoImpl();
		int totalPage = -1;	//總頁數
		int totalSize = -1;	//總個數
		totalSize = stu.findCount();
		totalPage = (totalSize%stu.Page_Size==0)?(totalSize/stu.Page_Size):(totalSize/stu.Page_Size)+1;
		pb.setCurrentPage(currentPage);
		pb.setCurrentSize(stu.Page_Size);
		pb.setTotalSize(stu.findCount());
		pb.setTotalPage(totalPage);
		pb.setList(stu.findStudentByPage(currentPage));
		pb.setSex("");
		pb.setName("");
		pb.setMethod(1);
		return (PageBean<T>) pb;
	}
	
	@Override
	public PageBean<T> searchByPage(String name,String sex,int currentPage,int method) {
		PageBean<Student> pb = new PageBean<Student>();
		StudentDao stu = new StudentDaoImpl();
		List<Student> students1 = stu.search(name, sex);
		List<Student> students2 = new ArrayList<Student>();
		for(int i = (currentPage-1)*5;i<currentPage*5;i++) {
			if(students1.size()<=i)break;
			students2.add(students1.get(i));
		}
		int totalPage = -1;	//總頁數
		int totalSize = -1;	//總個數
		totalSize = students1.size();
		totalPage = (totalSize%stu.Page_Size==0)?(totalSize/stu.Page_Size):(totalSize/stu.Page_Size)+1;
		pb.setList(students2);
		pb.setCurrentPage(currentPage);
		pb.setCurrentSize(stu.Page_Size);
		pb.setTotalSize(students1.size());
		pb.setTotalPage(totalPage);
		pb.setName(name);
		pb.setSex(sex);
		pb.setMethod(method);
		return (PageBean<T>) pb;
	}
}

Servlet層

1、AddStudent

package Servlet;

import java.io.IOException;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.dbutils.QueryRunner;

import Model.Student;
import Service.SystemService;
import Service.ServiceImpl.SystemServiceImpl;
import Utils.JDBCUtil_c3p0;

/**
 * Servlet implementation class AddStudent
 */
public class AddStudent extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		this.doPost(request, response);
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		try {
			request.setCharacterEncoding("UTF-8");
			response.setContentType("text/html; charset=UTF-8");
			String school = request.getParameter("school");
			String department = request.getParameter("dept");
			String banji = request.getParameter("banji");
			String name = request.getParameter("name");
			String agestr = request.getParameter("age");
			String sex = request.getParameter("sex");
			String location = request.getParameter("location");
			String timestr = request.getParameter("time");
			Date time = null;
			int age = -1;
			if(school.trim().equals("")||department.trim().equals("")||banji.trim().equals("")||name.trim().equals("")||agestr.trim().equals("")||sex.trim().equals("")||location.trim().equals("")||timestr.trim().equals("")){
				response.getWriter().write("所填信息不能爲空!");
				response.setHeader("refresh", "3;addStudent.jsp");
			}else{
				age = Integer.parseInt(agestr);
				time = new SimpleDateFormat("yyyy-MM-dd").parse(timestr);
				Student stu = new Student(school,department,banji,name,age,sex,location,time);
				SystemService service = new SystemServiceImpl();
				service.insert(stu);
				request.getRequestDispatcher("student").forward(request, response);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

2、DeleteStudent

package Servlet;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import Service.SystemService;
import Service.ServiceImpl.SystemServiceImpl;

/**
 * Servlet implementation class DeleteStudent
 */
public class DeleteStudent extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		SystemService service = new SystemServiceImpl();
		int id = Integer.parseInt(request.getParameter("id"));
		service.delete(id);
		request.getRequestDispatcher("student").forward(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

}

3、FindStudent

package Servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import Model.Student;
import Service.SystemService;
import Service.ServiceImpl.SystemServiceImpl;

/**
 * Servlet implementation class UpdateStudent
 */
public class FindStudent extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		int id = Integer.parseInt(request.getParameter("id"));
		SystemService service = new SystemServiceImpl();
		Student stu = service.findOne(id);
		request.getSession().setAttribute("student", stu);
		response.sendRedirect("update.jsp");
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

4、LoginServlet

package Servlet;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import Dao.UserDao;
import Dao.DaoImpl.UserDaoImpl;
import Service.SystemService;
import Service.ServiceImpl.SystemServiceImpl;

/**
 * 用於處理登錄功能的Servlet
 */
public class LoginServlet extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=utf-8");
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		SystemService service = new SystemServiceImpl();
		if(service.login(username, password)){
			response.sendRedirect("index.jsp");
		}else{
			response.setHeader("refresh", "3;login.jsp");
			response.getWriter().write("登陸失敗,請重試!");
		}
	}
}

5、SearchStudent

package Servlet;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.catalina.core.ApplicationContext;

import com.sun.glass.ui.Application;

import Model.PageBean;
import Model.Student;
import Service.SystemService;
import Service.ServiceImpl.SystemServiceImpl;
import Utils.StringUtil;

/**
 * Servlet implementation class SearchStudent
 */
public class SearchStudent extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doPost(request, response);
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=utf-8");
		String name = request.getParameter("name");
		String sex = request.getParameter("sex");
		int method = Integer.parseInt(request.getParameter("method"));
		if(method==1){
			SystemService service = new SystemServiceImpl();
			List<Student> students = service.search(name,sex);
			request.getSession().setAttribute("student", students);
			request.getSession().setAttribute("name", name);
			request.getSession().setAttribute("sex", sex);
			request.getRequestDispatcher("student1.jsp").forward(request, response);
		}
		if(method==2){
			int currentPage = Integer.parseInt(request.getParameter("currentPage"));
			SystemService service = new SystemServiceImpl();
			if(StringUtil.isEmpty(name)&&StringUtil.isEmpty(sex)){
				method=1;
			}
			PageBean pb = service.searchByPage(name, sex, currentPage, method);
			request.getSession().setAttribute("pb", pb);
			request.getRequestDispatcher("student2.jsp").forward(request, response);
		}
		if(method==3){
			int currentPage = Integer.parseInt(request.getParameter("currentPage"));
			SystemService service = new SystemServiceImpl();
			PageBean<Student> pb1 = (PageBean<Student>) request.getSession().getAttribute("pb");
			PageBean pb2 = service.searchByPage(pb1.getName(), pb1.getSex(), currentPage,method);
			request.getSession().setAttribute("pb", pb2);
			request.getRequestDispatcher("student2.jsp").forward(request, response);
		}
	}
}

6、StudentPageServlet

package Servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import Model.PageBean;
import Model.Student;
import Service.SystemService;
import Service.ServiceImpl.SystemServiceImpl;

/**
 * Servlet implementation class StudentPageServlet
 */
public class StudentPageServlet extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		int currentPage = Integer.parseInt(request.getParameter("currentPage"));
		SystemService service = new SystemServiceImpl();
		PageBean pb = service.findStudentByPage(currentPage);
		request.getSession().setAttribute("pb", pb);
		/*for (Object student : pb.getList()) {
			System.out.println(student.toString());
		}*/
		request.getRequestDispatcher("student2.jsp").forward(request, response);
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

}

7、StudentServlet

package Servlet;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import Model.Student;
import Service.SystemService;
import Service.ServiceImpl.SystemServiceImpl;

/**
 * Servlet implementation class StudentServlet
 */
public class StudentServlet extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		SystemService service = new SystemServiceImpl();
		List<Student> list = service.findAll();
		request.getSession().setAttribute("student", list);
		response.sendRedirect("student1.jsp");
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		this.doGet(request, response);
	}

}

8、UpdateStudent

package Servlet;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import Model.Student;
import Service.SystemService;
import Service.ServiceImpl.SystemServiceImpl;

/**
 * Servlet implementation class UpdateStudent
 */
public class UpdateStudent extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		this.doPost(request, response);
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		try {
			request.setCharacterEncoding("UTF-8");
			response.setContentType("text/html; charset=UTF-8");
			int id = Integer.parseInt(request.getParameter("id"));
			String school = request.getParameter("school");
			String department = request.getParameter("dept");
			String banji = request.getParameter("banji");
			String name = request.getParameter("name");
			String agestr = request.getParameter("age");
			String sex = request.getParameter("sex");
			String location = request.getParameter("location");
			String timestr = request.getParameter("time");
			Date time = null;
			int age = -1;
			if(school.trim().equals("")||department.trim().equals("")||banji.trim().equals("")||name.trim().equals("")||agestr.trim().equals("")||sex.trim().equals("")||location.trim().equals("")||timestr.trim().equals("")){
				response.getWriter().write("所填信息不能爲空!");
				response.setHeader("refresh", "3;addStudent.jsp");
			}else{
				age = Integer.parseInt(agestr);
				time = new SimpleDateFormat("yyyy-MM-dd").parse(timestr);
				Student stu = new Student(id,school,department,banji,name,age,sex,location,time);
				SystemService service = new SystemServiceImpl();
				service.update(stu);
				request.getRequestDispatcher("student").forward(request, response);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

Utils層

1、C3P0

package Utils;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

import org.apache.commons.dbutils.QueryRunner;

import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
 * 該類爲c3p0實現的JDBCUtil工具類
 * @author Hillain
 *
 */
public class JDBCUtil_c3p0 {
	static ComboPooledDataSource cpds = null;
	static QueryRunner qr = null;
	//靜態初始塊
	static{
		cpds = new ComboPooledDataSource();
		qr = new QueryRunner(cpds);
	}
	public static Connection getConn() throws SQLException{
		//註冊驅動
		//獲取Connection
		return cpds.getConnection();
	}
	public static QueryRunner getQR(){
		return qr;
	}
	/**
	 * 利用方法關閉JDBC資源
	 * @param rs
	 * @param st
	 * @param conn
	 */
	public static void closeAll(ResultSet rs,Statement st,Connection conn){
		closeRs(rs);
		closeSt(st);
		closeConn(conn);
	}
	//關閉ResultSet
	public static void closeRs(ResultSet rs){
		try {
			if(rs!=null){
				rs.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			rs = null;
		}
	}
	//關閉Statement
	public static void closeSt(Statement st){
		try {
			if(st!=null){
				st.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			st = null;
		}
	}
	//關閉Connection
	public static void closeConn(Connection conn){
		try {
			if(conn!=null){
				conn.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			conn = null;
		}
	}
}

2、StringUtil

package Utils;

public class StringUtil {
	static public boolean isEmpty(CharSequence s){
		return s==null||s.length()==0;
	}
}

3、C3P0-config

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
  <default-config>
  	<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
  	<property name="jdbcUrl">jdbc:mysql://localhost:3306/sorm?useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai</property>
  	<property name="user">root</property>
  	<property name="password">1044722314</property>
    <property name="checkoutTimeout">30000</property>
    <property name="idleConnectionTestPeriod">30</property>
    <property name="initialPoolSize">10</property>
    <property name="maxIdleTime">30</property>
    <property name="maxPoolSize">100</property>
    <property name="minPoolSize">10</property>
    <property name="maxStatements">200</property>
  </default-config>


  <!-- This app is massive! -->
  <named-config name="intergalactoApp"> 
    <property name="acquireIncrement">50</property>
    <property name="initialPoolSize">100</property>
    <property name="minPoolSize">50</property>
    <property name="maxPoolSize">1000</property>

    <!-- intergalactoApp adopts a different approach to configuring statement caching -->
    <property name="maxStatements">0</property> 
    <property name="maxStatementsPerConnection">5</property>

    <!-- he's important, but there's only one of him -->
    <user-overrides user="master-of-the-universe"> 
      <property name="acquireIncrement">1</property>
      <property name="initialPoolSize">1</property>
      <property name="minPoolSize">1</property>
      <property name="maxPoolSize">5</property>
      <property name="maxStatementsPerConnection">50</property>
    </user-overrides>
  </named-config>
</c3p0-config>

JSP層

1、addStudent

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<form action="AddStudent" method="post">
		<table border="1">
			 <tr align="center">
				<td>學校</td>
				<td><input type="text" name="school"></td>
			 </tr>
			 <tr align="center">
				<td>學院</td>
				<td><input type="text" name="dept"></td>
			 </tr>
			 <tr align="center">
				<td>班級</td>
				<td><input type="text" name="banji"></td>
			 </tr>
			 <tr align="center">
				<td>姓名</td>
				<td><input type="text" name="name"></td>
			 </tr>
			 <tr align="center">
				<td>年齡</td>
				<td><input type="text" name="age"></td>
			 </tr>
			 <tr align="center">
				<td>性別</td>
				<td>
					<input type="radio" name="sex" value="男"><input type="radio" name="sex" value="女"></td>
			 </tr>
			 <tr align="center">
				<td>籍貫</td>
				<td><input type="text" name="location"></td>
			 </tr>
			 <tr align="center">
				<td>註冊日期</td>
				<td><input type="text" name="time"></td>
			 </tr>
			 <tr align="center">
				<td><input value="添加信息" type="submit"></td>
			 </tr>
		</table>
	</form>
</body>
</html>

2、index

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>index</title>
</head>
<body>
	<h3><a href="student">顯示所有學生信息列表</a></h3>
	<h3><a href="StudentPageServlet?currentPage=1">分頁顯示所有學生信息列表</a></h3>
</body>
</html>

3、login

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>student</title>
<script type="text/javascript">
	function deleteStudent(id) {
		var flag = confirm("是否要刪除學生?");
		if(flag){
			//表明點了確定,訪問Servlet,在當前標籤頁上打開超鏈接
			window.location.href="DeleteStudent?id="+id
		}
	}
</script>
</head>
<body>
	<form action="SearchStudent" method="post">
		<table border="1">
			<tr>
				<td colspan="10">
					&nbsp
					姓名:<input type="text" name="name" <c:if test="${!empty name }">value="${name }"</c:if>>
					<input type="hidden" name="method" value="1">
					&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp
					性別:<select name="sex" style="text-align: center;">
							<option value="" <c:if test="${empty sex }">selected="selected"</c:if>>--請選擇--</option>
							<option value="男" <c:if test="${sex eq '男' }">selected="selected"</c:if>></option>
							<option value="女" <c:if test="${sex eq '女' }">selected="selected"</c:if>></option>
						 </select>
					&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp
					<input type="submit" value="查詢學生信息">
					&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp|&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp
					<input type="button" value="添加學生信息" onclick="window.location.href='addStudent.jsp'">
				</td>
			</tr>
			<tr align="center">
				<td>編號</td>
				<td>學校</td>
				<td>學院</td>
				<td>班級</td>
				<td>姓名</td>
				<td>年齡</td>
				<td>性別</td>
				<td>籍貫</td>
				<td>註冊日期</td>
				<td>操作</td>
			</tr>
			<c:forEach items="${student }" var="stu">
				<tr align="center">
					<td>${stu.id }</td>
					<td>${stu.school }</td>
					<td>${stu.department }</td>
					<td>${stu.banji }</td>
					<td>${stu.name }</td>
					<td>${stu.age }</td>
					<td>${stu.sex }</td>
					<td>${stu.location }</td>
					<td>${stu.rt }</td>
					<td><a href="FindStudent?id=${stu.id }">更新</a> <a href="#" onclick="deleteStudent(${stu.id })">刪除</a></td>
				</tr>
			</c:forEach>
		</table>
	</form>
</body>
</html>

4、student1

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>student</title>
<script type="text/javascript">
	function deleteStudent(id) {
		var flag = confirm("是否要刪除學生?");
		if(flag){
			//表明點了確定,訪問Servlet,在當前標籤頁上打開超鏈接
			window.location.href="DeleteStudent?id="+id
		}
	}
</script>
</head>
<body>
	<form action="SearchStudent" method="post">
		<table border="1">
			<tr>
				<td colspan="10">
					&nbsp
					姓名:<input type="text" name="name" <c:if test="${!empty name }">value="${name }"</c:if>>
					<input type="hidden" name="method" value="1">
					&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp
					性別:<select name="sex" style="text-align: center;">
							<option value="" <c:if test="${empty sex }">selected="selected"</c:if>>--請選擇--</option>
							<option value="男" <c:if test="${sex eq '男' }">selected="selected"</c:if>></option>
							<option value="女" <c:if test="${sex eq '女' }">selected="selected"</c:if>></option>
						 </select>
					&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp
					<input type="submit" value="查詢學生信息">
					&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp|&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp
					<input type="button" value="添加學生信息" onclick="window.location.href='addStudent.jsp'">
				</td>
			</tr>
			<tr align="center">
				<td>編號</td>
				<td>學校</td>
				<td>學院</td>
				<td>班級</td>
				<td>姓名</td>
				<td>年齡</td>
				<td>性別</td>
				<td>籍貫</td>
				<td>註冊日期</td>
				<td>操作</td>
			</tr>
			<c:forEach items="${student }" var="stu">
				<tr align="center">
					<td>${stu.id }</td>
					<td>${stu.school }</td>
					<td>${stu.department }</td>
					<td>${stu.banji }</td>
					<td>${stu.name }</td>
					<td>${stu.age }</td>
					<td>${stu.sex }</td>
					<td>${stu.location }</td>
					<td>${stu.rt }</td>
					<td><a href="FindStudent?id=${stu.id }">更新</a> <a href="#" onclick="deleteStudent(${stu.id })">刪除</a></td>
				</tr>
			</c:forEach>
		</table>
	</form>
</body>
</html>

5、student2

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>student</title>
<script type="text/javascript">
	function deleteStudent(id) {
		var flag = confirm("是否要刪除學生?");
		if(flag){
			//表明點了確定,訪問Servlet,在當前標籤頁上打開超鏈接
			window.location.href="DeleteStudent?id="+id
		}
	}
</script>
</head>
<body>
	<form action="SearchStudent" method="post">
		<table border="1">
			<tr>
				<td colspan="10">
					&nbsp
					姓名:<input type="text" name="name" <c:if test="${!empty pb.name }">value="${pb.name }"</c:if> >
					
					<input type="hidden" name="method" value="2">
					<input type="hidden" name="currentPage" value="1">
					&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp
					性別:<select name="sex" style="text-align: center;">
							<option value="" <c:if test="${empty pb.sex }">selected="selected"</c:if>>--請選擇--</option>
							<option value="男" <c:if test="${pb.sex eq '男' }">selected="selected"</c:if>></option>
							<option value="女" <c:if test="${pb.sex eq '女' }">selected="selected"</c:if>></option>
						 </select>
					&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp
					<input type="submit" value="查詢學生信息">
					&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp|&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp
					<input type="button" value="添加學生信息" onclick="window.location.href='addStudent.jsp'">
				</td>
			</tr>
			<tr align="center">
				<td>編號</td>
				<td>學校</td>
				<td>學院</td>
				<td>班級</td>
				<td>姓名</td>
				<td>年齡</td>
				<td>性別</td>
				<td>籍貫</td>
				<td>註冊日期</td>
				<td>操作</td>
			</tr>
			<c:forEach items="${pb.list }" var="stu">
				<tr align="center">
					<td>${stu.id }</td>
					<td>${stu.school }</td>
					<td>${stu.department }</td>
					<td>${stu.banji }</td>
					<td>${stu.name }</td>
					<td>${stu.age }</td>
					<td>${stu.sex }</td>
					<td>${stu.location }</td>
					<td>${stu.rt }</td>
					<td><a href="FindStudent?id=${stu.id }">更新</a> <a href="#" onclick="deleteStudent(${stu.id })">刪除</a></td>
				</tr>
			</c:forEach>
			<c:if test="${pb.method == 1}">
				<tr>
					<td colspan="10">
						第${pb.currentPage }/${pb.totalPage }&nbsp&nbsp&nbsp
						每頁顯示:${pb.currentSize }條數據&nbsp&nbsp&nbsp
						總記錄數:${pb.totalSize }條數據&nbsp&nbsp&nbsp
						<c:if test="${pb.currentPage!=1 }">
							<a href="StudentPageServlet?currentPage=1">首頁</a>|<a href="StudentPageServlet?currentPage=${pb.currentPage-1 }">上一頁</a>
						</c:if>
						
						<c:forEach begin="1" end="${pb.totalPage }" var="i">
							<c:if test="${pb.currentPage== i }">
								${i }
							</c:if>
							<c:if test="${pb.currentPage!= i }">
								<a href="StudentPageServlet?currentPage=${i }">${i }</a>
							</c:if>
						</c:forEach>
						
						<c:if test="${pb.currentPage!=pb.totalPage }">
							<a href="StudentPageServlet?currentPage=${pb.currentPage+1 }">下一頁</a>|<a href="StudentPageServlet?currentPage=${pb.totalPage}">尾頁</a>
						</c:if>
					</td>
				</tr>
			</c:if>
			<c:if test="${pb.method == 2||pb.method == 3}">
				<tr>
					<td colspan="10">
						第${pb.currentPage }/${pb.totalPage }&nbsp&nbsp&nbsp
						每頁顯示:${pb.currentSize }條數據&nbsp&nbsp&nbsp
						總記錄數:${pb.totalSize }條數據&nbsp&nbsp&nbsp
						<%
							session.setAttribute("name", session.getAttribute("pb"));
							session.setAttribute("sex", session.getAttribute("pb"));
						%>
						<c:if test="${pb.currentPage!=1 }">
							<a href="SearchStudent?currentPage=1&method=3">首頁</a>|<a href="SearchStudent?currentPage=${pb.currentPage-1 }&method=3">上一頁</a>
						</c:if>
						
						<c:forEach begin="1" end="${pb.totalPage }" var="i">
							<c:if test="${pb.currentPage== i }">
								${i }
							</c:if>
							<c:if test="${pb.currentPage!= i }">
								<a href="SearchStudent?currentPage=${i }&method=3">${i }</a>
							</c:if>
						</c:forEach>
						
						<c:if test="${pb.currentPage!=pb.totalPage }">
							<a href="SearchStudent?currentPage=${pb.currentPage+1 }&method=3">下一頁</a>|<a href="SearchStudent?currentPage=${pb.totalPage}&method=3">尾頁</a>
						</c:if>
					</td>
				</tr>
			</c:if>
		</table>
	</form>
</body>
</html>

6、update

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>update</title>
</head>
<body>
	<form method="post" action="UpdateStudent">
		<table border="1">
			<tr>
				<td>編號</td>
				<td><input type="hidden" name="id" value="${student.id }">${student.id }</td>
			</tr>
			<tr>
				<td>學校</td>
				<td><input type="text" name="school" value="${student.school }"></td>
			</tr>
			<tr>
				<td>學院</td>
				<td><input type="text" name="dept" value="${student.department }"></td>
			</tr>
			<tr>
				<td>班級</td>
				<td><input type="text" name="banji" value="${student.banji }"></td>
			</tr>
			<tr>
				<td>姓名</td>
				<td><input type="text" name="name" value="${student.name }"></td>
			</tr>
			<tr>
				<td>年齡</td>
				<td><input type="text" name="age" value="${student.age }"></td>
			</tr>
			<tr>
				<td>性別</td>
				<td>
					<input type="radio" name="sex"<c:if test="${student.sex =='男'}"> checked="true"</c:if> value="男"><input type="radio" name="sex"<c:if test="${student.sex =='女'}"> checked="true"</c:if> value="女"></td>
			</tr>
			<tr>
				<td>籍貫</td>
				<td><input type="text" name="location" value="${student.location }"></td>
			</tr>
			<tr>
				<td>註冊日期</td>
				<td><input type="text" name="time" value="${student.rt }"></td>
			</tr>
			<tr>
				<td>操作</td>
				<td><input value="更新" type="submit"><input value="返回" type="button" onclick="window.location.href='index.jsp'"></td>
			</tr>
		</table>
	</form>
</body>
</html>

注意:關於Dao類等接口這裏沒有貼上代碼,但不影響代碼的理解

4、項目的運行效果

具體運行效果請看視頻:項目演示視頻

5、項目的問題總結

由於Tomcat版本問題,學生管理系統項目提取不出學生的信息(已嘗試n次)✘
前兩週學習時,突然想最後一次嘗試,由於學生管理系統項目需要添加新功能✔
--------------------------以上分別是我解決問題前後的兩個階段----------------------------

遇到了N多個問題,不過最後解決了,感覺如磐涅重生了一般,在此寫下個人心得。
1、還是上一次的問題,我一直認爲是Tomcat版本的問題導致我的數據庫無法和Servlet連接,一直提不出數據。成功的原因是因爲學了數據庫連接池之後,打算使用 ComboPooledDataSource來重寫JDBCUtil,發現使用ComboPooledDataSource還是不行,而且是根本無法運行,錯誤爲找不到c3p0所在類?
2、喫驚的我百度之後發現JavaWeb不僅僅資源文件的實際位置不在Java工程,就連添加Build-Path都要放在WebContent的lib下進行纔可以成功使用jar包中的類。
3、終於,c3p0是成功使用了,在登陸頁面也終於可以嘗試登錄,只不過我無論輸入賬號密碼是否正確,最後永遠彈出登錄錯誤提示,其實這個錯誤也是因爲connection的jar包沒有正確導入。
4、jar包的問題解決之後,就是jsp頁面的問題,在jsp頁面中,無法顯示學生的信息,發現沒有導入taglib,導致c:forEach無法正確運行。
5、導入了jstl包之後,jsp頁面運行出現錯誤(雖然錯,但起碼前面的錯都對了),說在StudentImpl中沒有找到School這個屬性,其實我是有這個變量,只不過可能jsp只識別小寫,最後改爲小寫執行成功!
6、解決問題Invalid character found in the request target. The valid characters are defined in RFC 7230 and RF的問題,原因是利用href或者表單傳值的method爲get,但是不同版本的tomcat接收能力不一。我的tomcat不能用get接收中文數值(在Servlet設置編碼問題也解決不了、xml配置文件修改了也無濟於事),所以最後乾脆把href的數據利用<%%>把查詢的數據保存到Session中,這樣的話解決了這個問題,並且達到了我想要的效果(查詢過後,跳轉的信息頁面中仍然保存我之前所要查詢的信息)
------------------------------------------------------------------------------------------------------------
以上爲項目解決問題的過程以及心得,爲了加強記憶以及記錄這一神聖的時刻,因此寫了個人第二份博客,以下爲重點概括:
1、顯示數據:EL + JSP + JSTL + 表格。
2、在jsp頁面中想要使用c:forEach等系統未自帶的方法,必須要在首行導入taglib。
3、JUnit4調試失敗的原因很多,我是因爲方法前有返回值,而返回值只能爲void。
4、JSP頁面與Servlet是一對一的,一個個實現而不是同時寫出多個JSP或Servlet。
5、除MVC模式的三個部分,最好有一個業務邏輯類,專門負責處理業務。
6、JSP頁面使用doGet方法無法傳中文字符,故到Servlet寫入數據庫時就是亂碼。
7、如果傳到Servlet中的值有多個,則使用getParameterValues,返回值爲數組。
8、Web項目中lib不與src同級,故要把所有jar包放在WEB-INF的lib下添加纔可以。
9、在jsp的El表達式中,抓取對象的屬性,儘量寫爲小寫,否則無法識別,產生錯誤(這點沒有查詢過多的資料,自己判斷的結果)。
10、如果Servlet的請求轉發是doPost發送的,那麼在另一個Servlet中必須寫doPost,否則無效(最好在doGet中也調用doPost)。
11、完成了這些存儲工作後,需要跳轉到列表頁面。這裏不能直接跳轉到列表頁面,應該先跳轉到Servlet,由Servlet跳轉到列表頁面。
12、隱藏表單的妙用:在更新信息的功能中,肯定要將id傳遞給Servlet,但是學生的id肯定是不可修改,但還是要傳遞。所以手動創建一個隱藏的輸入框,給定id的值,以便提交表單。
13、模糊查詢和普通查詢方式不一,若模糊查詢中使用普通查詢的方式,則根據不同的情況可能要寫4、5種sql查詢。而"select * from stu where 1=1",可以保留sql語句中的where,默認查詢所有,其他情況直接在末端添加相應語句即可。

6、項目的感想與展望

1、文章開頭所提到的兩個階段確實是兩個實打實的階段,第一個階段在解決了看似無法解決的問題之後,引發了一系列後續問題,所以初版的學生管理系統並不是這麼簡單。其次就是第二個階段,後續功能的實現花了比較長的時間(在實現過程中還是有很多錯誤以及學校正常學習、Web的視頻學習等也佔了很多時間)

2、這一次的Web項目相比之前做過的項目,也許總耗時不是最長,但是邏輯層面和代碼量是之前做過的項目所不及的。而且這個完整的管理系統並不是一次性就可以寫好,而是一直改錯一直改進所完成的。讓我明白了,不管是什麼作品,都可能是經過一波三折之後才寫出來的。

3、這次的項目經歷使我對MVC模式更加的瞭解,對數據庫連接池、JSP頁面的書寫、JSTL以及EL表達式等這類代碼書寫更加的熟練,對分頁功能的印象更加深刻。PS:一個看似簡單的分頁功能可能會讓你可以整一個下午。

4、關於後續的學習,如果有好的項目,則還是會與這次一樣把一系列的內容寫到博客中進行記錄與交流。

5、Web學生管理系統項目基本到此結束,之前做過一個應用程序版的學生管理系統(內包含:仿TIM登錄界面、較爲精美的管理系統界面,以及一系列功能),也是在學校花了近兩個星期的週期寫了這個項目(PS:關於界面的製作花了大量時間,畢竟想做一個讓自己滿意的項目),所以我可能也會在近期有空寫一個博客,來記錄並回憶一下所學所做的內容。

最後:本人爲大二菜雞一枚,花了近半天時間寫下這篇博客來記錄一下項目經驗。同時也希望能夠幫助到一些對Web或者項目有問題的同學。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章