項目經驗: 程序框架、代碼書寫、bug調試
程序框架:
程序模型:MVC模型
M(model)-Dao->extends BaseDao(數據庫操作)
CRUD代碼
C(controller)-Servlet->url-class WebServlet(“/***”)(前端頁面與數據庫操作)
- 獲得請求的數據
- 調用Dao層方法實現業務
- 輸出返回數據(DML 0 -1 1/DQL Obj List<obj>)
V(view)-html
- ajax提交數據到web服務器
- 使用jquery實現頁面的動態佈局
- 表單驗證操作
代碼書寫:業務流、數據流(前端)
- 添加功能
填寫數據->單擊提交按鈕(click)->ajax
(url=”addMsg”\data{name:.val(),msg:.val()}\success{result==”1” layer 提示})
InsertMsgServlet->request.get…(“name”)(“msg”)
New Message msg(name,msg) ->MsgDao.insert(msg) ->out.print(ret)
Int MsgDao.insert(msg) insert(){excuteUpdate(“insert..”,[msg])}
Msg entity
*****開發的順序是數據流逆序。
- 查看功能
$(ajax(url=”/showMsg”\success {result=List<Msg> for}))
ShowMsgServlet->List<Msg>MsgDao.showUser()->JSONArray(list) ->out()
MsgDao.ShowMsg()->{excuteQuery(“select ”)}
準備工作:導入jar包
導入靜態原型
只要有表單就一定要在瀏覽器中校驗
難點:1、前後端開發方式
自從參加了藍橋培訓以來,我做的第一個小項目留言板,使用的製作工具是idea,數據庫是mysql
- 首先安裝mysql數據庫
安裝直接按下一步就行,但是一定要記住自己設置的密碼
- 建message表
如下圖:
id不但要設置爲主鍵,還要設置它自動遞增。
username和msg要設置它們的字符集爲utf8
createtime要設置其默認CURRENT_TIMESTAMP
就這樣,一個數據庫的表就建好了。
- 再所有的操作之前需要在pom.xml中導入之後所需要的包,必須在有網的情況下,纔可以完成。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.lanqiao</groupId> <artifactId>message</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>message Maven Webapp</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</artifactId> <version>2.3</version> <classifier>jdk15</classifier> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> </dependencies> <build> <finalName>message</finalName> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.0.0</version> </plugin> <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging --> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.20.1</version> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.2.0</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> </plugins> </pluginManagement> </build> </project>
- 首先要在maven中建好目錄
然後在entity下建一個message.java
package entity; import java.util.Date; public class message { private int id; private String username; private String msg; private Date createtime; public message(){} public message(String username, String msg) { this.username=username; this.msg=msg; } 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 getMsg(){ return msg; } public void setMsg(String msg){ this.msg=msg; } public Date getCreatetime(){ return createtime; } public void setCreatetime(Date createtime){ this.createtime=createtime; } }
五、Dao層,有BaseDao(這是老師寫的),messageDao(我寫的)
BaseDao中需要修改其數據庫名稱和之前設置的密碼
老師寫的BaseDao
package dao; import com.mchange.v2.c3p0.ComboPooledDataSource; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.sql.*; import java.util.ArrayList; import java.util.List; public abstract class BaseDao<T> { private static ComboPooledDataSource dataSource;//連接池 private Class<T> clazz; static { try { //加載配置文件,導入一個核心類。 dataSource = new ComboPooledDataSource(); } catch (Exception e) { e.printStackTrace(); } } //反射獲得clazz public BaseDao() { clazz = (Class<T>) ((ParameterizedType) this.getClass() .getGenericSuperclass()).getActualTypeArguments()[0]; } //提供獲得數據源 public static ComboPooledDataSource getDataSource() { return dataSource; } //提供獲得連接 public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } //關閉資源 public void closeAll(Connection conn, Statement stmt, ResultSet rs) { try { if (rs != null) rs.close(); if (stmt != null) stmt.close(); if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } public void closeAll(Connection conn, Statement stmt) { try { if (stmt != null) stmt.close(); if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } // DML public int executeUpdate(String sql, Object[] obj) { Connection conn = null; PreparedStatement stat = null; int ret = 0; try { conn = getConnection(); // 3、創建傳輸對象statmemnt stat = conn.prepareStatement(sql);// ?不確定:類型、數量 // 3+、綁定替換數據 for (int i = 0; i < obj.length; i++) { stat.setObject(i + 1, obj[i]); } // 4、發送sql語句,並且接收返回結果 : DML -> executeUpdate ; DQL -> executeQuery ret = stat.executeUpdate(); //打印sql語句 printSql(sql,obj); } catch (Exception e) { e.printStackTrace(); } finally { closeAll(conn, stat); } return ret; } // DQL public List<T> executeQuery(String sql, Object[] obj) { Connection conn = null; PreparedStatement stat = null; ResultSet rs = null; List<T> list = new ArrayList<T>(); try { conn = getConnection(); // 3、創建傳輸對象statmemnt stat = conn.prepareStatement(sql);// ?不確定:類型、數量 // 3+、綁定替換數據 for (int i = 0; i < obj.length; i++) { stat.setObject(i + 1, obj[i]); } // 4、發送sql語句,並且接收返回結果 : DML -> executeUpdate ; DQL -> executeQuery rs = stat.executeQuery(); // 5、如果返回rs類型的數據,需要將數據轉換成list ResultSetMetaData rsmd = rs.getMetaData();//列名信息 int columuCount = rsmd.getColumnCount();//列的數量 while (rs.next()) { T t = (T) clazz.newInstance();//創建對象 for (int i = 0; i < columuCount; i++) {//封裝數據 Field f = clazz.getDeclaredField(rsmd.getColumnName(i + 1));//列名->屬性名->屬性對象 f.setAccessible(true); //對日期類型進行處理 String typeName = f.getType().getName(); if (typeName.equals("java.sql.Timestamp") || typeName.equals("java.util.Date") || typeName.equals("java.sql.Date")) { f.set(t, rs.getDate(i + 1)); } else { f.set(t, rs.getObject(i + 1));//將rs列中的值賦給屬性 } } list.add(t); } //打印sql語句 printSql(sql,obj); } catch (Exception e) { e.printStackTrace(); } finally { closeAll(conn, stat, rs); } return list; } // DQL public List<T> executeQuery(String sql) { Connection conn = null; PreparedStatement stat = null; ResultSet rs = null; List<T> list = new ArrayList<T>(); try { conn = getConnection(); // 3、創建傳輸對象statmemnt stat = conn.prepareStatement(sql);// ?不確定:類型、數量 // 3+、綁定替換數據 // 4、發送sql語句,並且接收返回結果 : DML -> executeUpdate ; DQL -> executeQuery rs = stat.executeQuery(); // 5、如果返回rs類型的數據,需要將數據轉換成list ResultSetMetaData rsmd = rs.getMetaData();//列名信息 int columuCount = rsmd.getColumnCount();//列的數量 while (rs.next()) { T t = (T) clazz.newInstance();//創建對象 for (int i = 0; i < columuCount; i++) {//封裝數據 Field f = clazz.getDeclaredField(rsmd.getColumnName(i + 1));//列名->屬性名->屬性對象 f.setAccessible(true); f.set(t, rs.getObject(i + 1));//將rs列中的值賦給屬性 } list.add(t); } } catch (Exception e) { e.printStackTrace(); } finally { closeAll(conn, stat, rs); } return list; } // DQL public int getRecordCount(String sql) {//select count(*) from msg; Connection conn = null; PreparedStatement stat = null; ResultSet rs = null; int count = 0; try { conn = getConnection(); // 3、創建傳輸對象statmemnt stat = conn.prepareStatement(sql);// ?不確定:類型、數量 // 3+、綁定替換數據 // 4、發送sql語句,並且接收返回結果 : DML -> executeUpdate ; DQL -> executeQuery rs = stat.executeQuery(); // 5、如果返回rs類型的數據,需要將數據轉換成list if (rs.next()) { count = rs.getInt(1); } } catch (Exception e) { e.printStackTrace(); } finally { closeAll(conn, stat, rs); } return count; } // 輸出預編譯的sql語句的具體內容(便於調試) private void printSql(String sql, Object[] params) { StringBuffer sb = new StringBuffer(sql); int fromIndex = 0; if (params != null) { for (int i = 0; i < params.length; i++) { int index = sb.indexOf("?", fromIndex); if (index == -1) { sb.append(" ---> error: value too many "); break; } if (params[i] instanceof String) { sb.replace(index, index + 1, "'" + this.valueOf(params[i]) + "'"); } else if (params[i] instanceof Number) { sb.replace(index, index + 1, this.valueOf(params[i])); } else if (params[i] instanceof Character) { sb.replace(index, index + 1, "'" + this.valueOf(params[i]) + "'"); } else if (params[i] instanceof Boolean) { sb.replace(index, index + 1, "'" + this.valueOf(params[i]) + "'"); } else if (params[i] instanceof Object[]) { sb.replace(index, index + 1, "'" + this.valueOf(params[i]) + "'"); } else if (params[i] instanceof Date) { sb.replace(index, index + 1, " date '" + this.valueOf(params[i]) + "'"); } else if (params[i] instanceof java.util.Date) { sb.replace(index, index + 1, "'java.util.Date'"); } fromIndex = index + 1; } } System.out.println(sb.toString()); } public String valueOf(Object obj) { return (obj == null) ? "" : obj.toString(); } }
然後我把它改成了c3p0-config.xml,同樣也是需要修改數據庫名稱和之前設置的密碼
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <!--默認配置--> <default-config> <!--mysql數據庫連接的各項參數--> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/mysql?characterEncoding=utf8</property> <property name="user">root</property> <property name="password">123</property> <!--配置數據庫連接池的最小連接數,最大連接數,初始連接數--> <property name="initialPoolSize">5</property> <property name="maxPoolSize">15</property> <property name="minPoolSize">5</property> <property name="maxIdleTime">10</property> </default-config> </c3p0-config>
然後在messegeDao中,必須繼承BaseDao
package dao;
import entity.message;
import java.util.List;
public class messageDao extends BaseDao<message> {
//留言插入數據庫
public int insert(message u) {
return executeUpdate("insert into message(username,msg) values(?,?)", new Object[]{u.getUsername(), u.getMsg()});
}
//展示留言並分頁
public List<message> showMsg(int pageNum,int pageSize){
return executeQuery("select * from message limit ?,?",new Object[]{(pageNum-1)*pageSize,pageSize});
}
//分頁
public int getMsgCount(){
return getRecordCount("select count(*) from message");
}
}
六、老師上課時收了,要對界面進行什麼樣的操作,就要建幾個servlet。所以我需要對界面進行查詢和刪除的操作,所以我建了InsertServlet和ShowServlet
InsertMsgServlet的代碼如下:
package servlet; import dao.messageDao; import entity.message; 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 java.io.IOException; import java.io.PrintWriter; @WebServlet("/InsertMsgServlet") public class InsertMsgServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1、獲得表單(頁面)數據 String username=request.getParameter("username"); String msg=request.getParameter("msg"); System.out.println(username); //2、封裝對象 message msgBean=new message(username,msg); //3、dao操作 int ret=new messageDao().insert(msgBean); //4、輸出結果 PrintWriter out=response.getWriter(); out.print(ret); out.flush(); out.close(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request,response); } }
ShowMsgServlet的代碼如下:
package servlet; import dao.messageDao; import entity.message; import net.sf.json.JSONArray; import PeiZhi.JsonDateValueProcessor; import net.sf.json.JsonConfig; 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 java.io.IOException; import java.io.PrintWriter; import java.util.Date; import java.util.List; @WebServlet("/ShowMsgServlet") public class ShowMsgServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1、頁數和每頁記錄 int pageNum=1; String num=request.getParameter("num"); if(num!=null&&!num.equals("")){ pageNum=Integer.parseInt(num);//字符串轉換爲整數 } //轉換格式 response.setCharacterEncoding("utf-8"); //3.dao操作 messageDao msgDao=new messageDao(); List<message> list=msgDao.showMsg(pageNum,3); // System.out.println(list.size()); //日期 JsonConfig jsonConfig = new JsonConfig(); jsonConfig.registerJsonValueProcessor(Date.class , new JsonDateValueProcessor()); //4、輸出結果 JSONArray jsonArray=JSONArray.fromObject(list,jsonConfig); jsonArray.add(msgDao.getMsgCount()); PrintWriter out=response.getWriter(); out.print(jsonArray); out.flush(); out.close(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request,response); } }
在顯示日期的時間時,需要自己寫一個JsonDateValueProcessor.java其中代碼如下(下面的代碼只能顯示出年月日,如果要顯示時間需要在後加上HH-mm-ss,HH代表的是24小時,hh代表的是12小時)
package PeiZhi; import net.sf.json.JsonConfig; import net.sf.json.processors.JsonValueProcessor; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; public class JsonDateValueProcessor implements JsonValueProcessor { // private String format ="%Y-%M-%D %h:%i:%s"; private String format ="yyyy-MM-dd"; public Object processArrayValue(Object value, JsonConfig config) { return process(value); } public Object processObjectValue(String key, Object value, JsonConfig config) { return process(value); } private Object process(Object value){ if(value instanceof Date){ SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.UK); return sdf.format(value); } return value == null ? "" : value.toString(); } }
七、接下來是最重要的地方了就是集齊html+ajax+jquery+layer(彈出框)
<!DOCTYPE html> <html lang="en"> <head> <title>留言板</title> <meta charset="UTF-8"> <style> div { margin: 0; padding: 0; font-size: 12px; margin: 0 auto; } h3 { text-align: center } #container { width: 500px; } .article { border: 1px solid #a6cbe7; margin-top: 5px; } .author { background-color: #0099FF; width: 100%; height: 24px; line-height: 24px; } .content { height: 40px; padding: 10px; } .author span { float: right; padding-right: 10px; } .time { border-top: solid 1px #a6cbe7; } .page { text-align: right; height: 30px; line-height: 30px; padding-right: 10px; } </style> <script src="js/jquery-3.3.1.js" type="application/javascript"></script> <script src="js/layer.js" type="text/javascript" rel="stylesheet"></script> <link href="css/layer.css" type="text/css" rel="stylesheet"> <script src="js/jquery.validate.js"></script> <script> var num=1; var rsCount=0; var preList; var pageSize=3; var maxPage=0; function load() { $.ajax({ url:"/ShowMsgServlet", type:"post", data:{"num": num}, dataType:"json", success:function (list) { //若當前顯示不出來,則說明當前是最後一頁,使用前一頁的信息顯示 if(list.length==0){ list=preList; num--; }else{ preList=list; } for(var i=0;i<list.length-1;i++){ var $node=$('<div class="article"><div class="author">用戶:'+list[i].username+'<span>#'+list[i].id+'</span></div><div class="content">'+list[i].msg+'</div><div class="time page">發表於:'+list[i].createtime+'</div></div>'); $("#show").append($node); } // alert(list[list.length-1]); rsCount=list[list.length-1] } }); } $(function () { load(); $(".btn1:eq(0)").click(function () { num =num>1 ? --num:num; $("#show").children().remove(); load(); }); $(".btn1:eq(1)").click(function () { // //計算最大頁數 var maxPage=Math.ceil(rsCount/pageSize); // alert(maxPage); // num++; num=num<maxPage?++num:maxPage; $("#show").children().remove(); load(); }); $("#cForm").validate({ onsubmit:true,// 是否在提交是驗證 onfocusout:false,// 是否在獲取焦點時驗證 onkeyup :false,// 是否在敲擊鍵盤時驗證 rules: { //規則 author: { //要對應相對應的input中的name屬性 required: true }, message: { required: true } }, messages:{ //驗證錯誤信息 author: { required: "請輸入用戶名" }, message: { required: "請輸入留言" } }, submitHandler: function(form) { //通過之後回調 //進行ajax傳值 $.ajax({ url : "/ShowMsgServlet", type : "post", dataType : "json", data: { author: $("#user").val(), password: $("#password").val() }, success : function(msg) { //要執行的代碼 //插入數據並彈出框 $("input[type='submit']").click(function (e) { e.preventDefault(); $.ajax({ url:"/InsertMsgServlet", type:"post", data:{"username":$("input[name='author']").val(),"msg":$("textarea[name='message']").val()}, dataType:"text", success:function (ret) { if(ret=="1"){ layer.msg('留言成功',{time:1000},function () { location.reload(); }); }else{ layer.msg('留言失敗',{time:1000},function () { location.reload(); }); } } }); }); } }); }, invalidHandler: function(form, validator) {return false;} }); }) </script> </head> <body> <div id="container"> <div><h3>留言板</h3></div> <div id="show"> </div> <div class="page"> <input class="btn1" type="button" value="上一頁" onClick=""> <input class="btn1" type="button" value="下一頁" onClick=""> </div><br> <div> <form id="cForm"> <div> 用戶: <input type="text" name="author" value=""/> </div><br> <div> 留言: <textarea name="message" rows="5" cols="72"></textarea> </div> <div align="center"><input type="reset" value="清除"/> <input type="submit" value="發表"/></div> </form> </div> </div> </body> </html>
- 彈出框,需要導入layer.css和layer.js的包,但是在導入之前必須先導入jquery.js包,必須是1.8以上的纔可以。於是彈出框就做好了。
- 表單驗證,可以參考網址https://www.cnblogs.com/jingmin/p/6294982.html,其中的元素必須是頁面元素。
留言板截圖:
表單驗證:
留言成功,layer彈出框顯示,過一秒後頁面刷新
上一頁和下一頁截圖: