LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

油画项目(介于JavaWeb与数据库之间)

2023/10/1 后端

企业门户网站项目实战

  • 需求说明与环境准备
  • 实现前端展示模块
  • 实现后台数据管理模块

(MVC架构模型) 数据显示与数据处理分开

Model模型 View试图 Controller控制器 [是一种设计理念]

Model - 模型
  • 模型(Model)负责生产业务需要的数据 Service[结尾]实现业务逻辑

    public class MathService{
        public List square(int max){
            List list = new ArrayList();
            for(int i = 0; i <= max; i++){
                long result = i * i;
                list.add(result);
            }
            return list;
        }
    }
    
Controller - 控制器 (Java Web领域是WebServlet)
  • 控制器(Controller)是界面(View)与模型(Model)的粘合剂
@WebServlet("/square")
public class HelloWord  extends HttpServlet{
    public void doGet(HttpServletRequest req, HttpServletResponse res){
        //1.接收数据 接收web的http请求参数
        int max = Integer.parselnt(request.getParameter("max"));
        //2.调用处理类(模型)进行处理 
        MathService ms = new MathService();
        List list = ms.square(max);//前台调用的参数传入square方法中
        req.setAttribute("squareList", list);//执行结果放到请求属性中
        //3.跳转界面(View)
        request.getRequestDispatcher("/result.jsp").forward(req,res);
    }
}
View - 视图
  • 试图(View)用于展示最终结果
URL:http://localhost:8080/square?max=100
----------------------------------------
<ul>
<c:foreach items = "${squareList}" var = "r" varStatus = "idx">
   <li>${idx.index}的平方是${r}</li>
</<c:foreach>
</ul>

MVC架构模式优点

  • 保证了软件层面的解耦,同时保障了团队合作的组织架构

  • 软件团队分工合作,成员各司其职 通过controller联合

  • 组件可灵活替代,互不影响

通过控制器将前端传入的参数进行接收调用后台的模型产生结果,结果通过控制器保存在当前的属性中在视图中进行展现

工程结构与开发规约

工程结构
mgallery - eclipse工程项目
 /src - java源代码目录
 /WebContent - Web资源目录
 /css - css文件目录
 /js - js文件目录
 /image - 图片资源目录
 /upload - 上传文件目录
 /WEB-INF   //jsp数据来自controller 不允许在web中直接访问 要从控制器跳转
   /jsp - jsp页面目录
   /lib - jar文件目录
   /classes - 编译后的class目录
   /web.xml web描述符文件
包结构
com.imooc.mgallery //逆命名法
    /controller - 存放Servlet控制器类 //承上启下接收参数 调用逻辑 返回处理结果
    /service - 存放处理逻辑类model[伪数据库] //完成业务逻辑 service与dao进行传递调用
    /dao - Data Access Object 数据访问对象类 数据读写的java类 数据来自xml文件
    /entity - 存放实体类 JavaBean java中的简单对象
    /utils - 通用工具类 底层通用的工具类或方法

Dao类[Data Access Object]

  • XxxDao类只负责对数据进行读取、写入操作
  • 只负责对数据 增、删、改、查
示例:PaintingDao //针对油画数据进行增删改查
public class PaintingDao{
    public void append(){...} //新增数据
    public void update(){...} //修改数据
    public void delete(){...} //删除数据
    public void findAll(){...} //查询数据
}

Service与Dao的关系

  • Service负责进行流程处理,需**持久化[java处理在内存中 存储在数据库防止丢失]**时调用Dao
  • Dao只负责单纯对数据进行增删改查操作

JavaBean

  • 对一种类的使用形式的统称

  • JavaBean是一种Java中可重用组件

  • JavaBean不是技术,而是一种Java类的格式要求

  • JavaBean在Java领域非常常见,通常用于存储数据

JavaBean格式要求

  • 类必须是pubilc并提供默认构造函数
  • 所有属性private私有化
  • 属性通过getter与setter方法进行读写
public class Painting{//类公有化
    public Painting(){...}; //提供默认构造函数,可不写
    private Integer id; //属性私有
    private String pname;
    public Integer getId(){return id;} //getter获取属性值
    public void setId(Integer id){this.id = id;} //setter设置属性值
    public String getPname(){return pname;}
    public void setPname(String pname){this.pname = pname;}
}

创建mgallery工程(ViewControllerServiceDao){VCSD}

  • 开发PaintingDao读取XML数据,实现分页操作
  • 开发PaintingService服务类,对PaintingDao进行调用
  • 开发PaintingController控制器,调用PaintingService
  • 重写index.html,利用JSP技术读取分页数据

关键类与方法

  • XmlDataSource类 - XML数据源工具类,简化Dao提取操作
  • PaintingDao.pagination() - 数据分页查询方法
  • PageModel类 - 分页结果的包装类

Dom4j

  • Dom4j是一个易用的、开源的库,用于解析XML。它应用于Java平台
  • Dom4j将XML视为Document对象
  • XML标签被Dom4j定义为Element对象
Dom4j开发流程回顾
  • SAXReader.read()读取XML文件,得到Document对象
  • document.selectNodes()利用Xpath得到XML节点集合
  • 遍历XML节点,包装成JavaBean或者集合对象返回

开发XmlDataSource (utils)

【详细】IntelliJ IDEA: 无法创建Java Class文件_idea 不能新建java class-CSDN博客
IDEA部署Tomcat提示:Warning no artifacts configured-CSDN博客
Class.getResource(“xxx.css”)得到值为null-CSDN博客

utils/XmlDataSource.java
package com.example.mgallery.utils;

import com.example.mgallery.entity.Painting;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;

//用于将XML文件解析为Java对象
public class XmlDataSource {
    //通过static静态关键字保证数据全局唯一
    private static List data = new ArrayList(); //油画集合
    private static String dataFile;
    static{ //程序运行以后去得到类路径目录下的/painting.xml的路径地址
        dataFile = XmlDataSource.class.getResource("/painting.xml").getPath();
        System.out.println(dataFile);
          //若得到特殊字符进行编码转换 空格 c:\new style\painting.xml
        try {
            URLDecoder.decode(dataFile, "UTF-8");
            //利用Dom4j对XML进行解析 读取XML
            SAXReader reader = new SAXReader();
            //1.获取Document文档对象
            Document document = reader.read(dataFile);
            //2.Xpath得到XML节点集合 获取多个xml节点
            List<Node> nodes = document.selectNodes("/root/painting");
            for (Node node : nodes){
                Element element = (Element) node;
                String id = element.attributeValue("id");//获得id
                String pname = element.elementText("pname");//获得子节点
                Painting painting = new Painting();
                painting.setId(Integer.parseInt(id));
                painting.setPname(pname);
                painting.setCategory(Integer.parseInt(element.elementText("category")));
                painting.setPrice(Integer.parseInt(element.elementText("price")));
                painting.setPreview(element.elementText("preview"));
                painting.setDescription(element.elementText("description"));
                data.add(painting);//将List data 保存油画的完整信息

                System.out.println(id + ";" + pname);
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }
    /**
     *  获取所有油画Painting对象
     * @return Painting List
     */
    public static List<Painting> getRawData(){
        return data;
    }
    public static void main(String[] args) {
//        new XmlDataSource();
        List<Painting> ps = XmlDataSource.getRawData();
        System.out.println(ps);
    }
}
META-INF/painting.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 数据文件 -->
<root>
    <painting id="1">
        <pname>古典油画鉴赏1</pname>
        <category>1</category>
        <price>3800</price>
        <preview>/upload/1.jpg</preview>
        <description>古典油画鉴赏1的描述文本</description>
    </painting>
    <painting id="2">
        <pname>古典油画鉴赏2</pname>
        <category>1</category>
        <price>3800</price>
        <preview>/upload/2.jpg</preview>
        <description>古典油画鉴赏2的描述文本</description>
    </painting>
    <painting id="3">
        <pname>古典油画鉴赏3</pname>
        <category>1</category>
        <price>3800</price>
        <preview>/upload/3.jpg</preview>
        <description>古典油画鉴赏3的描述文本</description>
    </painting>
    <painting id="4">
        <pname>古典油画鉴赏4</pname>
        <category>1</category>
        <price>3800</price>
        <preview>/upload/4.jpg</preview>
        <description>古典油画鉴赏4的描述文本</description>
    </painting>
    <painting id="5">
        <pname>古典油画鉴赏5</pname>
        <category>1</category>
        <price>3800</price>
        <preview>/upload/5.jpg</preview>
        <description>古典油画鉴赏5的描述文本</description>
    </painting>
    <painting id="6">
        <pname>古典油画鉴赏6</pname>
        <category>1</category>
        <price>3800</price>
        <preview>/upload/6.jpg</preview>
        <description>古典油画鉴赏6的描述文本</description>
    </painting>
    <painting id="7">
        <pname>古典油画鉴赏7</pname>
        <category>1</category>
        <price>3800</price>
        <preview>/upload/7.jpg</preview>
        <description>古典油画鉴赏7的描述文本</description>
    </painting>
    <painting id="8">
        <pname>古典油画鉴赏8</pname>
        <category>1</category>
        <price>3800</price>
        <preview>/upload/8.jpg</preview>
        <description>古典油画鉴赏8的描述文本</description>
    </painting>
    <painting id="9">
        <pname>古典油画鉴赏9</pname>
        <category>1</category>
        <price>3800</price>
        <preview>/upload/9.jpg</preview>
        <description>古典油画鉴赏9的描述文本</description>
    </painting>
    <painting id="9">
        <pname>古典油画鉴赏9</pname>
        <category>1</category>
        <price>3800</price>
        <preview>/upload/9.jpg</preview>
        <description>古典油画鉴赏9的描述文本</description>
    </painting>
    <painting id="10">
        <pname>抽象派油画鉴赏1</pname>
        <category>2</category>
        <price>3800</price>
        <preview>/upload/10.jpg</preview>
        <description>抽象派油画鉴赏1的描述文本</description>
    </painting>
    <painting id="11">
        <pname>抽象派油画鉴赏2</pname>
        <category>2</category>
        <price>3800</price>
        <preview>/upload/11.jpg</preview>
        <description>抽象派油画鉴赏2的描述文本</description>
    </painting>
    <painting id="12">
        <pname>抽象派油画鉴赏3</pname>
        <category>2</category>
        <price>3800</price>
        <preview>/upload/12.jpg</preview>
        <description>抽象派油画鉴赏3的描述文本</description>
    </painting>
    <painting id="13">
        <pname>抽象派油画鉴赏4</pname>
        <category>2</category>
        <price>3800</price>
        <preview>/upload/13.jpg</preview>
        <description>抽象派油画鉴赏4的描述文本</description>
    </painting>
    <painting id="14">
        <pname>抽象派油画鉴赏5</pname>
        <category>2</category>
        <price>3800</price>
        <preview>/upload/14.jpg</preview>
        <description>抽象派油画鉴赏5的描述文本</description>
    </painting>
    <painting id="15">
        <pname>抽象派油画鉴赏6</pname>
        <category>2</category>
        <price>3800</price>
        <preview>/upload/15.jpg</preview>
        <description>抽象派油画鉴赏6的描述文本</description>
    </painting>
    
</root>
entity/Painting.java
public class Painting{
    private Integer id;
    private String pname;
    private Integer category;
    private Integer price;
    private String preview; //油画图片地址
    private String description; //描述
    ......
}

开发PageMode1 (utils)

utils/PageModel.java
public class PageModel {
    private int page;
    private int totalPages;
    private int rows; //每页几条数据
    private int totalRows; //原始数据多少条
    private int pageStartRow; //当前页是从第几行开始的
    private int pageEndRow; //到第几行结束的 结束行号
    private boolean hasNextPage; //是否下一页(尾页)
    private boolean hasPreviousPage; //是否上一页(首页)
    private List pageData; //当前页面数据

    public PageModel() {
    }

    /**
     * 初始化PageMode1对象,计算分页属性
     * @param page
     * @param rows
     * @param data
     */
    public PageModel(List data, int page, int rows) {
        this.page = page;
        this.rows = rows;
        totalRows = data.size();
        //总页数计算规则:总行数/每页记录数,能整除页数取整,不能整除向上取整
        //18/6=3 | 2/6≈3.33 向上取整=4  intValue()得到整数部分
        //Nath.ceil向上取整  Math.floor浮点数向下取整
        //小细节: 20/6≈3.33 但是totalRows 和 rows都是整数 20/6=3 向上取整还是3
        //仅需在一个后面×1f即可解决问题 (rows*1f)默认类型转换返回浮点数
        totalPages = new Double(Math.ceil(totalRows/(rows * 1f))).intValue();
        pageStartRow = (page - 1) * rows; //0
        pageEndRow = page * rows; //6
        //totalRows:20 | totalPage:4 | rows:6
        //pageEndRow=4*6=24>20 执行subList()抛出下标越界异常
        if (pageEndRow > totalRows){
            pageEndRow = totalRows; //让20作为结束行号 不会越界
        }
        pageData = data.subList(pageStartRow, pageEndRow); //得到分页数据
        if (page > 1) {
            hasPreviousPage = true;
        }else {
            hasPreviousPage = false;
        }
        if (page < totalPages) { //判断是否存在下一页
            hasNextPage = true;
        }else {
            hasNextPage = false;
        }
    }

    public static void main(String[] args) {
        List sample = new ArrayList();
        for (int i = 1; i < 100; i++) {
            sample.add(i);
        }
        PageModel pageModel = new PageModel(sample,6,8);
        System.out.println(pageModel.getPageData()); //当前页的list集合
        System.out.println(pageModel.getTotalPages());
        System.out.println(pageModel.getPageStartRow() + ":" + pageModel.getPageEndRow()) ;
    }
+getter and setter 

油画数据分页设计思路

@Param注解的使用方法

@Param的作用就是給参数命名,比如在mapper里面某个方法A(int id),当添加注解后A(@Param(“userId”) int id),也就是说外部想要取出传入的id值,只需要取它的参数名userId就可以了。若在SQL中,通过#{userId}进行取值給SQL的参数赋值。

快速添加注解只需要在前面 /** + 回车

油画数据分页展示

开发PaintingDao与PaintingService
service/PaintingDao.java
package com.example.mgallery.dao;

import com.example.mgallery.entity.Painting;
import com.example.mgallery.utils.PageModel;
import com.example.mgallery.utils.XmlDataSource;

import java.util.List;

//获得最原始的 对其进行分页处理
public class PaintingDao {
    public PageModel pagination(int page, int rows){
        //Painting油画对象集合
        List<Painting> list = XmlDataSource.getRawData();
        //PageModel分页处理得到分页数据及分页附加
        PageModel pageModel = new PageModel(list,page,rows);
        return pageModel;
    }
}
service/PaintingService
package com.example.mgallery.service;

import com.example.mgallery.dao.PaintingDao;
import com.example.mgallery.entity.Painting;
import com.example.mgallery.utils.PageModel;

import java.util.List;
//完成业务逻辑 service与dao进行传递调用
public class PaintingService {
    private PaintingDao paintingDao = new PaintingDao();
    public PageModel pagination(int page, int rows){
        if (rows == 0){
            throw new RuntimeException("无效的rows参数");
        }
        return paintingDao.pagination(page, rows); //调用并返回
    }

    public static void main(String[] args) {
        PaintingService paintingService = new PaintingService();
        PageModel pageModel = paintingService.pagination(2, 6);//每页显示六条
        List<Painting> paintingList = pageModel.getPageData();
        for (Painting painting : paintingList){
            System.out.println(painting.getPname());
        }
        System.out.println(pageModel.getPageStartRow() + ":" + pageModel.getPageEndRow());

    }
}
开发PaintingController控制器
controller/PaintingController.java
package com.example.mgallery.controller;

import com.example.mgallery.service.PaintingService;
import com.example.mgallery.utils.PageModel;

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;

@WebServlet("/page")
public class PaintingController extends HttpServlet {
    private PaintingService paintingService = new PaintingService();
    public PaintingController(){
        super();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.接收Http数据
        String page = req.getParameter("p"); //页号
        String rows = req.getParameter("r"); //每页记录数
        if (page == null){
            page = "1"; //没传入则默认查询第一个
        }if (rows == null){
            rows = "6"; //每页默认显示六条数据
        }
        //2.调用Service方法,得到处理结果
        PageModel pageModel = paintingService.pagination(Integer.parseInt(page), Integer.parseInt(rows));
        req.setAttribute("pageModel",pageModel);//数据解耦最关键的一步
        //3.请求转发至对应JSP(view)进行数据展现
        req.getRequestDispatcher("/src/main/webapp/index.jsp").forward(req,resp); //跳转jsp
    }
}

前台门户首页(由静态转换为动态)

HTML转换JSP变更流程
  • 打开HTML文件,在首行增加 < %@page contentType % >
  • 更改扩展名从 .html.jsp,移动到WEB-INF/jsp目录下
  • 提示:JSP不要实用<%%>代码块,实用EL+JSTL提取数据

输入(http://localhost:8080/)不能正常访问,但是添加项目名后可以访问-CSDN博客
实例化servlet类[web.LoginServlet]异常 servle-CSDN博客

WEB-INF/jsp/index.jsp
<%@page contentType = "text/html;charset=utf-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" type="text/css" href="css\common.css">
    <script type="text/javascript" src="js\js1.js"></script>
</head>
<body>
<div class="header">
    <div class="logo">
        <img src="image\logo.png">
    </div>
    <div class="menu" onclick="show_menu()" onmouseleave="show_menu1()">
        <div class="menu_title" ><a href="###">内容分类</a></div>
        <ul id="title">
            <li>现实主义</li>
            <li>抽象主义</li>
        </ul>
    </div>
    <div class="auth">
        <ul>
            <li><a href="#">登录</a></li>
            <li><a href="#">注册</a></li>
        </ul>
    </div>
</div>
<div class="content">
    <div class="banner">
        <img src="image/welcome.png" class="banner-img">
    </div>
    <div class="img-content">
        <ul>
            <c:forEach items="${pageModel.pageData}" var="painting"> <!--pageModel.java-->
                <li>
                    <img src="${painting.preview}" class="img-li">  <!--painting.java-->
                    <div class="info">
                        <h3>${painting.pname}</h3>
                        <p>
                                ${painting.description}
                        </p>
                        <div class="img-btn">
                            <div class="price"><fmt:formatNumber pattern="¥0.00" value="${painting.price}"></fmt:formatNumber></div>
                            <a href="#" class="cart">
                                <div class="btn">
                                    <img src="image/cart.svg">
                                </div>
                            </a>
                        </div>
                    </div>
                </li>
            </c:forEach>
        </ul>
    </div>
    <div class="page-nav">
        <ul>
            <li><a href="/mgallery/page?p=1">首页</a></li> <!--当前页减一就是上一页 否则就是1-->
            <li><a href="/mgallery/page?p=${pageModel.hasPreviousPage?pageModel.page-1:1}">上一页</a></li>
            <c:forEach begin="1" end="${pageModel.totalPages}" var="pno" step="1">
                <li><span ${pno==pageModel.page?"class='first-page'":""}> <!--选中的页才有⚪圈圈 三目运算符不满足产生空字符串-->
                    <a href="/mgallery/page?p=${pno}">
                            ${pno}
                    </a></span></li>
            </c:forEach>
            <li><a href="/mgallery/page?p=${pageModel.hasNextPage?pageModel.page+1:pageModel.totalPages}">下一页</a></li>
            <li><a href="/mgallery/page?p=${pageModel.totalPages}">尾页</a></li>
        </ul>
    </div>
</div>
<div class="footer">
    <p><span>P_luminary</span>©2023.10.3 POWERED BY GITHUB.COM</p>
</div>
</body>
</html>

实现分类查询功能 [改造代码]

从Dao层面进行对数据的过滤

PaintingDao.java
package com.example.mgallery.dao;

import com.example.mgallery.entity.Painting;
import com.example.mgallery.utils.PageModel;
import com.example.mgallery.utils.XmlDataSource;

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

//获得最原始的 对其进行分页处理
public class PaintingDao {
    public PageModel pagination(int page, int rows){
        //Painting油画对象集合
        List<Painting> list = XmlDataSource.getRawData();
        //PageModel分页处理得到分页数据及分页附加
        PageModel pageModel = new PageModel(list,page,rows);
        return pageModel;
    }

    public PageModel pagination(int catagory, int page, int rows){ //int catagory添加Dao层对数据进行筛选
        List<Painting> list = XmlDataSource.getRawData();
        List<Painting> categoryList = new ArrayList();
        for (Painting p : list){
            //如果等于从外侧添加的筛选条件 则添加categoryList内
            if (p.getCategory() == catagory){
                categoryList.add(p);
            }
        }
        PageModel pageModel = new PageModel(categoryList,page,rows);
        return  pageModel;
    }
}
----------------------------------------------------------------------
PaintingService.java
package com.example.mgallery.service;

import com.example.mgallery.dao.PaintingDao;
import com.example.mgallery.entity.Painting;
import com.example.mgallery.utils.PageModel;

import java.util.List;
//完成业务逻辑 service与dao进行传递调用
public class PaintingService {
    private PaintingDao paintingDao = new PaintingDao();
    public PageModel pagination(int page, int rows, String...category){  //最后一个是添加 可选参数(可能/不一定出现一个或多个)
        if (rows == 0){
            throw new RuntimeException("无效的rows参数");
        }
        if (category.length==0 || category[0] == null){
        return paintingDao.pagination(page, rows); //调用并返回
        }else { //程序进行可选调用 两个不同路径 尽量不要去修改类结构
            return paintingDao.pagination(Integer.parseInt(category[0]), page, rows);
        }
    }

    public static void main(String[] args) {
        PaintingService paintingService = new PaintingService();
        PageModel pageModel = paintingService.pagination(2, 6);//每页显示六条
        List<Painting> paintingList = pageModel.getPageData();
        for (Painting painting : paintingList){
            System.out.println(painting.getPname());
        }
        System.out.println(pageModel.getPageStartRow() + ":" + pageModel.getPageEndRow());

    }
}
---------------------------------------------------------------------
PaintingController.java
package com.example.mgallery.controller;

import com.example.mgallery.service.PaintingService;
import com.example.mgallery.utils.PageModel;

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

public class PaintingController extends HttpServlet {
    private PaintingService paintingService = new PaintingService();
    public PaintingController(){
        super();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.接收Http数据
        String page = req.getParameter("p"); //页号
        String rows = req.getParameter("r"); //每页记录数
        String category = req.getParameter("c");
        if (page == null){
            page = "1"; //没传入则默认查询第一个
        }if (rows == null){
            rows = "6"; //每页默认显示六条数据
        }
        //2.调用Service方法,得到处理结果      增加了按类型筛选category
        PageModel pageModel = paintingService.pagination(Integer.parseInt(page), Integer.parseInt(rows), category);
        req.setAttribute("pageModel",pageModel);//数据解耦最关键的一步
        //3.请求转发至对应JSP(view)进行数据展现
        req.getRequestDispatcher("/WEB-INF/jsp/index.jsp").forward(req,resp); //跳转jsp
    }
}
---------------------------------------------------------------------
webapp/WEB-INF/jsp/index.jsp
<%@page contentType = "text/html;charset=utf-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" type="text/css" href="css\common.css">
    <script type="text/javascript" src="js\js1.js"></script>
</head>
<body>
<%--JSTL混合书写判断 判断c是否存在 存在追加链接--%>
<c:if test="${param.c != null}">
    <c:set var = "categoryParam" value="&c=${param.c}"></c:set>
</c:if>
<c:if test="${param.c == null}">
    <c:set var = "categoryParam" value=""></c:set>
</c:if>
<div class="header">
    <div class="logo">
        <img src="image\logo.png">
    </div>
    <div class="menu"   onclick="show_menu()" onmouseleave="show_menu1()">
        <div class="menu_title" ><a href="###">内容分类</a></div>
        <ul id="title">
            <li><a href="/mgallery/page?c=1">现实主义</a></li>
            <li><a href="/mgallery/page?c=2">抽象主义</a></li>
        </ul>
    </div>
    <div class="auth">
        <ul>
            <li><a href="#">登录</a></li>
            <li><a href="#">注册</a></li>
        </ul>
    </div>
</div>
<div class="content">
    <div class="banner">
        <img src="image/welcome.png" class="banner-img">
    </div>
    <div class="img-content">
        <ul>
            <c:forEach items="${pageModel.pageData}" var="painting">
                <li>
                    <img src="${painting.preview}" class="img-li">
                    <div class="info">
                        <h3>${painting.pname}</h3>
                        <p>
                                ${painting.description}
                        </p>
                        <div class="img-btn">
                            <div class="price"><fmt:formatNumber pattern="¥0.00" value="${painting.price}"></fmt:formatNumber></div>
                            <a href="#" class="cart">
                                <div class="btn">
                                    <img src="image/cart.svg">
                                </div>
                            </a>
                        </div>
                    </div>
                </li>
            </c:forEach>
        </ul>
    </div>
    <div class="page-nav">
        <ul>
            <li><a href="/mgallery/page?p=1${categoryParam}">首页</a></li> <!--当前页减一就是上一页 否则就是1-->
            <li><a href="/mgallery/page?p=${pageModel.hasPreviousPage?pageModel.page-1:1}${categoryParam}">上一页</a></li>
            <c:forEach begin="1" end="${pageModel.totalPages}" var="pno" step="1">
                <li><span ${pno==pageModel.page?"class='first-page'":""}> <!--选中的页才有⚪圈圈 三目运算符不满足产生空字符串-->
                     <%--  c不存在,则href="/mgallery/page?p=1"  c存在,测href="/mgallery/page?p=1&c=1"  --%>
                    <a href="/mgallery/page?p=${pno}${categoryParam}">
                            ${pno}
                    </a></span></li>
            </c:forEach>
            <li><a href="/mgallery/page?p=${pageModel.hasNextPage?pageModel.page+1:pageModel.totalPages}${categoryParam}">下一页</a></li>
            <li><a href="/mgallery/page?p=${pageModel.totalPages}${categoryParam}">尾页</a></li>
        </ul>
    </div>
</div>
<div class="footer">
    <p><span>P_luminary</span>©2023.10.3 POWERED BY GITHUB.COM</p>
</div>
</body>
</html>
利用默认首页跳转到任何地址上 [常用开发技巧]
index.html
<script>
    window.location.href="/page"
</script>

实现后台数据管理

前台与后台的区别
前台系统 后台系统
开放度 对外开放 不对外开放
面向群体 客户 企业内部人员
功能 提供查询与交互 管理前台数据
设计侧重点 良好的用户体验 严密的业务逻辑
访问量
典型案例 猫眼电影网 [后台有评论审核系统] XX公司无纸质化办公系统
后台实现功能

油画列表 油画预览 删除油画 修改油画 新增与上传油画

实现油画列表功能

重用pagination方法实现分页列表

原有的PaintingController不可以继续被用 术业有专攻 要再创建一个使用

ManagementController.java
package com.example.mgallery.controller;

import com.example.mgallery.service.PaintingService;
import com.example.mgallery.utils.PageModel;

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;
//实现增删改查
@WebServlet("/management")
public class ManagementController extends HttpServlet {
    private PaintingService paintingService = new PaintingService();
    public ManagementController() {
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=utf-8");
        String method = req.getParameter("method");
        if (method.equals("list")){ //去显示分页的数据
            this.list(req, resp);
        } else if (method.equals("delete")) {
            // this.delete(req, resp);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
    // 控制器代码的实现
    private void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String p = req.getParameter("p");
        String r = req.getParameter("r");
        if (p==null){
            p = "1";
        }
        if (r==null){
            r = "6";
        }
        PageModel pageModel = paintingService.pagination(Integer.parseInt(p), Integer.parseInt(r));
        req.setAttribute("pageModel", pageModel);
        req.getRequestDispatcher("/WEB-INF/jsp/list.jsp").forward(req, resp);
    }
}
management.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>门户数据管理平台</title>
    <style>
        body{
            margin: 0 auto;
        }
        a{
            text-decoration: none;
        }
        .left{
            float: left;
        }
        .right{
            float: right;
        }
 
        .pg_header{
            position: fixed;
            top: 0;
            width: 100%;
            height: 48px;
            background-color: lightseagreen;
            color: white;
            z-index: 10;
        }
        .pg_header .logo{
            height: 48px;
            width: 200px;
            text-align: center;
            color: white;
        }
        .pg_header .logo a img{
            height: 48px;
            width: 200px;
            cursor: pointer;
        }
        .pg_header .person_info{
            position: relative;
            height: 48px;
            width: 160px;
            /*text-align: center;*/
        }
        .pg_header .person_info img{
            border: 0;
            height: 48px;
            width: 50px;
            /*使用border-radius可以定义边框的圆角程度*/
            border-radius: 50%;
        }
        .pg_header .person_info .info{
            position: absolute;
            width: 150px;
            background-color: lightseagreen;
            top: 50px;
            z-index: 20;
            display: none;
        }
        .pg_header .person_info .info a{
            display: block;
            color: white;
            padding: 5px;
        }
        .pg_header .person_info:hover .info{
            display: block;
        }
        .pg_header .icons{
            line-height: 48px;
            padding: 0 20px 0 5px;
        }
        .pg_header .icons:hover{
            background-color: lightseagreen;
        }
        .pg_header .icons span{
            padding: 1px 5px;
            line-height: 1px;
            border-radius: 50%;
            background-color: red;
            font-size: 12px;
        }
        .pg_content .menu{
            position: absolute;
            top: 50px;
            left: 0;
            bottom: 0;
            width: 300px;
            border:0px;
            border-right: 1px solid #ccc;
        }
        .pg_content .content{
            position: absolute;
            top: 50px;
            right: 0;
            bottom: 0;
            left: 302px;
            overflow: auto;
            min-width: 980px;
            z-index: 8;
            border:0px;
            overflow: hidden;
        }
        .menu_item{
            display: block;
            padding: 10px 20px;
            border-bottom: 1px solid #ccc;
            font-size: 20px; 
            color: #666666;
        }
        
        .menu_item:hover{
            color: white;
            background: lightseagreen;
        }
    </style>
</head>
<body>
    <!-- 顶端导航栏 -->
    <div class="pg_header">
        <!-- Logo与名称 -->
        <div class="logo left">
            <a href="javascript:void(0)" target="_blank">
                <img src="image/logo_1.png">    
            </a>
            
        </div>
        
        <!-- 用户头像与菜单 -->
        <div class="person_info right" style="vertical-align: top;" >
            <img src="image/head.png">
            <span style="line-height: 50px;vertical-align: top;">小企鹅</span>
            <div class="info">
                <a href="javascript:void(0)">我的信息</a>
                <a href="javascript:void(0)">修改密码</a>
                <a href="javascript:void(0)">注销</a>
            </div>
        </div>
        <div class="icons right">
            <i class="far fa-bell"></i>
        </div>
    </div>
    <div class="pg_content">
        <!-- 左侧功能区菜单 -->
        <div class="menu">
            <a href = "#" class="menu_item" target="ifcontent">油画列表</a>
            <a href = "#" class="menu_item" target="ifcontent">新增作品</a>
        </div>
        <!-- 主体框架 -->
        <div class="content">
             <iframe name="ifcontent" style="width:100%;height:100%;overflow-y: hidden;border:0px;min-width: 800px;" src=""></iframe>
        </div>
    </div>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>门户数据管理平台</title>
    <style>
        body{
            margin: 0 auto;
        }
        a{
            text-decoration: none;
        }
        .left{
            float: left;
        }
        .right{
            float: right;
        }

        .pg_header{
            position: fixed;
            top: 0;
            width: 100%;
            height: 48px;
            background-color: lightseagreen;
            color: white;
            z-index: 10;
        }
        .pg_header .logo{
            height: 48px;
            width: 200px;
            text-align: center;
            color: white;
        }
        .pg_header .logo a img{
            height: 48px;
            width: 200px;
            cursor: pointer;
        }
        .pg_header .person_info{
            position: relative;
            height: 48px;
            width: 160px;
            /*text-align: center;*/
        }
        .pg_header .person_info img{
            border: 0;
            height: 48px;
            width: 50px;
            /*使用border-radius可以定义边框的圆角程度*/
            border-radius: 50%;
        }
        .pg_header .person_info .info{
            position: absolute;
            width: 150px;
            background-color: lightseagreen;
            top: 50px;
            z-index: 20;
            display: none;
        }
        .pg_header .person_info .info a{
            display: block;
            color: white;
            padding: 5px;
        }
        .pg_header .person_info:hover .info{
            display: block;
        }
        .pg_header .icons{
            line-height: 48px;
            padding: 0 20px 0 5px;
        }
        .pg_header .icons:hover{
            background-color: lightseagreen;
        }
        .pg_header .icons span{
            padding: 1px 5px;
            line-height: 1px;
            border-radius: 50%;
            background-color: red;
            font-size: 12px;
        }
        .pg_content .menu{
            position: absolute;
            top: 50px;
            left: 0;
            bottom: 0;
            width: 300px;
            border:0px;
            border-right: 1px solid #ccc;
        }
        .pg_content .content{
            position: absolute;
            top: 50px;
            right: 0;
            bottom: 0;
            left: 302px;
            overflow: auto;
            min-width: 980px;
            z-index: 8;
            border:0px;
            overflow: hidden;
        }
        .menu_item{
            display: block;
            padding: 10px 20px;
            border-bottom: 1px solid #ccc;
            font-size: 20px;
            color: #666666;
        }

        .menu_item:hover{
            color: white;
            background: lightseagreen;
        }
    </style>
</head>
<body>
<!-- 顶端导航栏 -->
<div class="pg_header">
    <!-- Logo与名称 -->
    <div class="logo left">
        <a href="javascript:void(0)" target="_blank">
            <img src="image/logo_1.png">
        </a>

    </div>

    <!-- 用户头像与菜单 -->
    <div class="person_info right" style="vertical-align: top;" >
        <img src="image/head.png">
        <span style="line-height: 50px;vertical-align: top;">小企鹅</span>
        <div class="info">
            <a href="javascript:void(0)">我的信息</a>
            <a href="javascript:void(0)">修改密码</a>
            <a href="javascript:void(0)">注销</a>
        </div>
    </div>
    <div class="icons right">
        <i class="far fa-bell"></i>
    </div>
</div>
<div class="pg_content">
    <!-- 左侧功能区菜单 -->
    <div class="menu">
        <a href = "/mgallery/management?method=list" class="menu_item" target="ifcontent">油画列表</a>
        <a href = "#" class="menu_item" target="ifcontent">新增作品</a>
    </div>
    <!-- 主体框架 -->
    <div class="content">
        <iframe name="ifcontent" style="width:100%;height:100%;overflow-y: hidden;border:0px;min-width: 800px;" src="/mgallery/management?method=list"></iframe>
    </div>
</div>
</body>
</html>

利用SweetAlert实现预览功能 [美观对话框]

正确使用 equals 方法避免产生空指针异常 - 简书 (jianshu.com)

list.jsp
<%@page contentType = "text/html;charset=utf-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>油画列表</title>
    <script src="js\jquery-3.4.1.min.js" type="text/javascript"></script>
    <script src="js\sweetalert2.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="css\list.css">
    <script type="text/javascript">
        //对话框显示预览 // 变成jquary对象 对自定义属性名进行提取
        function showPreview(previewObj){
            var preview = $(previewObj).attr("data-preview");
            var pname = $(previewObj).attr("data-pname");
            Swal.fire({
                title: pname,
                html : "<img src='" + preview + "' style='width:361px;height:240px'>",
                showCloseButton: true,
                showConfirmButton: false
            })
        }
    </script>
</head>
<body>
    <div class="container">
        <fieldset>
            <legend>油画列表</legend>
            <div style="height: 40px">
                <a href="#" class="btn-button">新增</a>
            </div>
            <!-- 油画列表 -->
            <table cellspacing="0px">
                <thead>
                    <tr style="width: 150px;">
                        <th style="width: 100px">分类</th>
                        <th style="width: 150px;">名称</th>
                        <th style="width: 100px;">价格</th>
                        <th style="width: 400px">描述</th>
                        <th style="width: 100px">操作</th>
                    </tr>
                </thead>
                <c:forEach items="${pageModel.pageData }" var="painting">
                    <tr>
                        <td>
                            <c:choose>
                                <c:when test="${painting.category==1 }">现实主义</c:when>
                                <c:when test="${painting.category==2 }">抽象主义</c:when>
                                <c:otherwise>未知的类型</c:otherwise>
                            </c:choose>
                        </td>
                        <td>${painting.pname }</td>
                        <td><fmt:formatNumber pattern="¥0.00" value="${painting.price }"></fmt:formatNumber> </td>
                        <td>${painting.description }</td>
                        <td> <!-- 自定义用 data-preview data-pname    this就是指向这个超链接的本身 -->
                            <a class="oplink" data-preview="${painting.preview}" data-pname = "${painting.pname}" href="javascript:void(0)" onclick="showPreview(this)">预览</a>
                            <a class="oplink" href="#">修改</a>
                            <a class="oplink" href="#">删除</a>
                        </td>
                    </tr>
                </c:forEach>
            </table>
            <!-- 分页组件 -->
            <ul class="page">
                <li><a href="/mgallery/management?method=list&p=1">首页</a></li>
                <li><a href="/mgallery/management?method=list&p=${pageModel.hasPreviousPage?pageModel.page-1:1}">上页</a></li>
                <c:forEach begin="1" end="${pageModel.totalPages }" step="1" var="pno">
                    <li ${pno==pageModel.page?"class='active'":""}>
                        <a href="/mgallery/management?method=list&p=${pno }">${pno }</a>
                    </li>
                </c:forEach>
                <li><a href="/mgallery/management?method=list&p=${pageModel.hasNextPage?pageModel.page+1:pageModel.totalPages}">下页</a></li>
                <li><a href="/mgallery/management?method=list&p=${pageModel.totalPages}">尾页</a></li>
            </ul>
        </fieldset>
    </div>

</body>
</html>

处理文件上传页面 [表单校验 文件上传 处理XML文件]

  • 利用Apache Commons FileUpload组件实现上传功能
  • 封装可重用的js脚本解决表单校验问题
  • 基于Dom4j对XML文件进行追加操作
create.jsp
<!-- 新增油画页面 -->
<%@page contentType = "text/html;charset=utf-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>新增油画</title>
<link rel="stylesheet" type="text/css" href="css\create.css">
<script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
</head>
<body>
    <div class="container">
        <fieldset>
            <legend>新增油画</legend>
            <form action="" method="post"
                autocomplete="off" enctype="multipart/form-data">
                <ul class="ulform">
                    <li>
                        <span>油画名称</span>
                        <span id="errPname"></span>
                        <input id="pname" name="pname" />
                    </li>
                    <li>
                        <span>油画类型</span>
                        <span id="errCategory"></span>
                        <select id="category" name="category">
                            <option value="-1">请选择油画类型</option>
                            <option value="1">现实主义</option>
                            <option value="2">抽象主义</option>
                        </select>
                    </li>
                    <li>
                        <span>油画价格</span>
                        <span id="errPrice"></span>
                        <input id="price" name="price"/>
                    </li>
                    <li>
                        <span>作品预览</span>
                        <span id="errPainting"></span>
                        <input id="painting" name="painting" type="file" 
                            style="padding-left: 0px;" accept="image/*" />
                    </li>

                    <li>
                        <span>详细描述</span>
                        <span id="errDescription"></span>
                        <textarea
                            id="description" name="description"></textarea>
                    </li>
                    <li style="text-align: center;">
                        <button type="submit" class="btn-button">提交表单</button>
                    </li>
                </ul>
            </form>
        </fieldset>
    </div>

</body>
</html>
package com.example.mgallery.controller;

import com.example.mgallery.service.PaintingService;
import com.example.mgallery.utils.PageModel;

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.util.Objects;

//实现增删改查
@WebServlet("/management")
public class ManagementController extends HttpServlet {
    private PaintingService paintingService = new PaintingService();

    public ManagementController() {
        super();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=utf-8");
        //通过method参数区分不同的操作
        String method = req.getParameter("method");
        if(Objects.equals(method,"list")) {//分页查询列表
            this.list(req,resp);
            //正确使用 equals 方法避免产生空指针异常https://www.jianshu.com/p/306de20dd228
        } else if (Objects.equals(method,"delete")) {
            //
        } else if (Objects.equals(method,"show_create")) {
            this.showCreatePage(req,resp); //带show的一定是跳转页面
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }

    // 控制器代码的实现
    private void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String p = req.getParameter("p");
        String r = req.getParameter("r");
        if (p == null) {
            p = "1";
        }
        if (r == null) {
            r = "6";
        }
        //2.调用Service方法,得到处理结果      增加了按类型筛选category
        PageModel pageModel = paintingService.pagination(Integer.parseInt(p), Integer.parseInt(r));
        req.setAttribute("pageModel",pageModel);//数据解耦最关键的一步
        //3.请求转发至对应JSP(view)进行数据展现
        req.getRequestDispatcher("/WEB-INF/jsp/list.jsp").forward(req,resp); //跳转jsp
    }
    private void showCreatePage(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getRequestDispatcher("/WEB-INF/jsp/create.jsp").forward(req, resp); //请求转发
    }
}

文件上传必要前提条件

  • form表单method = “post 因为二进制文件不可以放在url中传递
  • form表单enctype = “multipart/form-data 允许保存二进制数据存放在请求体中发送到服务端
    enctype=”application/x-www-form-urlencoded 采用url编码的方式以字符串的形式将数据保存在请求体中发送到服务器
  • form表单持有file类型input进行文件选择

Apache Commons FileUpload [简化文件上传]

在java服务器端完成上传文件的处理
  • FileUpload组件提供了java文件上传底层支持

FileItem其本质就是对前台数据进行封装

ManagementController.java
package com.example.mgallery.controller;

import com.example.mgallery.service.PaintingService;
import com.example.mgallery.utils.PageModel;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

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.File;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

//实现增删改查
@WebServlet("/management")
public class ManagementController extends HttpServlet {
    private PaintingService paintingService = new PaintingService();

    public ManagementController() {
        super();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=utf-8");
        //通过method参数区分不同的操作
        String method = req.getParameter("method");
        if(Objects.equals(method,"list")) {//分页查询列表
            this.list(req,resp);
            //正确使用 equals 方法避免产生空指针异常https://www.jianshu.com/p/306de20dd228
        } else if (Objects.equals(method,"delete")) {
            //
        } else if (Objects.equals(method,"show_create")) {
            this.showCreatePage(req,resp); //带show的一定是跳转页面
        } else if (Objects.equals(method, "create")) {
            this.create(req, resp);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }

    // 控制器代码的实现
    private void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String p = req.getParameter("p");
        String r = req.getParameter("r");
        if (p == null) {
            p = "1";
        }
        if (r == null) {
            r = "6";
        }
        //2.调用Service方法,得到处理结果      增加了按类型筛选category
        PageModel pageModel = paintingService.pagination(Integer.parseInt(p), Integer.parseInt(r));
        req.setAttribute("pageModel",pageModel);//数据解耦最关键的一步
        //3.请求转发至对应JSP(view)进行数据展现
        req.getRequestDispatcher("/WEB-INF/jsp/list.jsp").forward(req,resp); //跳转jsp
    }
    //显示新增页面
    private void showCreatePage(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getRequestDispatcher("/WEB-INF/jsp/create.jsp").forward(req, resp); //请求转发
    }
    //新增油画方法
    private void create(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*文件上传时的数据处理与标准表单完全不同
        String pname = req.getParameter("pname"); form表单enctype = "multipart/form-data"运用后 无法得到字符串格式的数据
        System.out.println(pname);
        req.getRequestDispatcher("/WEB-INF/jsp/create.jsp").forward(req, resp); 请求转发*/
        //1.初始化FileUpload组件 包含表单项的数据对象
        FileItemFactory factory = new DiskFileItemFactory();
        /**
         * FileItemFactory 用于将前端表单的数据转换为一个个FileItem对象
         * ServletFileUpload 是为FileUpload组件提供Java web的Http请求解析
         */
        ServletFileUpload sf = new ServletFileUpload(factory);
        //2.遍历所有FileItem
        try {
            List<FileItem> formData = sf.parseRequest(req);//将表单数据转换为FileItem对象 和前台输入项一一对应
            //区分哪个是普通对象 哪个是文件对象
            for (FileItem fi : formData){
                if (fi.isFormField() == true){//普通输入框
                    System.out.println("普通输入项:" + fi.getFieldName() + ":" + fi.getString("UTF-8"));
                }else { //文件上传框
                    System.out.println("文件上传项:" + fi.getFieldName()); //没有文本数值不用输出
                    //3.文件保存到服务器目录 已经确定了文件上传项
                    String path = req.getServletContext().getRealPath("/upload");
                    System.out.println("上传文件目录:" + path);
//                    String fileName = "test.jpg";
                    String fileName = UUID.randomUUID().toString();//随机生成文件名 根据计算机的特性 生成随机唯一字符串
                    //fi.getName()得到原始文件名, 截取最后一个"."后所有字符串,例如:wxml.jpg -> .jpg
                    String suffix = fi.getName().substring(fi.getName().lastIndexOf("."));
                    fi.write(new File(path, fileName + suffix)); //传入文件对象会自动的帮我们把客户端上传的文件传到服务器某个文件中
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Dom4j写操作开发流程

  • **SAXReader.read()**读取XML文件,得到Document对象
  • p=root,addElement(“painting”) - 创建新的节点
  • p.addElement(“pname”) - 创建新的子节点
  • document.write(writer) - 向XML写入新的节点
请求转发
请求转发是一种服务器端的行为,通过request对象来完成操作。当客户端发送请求以后,对应的Servlet会做出业务逻辑处理,然后调用forward()方法,将请求发送到服务器中另外的Servlet中去。

实现方法:
request.getRequestDispatcher("URL地址").forward(request,response);
响应重定向
响应重定向是一种客户端的行为,通过response对象来完成操作。当客户端第一次发出请求后,服务器中Servlet接收请求以后,通过调用sendRedirect()方法指定另一个Servlet,此时客户端会根据路径再次发出请求访问下一个Servlet,服务器再次接收请求后做出响应返回给客户端。

实现方法:
response.sendRedirect("URL地址")
请求转发与响应重定向的区别
1、请求转发以后浏览器URL地址栏不变,响应重定向以后浏览器URL地址栏发生改变
2、请求转发是服务器端行为,响应重定向是客户端行为
3、请求转发只做了一次访问请求,得到一次响应;响应重定向是做了两次请求,得到两次响应
4、请求转发是在服务器内部进行的,所以不可以跨域访问;响应重定向可以做到跨域访问
5、请求转发可以使用request作用域共享数据;响应重定向不可以使用request作用域,但是可以使用session域共享资源
XmlDataSource.java[新增了append追加操作]
package com.example.mgallery.utils;

import com.example.mgallery.entity.Painting;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.*;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;

//用于将XML文件解析为Java对象
public class XmlDataSource {
    //通过static静态关键字保证数据全局唯一
    private static List data = new ArrayList(); //油画集合
    private static String dataFile;

    static { //程序运行以后去得到类路径目录下的/painting.xml的路径地址
        dataFile = XmlDataSource.class.getResource("/painting.xml").getPath();
        reload(); //在所有的写入操作以后都要写入
    }
    private static void reload(){
        //若得到特殊字符进行编码转换 空格 c:\new style\painting.xml
        try {
            URLDecoder.decode(dataFile, "UTF-8");
            System.out.println(dataFile);
            //利用Dom4j对XML进行解析 读取XML
            SAXReader reader = new SAXReader();
            //1.获取Document文档对象
            Document document = reader.read(dataFile);
            //2.Xpath得到XML节点集合 获取多个xml节点
            List<Node> nodes = document.selectNodes("/root/painting");
            data.clear(); //清空 在空的数据基础上重新添加
            for (Node node : nodes) {
                Element element = (Element) node;
                String id = element.attributeValue("id");//获得id
                String pname = element.elementText("pname");//获得子节点
                Painting painting = new Painting();
                painting.setId(Integer.parseInt(id));
                painting.setPname(pname);
                painting.setCategory(Integer.parseInt(element.elementText("category")));
                painting.setPrice(Integer.parseInt(element.elementText("price")));
                painting.setPreview(element.elementText("preview"));
                painting.setDescription(element.elementText("description"));
                data.add(painting);//将List data 保存油画的完整信息

                System.out.println(id + ";" + pname);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 获取所有油画Painting对象
     *
     * @return Painting List
     */
    public static List<Painting> getRawData() {
        return data;
    }

    //Dom4j实现XML追加操作
    public static void append(Painting painting) { //末尾追加新的油画
        //1.读取XML文档,得到Document对象
        SAXReader reader = new SAXReader();
        Writer writer = null;
        try {
            Document document = reader.read(dataFile);
            //2.创建新的painting节点
            Element root = document.getRootElement();//得到原始文档xml根节点 <root>
            Element p = root.addElement("painting");//创建一个新的子节点
            //3.创建painting节点的各个子节点
            p.addAttribute("id", String.valueOf(data.size() + 1)); //生成新的id对象
            p.addElement("pname").setText(painting.getPname()); //返回新的节点 设置其文本值
            p.addElement("category").setText(painting.getCategory().toString());
            p.addElement("price").setText(painting.getPrice().toString());
            p.addElement("preview").setText(painting.getPreview());
            p.addElement("description").setText(painting.getDescription());
            //4.写入XML,完成追加操作
            writer = new OutputStreamWriter(new FileOutputStream(dataFile), "UTF-8");
            document.write(writer); //向目标dataFile原始xml中进行新节点的更新
            System.out.println(dataFile);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (writer != null) { //write有开就有关 已经被实例化
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace(); //遇到异常 打印堆栈
                }
            }
            //清空 在空的数据基础上重新添加 无论成功与否都会重新加载数据 如果照片没找到 就清空数据
            reload();
        }

    }

    public static void main(String[] args) {
//        new XmlDataSource();
//        List<Painting> ps = XmlDataSource.getRawData();
//        System.out.println(ps);
        Painting p = new Painting();
        p.setPname("油画测试");
        p.setCategory(1);
        p.setPrice(4000);
        p.setPreview("upload/10.jpg");
        p.setDescription("测试油画描述");
        XmlDataSource.append(p);
    }
}
ManagementController.java[新增了create方法]
package com.example.mgallery.controller;

import com.example.mgallery.entity.Painting;
import com.example.mgallery.service.PaintingService;
import com.example.mgallery.utils.PageModel;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

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.File;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

//实现增删改查
@WebServlet("/management")
public class ManagementController extends HttpServlet {
    private PaintingService paintingService = new PaintingService();

    public ManagementController() {
        super();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=utf-8");
        //通过method参数区分不同的操作
        String method = req.getParameter("method");
        if(Objects.equals(method,"list")) {//分页查询列表
            this.list(req,resp);
            //正确使用 equals 方法避免产生空指针异常https://www.jianshu.com/p/306de20dd228
        } else if (Objects.equals(method,"delete")) {
            //
        } else if (Objects.equals(method,"show_create")) {
            this.showCreatePage(req,resp); //带show的一定是跳转页面
        } else if (Objects.equals(method, "create")) {
            this.create(req, resp);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }

    // 控制器代码的实现
    private void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String p = req.getParameter("p");
        String r = req.getParameter("r");
        if (p == null) {
            p = "1";
        }
        if (r == null) {
            r = "6";
        }
        //2.调用Service方法,得到处理结果      增加了按类型筛选category
        PageModel pageModel = paintingService.pagination(Integer.parseInt(p), Integer.parseInt(r));
        req.setAttribute("pageModel",pageModel);//数据解耦最关键的一步
        //3.请求转发至对应JSP(view)进行数据展现
        req.getRequestDispatcher("/WEB-INF/jsp/list.jsp").forward(req,resp); //跳转jsp
    }
    //显示新增页面
    private void showCreatePage(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getRequestDispatcher("/WEB-INF/jsp/create.jsp").forward(req, resp); //请求转发
    }
    //新增油画方法
    private void create(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*文件上传时的数据处理与标准表单完全不同
        String pname = req.getParameter("pname"); form表单enctype = "multipart/form-data"运用后 无法得到字符串格式的数据
        System.out.println(pname);
        req.getRequestDispatcher("/WEB-INF/jsp/create.jsp").forward(req, resp); 请求转发*/
        //1.初始化FileUpload组件 包含表单项的数据对象
        FileItemFactory factory = new DiskFileItemFactory();
        /**
         * FileItemFactory 用于将前端表单的数据转换为一个个FileItem对象
         * ServletFileUpload 是为FileUpload组件提供Java web的Http请求解析
         */
        ServletFileUpload sf = new ServletFileUpload(factory);
        //2.遍历所有FileItem
        try {
            List<FileItem> formData = sf.parseRequest(req);//将表单数据转换为FileItem对象 和前台输入项一一对应
            Painting painting = new Painting();//进行封装
            //区分哪个是普通对象 哪个是文件对象
            for (FileItem fi : formData){
                if (fi.isFormField() == true){//普通输入框
                    System.out.println("普通输入项:" + fi.getFieldName() + ":" + fi.getString("UTF-8"));
                    switch (fi.getFieldName()) { //得到字段名
                        case "pname":
                            painting.setPname(fi.getString("UTF-8"));
                            break;
                        case "category":
                            painting.setCategory(Integer.parseInt(fi.getString("UTF-8")));
                            break;
                        case "price":
                            painting.setPrice(Integer.parseInt(fi.getString("UTF-8")));
                            break;
                        case "description":
                            painting.setDescription(fi.getString("UTF-8"));
                            break;
                        default:
                            break;
                    }
                }else { //文件上传框
                    System.out.println("文件上传项:" + fi.getFieldName()); //没有文本数值不用输出
                    //3.文件保存到服务器目录 已经确定了文件上传项
                    String path = req.getServletContext().getRealPath("/upload");
                    System.out.println("上传文件目录:" + path);
//                    String fileName = "test.jpg";
                    String fileName = UUID.randomUUID().toString();//随机生成文件名 根据计算机的特性 生成随机唯一字符串
                    //fi.getName()得到原始文件名, 截取最后一个"."后所有字符串,例如:wxml.jpg -> .jpg
                    String suffix = fi.getName().substring(fi.getName().lastIndexOf("."));
                    fi.write(new File(path, fileName + suffix)); //传入文件对象会自动的帮我们把客户端上传的文件传到服务器某个文件中
                    painting.setPreview("upload/" + fileName + suffix); //形成一个完整的可以访问的url地址
                }
            }
            paintingService.create(painting); //新增功能
            //若弹出另一个页面进行另外一个操作 新页面以后的后续操作 和前面的操作紧密联系的 用请求转发 当前请求給下一个功能继续操作
            resp.sendRedirect("/mgallery/management?method=list");//响应重定向跳转列表页继续相应的处理 跟前面的新增功能联系不那么紧密
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

开发表单校验JS脚本[可重用的表单校验]

  • 以独立的js脚本文件存储可被不同表单项重用的校验规则
  • 每一个校验逻辑均独立存储
  • 可自定义触发时机,例如:失去焦点、选择时、提交前…
实现思路
/*
检查是否为空 input表单域选择器    errSelector错误提示选择器    true-校验成功 false-校验失败
*/
function checkEmpty(input, errSelector){
    var val = $(input).val();
    if($.trim(val)==""){
        switchValid(false, input, errSelector, "请输入内容")
        return false;
    }else{
        switchValid(true, input, errSelector);
        return true;
    }
}
调用方法1 失去焦点的时候触发onblur
<span>详细描述</span>
<span id="errDescription"></span>
<textarea id="description" name="description" onblur="checkEmpty('#description','#errDescription')"></textarea>
调用方法2 在提交前组合校验
<script type="text/javascript">
    function checkSubmit(){ //提交前表单校验
        var result = true;
        var r1 = checkPname('#pname','#errPname',-1); //检查名称
        var r2 = checkCategory('#category','#errCategory'); //检查分类
        var r3 = checkPrice('#price','#errPrice'); //检查价格
        var r4 = checkFile('#painting', '#errPainting'); //检查上传文件
        var r5 = checkEmpty('#description', '#errDescription'); //检查描述
        if(r1 && r2 && r3 && r4 && r5){//所有检查通过允许提交表单
            return true;
        }else{
            return false;
        }
    }
</script>
validation.js 且在create.jsp中把引用的标签打上js状态 onblur/onchange
/**
 * 隐藏、显示错误信息
 * @param onOff true 验证成功, 隐藏错误     false 校验失败,显示错误
 * @param input 表单域选择器
 * @param errSelector 错误提示选择器
 * @param message 错误信息
 * @returns
 */
function switchValid(onOff, input, errSelector, message) {
    if (onOff == false) {
        $(errSelector).text(message);
        $(input).addClass("error_input"); //错误标红
        $(errSelector).addClass("error_message");
    } else {
        $(errSelector).text("");
        $(input).removeClass("error_input"); //错误标红
        $(errSelector).removeClass("error_message");
    }
}

/**
 * 检查是否为空
 * @param input 表单域选择器
 * @param errSelector 错误提示选择器
 * @returns true-校验成功 false-校验失败
 */
function checkEmpty(input, errSelector) {
    var val = $(input).val(); //获取当前选择的数值
    if ($.trim(val) == "") { //将字符串前后空格删掉
        switchValid(false, input, errSelector, "请输入内容"); //非空校验失败时显示错误
        return false;
    } else {
        switchValid(true, input, errSelector); //正确情况
        return true;
    }
}

function checkCategory(input, errSelector) {
    var val = $(input).val(); //获取当前选择的数值
    if (val == -1) {
        switchValid(false, input, errSelector, "请选择油画类型"); //非空校验失败时显示错误
        return false;
    } else {
        switchValid(true, input, errSelector); //正确情况
        return true;
    }
}

/**
 * 价格必须是整数
 * @param input 表单域选择器
 * @param errSelector 错误提示选择器
 * @returns true-校验成功 false-校验失败
 */
function checkPrice(input, errSelector) {
    var val = $(input).val(); //获取当前选择的数值
    var regex = /^[1-9][0-9]*$/   //利用正则表达式进行校验信息
    if (!regex.test(val)) {
        switchValid(false, input, errSelector, "无效的价格"); //非空校验失败时显示错误
        return false;
    } else {
        switchValid(true, input, errSelector); //正确情况
        return true;
    }
}

/**
 * 上传文件必须是图片
 * @param input 表单域选择器
 * @param errSelector 错误提示选择器
 * @returns true-校验成功 false-校验失败
 */
function checkFile(input, errSelector) {
    if (checkEmpty(input, errSelector) == false) {
        return false;
    }
    var val = $(input).val().toLowerCase()//小写转换 PNG png
    if (val.length < 4) { //x.xxxx
        switchValid(false, input, errSelector, "请选择有效的图片"); //非空校验失败时显示错误
        return false;
    }
    suffix = val.substring(val.length - 3); //拿到最后的扩展名
    if (suffix == "jpg" || suffix == "png" || suffix == "gif") {
        switchValid(true.input, errSelector);
        return true;
    }else {
        switchValid(false, input, errSelector, "请选择有效的图片");
        return false;
    }
}

实现新增油画功能[表单submit全验证]

添加一个全局验证
create.jsp
<!-- 新增油画页面 -->
<%@page contentType="text/html;charset=utf-8" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>新增油画</title>
    <link rel="stylesheet" type="text/css" href="css\create.css">
    <script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
    <script type="text/javascript" src="js/validation.js"></script>
    <script type="text/javascript">
        function checkSubmit() {
            var result = true;
            var r1 = checkEmpty("#pname", "#errPname");
            var r2 = checkCategory('#category', '#errCategory');
            var r3 = checkPrice('#price', '#errPrice');
            var r4 = checkFile('#painting', '#errPainting');
            var r5 = checkEmpty('#description', '#errDescription');
            if (r1 && r2 && r3 && r4 && r5) {
                return true;
            }else {
                return false;
            }
        }
    </script>
</head>
<body>
<div class="container">
    <fieldset>
        <legend>新增油画</legend>
        <form action="/mgallery/management?method=create" method="post"
              autocomplete="off" enctype="multipart/form-data" onsubmit="return checkSubmit()">
            <ul class="ulform">
                <li>
                    <span>油画名称</span>
                    <span id="errPname"></span>
                    <input id="pname" name="pname" onblur="checkEmpty('#pname','#errPname')"> <!--在失去焦点时触发-->
                </li>
                <li>
                    <span>油画类型</span>
                    <span id="errCategory"></span>
                    <select id="category" name="category" onchange="checkCategory('#category','#errCategory')">
                        <option value="-1">请选择油画类型</option>
                        <option value="1">现实主义</option>
                        <option value="2">抽象主义</option>
                    </select>
                </li>
                <li>
                    <span>油画价格</span>
                    <span id="errPrice"></span>
                    <input id="price" name="price" onblur="checkPrice('#price','#errPrice')">
                </li>
                <li>
                    <span>作品预览</span>
                    <span id="errPainting"></span>
                    <input id="painting" name="painting" type="file"
                           style="padding-left: 0px;" accept="image/*"
                           onchange="checkFile('#painting','#errPainting')"/>
                    <%--    accept="image/*" 默认保留所有图片格式的文件--%>
                </li>

                <li>
                    <span>详细描述</span>
                    <span id="errDescription"></span>
                    <textarea
                            id="description" name="description"
                            onblur="checkEmpty('#description','#errDescription')"></textarea>
                </li>
                <li style="text-align: center;">
                    <button type="submit" class="btn-button">提交表单</button>
                </li>
            </ul>
        </form>
    </fieldset>
</div>

</body>
</html>
management.html  和  list.jsp 添加href超链接 
客户点击新增的时候可以跳转到新增页面 
<a href = "/mgallery/management?method=show_create"></a>

实现修改页表单数据回填

修改实现思路
  • 修改与新增的最大不同是在修改前要加载原有数据
  • 在修改页面放置hidden隐藏域保存id编号,随表单提交
  • 对XML更新时,先按id获取原始记录,在此基础上覆盖更新

Dao service

PaintingService.java
package com.example.mgallery.service;

import com.example.mgallery.dao.PaintingDao;
import com.example.mgallery.entity.Painting;
import com.example.mgallery.utils.PageModel;

import java.util.List;
//完成业务逻辑 service与dao进行传递调用
public class PaintingService {
    private PaintingDao paintingDao = new PaintingDao();
    public PageModel pagination(int page, int rows, String...category){  //最后一个是添加 可选参数(可能/不一定出现一个或多个)
        if (rows == 0){
            throw new RuntimeException("无效的rows参数");
        }
        if (category.length==0 || category[0] == null){
        return paintingDao.pagination(page, rows); //调用并返回
        }else { //程序进行可选调用 两个不同路径 尽量不要去修改类结构
            return paintingDao.pagination(Integer.parseInt(category[0]), page, rows);
        }
    }

    public void create(Painting painting){
        paintingDao.create(painting);
    }
    // 按编号查询油画 id油画编号 return油画对象
    public Painting findById(Integer id){
        Painting p = paintingDao.findById(id);
        if (p==null){
            throw new RuntimeException("[id=]" + id + "]油画不存在");
        }
        return p;
    }

    public static void main(String[] args) {
        PaintingService paintingService = new PaintingService();
        PageModel pageModel = paintingService.pagination(2, 6);//每页显示六条
        List<Painting> paintingList = pageModel.getPageData();
        for (Painting painting : paintingList){
            System.out.println(painting.getPname());
        }
        System.out.println(pageModel.getPageStartRow() + ":" + pageModel.getPageEndRow());

    }
}
update.jsp
<%@page contentType="text/html;charset=utf-8" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!-- 修改油画页面 -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>作品更新</title>
    <link rel="stylesheet" type="text/css" href="css\create.css">
    <script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
    <script type="text/javascript" src="js/validation.js"></script>
    <script type="text/javascript">
        <!-- 提交前表单校验 -->
        function checkSubmit() {
            var result = true;
            var r1 = checkEmpty("#pname", "#errPname");
            var r2 = checkCategory('#category', '#errCategory');
            var r3 = checkPrice('#price', '#errPrice');
            var r4 = checkFile('#painting', '#errPainting');
            var r5 = checkEmpty('#description', '#errDescription');
            if (r1 && r2 && r3 && r4 && r5) {
                return true;
            } else {
                return false;
            }
        }
        //整个html被解析完后执行代码
        $(function(){//前面的被html解释完才执行 后面的EL表达式jsp渲染在服务器端产生 油画类型默认产生
            $("#category").val(${painting.category})
        })
    </script>
</head>
<body>
<div class="container">
    <fieldset>
        <legend>作品名称</legend>
        <form action="[这里写更新URL]" method="post"
              autocomplete="off" enctype="multipart/form-data"
              onsubmit="return checkSubmit()">
            <ul class="ulform">
                <li>
                    <span>油画名称</span>
                    <span id="errPname"></span>
                    <input id="pname" name="pname" onblur="checkEmpty('#pname','#errPname')" value="${painting.pname}"/>
                </li>
                <li>
                    <span>油画类型</span>
                    <span id="errCategory"></span>
                    <select id="category" name="category" onchange="checkCategory('#category','#errCategory')"
                            value="${painting.category}">
                        <option value="-1">请选择油画类型</option>
                        <option value="1">现实主义</option>
                        <option value="2">抽象主义</option>
                    </select>
                </li>
                <li>
                    <span>油画价格</span>
                    <span id="errPrice"></span>
                    <input id="price" name="price" onblur="checkPrice('#price','#errPrice')" value="${painting.price}"/>
                    </li>
                    <li>
                        <span>作品预览</span>
                        <span id=" errPainting"></span><br/>
                    <img id="preview" src="${painting.preview}" style="width:361px;height:240px"/><br/>
                    <input id="painting" name="painting" type="file" style="padding-left:0px;" accept="image/*"/>
                </li>
                <li>
                    <span>详细描述</span>
                    <span id="errDescription"></span>
                    <textarea
                            id="description" name="description"
                            onblur="checkEmpty('#description','#errDescription')"
                    >
                        ${painting.description}
                    </textarea>
                </li>
                <li style="text-align: center;">
                    <button type="submit" class="btn-button">提交表单</button>
                </li>
            </ul>
        </form>
    </fieldset>
</div>

</body>
</html>
ManagmentController.java
package com.example.mgallery.controller;

import com.example.mgallery.entity.Painting;
import com.example.mgallery.service.PaintingService;
import com.example.mgallery.utils.PageModel;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

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.File;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

//实现增删改查
@WebServlet("/management")
public class ManagementController extends HttpServlet {
    private PaintingService paintingService = new PaintingService();

    public ManagementController() {
        super();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=utf-8");
        //通过method参数区分不同的操作
        String method = req.getParameter("method");
        if(Objects.equals(method,"list")) {//分页查询列表
            this.list(req,resp);
            //正确使用 equals 方法避免产生空指针异常https://www.jianshu.com/p/306de20dd228
        } else if (Objects.equals(method,"delete")) {
            //
        } else if (Objects.equals(method,"show_create")) {
            this.showCreatePage(req,resp); //带show的一定是跳转页面
        } else if (Objects.equals(method, "create")) {
            this.create(req, resp);
        } else if (Objects.equals(method,"show_update")) {
            this.showUpdatePage(req, resp);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }

    // 控制器代码的实现
    private void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String p = req.getParameter("p");
        String r = req.getParameter("r");
        if (p == null) {
            p = "1";
        }
        if (r == null) {
            r = "6";
        }
        //2.调用Service方法,得到处理结果      增加了按类型筛选category
        PageModel pageModel = paintingService.pagination(Integer.parseInt(p), Integer.parseInt(r));
        req.setAttribute("pageModel",pageModel);//数据解耦最关键的一步
        //3.请求转发至对应JSP(view)进行数据展现
        req.getRequestDispatcher("/WEB-INF/jsp/list.jsp").forward(req,resp); //跳转jsp
    }
    //显示新增页面
    private void showCreatePage(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getRequestDispatcher("/WEB-INF/jsp/create.jsp").forward(req, resp); //请求转发
    }
    //新增油画方法
    private void create(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*文件上传时的数据处理与标准表单完全不同
        String pname = req.getParameter("pname"); form表单enctype = "multipart/form-data"运用后 无法得到字符串格式的数据
        System.out.println(pname);
        req.getRequestDispatcher("/WEB-INF/jsp/create.jsp").forward(req, resp); 请求转发*/
        //1.初始化FileUpload组件 包含表单项的数据对象
        FileItemFactory factory = new DiskFileItemFactory();
        /**
         * FileItemFactory 用于将前端表单的数据转换为一个个FileItem对象
         * ServletFileUpload 是为FileUpload组件提供Java web的Http请求解析
         */
        ServletFileUpload sf = new ServletFileUpload(factory);
        //2.遍历所有FileItem
        try {
            List<FileItem> formData = sf.parseRequest(req);//将表单数据转换为FileItem对象 和前台输入项一一对应
            Painting painting = new Painting();//进行封装
            //区分哪个是普通对象 哪个是文件对象
            for (FileItem fi : formData){
                if (fi.isFormField() == true){//普通输入框
                    System.out.println("普通输入项:" + fi.getFieldName() + ":" + fi.getString("UTF-8"));
                    switch (fi.getFieldName()) { //得到字段名
                        case "pname":
                            painting.setPname(fi.getString("UTF-8"));
                            break;
                        case "category":
                            painting.setCategory(Integer.parseInt(fi.getString("UTF-8")));
                            break;
                        case "price":
                            painting.setPrice(Integer.parseInt(fi.getString("UTF-8")));
                            break;
                        case "description":
                            painting.setDescription(fi.getString("UTF-8"));
                            break;
                        default:
                            break;
                    }
                }else { //文件上传框
                    System.out.println("文件上传项:" + fi.getFieldName()); //没有文本数值不用输出
                    //3.文件保存到服务器目录 已经确定了文件上传项
                    String path = req.getServletContext().getRealPath("/upload");
                    System.out.println("上传文件目录:" + path);
//                    String fileName = "test.jpg";
                    String fileName = UUID.randomUUID().toString();//随机生成文件名 根据计算机的特性 生成随机唯一字符串
                    //fi.getName()得到原始文件名, 截取最后一个"."后所有字符串,例如:wxml.jpg -> .jpg
                    String suffix = fi.getName().substring(fi.getName().lastIndexOf("."));
                    fi.write(new File(path, fileName + suffix)); //传入文件对象会自动的帮我们把客户端上传的文件传到服务器某个文件中
                    painting.setPreview("upload/" + fileName + suffix); //形成一个完整的可以访问的url地址
                }
            }
            paintingService.create(painting); //新增功能
            //若弹出另一个页面进行另外一个操作 新页面以后的后续操作 和前面的操作紧密联系的 用请求转发 当前请求給下一个功能继续操作
            resp.sendRedirect("/mgallery/management?method=list");//响应重定向跳转列表页继续相应的处理 跟前面的新增功能联系不那么紧密
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //显示更新页面
    private void showUpdatePage(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String id = req.getParameter("id");//前台传来的id号
        Painting painting = paintingService.findById(Integer.parseInt(id));
        req.setAttribute("painting", painting); //将得到的放入其中
        req.getRequestDispatcher("/WEB-INF/jsp/update.jsp").forward(req,resp);
    }
}
list.jsp
<ul class="page">
                <li><a href="/mgallery/management?method=list&p=1">首页</a></li>
                <li><a href="/mgallery/management?method=list&p=${pageModel.hasPreviousPage?pageModel.page-1:1}">上页</a></li>
                <c:forEach begin="1" end="${pageModel.totalPages }" step="1" var="pno">
                    <li ${pno==pageModel.page?"class='active'":""}>
                        <a href="/mgallery/management?method=list&p=${pno }">${pno }</a>
                    </li>
                </c:forEach>
                <li><a href="/mgallery/management?method=list&p=${pageModel.hasNextPage?pageModel.page+1:pageModel.totalPages}">下页</a></li>
                <li><a href="/mgallery/management?method=list&p=${pageModel.totalPages}">尾页</a></li>
            </ul>

利用Dom4j对XML进行更新

XmlDataSource.java
package com.example.mgallery.utils;

import com.example.mgallery.entity.Painting;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.*;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;

//用于将XML文件解析为Java对象
public class XmlDataSource {
    //通过static静态关键字保证数据全局唯一
    private static List data = new ArrayList(); //油画集合
    private static String dataFile;

    static { //程序运行以后去得到类路径目录下的/painting.xml的路径地址
        dataFile = XmlDataSource.class.getResource("/painting.xml").getPath();
        reload(); //在所有的写入操作以后都要写入
    }
    private static void reload(){
        //若得到特殊字符进行编码转换 空格 c:\new style\painting.xml
        try {
            URLDecoder.decode(dataFile, "UTF-8");
            System.out.println(dataFile);
            //利用Dom4j对XML进行解析 读取XML
            SAXReader reader = new SAXReader();
            //1.获取Document文档对象
            Document document = reader.read(dataFile);
            //2.Xpath得到XML节点集合 获取多个xml节点
            List<Node> nodes = document.selectNodes("/root/painting");
            data.clear(); //清空 在空的数据基础上重新添加
            for (Node node : nodes) {
                Element element = (Element) node;
                String id = element.attributeValue("id");//获得id
                String pname = element.elementText("pname");//获得子节点
                Painting painting = new Painting();
                painting.setId(Integer.parseInt(id));
                painting.setPname(pname);
                painting.setCategory(Integer.parseInt(element.elementText("category")));
                painting.setPrice(Integer.parseInt(element.elementText("price")));
                painting.setPreview(element.elementText("preview"));
                painting.setDescription(element.elementText("description"));
                data.add(painting);//将List data 保存油画的完整信息

                System.out.println(id + ";" + pname);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 获取所有油画Painting对象
     *
     * @return Painting List
     */
    public static List<Painting> getRawData() {
        return data;
    }

    //Dom4j实现XML追加操作
    public static void append(Painting painting) { //末尾追加新的油画
        //1.读取XML文档,得到Document对象
        SAXReader reader = new SAXReader();
        Writer writer = null;
        try {
            Document document = reader.read(dataFile);
            //2.创建新的painting节点
            Element root = document.getRootElement();//得到原始文档xml根节点 <root>
            Element p = root.addElement("painting");//创建一个新的子节点
            //3.创建painting节点的各个子节点
            p.addAttribute("id", String.valueOf(data.size() + 1)); //生成新的id对象
            p.addElement("pname").setText(painting.getPname()); //返回新的节点 设置其文本值
            p.addElement("category").setText(painting.getCategory().toString());
            p.addElement("price").setText(painting.getPrice().toString());
            p.addElement("preview").setText(painting.getPreview());
            p.addElement("description").setText(painting.getDescription());
            //4.写入XML,完成追加操作
            writer = new OutputStreamWriter(new FileOutputStream(dataFile), "UTF-8");
            document.write(writer); //向目标dataFile原始xml中进行新节点的更新
            System.out.println(dataFile);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (writer != null) { //write有开就有关 已经被实例化
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace(); //遇到异常 打印堆栈
                }
            }
            //清空 在空的数据基础上重新添加 无论成功与否都会重新加载数据 如果照片没找到 就清空数据
            reload();
        }

    }

    public static void main(String[] args) {
//        new XmlDataSource();
//        List<Painting> ps = XmlDataSource.getRawData();
//        System.out.println(ps);
        Painting p = new Painting();
        p.setPname("油画测试");
        p.setCategory(1);
        p.setPrice(4000);
        p.setPreview("upload/10.jpg");
        p.setDescription("测试油画描述");
        XmlDataSource.append(p);
    }

    /**
     * 更新对应id的XML油画数据
     * @param painting 要更新的油画数据
     * @throws IOException
     */
    public static void update(Painting painting){
        SAXReader reader = new SAXReader();
        Writer writer = null;
        try {
            Document document = reader.read(dataFile);
            //节点路径[@属性名=属性值]
            // /root/paintin[@id=x]  根节点
            List<Node> nodes = document.selectNodes("/root/painting[@id=" + painting.getId() + "]");
            if (nodes.size() == 0){
                throw new RuntimeException("id=" + painting.getId() + "编号油画不存在");
            }
            Element p = (Element) nodes.get(0); //唯一的节点提取出来
            p.selectSingleNode("pname").setText(painting.getPname()); //得到指定标签名的唯一节点 指定油画更新id操作
            p.selectSingleNode("category").setText(painting.getCategory().toString());
            p.selectSingleNode("price").setText(painting.getPrice().toString());
            p.selectSingleNode("preview").setText(painting.getPreview());
            p.selectSingleNode("description").setText(painting.getDescription());
            writer = new OutputStreamWriter(new FileOutputStream(dataFile),"UTF-8");
            document.write(writer);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (writer != null){
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            reload(); //对原始集合进行重载更新
        }
    }

    /**
     * 按id号删除XML油画数据
     * @param id 油画id
     * @throws IOException
     */

    public static void delete(Integer id) {
        SAXReader reader = new SAXReader();
        Writer writer = null;
        try {
            Document document = reader.read(dataFile);
            List<Node> nodes = document.selectNodes("/root/painting[@id=" + id + "]");
            if(nodes.size() == 0) {
                throw new RuntimeException("id=" + id + "编号油画不存在");
            }
            Element p = (Element)nodes.get(0);
            p.getParent().remove(p);
            writer = new OutputStreamWriter(new FileOutputStream(dataFile),"UTF-8");
            document.write(writer);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if(writer!=null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            reload();
        }
    }
}


项目总结

CRUD增删改查

工程结构
mgallery - eclipse工程项目
 /src - java源代码目录
 /WebContent - Web资源目录
 /css - css文件目录
 /js - js文件目录
 /image - 图片资源目录
 /upload - 上传文件目录
 /WEB-INF   //jsp数据来自controller 不允许在web中直接访问 要从控制器跳转
   /jsp - jsp页面目录
   /lib - jar文件目录
   /classes - 编译后的class目录
   /web.xml web描述符文件
包结构[src目录下 根据MVC进行结构划分]
com.imooc.mgallery //逆命名法
    /controller - 存放Servlet控制器类 //承上启下接收参数 调用逻辑 返回处理结果
    /service - 存放处理逻辑类model //完成业务逻辑 service与dao进行传递调用
    /dao - Data Access Object 数据访问对象类 数据读写的java类 数据来自xml文件
    /entity - 存放实体类 JavaBean java中的简单对象
    /utils - 通用工具类 底层通用的工具类或方法

Dao类[Data Access Object]

  • XxxDao类只负责对数据进行读取、写入操作
  • 只负责对数据 增、删、改、查
示例:PaintingDao //针对油画数据进行增删改查
public class PaintingDao{
    public void append(){...} //新增数据
    public void update(){...} //修改数据
    public void delete(){...} //删除数据
    public void findAll(){...} //查询数据
}

Service与Dao的关系

  • Service负责进行流程处理,需**持久化[java处理在内存中 存储在数据库防止丢失]**时调用Dao
  • Dao只负责单纯对数据进行增删改查操作
  • Service允许单向调用Dao,反向不允许

JavaBean

  • 对一种类的使用形式的统称

  • JavaBean是一种Java中可重用组件

  • JavaBean不是技术,而是一种Java类的格式要求

  • JavaBean在Java领域非常常见,通常用于存储数据

JavaBean格式要求

  • 类必须是pubilc并提供默认构造函数
  • 所有属性private私有化
  • 属性通过getter与setter方法进行读写
public class Painting{//类公有化
    public Painting(){...}; //提供默认构造函数,可不写
    private Integer id; //属性私有
    private String pname;
    public Integer getId(){return id;} //getter获取属性值
    public void setId(Integer id){this.id = id;} //setter设置属性值
    public String getPname(){return pname;}
    public void setPname(String pname){this.pname = pname;}
}

创建mgallery工程 实现思路

  • 开发PaintingDao读取XML数据,实现分页操作
  • 开发PaintingService服务类,对PaintingDao进行调用
  • 开发PaintingController控制器,调用PaintingService
  • 重写index.html,利用JSP技术读取分页数据

关键类与方法[对于前台来说]

  • XmlDataSource类[全局有且只有一份数据保存在内存中] - XML数据源工具类,简化Dao提取操作
  • PaintingDao.pagination() - 数据分页查询方法
  • PageModel类 - 分页结果的包装类 {分页处理的核心对象}

Dom4j

  • Dom4j是一个易用的、开源的库,用于解析XML。它应用于Java平台
  • Dom4j将XML视为Document对象
  • XML标签被Dom4j定义为Element对象

Dom4j开发流程回顾

  • SAXReader.read()读取XML文件,得到Document对象
  • document.selectNodes()利用Xpath得到XML节点集合
  • 遍历XML节点,包装成JavaBean或者集合对象返回

开发XmlDataSource (utils)

油画数据分页设计思路

​ [视图 控制器 模型]

浏览器会发送请求查询指定的数据 PaintingController得到分页信息以后 再调用PaintingService 再调用PaintingDao 首先从PaintingDao调用XmlDataSource得到xml所有数据集合传入到PageModel进行分页 再返回PaintingDao-PaintingService-PaintingController放在当前的请求中,将请求跳转至index.jsp对数据进行渲染返回浏览器。

前台系统与后台系统区别

前台系统 后台系统
开放度 对外开放 不对外开放
面向群体 客户 企业内部人员
功能 提供查询与交互 管理前台数据
设计侧重点 良好的用户体验 严密的业务逻辑
访问量
典型案例 猫眼电影网 [后台有评论审核系统] XX公司无纸质化办公OA系统
后台实现功能

油画列表 油画预览 删除油画 修改油画 新增与上传油画

SweetAlert对话框

替代了传统的alert函数 有精心设计的对话框

新增功能实现难点

表单校验、文件上传、处理XML文件

文件上传:Apache Commons FileUpload

文件上传必要前提条件

文件上传必要前提条件

  • form表单method = “post 因为二进制文件不可以放在url中传递
  • form表单enctype = “multipart/form-data 允许保存二进制数据存放在请求体中发送到服务端
    enctype=”application/x-www-form-urlencoded 采用url编码的方式以字符串的形式将数据保存在请求体中发送到服务器
  • form表单持有file类型input进行文件选择

//1.初始化FileUpload组件 包含表单项的数据对象 每个输入项解析成FileItem
//2.ServletFileUpload* *是为FileUpload组件提供Java webHttp请求解析 将factory传入
//3.遍历所有FileItem 判断各种框 isFormField(true/false)来走是普通框还是文件框 文件框先把文件保存到服务器 UUID使文件名不重复

可重用的表单js

封装成JavaScript函数 在validation.js中呈现 可以自定义触发条件 别忘了还有个全局触发条件function写上面

实现新增油画功能

在xml中执行写入操作 利用Dom4j写操作开发
  • SAXReader.read()读取XML文件,得到Document对象
  • p=root.addElement(“painting”) - 创建新的节点
  • p.addElement(“pname”) - 创建新的子节点
  • document.write(write) - 向XML写入新的节点

实现修改与删除功能

所有更新逻辑都是在原始数据基础上覆盖更新,通过id号得到原始的旧数据,对旧的对象进行update

客户端采用Ajax方式提交Http请求
Controller方法处理后不再跳转任何jsp,而是通过响应输出JSON格式字符串
Tips:作为Ajax与服务器交互后,得到的不是整页HTML,而是服务器处理后的数据