/*
*
* title: 從面向過程到面向對象再到MVC
* author: tanghao
* date: 2020.9.30
* version: 1.0
*
*/
前言
本文檔通過一個顯示2019年英雄聯盟半決賽的比賽信息及隊伍信息的實例,使用JSP+MySQL從面向過程編程,改寫成為面向對象編程,再改寫成為MVC的實現模式。利用代碼系統地演示從面向過程到面向對象,再到MVC的過程。使你能夠初步了解這些編程方法的基本區別一些思想方法。
參考了一篇PHP在這方面的文章。
- 數據庫:test_db
- 面向過程:test_pop
- 面向對象:test_oop
- MVC:test_mvc & test_mvc2
1 數據庫
建立數據庫
create database if not exists test_db default charset utf8 collate utf8_general_ci;
建立隊伍表team
、選手表player
和比賽表match
# 隊伍表 team
CREATE TABLE `team` (
`t_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '隊伍ID',
`t_name` varchar(255) NOT NULL COMMENT '隊名',
PRIMARY KEY pk_id (`t_id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8;
# 選手表 player
CREATE TABLE `player` (
`p_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '選手ID',
`p_name` varchar(255) NOT NULL COMMENT '選手姓名',
`t_id` INT(11) UNSIGNED NOT NULL COMMENT '隊伍ID',
PRIMARY KEY pk_id (`p_id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8;
# 比賽表 match
CREATE TABLE `match` (
`m_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '比賽ID',
`t1_id` INT(11) UNSIGNED NOT NULL COMMENT '隊伍1ID',
`t2_id` INT(11) UNSIGNED NOT NULL COMMENT '隊伍2ID',
`t1_score` INT(11) UNSIGNED NOT NULL COMMENT '隊伍1得分',
`t2_score` INT(11) UNSIGNED NOT NULL COMMENT '隊伍2得分',
PRIMARY KEY pk_id (`m_id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8;
向三張表中添加數據
INSERT INTO
`team` (`t_id`, `t_name`)
VALUES
('1', 'IG'),
('2', 'FPX'),
('3', 'SKT'),
('4', 'G2');
INSERT INTO
`player` (`p_id`, `p_name`, `t_id`)
VALUES
('1', 'The Shy', '1'),
('2', 'Ning', '1'),
('3', 'Rookie', '1'),
('4', 'JackeyLove', '1'),
('5', 'Baolan', '1'),
('6', 'Gimgoon', '2'),
('7', 'Tian', '2'),
('8', 'Doinb', '2'),
('9', 'Lwx', '2'),
('10', 'Crisp', '2'),
('11', 'Khan', '3'),
('12', 'Clid', '3'),
('13', 'Faker', '3'),
('14', 'Teddy', '3'),
('15', 'Mata', '3'),
('16', 'Wunder', '4'),
('17', 'Jankos', '4'),
('18', 'Caps', '4'),
('19', 'Perkz', '4'),
('20', 'Mikyx', '4');
INSERT INTO
`match` (`m_id`, `t1_id`, `t2_id`, `t1_score`, `t2_score`)
VALUES
('1', '1', '2', '1', '3'),
('2', '3', '4', '1', '3');
最終的三張表
2. 面向過程的程序
第一個頁面match.jsp,顯示比賽信息,具體有隊伍1的隊名,隊伍2的隊名,隊伍1的得分,隊伍2的得分。這些數據需要從match和team表中獲取。
先編寫SQL語句
SELECT
t1_team.t_name AS t1_name,
t2_team.t_name AS t2_name,
m.t1_score,
m.t2_score
FROM
`match` AS m
LEFT JOIN `team` AS t1_team ON m.t1_id = t1_team.t_id
LEFT JOIN `team` AS t2_team ON m.t2_id = t2_team.t_id;
然后編寫JSP頁面
match.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*,java.sql.*"%>
<%
// 數據庫連接信息
String Driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/test_db";
String user = "root";
String password = "123456";
// 連接數據庫
Class.forName(Driver);
Connection conn = DriverManager.getConnection(url, user, password);
// SQL查詢語句
String sql = " SELECT "
+ " t1_team.t_name AS t1_name, "
+ " t2_team.t_name AS t2_name, "
+ " m.t1_score, "
+ " m.t2_score "
+ " FROM "
+ " `match` AS m "
+ " LEFT JOIN `team` AS t1_team ON m.t1_id = t1_team.t_id "
+ " LEFT JOIN `team` AS t2_team ON m.t2_id = t2_team.t_id; ";
// 執行SQL語句,返回結果集
PreparedStatement pst = conn.prepareStatement(sql);
ResultSet rst = pst.executeQuery();
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>比賽信息</title>
</head>
<body>
<h1>比賽信息</h1>
<table border="1">
<tr>
<th>隊伍1</th>
<th>隊伍2</th>
<th>隊伍1得分</th>
<th>隊伍2得分</th>
</tr>
<%
while (rst.next()) {
%>
<tr>
<td><%=rst.getString("t1_name")%></td>
<td><%=rst.getString("t2_name")%></td>
<td><%=rst.getString("t1_score")%></td>
<td><%=rst.getString("t2_score")%></td>
</tr>
<%
}
%>
</table>
</body>
</html>
<%
// 關閉數據庫連接
rst.close();
pst.close();
conn.close();
%>
然后編寫第二個頁面team.jsp。該頁面可以通過第一個頁面上的隊伍鏈接跳轉到具體的隊伍頁面,顯示隊伍中的隊員信息。
首先需要對sql進行一點更改,加入隊伍的ID信息。
SELECT
t1_team.t_name AS t1_name,
t2_team.t_name AS t2_name,
m.t1_score,
m.t2_score,
m.t1_id,
m.t2_id
FROM
`match` AS m
LEFT JOIN `team` AS t1_team ON m.t1_id = t1_team.t_id
LEFT JOIN `team` AS t2_team ON m.t2_id = t2_team.t_id;
同時在match加入點擊隊伍的鏈接。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*,java.sql.*"%>
<%
// 數據庫連接信息
String Driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/test_db";
String user = "root";
String password = "123456";
// 連接數據庫
Class.forName(Driver);
Connection conn = DriverManager.getConnection(url, user, password);
// SQL查詢語句
String sql = " SELECT "
+ " t1_team.t_name AS t1_name, "
+ " t2_team.t_name AS t2_name, "
+ " m.t1_score, "
+ " m.t2_score, "
+ " m.t1_id, "
+ " m.t2_id "
+ " FROM "
+ " `match` AS m "
+ " LEFT JOIN `team` AS t1_team ON m.t1_id = t1_team.t_id "
+ " LEFT JOIN `team` AS t2_team ON m.t2_id = t2_team.t_id; ";
// 執行SQL語句,返回結果集
PreparedStatement pst = conn.prepareStatement(sql);
ResultSet rst = pst.executeQuery();
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>比賽信息</title>
</head>
<body>
<h1>比賽信息</h1>
<table border="1">
<tr>
<th>隊伍1</th>
<th>隊伍2</th>
<th>隊伍1得分</th>
<th>隊伍2得分</th>
</tr>
<%
while (rst.next()) {
%>
<tr>
<td><a href='team.jsp?t_id=<%=rst.getInt("t1_id")%>'><%=rst.getString("t1_name")%></a></td>
<td><a href='team.jsp?t_id=<%=rst.getInt("t2_id")%>'><%=rst.getString("t2_name")%></a></td>
<td><%=rst.getString("t1_score")%></td>
<td><%=rst.getString("t2_score")%></td>
</tr>
<%
}
%>
</table>
</body>
</html>
<%
// 關閉數據庫連接
rst.close();
pst.close();
conn.close();
%>
接下來,編寫team.jsp頁面。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*,java.sql.*"%>
<%
// 數據庫連接信息
String Driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/test_db";
String user = "root";
String password = "123456";
// 連接數據庫
Class.forName(Driver);
Connection conn = DriverManager.getConnection(url, user, password);
// 獲取地址欄傳遞的隊伍ID
String t_id = request.getParameter("t_id");
// SQL查詢語句
String sql1 = " SELECT t_name FROM `team` WHERE t_id = " + t_id + " ; ";
String sql2 = " SELECT p_name FROM `player` WHERE t_id = " + t_id + " ; ";
// 執行SQL語句,返回結果集
PreparedStatement pst1 = conn.prepareStatement(sql1);
ResultSet rst1 = pst1.executeQuery();
// 獲取rst1的第一個記錄
String t_name = "";
if (rst1.next()) {
t_name = rst1.getString("t_name");
}
PreparedStatement pst2 = conn.prepareStatement(sql2);
ResultSet rst2 = pst2.executeQuery();
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>比賽信息</title>
</head>
<body>
<h1>隊伍信息</h1>
<h2><%=t_name%></h2>
<table border="1">
<tr>
<th>隊員</th>
</tr>
<%
while (rst2.next()) {
%>
<tr>
<td><%=rst2.getString("p_name")%></td>
</tr>
<%
}
%>
</table>
</body>
</html>
<%
// 關閉數據庫連接
rst1.close();
pst1.close();
rst2.close();
pst2.close();
conn.close();
%>
POP代碼的完整演示
面向過程的編程到此結束,下面會介紹如何將這個面向過程的程序改寫為面向對象的程序。
3. 面向對象的程序
分析上面的JSP文件中的Java代碼部分,會看到操作數據庫的代碼有很多都是重復的,因此我們可以把操作數據庫的代碼封裝成一個類的方法,方便以后再次使用時,直接調用類的方法即可,實現復用。
DBUtil類
package util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DBUtil {
// 數據庫連接信息
String Driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/test_db";
String user = "root";
String password = "123456";
Connection conn;
PreparedStatement pst;
ResultSet rst;
// 連接數據庫,根據SQL語句返回結果集
public ResultSet getResultSet(String sql) throws ClassNotFoundException, SQLException {
Class.forName(Driver);
conn = DriverManager.getConnection(url, user, password);
pst = conn.prepareStatement(sql);
rst = pst.executeQuery();
return rst;
}
// 關閉數據庫
public void colse() throws SQLException {
rst.close();
pst.close();
conn.close();
}
}
改寫后的match.jsp
連接數據庫獲取數據只需要利用DBUtil
對象的getResultSet()
方法。
關閉數據庫只需要利用DBUtil
對象的close()
方法。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*,java.sql.*"%>
<%@ page import="util.DBUtil"%>
<%
DBUtil dbUtil = new DBUtil();
// SQL查詢語句
String sql = " SELECT "
+ " t1_team.t_name AS t1_name, "
+ " t2_team.t_name AS t2_name, "
+ " m.t1_score, "
+ " m.t2_score, "
+ " m.t1_id, "
+ " m.t2_id "
+ " FROM "
+ " `match` AS m "
+ " LEFT JOIN `team` AS t1_team ON m.t1_id = t1_team.t_id "
+ " LEFT JOIN `team` AS t2_team ON m.t2_id = t2_team.t_id; ";
ResultSet rst = dbUtil.getResultSet(sql);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>比賽信息</title>
</head>
<body>
<h1>比賽信息</h1>
<table border="1">
<tr>
<th>隊伍1</th>
<th>隊伍2</th>
<th>隊伍1得分</th>
<th>隊伍2得分</th>
</tr>
<%
while (rst.next()) {
%>
<tr>
<td><a href='team.jsp?t_id=<%=rst.getInt("t1_id")%>'><%=rst.getString("t1_name")%></a></td>
<td><a href='team.jsp?t_id=<%=rst.getInt("t2_id")%>'><%=rst.getString("t2_name")%></a></td>
<td><%=rst.getString("t1_score")%></td>
<td><%=rst.getString("t2_score")%></td>
</tr>
<%
}
%>
</table>
</body>
</html>
<%
// 關閉數據庫連接
dbUtil.colse();
%>
完成之后的頁面效果與之前的寫法沒有任何差別
同理可以對team.jsp頁面進行修改
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*,java.sql.*"%>
<%@ page import="util.DBUtil"%>
<%
DBUtil dbUtil = new DBUtil();
// 獲取地址欄傳遞的隊伍ID
String t_id = request.getParameter("t_id");
// SQL查詢語句
String sql1 = " SELECT t_name FROM `team` WHERE t_id = " + t_id + " ; ";
String sql2 = " SELECT p_name FROM `player` WHERE t_id = " + t_id + " ; ";
// 執行SQL語句,返回結果集
ResultSet rst1 = dbUtil.getResultSet(sql1);
ResultSet rst2 = dbUtil.getResultSet(sql2);
// 獲取rst1的第一個記錄
String t_name = "";
if (rst1.next()) {
t_name = rst1.getString("t_name");
}
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>比賽信息</title>
</head>
<body>
<h1>隊伍信息</h1>
<h2><%=t_name%></h2>
<table border="1">
<tr>
<th>隊員</th>
</tr>
<%
while (rst2.next()) {
%>
<tr>
<td><%=rst2.getString("p_name")%></td>
</tr>
<%
}
%>
</table>
</body>
</html>
<%
// 關閉數據庫連接
dbUtil.colse();
%>
所以,可以初略簡單地認為面向對象是將面向過程中的一些通用共性的代碼提取出來,成為類或者對象的方法,方便后面再次書寫時直接通過.方法
的形式進行使用,利於代碼的復用。
OOP代碼的完整演示
4. MVC
4.1
在上面的代碼中,一個JSP上既有業務邏輯處理,又有視圖的展示。以match.jsp頁面為例,既要從數據庫中取出相應的數據,又要對數據進行前端展示,代碼結構略顯混亂。
如果要通過MVC的方式進行改造,那么可以將業務邏輯部分單獨提取成一個JSP文件——match_m.jsp
,視圖展示部分也單獨提取成一個JSP文件——match_v.jsp
,同時新構建一個JSP文件——match_c.jsp
將這兩個文件聯系起來,那么這樣就可以做到代碼的分離,結構清晰也利於維護。
mathch_m.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*,java.sql.*"%>
<%@ page import="util.DBUtil"%>
<%
DBUtil dbUtil = new DBUtil();
// SQL查詢語句
String sql = " SELECT "
+ " t1_team.t_name AS t1_name, "
+ " t2_team.t_name AS t2_name, "
+ " m.t1_score, "
+ " m.t2_score, "
+ " m.t1_id, "
+ " m.t2_id "
+ " FROM "
+ " `match` AS m "
+ " LEFT JOIN `team` AS t1_team ON m.t1_id = t1_team.t_id "
+ " LEFT JOIN `team` AS t2_team ON m.t2_id = t2_team.t_id; ";
ResultSet rst_m = dbUtil.getResultSet(sql);
request.setAttribute("rst_m", rst_m);
%>
match_v.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*,java.sql.*"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>比賽信息</title>
</head>
<body>
<h1>比賽信息</h1>
<table border="1">
<tr>
<th>隊伍1</th>
<th>隊伍2</th>
<th>隊伍1得分</th>
<th>隊伍2得分</th>
</tr>
<%
ResultSet rst_v = (ResultSet)request.getAttribute("rst_m");
while (rst_v.next()) {
%>
<tr>
<td><a href='team.jsp?t_id=<%=rst_v.getInt("t1_id")%>'><%=rst_v.getString("t1_name")%></a></td>
<td><a href='team.jsp?t_id=<%=rst_v.getInt("t2_id")%>'><%=rst_v.getString("t2_name")%></a></td>
<td><%=rst_v.getString("t1_score")%></td>
<td><%=rst_v.getString("t2_score")%></td>
</tr>
<%
}
%>
</table>
</body>
</html>
match_c.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ include file="match_m.jsp" %>
<%@ include file="match_v.jsp" %>
4.2
那么在一般的Javaweb項目開發中
M通常是由實體類和相應的數據庫操作組成;
V通常是JSP頁面;
C通常是Servlet類。
Match.java
package m;
public class Match {
private String t1Name;
private String t2Name;
private String t1Score;
private String t2Score;
public String getT1Name() {
return t1Name;
}
public void setT1Name(String t1Name) {
this.t1Name = t1Name;
}
public String getT2Name() {
return t2Name;
}
public void setT2Name(String t2Name) {
this.t2Name = t2Name;
}
public String getT1Score() {
return t1Score;
}
public void setT1Score(String t1Score) {
this.t1Score = t1Score;
}
public String getT2Score() {
return t2Score;
}
public void setT2Score(String t2Score) {
this.t2Score = t2Score;
}
}
DBUtil.java
package m;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DBUtil {
// 數據庫連接信息
String Driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/test_db";
String user = "root";
String password = "123456";
Connection conn;
PreparedStatement pst;
ResultSet rst;
// 連接數據庫,根據SQL語句返回結果集
public ResultSet getResultSet(String sql) throws ClassNotFoundException, SQLException {
Class.forName(Driver);
conn = DriverManager.getConnection(url, user, password);
pst = conn.prepareStatement(sql);
rst = pst.executeQuery();
return rst;
}
// 關閉數據庫
public void colse() throws SQLException {
rst.close();
pst.close();
conn.close();
}
}
MatchServlet.java
package c;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
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 m.DBUtil;
import m.Match;
@WebServlet("/MatchServlet")
public class MatchServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
List<Match> matchs = new ArrayList<Match>();
DBUtil dbUtil = new DBUtil();
String sql = " SELECT "
+ " t1_team.t_name AS t1_name, "
+ " t2_team.t_name AS t2_name, "
+ " m.t1_score, "
+ " m.t2_score, "
+ " m.t1_id, "
+ " m.t2_id "
+ " FROM "
+ " `match` AS m "
+ " LEFT JOIN `team` AS t1_team ON m.t1_id = t1_team.t_id "
+ " LEFT JOIN `team` AS t2_team ON m.t2_id = t2_team.t_id; ";
ResultSet rst;
try {
rst = dbUtil.getResultSet(sql);
while (rst.next()) {
Match match = new Match();
match.setT1Name(rst.getString("t1_name"));
match.setT2Name(rst.getString("t2_name"));
match.setT1Score(rst.getString("t1_score"));
match.setT2Score(rst.getString("t2_score"));
matchs.add(match);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
request.setAttribute("matchs", matchs);
request.getRequestDispatcher("/match.jsp").forward(request, response);
}
}
match.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*,java.sql.*"%>
<%@ page import="m.Match"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>比賽信息</title>
</head>
<body>
<h1>比賽信息</h1>
<table border="1">
<tr>
<th>隊伍1</th>
<th>隊伍2</th>
<th>隊伍1得分</th>
<th>隊伍2得分</th>
</tr>
<%
List<Match> matchs = (ArrayList)request.getAttribute("matchs");
for (int i = 0; i < matchs.size(); i++) {
Match match = new Match();
match = matchs.get(i);
%>
<tr>
<td><%=match.getT1Name()%></td>
<td><%=match.getT2Name()%></td>
<td><%=match.getT1Score()%></td>
<td><%=match.getT2Score()%></td>
</tr>
<%
}
%>
</table>
</body>
</html>
最終截圖
4.3
MVC繼續細分就是JavaEE的經典五層架構了,這里可以直接去參考我的另外一篇博客:
JSP+Servlet+Ajax實現用戶增刪改查的例子
如果繼續下去就是SSH SSM SpringBoot這些經典框架,暫且不表。