前言
為了鞏固開發的流程,我們再拿一個客戶關系管理系統來練手...!
成果圖
我們完成的就是下面的項目!
搭建配置環境
- 配置Tomcat
- 導入開發包
- 建立開發用到的程序包
- 在數據庫創建相對應的表
CREATE TABLE customer (
id VARCHAR(40) PRIMARY KEY,
name VARCHAR(20) NOT NULL,
gender VARCHAR(10) NOT NULL,
birthday DATE,
cellphone VARCHAR(30) NOT NULL,
email VARCHAR(30),
preference VARCHAR(200),
type VARCHAR(20),
description VARCHAR(255)
);
開發實體
開發實體十分簡單,對照着數據庫的表就行了!
private String id;
private String name ;
private String gender ;
private Date birthday ;
private String cellphone ;
private String eamil ;
private String preference ;
private String type ;
private String description;
//....各種setter、getter
開發獲取數據庫連接池的Utils
導入配置文件
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/zhongfucheng</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
<named-config name="mysql">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/zhongfucheng</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</named-config>
<named-config name="oracle">
<property name="driverClass">oracle.jdbc.driver.OracleDriver</property>
<property name="jdbcUrl">jdbc:oracle:thin:@//localhost:1521/事例名...</property>
<property name="user">用戶名</property>
<property name="password">密碼</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</named-config>
</c3p0-config>
開發提供數據連接池的工具類
public class Utils2DB {
private static ComboPooledDataSource comboPooledDataSource = null;
static {
//它會自動尋找配置文件,節點為mysql的數據庫(默認就是Mysql)
comboPooledDataSource = new ComboPooledDataSource();
}
public static DataSource getDataSource() {
return comboPooledDataSource ;
}
public static Connection connection() {
try {
return comboPooledDataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException("數據庫初始化失敗了!");
}
}
}
開發UUID工具類##
public class WebUtils {
public static String makeId() {
return UUID.randomUUID().toString();
}
}
開發DAO
DAO應該提供增加客戶和查詢用戶的功能
增加用戶
public void addCustomer(Customer customer) {
QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
String sql = "INSERT INTO customer (id,name, gender, birthday, cellphone, preference, type, description) VALUES (?, ?, ?, ?, ?, ?, ?, ?,?)";
//得到用戶傳遞進來的數據
String id = customer.getId();
String name = customer.getName();
String gender = customer.getGender();
String cellphone = customer.getCellphone();
String email = customer.getEmail();
String preference = customer.getPreference();
String type = customer.getType();
String description = customer.getDescription();
//對於日期,要轉換一下
Date date = customer.getBirthday();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String birthday = simpleDateFormat.format(date);
try {
//向數據庫插入數據
queryRunner.update(sql, new Object[]{id, name, gender, birthday, cellphone, email, preference, type, description});
//插入記錄成功!
} catch (SQLException e) {
//如果出現了異常,就拋出Dao異常吧(自定義的異常)
e.printStackTrace();
throw new DaoException("添加用戶出錯了!");
}
}
測試增加用戶
寫完一個功能,不要急着去寫其他的功能,先測試一下!
@Test
public void add() {
//為了測試的方便,直接使用構造函數了!
Customer customer = new Customer("1", "zhongfucheng", "男", new Date(), "1234", "aa@sina.com", "打代碼", "高貴的用戶", "我是個好人");
CustomerDao customerDao = new CustomerDao();
customerDao.addCustomer(customer);
}
- 好的,沒有報錯!再看看數據庫-----------只要是中文的數據,都亂碼了!
解決的辦法,看我另外一篇博文:https://zhongfucheng.bitcron.com/post/jie-jue-cuo-wu/mysqlzhong-wen-luan-ma
查詢用戶
將所有的客戶查詢出來就行了!
//得到所有的用戶
public List<Customer> getAll() {
QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
String sql = "SELECT * FROM customer";
try {
List<Customer> customers = (List<Customer>) queryRunner.query(sql, new BeanListHandler(Customer.class));
//如果集合大於個數大於0,就返回集合,不大於0,就返回null
return customers.size() > 0 ? customers : null;
} catch (SQLException e) {
e.printStackTrace();
throw new DaoException("獲取所有的用戶出錯了!");
}
}
測試查詢用戶
@Test
public void find() {
CustomerDao customerDao = new CustomerDao();
List<Customer> customers = customerDao.getAll();
for (Customer customer : customers) {
System.out.println(customer.getName());
}
}
修改用戶信息
修改用戶信息首先要知道用戶的信息,在web端,只有id能唯一標識用戶,我們需要通過id,獲取用戶全部信息(也就是Customer對象)
public Customer find(String id) {
QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
String sql = "SELECT * FROM customer WHERE id = ?";
try {
Customer customer = (Customer) queryRunner.query(sql, new BeanHandler(Customer.class), new Object[]{id});
return customer;
} catch (SQLException e) {
e.printStackTrace();
throw new DaoException("查找用戶失敗了");
}
}
修改用戶都是外邊傳遞個對象進來,Dao層取出對象的數據,從而對數據庫的數據進行修改!
public void update(Customer customer) {
QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
String sql = "UPDATE customer set name=?,gender=?,birthday=?,cellphone=?,email=?,preference=?,type=?,description=? WHERE id = ?";
try {
queryRunner.update(sql, new Object[]{customer.getName(), customer.getGender(), customer.getBirthday(),customer.getCellphone(), customer.getEmail(), customer.getPreference(), customer.getType(), customer.getDescription(), customer.getId()});
} catch (SQLException e) {
e.printStackTrace();
throw new DaoException("更新失敗");
}
}
測試修改用戶
@Test
public void update() {
CustomerDao customerDao = new CustomerDao();
//我們已經知道了某id,通過id獲取得到用戶信息(Customer)
String id = "043f7cce-c6f1-4155-b688-ba386cae1636";
Customer customer = customerDao.find(id);
//修改用戶信息
customer.setName("看完博客要點贊");
customerDao.update(customer);
}
- 原來該用戶的名字是d
- 測試完之后:
刪除用戶
- 通過外界傳遞進來的id,就可以刪除數據庫表中的記錄了
public void delete(String id) {
QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
String sql = "DELETE from customer WHERE id = ?";
try {
queryRunner.update(sql, new Object[]{id});
} catch (SQLException e) {
e.printStackTrace();
throw new DaoException("刪除用戶失敗了");
}
}
測試刪除用戶
@Test
public void delete() {
CustomerDao customerDao = new CustomerDao();
//我們已經知道了某id,通過id刪除數據庫中的記錄
String id = "043f7cce-c6f1-4155-b688-ba386cae1636";
customerDao.delete(id);
}
數據庫已經查詢不到id為043f7cce-c6f1-4155-b688-ba386cae1636的記錄了!
開發service
public class BusinessService {
CustomerDao customerDao = new CustomerDao();
public List<Customer> getAll() {
return customerDao.getAll();
}
public void addCustomer(Customer customer) {
customerDao.addCustomer(customer);
}
public void deleteCustomer(String id) {
customerDao.delete(id);
}
public void updateCustomer(Customer customer) {
customerDao.update(customer);
}
public Customer findCustomer(String id) {
return customerDao.find(id);
}
}
開發web 的增加和查詢#
提供UI,增加客戶的Servlet
//直接跳轉到顯示增加用戶頁面的jsp
request.getRequestDispatcher("/WEB-INF/addCustomer.jsp").forward(request, response);
開發顯示添加客戶頁面
<form action="${pageContext.request.contextPath}/addCustomerController">
<table border="1px">
<tr>
<td>用戶名:</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td>性別:</td>
<td>
<input type="radio" name="gender" value="female">女
<input type="radio" name="gender" value="male">男
</td>
</tr>
<tr>
<td>生日:</td>
<td>
<select id="year">
<option value="1900">1900</option>
</select>
<select id="month">
<option value="01">01</option>
</select>
<select id="day">
<option value="01">01</option>
</select>
</td>
</tr>
<tr>
<td>電話號碼:</td>
<td><input type="text" name="cellphone"></td>
</tr>
<tr>
<td>郵箱:</td>
<td><input type="text" name="email"></td>
</tr>
<tr>
<td>愛好:</td>
<td>
<input type="checkbox" name="hobbies" value="唱歌">唱歌
<input type="checkbox" name="hobbies" value="跳舞">跳舞
<input type="checkbox" name="hobbies" value="打代碼">打代碼
</td>
</tr>
<tr>
<td>客戶類型</td>
<td>
<input type="radio" name="type" value="VIP">VIP
<input type="radio" name="type" value="普通客戶">普通客戶
<input type="radio" name="type" value="黑名單客戶">黑名單客戶
</td>
</tr>
<tr>
<td>描述</td>
<td>
<textarea name="description" cols="30" rows="10"></textarea>
</td>
</tr>
<tr>
<td><input type="submit" value="增添客戶"></td>
<td><input type="reset" value="重置"></td>
</tr>
</table>
</form>
- 效果是這樣子的
我們發現,在日期的下拉框中,只有一個數據(因為我們在value中只寫了一個數據)
要想在下拉框中可以選擇很多的數據,那么value的值就不能單單只有一個。當然了,也不可能在JSP頁面中寫下面的代碼
<option value="1900">1900</option>
<option value="1901">1900</option>
<option value="1902">1900</option>
<option value="1903">1900</option>
我們用javaScript生成下拉框的數據就行了!!
- 獲取年份!
function makeYear() {
//得到下拉框的控件
var year = document.getElementById("year");
//要想下拉框有更多的數據,就需要有更多的option控件
//js獲取得到年份是getFullYear(),單單的getYear()只是獲取兩位數
for (var i=1901; i<= new Date().getFullYear(); i++) {
//生成option控件
var option = document.createElement("option");
//option控件的值和文本內容為循環生成的年分!
option.value = i;
option.innerText = i;
//將生成option控件綁定到select控件上
year.appendChild(option);
}
}
- 獲取月份和日也類似
function makeMonth() {
var month = document.getElementById("month");
for (var i = 2; i <= 12; i++) {
var option = document.createElement("option");
if (i < 10) {
option.value = '0' + i;
option.innerText = '0' + i;
} else {
option.value = i;
option.innerText = i;
}
month.appendChild(option);
}
}
function makeDay()
{
var day = document.getElementById("day");
for(var i=2;i<=12;i++)
{
var option = document.createElement("option");
if(i<10)
{
option.value = '0' + i;
option.innerText = '0' + i;
}else{
option.value = i;
option.innerText = i;
}
day.appendChild(option);
}
}
-
在JSP頁面中導入javascript文件
-
注意:javasrcipt文件不能放在WEB-INF下面!!!!否則是獲取不到的!!!
<script type="text/javascript" src="${pageContext.request.contextPath}/customer.js" ></script>
- 這三個函數都是在頁面加載時就應該被初始化了,所以在body上綁定onload時間即可!!
function pageInit() {
makeYear();
makeMonth();
makeDay();
}
<body onload="pageInit()">
- 效果:
JavaScript拼湊數據
表單的數據非常多,毫無疑問,我們會使用BeanUtils來將數據封裝到Bean對象中!
對於表單的數據,還是有些雜亂的。表單中日期的年月日是分開的,我們要么在客戶端將年月日的數據拼湊起來,要么在服務器端將年月日拼湊起來!同理,客戶的喜好可能不單單有一個,但在Customer對象中,喜好單單用一個String類型來表示的。我們也要把客戶的喜好拼湊起來。
顯然,在客戶端用javaScript做拼湊是非常方便的!
function makeBirthday() {
//獲取下拉框的數據,把數據拼湊成日期字符串
var year = document.getElementById("year");
var month = document.getElementById("month");
var day = document.getElementById("day");
var birthday = year + "-" + month + "-" + day;
//想要將拼湊完的字符串提交給服務器,用隱藏域就行了
var input = document.createElement("input");
input.type = "hidden";
input.value = birthday;
input.name = "birthday";
//將隱藏域綁定在form下【為了方便,在form中設置id,id名字為form】
document.getElementById("form").appendChild(input);
}
function makePreference() {
//獲取喜好的控件
var hobbies = document.getElementsByName("hobbies");
//定義變量,記住用戶選中的選項
var preference = "";
//遍歷喜好的控件,看用戶選上了什么!
for (var i = 0; i < hobbies.length; i++) {
if (hobbies[i].checked == true) {
preference += hobbies[i].value + ",";
}
}
//剛才拼湊的時候,最后一個逗號是多余的,我們要把它去掉
preference = preference.substr(0, preference.length - 1);
//也是用隱藏域將數據帶過去給服務器
var input = document.createElement("input");
input.type = "hidden";
input.value = preference;
input.name = "preference";
//將隱藏域綁定到form表單上
document.getElementById("form").appendChild(input);
}
- 當表單提交的時候,觸發上面兩個函數就行了!所以在form表單上綁定onsumit事件!
function makeForm() {
makeBirthday();
makePreference();
return true;
}
<form action="${pageContext.request.contextPath}/addCustomerController" id="form" onsubmit=" return makeForm()" method="post">
開發處理表單數據的Servlet
- 將表單的數據封裝到Bean對象中,要開發工具類
public static <T> T request2Bean(HttpServletRequest httpServletRequest, Class<T> aClass) {
try {
//獲取Bean的對象
T bean = aClass.newInstance();
//獲取表單中所有的名字
Enumeration enumeration = httpServletRequest.getParameterNames();
//遍歷表單提交過來的名字
while (enumeration.hasMoreElements()) {
//每個名字
String name = (String) enumeration.nextElement();
//獲取得到值
String value = httpServletRequest.getParameter(name);
//如果用戶提交的數據不為空,那么將數據封裝到Bean中
if (!value.equals("") && value != null) {
BeanUtils.setProperty(bean, name, value);
}
}
return bean;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("封裝數據到Bean中,失敗了!");
}
}
- 處理表單數據的Servlet代碼:
//將表單的數據弄到Bean對象中
Customer customer = WebUtils.request2Bean(request, Customer.class);
try {
//調用BusinessService層的方法,添加客戶
BusinessService businessService = new BusinessService();
businessService.addCustomer(customer);
//如果執行到這里,說明成功了,如果被catch了,說明失敗了。
request.setAttribute("message", "添加成功!");
} catch (Exception e) {
e.printStackTrace();
request.setAttribute("message", "添加失敗");
}
request.getRequestDispatcher("/message.jsp").forward(request, response);
- 效果:
提供查詢客戶界面的Servlet
//跳轉到顯示客戶界面信息的jsp
request.getRequestDispatcher("/WEB-INF/lookCustomer.jsp").forward(request, response);
開發顯示客戶信息的頁面
<c:if test="${empty(list)}">
對不起,還沒有任何客戶的信息!
</c:if>
<c:if test="${!empty(list)}">
<table border="1px">
<tr>
<td>用戶名:</td>
<td>密碼:</td>
<td>性別:</td>
<td>生日:</td>
<td>電話號碼:</td>
<td>郵箱:</td>
<td>類型:</td>
<td>描述:</td>
</tr>
<c:forEach items="${list}" var="customer">
<tr>
<td>${customer.name}</td>
<td>${customer.gender}</td>
<td>${customer.birthday}</td>
<td>${customer.cellphone}</td>
<td>${customer.email}</td>
<td>${customer.preference}</td>
<td>${customer.type}</td>
<td>${customer.description}</td>
</tr>
</c:forEach>
</table>
</c:if>
- 效果:
將功能拼接在首頁上
采用分貞技術,讓界面更加好看!
index頁面:
<frameset rows="25%,*">
<frame src="${pageContext.request.contextPath }/head.jsp" name="head">
<frame src="${pageContext.request.contextPath }/body.jsp" name="body">
</frameset>
head頁面:
<body style="text-align: center;">
<h1>客戶管理系統!</h1>
<a href="${pageContext.request.contextPath}/AddCustomer" target="body">增添客戶</a>
<a href="${pageContext.request.contextPath}/LookCustomer" target="body">查看客戶</a>
</body>
body頁面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
</body>
</html>
- 效果:
改善顯示頁面
現在問題來了,如果我們客戶信息有非常非常地多,我們不可能把客戶信息全部都擠在同一個頁面上,如果我們這樣做的話,網頁的長度就會非常的長!
於是乎,我們就需要用到了分頁的技術,對於分頁技術基礎的講解,在我另一篇博文中有介紹: https://zhongfucheng.bitcron.com/post/jdbc/jdbcdi-si-pian-shu-ju-ku-lian-jie-chi-dbutilskuang-jia-fen-ye
看完上篇博文,我們知道,首先要做的就是:明確分頁技術中需要用到的4個變量的值!
查詢總記錄數
查詢總記錄數也就是查詢數據庫表的記錄有多少條,這是關於對數據庫數據的操作,所以肯定是在dao層做!
public Long getTotalRecord() {
QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
String sql = "SELECT * FROM customer";
try {
//獲取查詢的結果
Long l = (Long) queryRunner.query(sql, new ScalarHandler());
return l;
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException("查詢總記錄數失敗了!");
}
}
查詢分頁的數據
獲取分頁的數據也是查詢數據庫的記錄,這也是關於對數據庫的操作,所以也是在Dao層做的!
/*查詢分頁數據*/
//獲取分頁的數據是需要start和end兩個變量的【從哪條開始取,取到哪一條】
public List<Customer> getPageData(int start, int end) {
QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
String sql = "SELECT * FROM customer LIMIT ?,?";
try {
List<Customer> customers = (List<Customer>) queryRunner.query(sql, new BeanListHandler(Customer.class), new Object[]{start, end});
return customers;
} catch (SQLException e) {
e.printStackTrace();
throw new DaoException("獲取分頁數據失敗了!");
}
}
分析
現在我們已經可以知道總記錄數了,對於其他3個變量(每頁顯示記錄數【由程序員來指定】,當前是多少頁【由用戶來指定】,總頁數【由總記錄數和每頁顯示記錄數來算數來的】)
現在要分析的是,這些變量應該放在哪里呢???全部放在Dao層??全部放在Dao層是可以實現功能的,但是,這樣MVC結構就被破壞掉了(Dao層只用來對數據進行CRUD操作,4個變量存在Dao層,是不合理的)
最好的做法是這樣的:創建一個實體Page,將分頁用到的信息全部封裝在Page中實現!Page就代表着分頁的數據這樣就非常符合面向對象的思想了!
將數據封裝到Page中並在頁面上顯示分頁的數據
①:創建Page類
//保存着分頁的數據
private List<Customer> list;
//總記錄數
private long totalRecord;
//每頁顯示記錄數,這里我規定每頁顯示3條
private int linesize = 3;
//總頁數
private int totalPageCount;
//當前顯示的頁數
private long currentPageCount;
//...各種的setter、getter
②:BusinessService應該提供獲取分頁數據的服務
//既然Page對象代表是分頁數據,那么返回Page對象即可!
//web層應該傳入想要看哪一頁數據的參數!
public Page getPageData(String currentPageCount) {
Page page = new Page();
//獲取數據庫中有多少條記錄,並封裝到Page對象中
Long totalRecord = customerDao.getTotalRecord();
page.setTotalRecord(totalRecord);
//算出總頁數,並封裝到Page對象中
int totalPagecount = (int) (totalRecord % page.getLinesize() == 0 ? totalRecord / page.getLinesize() : totalRecord / page.getLinesize() + 1);
page.setTotalPageCount(totalPagecount);
int start ;
int end = page.getLinesize();
//現在又分兩種情況了,如果傳遞進來的參數是null的,那么說明外界是第一次查詢的
if (currentPageCount == null) {
//第一次查詢,就應該設置當前頁數是第一頁
page.setCurrentPageCount(1);
start = (int) ((page.getCurrentPageCount() - 1) * page.getLinesize());
List<Customer> customers = customerDao.getPageData(start, end);
page.setList(customers);
} else {
//如果不是第一次,就把外界傳遞進來的頁數封裝到Page對象中
page.setCurrentPageCount(Long.parseLong(currentPageCount));
start = (int) ((page.getCurrentPageCount() - 1) * page.getLinesize());
List<Customer> customers = customerDao.getPageData(start, end);
page.setList(customers);
}
return page;
}
③:web層調用BusinessService層的功能,獲取得到Page對象
//獲取用戶想要看的頁數,如果是第一次,那肯定為null
String currentPageCount = request.getParameter("currentPageCount");
//調用BusinessService的方法,獲取得到所有客戶信息
BusinessService businessService = new BusinessService();
Page page = businessService.getPageData(currentPageCount);
//把客戶信息帶過去給jsp頁面
request.setAttribute("page", page);
//跳轉到顯示客戶界面信息的jsp
request.getRequestDispatcher("/WEB-INF/lookCustomer.jsp").forward(request, response);
④:在JSP頁面中,使用EL表達式獲取到Page對象,從而輸出數據
<c:forEach items="${page.list}" var="customer">
<tr>
<td>${customer.name}</td>
<td>${customer.gender}</td>
<td>${customer.birthday}</td>
<td>${customer.cellphone}</td>
<td>${customer.email}</td>
<td>${customer.preference}</td>
<td>${customer.type}</td>
<td>${customer.description}</td>
</tr>
</c:forEach>
⑤:在JSP頁面中顯示頁碼,同時把碼數綁定到超鏈接去!
<%--提供頁數的界面--%>
<c:forEach var="pageNum" begin="1" end="${page.totalPageCount}">
<a href="${pageContext.request.contextPath}/LookCustomer?currentPageCount=${pageNum}">
${pageNum}
</a>
</c:forEach>
- 效果:
讓分頁的功能更加完善
增加上一步和下一步
一般的分頁不僅僅只有頁碼給你,還有上一步和下一步。我們在JSP頁面上也能添加這樣的功能,其實這是非常簡單的!
<%--如果當前的頁碼大於1,才顯示上一步--%>
<c:if test="${page.currentPageCount>1}">
<%--把傳遞過去的頁碼-1就行了--%>
<a href="${pageContext.request.contextPath}/LookCustomer?currentPageCount=${page.currentPageCount-1}">
上一步
</a>
</c:if>
<%--如果當前的頁碼小於總頁數,才顯示下一步--%>
<c:if test="${page.currentPageCount<page.totalPageCount}">
<%--把傳遞過去的頁碼-1就行了--%>
<a href="${pageContext.request.contextPath}/LookCustomer?currentPageCount=${page.currentPageCount+1}">
下一步
</a>
</c:if>
- 效果:
顯示當前頁數,總頁數,總記錄數
當前頁數是:[${page.currentPageCount}]
總頁數是:${page.totalPageCount}
總記錄數是:${page.totalRecord}
- 效果:
跳轉到某頁上
<input type="text" id="currentPageCount">
<input type="button" value="跳轉">
- 頁面效果:
我們現在要做的就是:怎么樣才能輸入框輸入內容,然后點擊跳轉按鈕,將輸入框的數據發送到Servlet上,然后實現跳轉到某頁上功能
明顯地,我們肯定要使用JavaScript代碼!
<script type="text/javascript">
/*既然寫上了JavaScript代碼了,就順便驗證輸入框輸入的數據是否合法吧*/
function goPage() {
/*獲取輸入框控件*/
var input = document.getElementById("currentPageCount");
/*獲取輸入框的數據*/
var value = input.value;
if(value==null || value==""){
alert("請輸入頁碼");
return false;
}
if(!value.match("\\d+")){
alert("請輸入數字");
return false;
}
if(value<1 || value>${page.totalPageCount}){
alert("請輸入合法數據");
return false ;
}
window.location.href="${pageContext.request.contextPath}/LookCustomer?currentPageCount="+value;
}
</script>
- 效果:
記錄JSP頁面的開始頁和結束頁
為什么我們要記錄JSP頁面的開始頁和結束頁呢?經過上面層層地優化,我們感覺不出有什么問題了。那是因為數據量太少!
我們試着多添加點記錄進數據庫,再回來看看!
從上面的圖我們可以發現頁數有多少,JSP頁面就顯示多少!這明顯不合理的,如果有100頁也顯示100頁嗎?
我們做一個規定,一次只能顯示10頁的數據。那么顯示哪10頁呢?這又是一個問題了,如果我們在看第11頁的數據,應該顯示的是第7到第16頁的數據(顯示11附近的頁數),我們在看第2頁的數據,應該顯示第1到第10頁的數據。用戶想要看的頁數是不明確的,我們顯示附近的頁數也是不明確的!。我們應該把用戶想要看的頁數記錄下來,然后根據邏輯判斷,顯示附近的頁數
我們顯示頁數的代碼是這樣的:
很明顯,我們只要控制了begin和end中的數據,就控制顯示哪10頁了!
①在Page類中多定義兩個成員變量
//記錄JSP頁面開始的頁數和結束的頁數
private int startPage;
private int endPage;
//Setter,Getter方法
②開始頁數和結束頁數受用戶想看的頁數影響,在BusinessService的getPageData()加入下面的邏輯
//第一次訪問
page.setStartPage(1);
page.setEndPage(10);
//不是第一次訪問
if (page.getCurrentPageCount() <= 10) {
page.setStartPage(1);
page.setEndPage(10);
} else {
page.setStartPage((int) (page.getCurrentPageCount() - 4));
page.setEndPage((int) (page.getCurrentPageCount() + 5));
//如果因為加減角標越界了,那么就設置最前10頁,或者最后10頁
if (page.getStartPage() < 1) {
page.setStartPage(1);
page.setEndPage(10);
}
if (page.getEndPage() > page.getTotalPageCount()) {
page.setEndPage(page.getTotalPageCount());
page.setStartPage(page.getTotalPageCount() - 9);
}
}
③:在JSP顯示頁數時,獲取得到開始頁和結束頁就行了
<%--提供頁數的界面--%>
<c:forEach var="pageNum" begin="${page.startPage}" end="${page.endPage}">
<a href="${pageContext.request.contextPath}/LookCustomer?currentPageCount=${pageNum}">
[${pageNum}]
</a>
</c:forEach>
- 效果:
重構優化
分頁重構
我們再回頭看看BusinessService中獲取分頁數據的代碼:
//既然Page對象代表是分頁數據,那么返回Page對象即可!
//web層應該傳入想要看哪一頁數據的參數!
public Page getPageData(String currentPageCount) {
Page page = new Page();
//獲取數據庫中有多少條記錄,並封裝到Page對象中
Long totalRecord = customerDao.getTotalRecord();
page.setTotalRecord(totalRecord);
//算出總頁數,並封裝到Page對象中
int totalPagecount = (int) (totalRecord % page.getLinesize() == 0 ? totalRecord / page.getLinesize() : totalRecord / page.getLinesize() + 1);
page.setTotalPageCount(totalPagecount);
int start ;
int end = page.getLinesize();
//現在又分兩種情況了,如果傳遞進來的參數是null的,那么說明外界是第一次查詢的
if (currentPageCount == null) {
//第一次查詢,就應該設置當前頁數是第一頁
page.setCurrentPageCount(1);
page.setStartPage(1);
page.setEndPage(10);
start = (int) ((page.getCurrentPageCount() - 1) * page.getLinesize());
List<Customer> customers = customerDao.getPageData(start, end);
page.setList(customers);
} else {
//如果不是第一次,就把外界傳遞進來的頁數封裝到Page對象中
page.setCurrentPageCount(Long.parseLong(currentPageCount));
start = (int) ((page.getCurrentPageCount() - 1) * page.getLinesize());
if (page.getCurrentPageCount() <= 10) {
page.setStartPage(1);
page.setEndPage(10);
} else {
page.setStartPage((int) (page.getCurrentPageCount() - 4));
page.setEndPage((int) (page.getCurrentPageCount() + 5));
//如果因為加減角標越界了,那么就設置最前10頁,或者最后10頁
if (page.getStartPage() < 1) {
page.setStartPage(1);
page.setEndPage(10);
}
if (page.getEndPage() > page.getTotalPageCount()) {
page.setEndPage(page.getTotalPageCount());
page.setStartPage(page.getTotalPageCount() - 9);
}
}
List<Customer> customers = customerDao.getPageData(start, end);
page.setList(customers);
}
return page;
}
太太太太太tm復雜,太太太太tm長了!!!!!我們BusinessService要做的僅僅是調用Dao層的功能,為web層提供數據,但我們在方法中使用大量了邏輯判斷,而且這些邏輯判斷都是屬於Page類的!
明確一下:只有獲取數據庫總記錄數是在BusinessService中做的,其他的數據變量都是應該在Page類中完成!
在BusinessService獲取了總記錄數之后,我們要對其他變量進行初始化(根據總記錄數,用戶想要看哪一頁的數據),算出其他的數據(JSP記錄開始頁數、結束頁數、總頁數等等),最好的辦法就是通過Page的構造函數來實現初始化!
- 改良后的BusinessService
public Page getPageData2(String currentPageCount) {
//獲取得到總記錄數
Long totalPageCount = customerDao.getTotalRecord();
if (currentPageCount == null) {
//如果是第一次,那么就將用戶想看的頁數設置為1
Page page = new Page(1, totalPageCount);
List<Customer> customers = customerDao.getPageData(page.getStartIndex(), page.getLinesize());
page.setList(customers);
return page;
} else {
//如果不是第一次,就將獲取得到的頁數傳遞進去
Page page = new Page(Integer.parseInt(currentPageCount), totalPageCount);
List<Customer> customers = customerDao.getPageData(page.getStartIndex(), page.getLinesize());
page.setList(customers);
return page;
}
}
- 改良后的Page類(原來的Page類只有成員變量和setter、getter方法)
public Page(int currentPageCount, long totalRecord) {
//將傳遞進來的currentPageCount初始化
this.currentPageCount = currentPageCount;
//總頁數
totalPageCount = (int) (totalRecord % linesize == 0 ? totalRecord / linesize : totalRecord / linesize + 1);
//總記錄數
this.totalRecord = totalRecord;
//開始取數據的位置
startIndex = (currentPageCount - 1) * linesize;
//如果當前頁小於10,那么開始頁為1,結束頁為10就行了
if (this.currentPageCount <= 10) {
this.startPage = 1;
this.endPage = 10;
} else {
startPage = this.currentPageCount - 4;
endPage = this.currentPageCount + 5;
//加減后頁數越界的情況
if (startPage < 1) {
this.startPage = 1;
this.endPage = 10;
}
if (endPage > totalPageCount) {
this.startPage = this.currentPageCount - 9;
this.endPage = this.totalPageCount;
}
}
}
分頁顯示頁面重構
分頁的顯示頁面都是永恆不變的,我們可以把代碼重構成一個jsp,需要用到分頁顯示頁面的地方,就包含進去就行了!
- page.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--顯示當前頁數--%>
當前頁數是:[${page.currentPageCount}]
<%--如果當前的頁碼大於1,才顯示上一步--%>
<c:if test="${page.currentPageCount>1}">
<%--把傳遞過去的頁碼-1就行了--%>
<a href="${pageContext.request.contextPath}/LookCustomer?currentPageCount=${page.currentPageCount-1}">
上一步
</a>
</c:if>
<%--提供頁數的界面--%>
<c:forEach var="page" begin="${page.startPage}" end="${page.endPage}">
<a href="${pageContext.request.contextPath}/LookCustomer?currentPageCount=${page}">
[${page}]
</a>
</c:forEach>
<%--如果當前的頁碼小於總頁數,才顯示下一步--%>
<c:if test="${page.currentPageCount<page.totalPageCount}">
<%--把傳遞過去的頁碼-1就行了--%>
<a href="${pageContext.request.contextPath}/LookCustomer?currentPageCount=${page.currentPageCount+1}">
下一步
</a>
</c:if>
<input type="text" id="currentPageCount">
<input type="button" value="跳轉" onclick="goPage()">
總頁數是:${page.totalPageCount}
總記錄數是:${page.totalRecord}
<script type="text/javascript">
/*既然寫上了JavaScript代碼了,就順便驗證輸入框輸入的數據是否合法吧*/
function goPage() {
/*獲取輸入框控件*/
var input = document.getElementById("currentPageCount");
/*獲取輸入框的數據*/
var value = input.value;
if(value==null || value==""){
alert("請輸入頁碼");
return false;
}
if(!value.match("\\d+")){
alert("請輸入數字");
return false;
}
if(value<1 || value>${page.totalPageCount}){
alert("請輸入合法數據");
return false ;
}
window.location.href="${pageContext.request.contextPath}/LookCustomer?currentPageCount="+value;
}
</script>
用需要用到的地方,導入即可!
<jsp:include page="page.jsp"></jsp:include>
為了做到更好的通用性,處理分頁數據的url應該由Servlet傳進去給Page類,讓Page類封裝起來!要使用的時候,再用Page取出來就行了。
下面寫法已經固定了,不夠靈活!也就是說,下面的url地址不應該寫死的
${pageContext.request.contextPath}/LookCustomer?currentPageCount=${page.currentPageCount+1}
我們可以這樣做:
- 在Controller上獲取Servlet的名稱,在傳遞用戶想要看的頁數的同時,把Servlet的url也傳遞進去
String servletName = this.getServletName();
//調用BusinessService的方法,獲取得到所有客戶信息
BusinessService businessService = new BusinessService();
//把Servlet的url也傳遞進去
Page page = businessService.getPageData2(currentPageCount, request.getContextPath() + "/" + servletName);
- 在Page類上,多增加一個成員變量
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
- 在BusinessService接受到web層傳遞進來的url,set到Page對象上就行了!
page.setUrl(url);
我們在jsp頁面跳轉到處理分頁數據的Servlet上,就再不用寫死了。直接用Page對象中獲取出來就行了!
<%--把傳遞過去的頁碼-1就行了--%>
<a href="${page.url}?currentPageCount=${page.currentPageCount-1}">
上一步
</a>
開發web的刪除和修改
在查詢jsp頁面上,增添刪除和修改的操作鏈接!
開發處理刪除操作的Servlet
超鏈接綁定要刪除用戶的id,帶過去給Controller
<a href="${pageContext.request.contextPath}/DeleteCustomer?id=${customer.id}">刪除</a>
controller的代碼也十分簡單:
String id = request.getParameter("id");
//調用BusinessService層的功能,就可以完成刪除操作了
BusinessService businessService = new BusinessService();
businessService.deleteCustomer(id);
刪除客戶記錄也是一件非常重要的事情,應該提供JavaSrcript代碼詢問是否要真的刪除
在超鏈接控件上綁定事件!
<a href="${pageContext.request.contextPath}/DeleteCustomer?id=${customer.id}" onclick=" return sureDelete()">刪除</a>
function sureDelete() {
var b = window.confirm("你確定要刪除嗎?");
if(b) {
return true;
}else {
return false;
}
}
測試:
修改操作
修改操作的流程是這樣的:點擊修改超鏈接,跳轉到該用戶的詳細信息頁面,在詳細信息頁面中修改數據,再提交修改!【跳轉到用戶詳細信息頁面時,用戶的id還在的,在提交數據的時候,記得把id也給到服務器,【id是不包含在表單中的,要我們自己提交過去】!】
<a href="${pageContext.request.contextPath}/UpdateCustomerUI?=${customer.id}">修改</a>
開發提供用戶詳細信息的Servlet
String id = request.getParameter("id");
BusinessService businessService = new BusinessService();
//通過id獲取得到用戶的詳細信息
Customer customer = businessService.findCustomer(id);
request.setAttribute("customer", customer);
//跳轉到顯示用戶詳細信息的jsp頁面上
request.getRequestDispatcher("/WEB-INF/customerInformation").forward(request, response);
開發顯示用戶信息的JSP【數據回顯】
想要日期能夠選擇,記得導入JavaScript代碼,響應事件!
注意:在顯示頁面上,一定要把id傳遞過去給處理表單的Servlet,不然服務器是不知道你要修改哪一條數據的!
<head>
<title>用戶詳細信息</title>
<script type="text/javascript" src="${pageContext.request.contextPath}/customer.js">
</script>
</head>
<body onload="pageInit()">
<form action="${pageContext.request.contextPath}/updateCustomer?id=${customer.id}" method="post" onsubmit="makeForm()">
<table border="1px">
<tr>
<td>用戶名:</td>
<td><input type="text" name="name" value="${customer.name}"></td>
</tr>
<tr>
<td>性別</td>
<td><input type="radio" name="gender" value="male" ${customer.gender=='male'?'checked':''}>男
<input type="radio" name="gender" value="female"${customer.gender=='female'?'checked':''}>女
</td>
</tr>
<tr>
<td>生日</td>
<td>
<select id="year">
<option value="${fn:split(customer.birthday,'-')[0]}">${fn:split(customer.birthday,'-')[0]}</option>
</select>
<select id="month">
<option value="${fn:split(customer.birthday,'-')[1]}">${fn:split(customer.birthday,'-')[1]}</option>
</select>
<select id="day">
<option value="${fn:split(customer.birthday,'-')[2]}">${fn:split(customer.birthday,'-')[2]}</option>
</select>
</td>
</tr>
<tr>
<td>電話號碼:</td>
<td><input type="text" name="cellphone" value="${customer.cellphone}"></td>
</tr>
<tr>
<td>郵箱:</td>
<td><input type="text" name="email"value="${customer.email}"></td>
</tr>
<tr>
<td>愛好:</td>
<td>
<input type="checkbox" name="hobbies" value="唱歌"${fn:contains(customer.preference, '唱歌')==true?'checked':''}>唱歌
<input type="checkbox" name="hobbies" value="跳舞"${fn:contains(customer.preference, '跳舞')==true?'checked':''}>跳舞
<input type="checkbox" name="hobbies" value="打代碼"${fn:contains(customer.preference, '打代碼')==true?'checked':''}>打代碼
</td>
</tr>
<tr>
<td>客戶類型</td>
<td>
<input type="radio" name="type" value="VIP" ${customer.type=='VIP'?'checked':''}>VIP
<input type="radio" name="type" value="普通客戶"${customer.type=='普通客戶'?'checked':''}>普通客戶
<input type="radio" name="type" value="黑名單客戶"${customer.type=='黑名單客戶'?'checked':''}>黑名單客戶
</td>
</tr>
<tr>
<td>描述</td>
<td>
<textarea name="description" cols="30" rows="10">${customer.description}</textarea>
</td>
</tr>
<tr>
<td><input type="submit" value="確定修改"></td>
<td><input type="reset" value="重置"></td>
</tr>
</table>
</form>
效果:
處理修改表單數據的Servlet
//將數據封裝到Bean中
Customer customer = WebUtils.request2Bean(request, Customer.class);
//將id封裝到Customer對象中!!!不要忘了id!!!在表單中獲取到的數據是沒有id的!!!!!記得!!!!
customer.setId(request.getParameter("id"));
//調用Service層的方法,實現修改
BusinessService businessService = new BusinessService();
businessService.updateCustomer(customer);
//修改成功就跳回查看客戶界面
request.getRequestDispatcher("/LookCustomer").forward(request, response);
- 效果:
總結
- 在dao層中,我們有添加客戶、通過id查找用戶、刪除用戶、修改用戶信息的方法。
- 日期我們一般用下拉框來給用戶選取,要想下拉框的信息有足夠多的數據,我們需要用到JavaScript【DOM編程動態增加和修改數據】
- javasrcipt文件不能放在WEB-INF目錄下面
- 日期的數據通過下拉框選取,年、月、日是分散的,我們需要把他們拼接,於是我們也用JavaScript來拼接【減低服務器端的壓力】
- 開發工具方法request2Bean,主要用到了BeanUtils框架,這樣就不用在Servlet一個一個封裝了。
- 在JSP判斷集合是否有元素時,我們可以用EL表達式${empty(集合)}。
- 如果記錄數有很多,我們應該使用分頁技術,一般地,我們使用Page類來封裝分頁的數據
- 要使用分頁技術,就必須在數據庫用查詢總記錄數,通過總記錄數,就可以算出總頁數了【每頁顯示多少條記錄由我們說了算】
- 在dao層還要編寫獲取具體的分頁數據,從哪里開始,哪里結束,返回一個List集合,再把List集合封裝到Page對象上
- 由於獲取分頁數據需要當前的頁數是多少,(所以在service中要判斷當前頁數是否存在,如果不存在,那么就設置為1)【更新,我認為在Controller判斷會好一點】
- 分頁中,我們還支持上一頁和下一頁的功能,如果頁數大於1,才顯示上一頁,如果頁數小於1,才顯示下一頁。
- 給出下拉框進行頁數跳轉,使用JavaScript事件機制,獲取頁數,再提交給Servlet處理即可
- 我們還要控制頁數的顯示,因為不可能有100頁,我們就顯示100頁,這樣是不可能的。在Page類中維護兩個變量,startPage,endPage。我們規定每次只能顯示10頁數據,如果第一次訪問就顯示1-10頁。如果當前頁數大於10,那么就顯示6-15頁。如果角標越界了,那么就顯示前10頁或者后10頁
- 我們把顯示分頁的頁面封裝成單獨的jsp,使用的Servlet連接也可以用url變量來維護。
- 前台數據做拼接,最終都是把拼接好的數據用一個隱藏域封裝起來,隨后讓form表單一起提交
如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章的同學,想要獲取更多的Java資源的同學,可以關注微信公眾號:Java3y