这个“Uncaught ReferenceError: $ is not defined”是什么原因啊

这个“Uncaught ReferenceError: $ is not defined”是什么原因啊

http://img1.sycdn.imooc.com//climg/612b24c7082452d319201080.jpg

还有这list.jsp怎么变形了啊?

http://img1.sycdn.imooc.com//climg/612b2519086839de04960880.jpg

http://img1.sycdn.imooc.com//climg/612b27ce091262b504630500.jpg

management.html

<!DOCTYPE html>
<html>
<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 = "/management?method=list" class="menu_item" target="ifcontent">油画列表</a>
         <a href = "/management?method=show_create" 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="/management?method=list"></iframe>
        </div>
    </div>
</body>
</html>

index.html

<script>
window.location.href="/page"
</script>

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>
<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>
<!-- 在分页跳转的时候,来判断之前是不是已经按类型进行筛选了,如果已经筛选这里c肯定存在啊,
那么我们就在当前页面创建一个变量名为categoryParam,然后增加这样一个字符串,&c=
这个字符串会被追加到超链接的url中 -->
<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="/page?c=1">现实主义</a></li>
<li><a href="/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>
<!-- 
<li>
<img src="image/wumingnvlang.jpg" class="img-li">
<div class="info">
<h3>无名女郎</h3>
<p>
图片描述可以分为多种,一种是单一说明,就比如直接的告诉看图者这篇文 章是要介绍什么样子的内容,一些配图可以分为含蓄类型的,这样的配图一般会 图片描述可以分为多种.
</p>
<div class="img-btn">
<div class="price">¥5800</div>
<a href="#" class="cart">
       <div class="btn">
      <img src="image/cart.svg">
       </div>
    </a>
</div>
</div>
</li>
-->

<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="#">首页</a></li>
<li><a href="#">上一页</a></li>
<li><span class="first-page">1</span></li>
<li><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#">4</a></li>
<li><a href="#">5</a></li>
<li><a href="#">…</a></li>
<li><a href="#">98</a></li>
<li><a href="#">99</a></li>
<li><a href="#">下一页</a></li>
<li><a href="#">尾页</a></li>
</ul> -->
<ul>
<li><a href="/page?p=1${categoryParam}">首页</a></li>
<li><a href="/page?p=${pageModel.hasPrePage?pageModel.page-1:1}${categoryParam}">上一页</a></li>
<!-- 形成一个数字的循环,每次循环的变量保存在pno这个变量中 -->
<c:forEach begin="1" end="${pageModel.totalPages}" var="pno" step="1">
<li>
<!-- 当前页才圆圈圈中显示 -->
<span ${pno==pageModel.page?"class='first-page'":""}>
     <!-- href里设置跳转地址 -->
     <%-- <a href="/page?p=${pno}"> --%>
<a href="/page?p=${pno}${categoryParam}">
${pno}
</a>
</span>
</li>
</c:forEach>
<li><a href="/page?p=${pageModel.hasNextPage?pageModel.page+1:pageModel.totalPages }${categoryParam}">下一页</a></li>
<li><a href="/page?p=${pageModel.totalPages }${categoryParam}">尾页</a></li>
</ul>
  </div>
</div>
<div class="footer">
<p><span>M-GALLARY</span>©2020 POWERED BY IMOOC.INC</p>
</div>
</body>
</html>

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 type="text/javascript" src="/js/jquery-3.4.1.min.js"></script>
<script type="text/javascript" src="/js/sweetalert2.js"></script>
<script type="text/javascript">
function showPreview(previewObj){//当调用showPreview就会调出一个对话框了,在点击预览按钮的时候调用
var preview=$(previewObj).attr("data-preview");
var pname=$(previewObj).attr("data-pname");
//previewObj是一个原 生的Dom对象,$可以让原生对象它进行拓展,变成一个jquery对象,只有变成jquery才可以.attr("")对里边的自定义属性进行提取
Swal.fire({//Swal是sweetalet的核心对象,.fire在当前页面弹出一个对话框
//title:"测试油画",//对话框的标题
title:pname,
//html:"<img src='/upload/1.jpg' style='width:361px;height:240px'>",//对话框的主体是什么
html:"<img src='"+preview+"' style='width:361px;height:240px'>",
showCloseButton:true,//是否显示关闭按钮
showConfirmButton:false,//不会出现关闭按钮
})
}
/* 触发后使用js删除油画  */
function del(delObj){
var id=$(delObj).attr("data-id");
var pname=$(delObj).attr("data-pname");
var preview=$(delObj).attr("data-preview");
Swal.fire({
title:"确定要删除["+pname+"]油画吗?",
html:"<img src='"+preview+"'style='width:361px;height:240px'>",
showCancelButton:true,
confirmButtonText:"是",
cancelButtonText:"否",
}).then(function(result){
if(result.value==true){
//alert("你点了'是'按钮")
$.ajax({
url:"/management?method=delete&id="+id,
type:"get",
dataType:"json",
success:function(json){
if(json.result=="ok"){
window.location.reload();
}else{
Swal.fire({
title:json.result,
})
}
console.log(json);
}
})
}
})
}
</script>
<link rel="stylesheet" type="text/css" href="css\list.css">
</head>
<body>
<div class="container">
<fieldset>
<legend>油画列表</legend>
<div style="height: 40px">
<a href="/management?method=show_create" 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>${painting.category==1?"现实主义":"抽象主义"}</td>
<td>${painting.pname }</td>
<td>
<fmt:formatNumber pattern="¥0.00" value="${painting.price }"></fmt:formatNumber>
</td>
<td>${painting.description}</td>
<td>
<a class="oplink" data-preview="${painting.preview }" data-pname="${painting.pname }" href="javascript:void(0)" onclick="showPreview(this)">预览</a>
<!--  onclick="showPreview(this)"的this是指向自身,这样就能得到data-preview等自定义属性 -->
<!-- this是当前触发的dom对象,通过this传入原始的参数showPreview(previewObj)previewObj中,再通过参数提取对应的自定义属性,
便可以让数据和我们对话框的内容产生联动,通过自定义属性来附加数据,这种开发技巧在我们实际的工作中是非常普遍的 -->
<!-- data-preview,data-pname,data-...:自定义属性 -->
<!-- href:javascript:void(0)不做任何事情 -->
<a class="oplink" href="/management?method=show_update&id=${painting.id}">修改</a>
<!--但是这一个method还不够,因为点击修改的时候我们还要知道到底要修改哪条原始数据所以在这之后还要附加一个id  -->
<a class="oplink" data-id="${painting.id}" data-name="${painting.pname}" data-preview="${painting.preview}" href="javascript:void(0)"  onclick="del(this)">删除</a>
</td>
</tr>
</c:forEach>
</table>
<!-- 分页组件 -->
<ul class="page">
<li><a href="/management?method=list&p=1">首页</a></li>
<li><a href="/management?method=list&p=${pageModel.hasPrePage?pageModel.page-1:1 }">上页</a></li>
<c:forEach begin="1" end="${pageModel.totalPages}" var="pno" step="1">
<li ${pno==pageModel.page?"class='active'":"" }>
<a href="/management?method=list&p=${pno}">${pno}</a>
</li>
</c:forEach>
<li><a href="/management?method=list&p=${pageModel.hasNextPage?pageModel.page+1:pageModel.totalPages}">下页</a></li>
<li><a href="/management?method=list&p=${pageModel.totalPages}">尾页</a></li>
</ul>
</fieldset>
</div>
</body>
</html>

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=false;
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="/management?method=create" method="post" autocomplete="off" enctype="multipart/form-data"> -->
<form action="/management?method=create" method="post" autocomplete="off" enctype="multipart/form-data" onsubmit="return checkSubmit()">
<!-- form里onsubmit=false的话,就会阻止表格提交 -->
<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"></textarea>
</li>
<li style="text-align: center;">
<button type="submit" class="btn-button">提交表单</button>
</li>
</ul>
</form>
</fieldset>
</div>

</body>
</html>

update.jsp

<%@page contentType="text/html;charset=utf-8"%>
<!-- 修改油画页面 -->
<!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 r4=null;
if($("#isPreviewModified").val()=="1"){
//本地文件已经重新选择
r4=checkFile('#painting','#errPainting');
}else{
r4=true;
}
var r5 = checkEmpty('#description', '#errDescription');
if (r1 && r2 && r3 && r4 && r5){
return true;
}else{
return false;
}
}
$(function(){
$("#category").val(${painting.category});
/*用$选中category下拉框
jquery的页面..函数,它的作用是我们整个html被解析完之后,来执行这里的代码,这相当于页面的初始化函数 */
/* $("#category")是选择器选择对象时使用的,在html被解析完了以后才被执行;${painting.category}是我们的el表达式,
它的执行时机是在jsp渲染的时候在服务器端生成的 */
})

//检查和改变预览图片,记录图片发生变化
function selectPreview(){
checkFile("#painting","#errPainting");
$("#preview").hide();
//因为文件产生了变化,所以我们要把原先的预览图片隐藏起来,否则会给用户产生歧义,为什么我选择了新的图片,你上面还是显示旧的图片?那就尴尬了,所以要把它隐藏
$("#isPreviewModified").val(1);
}
</script>
</head>
<body>
<div class="container">
<fieldset>
<legend>作品更新</legend>
<form action="/management?method=update" 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')">
<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>
<input type="hidden" id="isPreviewModified" name="isPreviewModified" value="0">
<!-- 0代表未发生变化,如果用户点击了文件上传框,并且选择了文件的话,就意味着用户希望改变文件,这个隐藏域的值就会从0变成1 -->
<span id="errPainting"></span><br/>
<img id="preview" src="${painting.preview}" style="width:361px;height:240px"/><br/>
<!-- String path=request.getServletContext().getRealPath("/upload"); -->
<input id="painting" name="painting" type="file" style="padding-left:0px;" accept="image/*"
value="${painting.preview}" onchange="selectPreview()"/>
<!-- 进行表单校验,也对是否修改图片值变量isPreviewModified产生相应的变化 -->
</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;">
<input type="hidden" id="id" name="id" value="${painting.id}">
<button type="submit" class="btn-button">提交表单</button>
</li>
</ul>
</form>
</fieldset>
</div>

</body>
</html>

ManagementController.java

package mgallery.controller;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.UUID;

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 org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import mgallery.entity.Painting;
import mgallery.service.PaintingService;
import mgallery.utils.PageModel;

/**
* 后台管理功能Controller
*/
@WebServlet("/management")
public class ManagementController extends HttpServlet {
private static final long serialVersionUID = 1L;
private PaintingService paintingService=new PaintingService();//无论前台的还是后台的controller,都统一使用service调用的方法
/**
* @see HttpServlet#HttpServlet()
*/
public ManagementController() {
super();
// TODO Auto-generated constructor stub
}

/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");//修改doPost中默认的请求体的字符集,doGet的代码未来也是为doPost服务的
response.setContentType("text/html;charset=utf-8");//设置字符集,解决输出的中文问题
//如何实现一个servlet实现处理不同的功能呢,原理非常简单,只需要在每次访问Management的时候增加一个参数比如menthod方法,代表我进行哪个处理
String method=request.getParameter("method");
if(method.equals("list")) {//分页查询列表
this.list(request,response);//这个list肯定没有啊,怎么办呢,我们在下面创建就可以了
}else if(method.equals("delete")) {
this.delete(request,response);
}else if(method.equals("show_create")) {//显示新增页面
this.showCreatePage(request,response);
}else if(method.equals("create")) {
this.create(request,response);
}else if(method.equals("show_update")) {//用于显示更新页面
this.showUpdatePage(request,response);
}else if(method.equals("update")){
this.updatePage(request,response);
}else{
System.out.println("method!=list&&method!=delete");
}
}

/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// doPost调用了doGet,也就是说doGet和doPost都做同样的事情,那代码只写一份就可以了
//我们强调过,service功能非常强大,要通过一个service实现正删改查的功能,那要怎么做呢
//我们可以通过标识参数的方法,来对这些应用进行区分处理
doGet(request, response);
}
//除名字外,其他参数和doGet()和doPost()都是一样的
//那这样做,当从前台传入methodd等于list的时候就自然会进入list方法进行处理了我们只需要将分页处理代码放在这里就可以了
//如果还有新增,修改,删除加其他判断和其他类似list方法就可以了
private void list(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//先接收分页号
String p=request.getParameter("p");
String r=request.getParameter("r");
if(p==null) {
p="1";
}
if(r==null) {
r="6";//默认显示第1页,每页6条记录
}
//接下来关键的地方来了
PageModel pageModel=paintingService.pagination(Integer.parseInt(p),Integer.parseInt(r));//分类是可选的,可以不用加入,p,r要进行类型转换。按要求分页
request.setAttribute("pageModel",pageModel);//将所有数据存放到request中
request.getRequestDispatcher("/WEB-INF/jsp/list.jsp").forward(request, response);//这个list.jsp还不存在,一样都要从html-jsp-controller-service-dao-model-xmlDataSource-xml完整写一遍
}
//显示新增页面
private void showCreatePage(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher("/WEB-INF/jsp/create.jsp").forward(request, response);
//jsp要经过controller才进行跳转,这是一个硬性规定
}
//新增油画方法
private void create(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
/*String pname=request.getParameter("pname");
System.out.println("pname:"+pname);*/
//文件上传时的数据处理与标准表单完全不同
//1 初始化FileUpload组件
FileItemFactory factory=new DiskFileItemFactory();
//FileItemFactory它是一个工厂,用于创建FileItem对象,前端表单每个input提交以后在服务端都会有一个FileItem对象,FileItem是泛指,
//可以把FileItem和前端每个输入数据进行对应,可以形象地理解为FileItem是包含了表单项的数据对象
//工厂对象它的职责就是负责将前台传入的数据一个一个转化为对应的FileItem
/**
* FileItemFactory 用于将前端表单的数据转化为一个个FileItem对象,用于数据转换
* ServletFileUpload 为FileUpload组件提供Java Web的Http请求解析,用于web支持
*/
ServletFileUpload sf=new ServletFileUpload(factory);//ServletFilUpload和FileItemFactory是联合使用的
//遍历所有FileItem
//以后别一堆注释和保留什么旧代码了!怎么简单怎么量少怎么写!!!不然一堆莫名其妙的告警!!!
try {
List<FileItem> formData=sf.parseRequest(request);
//将当前请求中每一个表单数据如各种input等转换为一个一个的FileItem对象,返回的是FileItem类型的List集合,也会抛出异常
//表单数据有文本(String和数字)、二进制流数据(file,上传的file)
//在遍历的同时判断每个input对应的FileItem文件项是什么类型数据,并进行想要的处理
Painting painting=new Painting();
for(FileItem fi:formData) {
if(fi.isFormField()==true) {//如果输入框数据是普通类型数据,isForField()就是true,二进制数据格式就是false
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()+",项值为:"+fi.getString("UTF-8"));
System.out.println("是文件上传项类型,项名为:"+fi.getFieldName());
//3上传文件保存到服务器目录
String path=request.getServletContext().getRealPath("/upload");//获取tomcat运行环境下对应的目录它的物理地址
System.out.println("上传文件目录:"+path);
//String fileName="test.jpg"; //文件先固定给它一个名字
String fileName=UUID.randomUUID().toString();//根据计算机本地特性(时间,网卡地址...独特特性)生成全世界唯一的字符串,UUID是java中内置的
//不用担心会出现重复的问题
//从原始文件的拓展名提取出拓展名来补充获得的fileName
//fi.getName()得到原始文件名,截取最后一个.后所有字符串,例如wxml.jpg->.jpg
String stuffix=fi.getName().substring(fi.getName().lastIndexOf("."));//substring截取文件名
fi.write(new File(path,fileName+stuffix));
//写方法,要求内部传入一个文件对象,我们只要把内部文件对象传入,它就会自动地帮我们把客户端上传的文件写入到这个文件所指定的某个目录的文件中
//执行后会在服务器端的写操作,path是文件保存目录,fileName是保存名称
painting.setPreview("/upload/"+fileName+stuffix);//生成图片名是随机名,保证不重复
}
}
paintingService.create(painting);//新增功能
response.sendRedirect("/management?method=list");//返回列表页,跳转页面里不再需要处理前面步骤数据,和之前页面联系少,就用返回重定向,否则用请求转发,把信息继续传下去处理
} catch (Exception e) {
e.printStackTrace();//对异常进行捕获,当进行解析时发现当前表单不是一个formData格式的就会抛出一个文件上传异常
}
//怎么区分哪些是普通输入项,哪些是二进制流输入,遍历来判断
}

/**
* 显示更新页面
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
private void showUpdatePage(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
String id=request.getParameter("id");//将前台传来的id捕捉
Painting painting=paintingService.findById(Integer.parseInt(id));//根据id查找
request.setAttribute("painting",painting);//经查找获得的油画存到request
request.getRequestDispatcher("/WEB-INF/jsp/update.jsp").forward(request, response);//跳到更新页面
}

/**
* 更改油画
* @param request
* @param response
*/
private void updatePage(HttpServletRequest request,HttpServletResponse response) {
FileItemFactory factory=new DiskFileItemFactory();
ServletFileUpload sf=new ServletFileUpload(factory);//ServletFilUpload和FileItemFactory是联合使用的
//2遍历所有FileItem
try {
List<FileItem> formData=sf.parseRequest(request);
//将当前请求中每一个表单数据如各种input等转换为一个一个的FileItem对象,返回的是FileItem类型的List集合,也会抛出异常
//表单数据有文本(String和数字)、二进制流数据(file,上传的file)
//在遍历的同时判断每个input对应的FileItem文件项是什么类型数据,并进行想要的处理
Painting painting=new Painting();
String isPreviewModified=null;
for(FileItem fi:formData) {
if(fi.isFormField()==true) {//如果输入框数据是普通类型数据,isForField()就是true,二进制数据格式就是false
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 "isPreviewModified":
isPreviewModified=fi.getString("UTF-8");
break;
case "description":
painting.setDescription(fi.getString("UTF-8"));
break;
case "id":
painting.setId(Integer.parseInt(fi.getString("UTF-8")));
break;
default:
break;
}
}else {
if(Integer.parseInt(isPreviewModified)==1) {
System.out.println("是文件上传项类型,项名为:"+fi.getFieldName());
//3上传文件保存到服务器目录
String path=request.getServletContext().getRealPath("/upload");//获取tomcat运行环境下对应的目录它的物理地址
System.out.println("上传文件目录:"+path);
//String fileName="test.jpg"; //文件先固定给它一个名字
String fileName=UUID.randomUUID().toString();//根据计算机本地特性(时间,网卡地址...独特特性)生成全世界唯一的字符串,UUID是java中内置的
System.out.println("fileName:"+fileName);
//不用担心会出现重复的问题
//从原始文件的拓展名提取出拓展名来补充获得的fileName
//fi.getName()得到原始文件名,截取最后一个.后所有字符串,例如wxml.jpg->.jpg
String stuffix=fi.getName().substring(fi.getName().lastIndexOf("."));//substring截取文件名
System.out.println("stuffix:"+stuffix);
fi.write(new File(path,fileName+stuffix));
//写方法,要求内部传入一个文件对象,我们只要把内部文件对象传入,它就会自动地帮我们把客户端上传的文件写入到这个文件所指定的某个目录的文件中
//执行后会在服务器端的写操作,path是文件保存目录,fileName是保存名称
painting.setPreview("/upload/"+fileName+stuffix);//生成图片名是随机名,保证不重复
}
}

}
paintingService.update(painting,Integer.parseInt(isPreviewModified));//更新功能
response.sendRedirect("/management?method=list");//返回列表页,跳转页面里不再需要处理前面步骤数据,和之前页面联系少,就用返回重定向,否则用请求转发,把信息继续传下去处理
} catch (Exception e) {
e.printStackTrace();//对异常进行捕获,当进行解析时发现当前表单不是一个formData格式的就会抛出一个文件上传异常
}

}
/**
* 删除油画
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
private void delete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("这是删除操作组");
int id=Integer.parseInt(request.getParameter("id"));//将前台传来的id捕捉
paintingService.delete(id);
response.setContentType("text/html;charset=utf-8");
PrintWriter out=response.getWriter();
String result;
try {
result="{\"result\":\"ok\"}";
//JSONObject json=JSON.parseObject(result);
//out.println(json);
}catch(Exception e){
//result="{\"result\":"+e.getMessage().replace("\"", "\\\"")+"\"}";
result="{\"result\":"+e.getMessage()+"}";
//JSONObject json=JSON.parseObject(result);
//out.println(json);
}
out.println(result);
request.getRequestDispatcher("/management?method=list").forward(request, response);//跳到更新页面
//response.sendRedirect("management?method=list");
}
}

PaintingController.java

package mgallery.controller;

import java.io.IOException;

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 mgallery.service.PaintingService;
import mgallery.utils.PageModel;

/**
* Servlet implementation class PaintingController
*/
@WebServlet("/page")
public class PaintingController extends HttpServlet {
private static final long serialVersionUID = 1L;
private PaintingService paintingService=new PaintingService();
/**
* @see HttpServlet#HttpServlet()
*/
public PaintingController() {
super();
// TODO Auto-generated constructor stub
}

/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//接收http数据,分页情况由地址栏的字符设定输入
String page=request.getParameter("p");//页号
String rows=request.getParameter("r");//每页记录数
String category=request.getParameter("c");
if(page==null) {//page设置默认值
page="1";
}
if(rows==null) {//rows设置默认值
rows="6";
}
//调用service方法,得到处理结果
/*PageModel pageModel=paintingService.pagination(Integer.parseInt(page), Integer.parseInt(rows));*/
PageModel pageModel=paintingService.pagination(Integer.parseInt(page), Integer.parseInt(rows),category);
request.setAttribute("pageModel", pageModel);
//请求准发至对应jsp进行数据展现
request.getRequestDispatcher("/WEB-INF/jsp/index.jsp").forward(request,response);
}

/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}

}

PaintingService.java

package mgallery.service;

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

public class PaintingService {
//开始分页
private PaintingDao paintingDao=new PaintingDao();//service准备调用dao,dao调用的是XmlDataSource和PageModel
/**
public PageModel pagination(int page,int rows) {//这函数和PaintingDao里的pagination相同,因为存在按层逐级调用的要求,所以要这么写
System.out.println("---PaintingService.pagination---");
if(rows==0) {
throw new RuntimeException("无效的rows参数");
}
return paintingDao.pagination(page,rows);//这里是PaintingService的pagination调用PaintingDao的pagination
//service里不单是要进行底层数据的访问,我们还要进行可能比如前置条件的选择,得到结果以后后续数据的处理,这些都是与底层数据无关的
//那在工作中也是如此,service处理完整的业务逻辑
//而dao代码是只与底层数据交互的,dao里方法要么是读取数据,要么是写数据,不掺杂任何额外的逻辑
}*/
//在不改变原有方法的情况下,为我们的程序额外增加类型筛选的功能,使用可选参数,使用办法是在类型后面变量名之前加...
//表示这个参数可能出现,也可能不出现,还有可能出现多个,可选参数本质是一个数组
//可选参数的存在,即使只出现原有参数,程序也不会报错
public PageModel pagination(int page,int rows,String...category) {//这函数和PaintingDao里的pagination相同,因为存在按层逐级调用的要求,所以要这么写
System.out.println("---PaintingService.pagination---");
if(rows==0) {
throw new RuntimeException("无效的rows参数");
}
if((category.length==0)||(category[0]==null)) {
return paintingDao.pagination(page,rows);//这里是PaintingService的pagination调用PaintingDao的pagination
//service里不单是要进行底层数据的访问,我们还要进行可能比如前置条件的选择,得到结果以后后续数据的处理,这些都是与底层数据无关的
//那在工作中也是如此,service处理完整的业务逻辑
//而dao代码是只与底层数据交互的,dao里方法要么是读取数据,要么是写数据,不掺杂任何额外的逻辑
}else {
//return paintingDao.pagination(page, rows,Integer.parseInt(category[0]));
return paintingDao.pagination(Integer.parseInt(category[0]),page, rows);
}

}
/**
* 新增油画
* @param painting 准备新增的Painting数据
*/
public void create(Painting painting) {
paintingDao.create(painting);
}

/**
* 按编号查询油画
* @param id 油画编号
* @return 油画对象
*/
public Painting findById(Integer id) {
Painting p=paintingDao.findById(id);//原始的Dao的fingById()会有为空的情况,那在Dao中你允许那么做,但在service中,如果你不对这个情况加以处理的话
//在我们程序一旦运行后,产生了空指针异常,这在未来无疑会提高我们程序调试的难度,所以在service中最好的做法是,判断一下
if(p==null) {
throw new RuntimeException("[id="+id+"]油画不存在");//那在调用这个id的人那呢,就会知道,哦,原来是什么导致了我这个异常
}
return p;
}

/**
* 更新油画
* @param painting
*/
public void update(Painting newPainting,int isPreviewModified) {
Painting oldPainting=paintingDao.findById(newPainting.getId());
/*oldPainting.setPname(newPainting.getPname());
oldPainting.setId(newPainting.getId());
oldPainting.setCategory(newPainting.getCategory());
oldPainting.setPrice(newPainting.getPrice());
oldPainting.setDescription(newPainting.getDescription());
if(isPreviewModified==1) {
oldPainting.setPreview(newPainting.getPreview());
}
paintingDao.update(oldPainting);*/

if(isPreviewModified==1) {}
else {
newPainting.setPreview(oldPainting.getPreview());
}
paintingDao.update(newPainting);
}

/**
* 删除油画
* @param id
*/
public void delete(int id) {
paintingDao.delete(id);
}
public static void main(String[] args) {
/*PaintingService paintingService=new PaintingService();//先由service调用pagination,查找分页计算出来的数据,得到的是PageModel类型对象
PageModel pageModel=paintingService.pagination(2, 6);
//PageModel pageModel=paintingService.pagination(3, 6);
List<Painting> paintingList=pageModel.getPageData();//计算出的分页数据,是PageModel类型对象,调用get方法,得到List类型的分页需要的数据
for(Painting painting:paintingList) {//List里存放的是Painting类型数据,从List里取Painting数据
System.out.println(painting.getPname());
}
System.out.println("查找数据的开始条数:"+pageModel.getPageStartRow()+" 查找数据的结束条数:"+pageModel.getPageEndRow());
*/
}
}

PaintingDao.java

package mgallery.dao;

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

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

public class PaintingDao {
//得到分页数据,载体是PageModel对象
//pagination只是得到分页数据
public PageModel pagination(int page,int rowsPerPage) {
System.out.println("---PaintingDao.pagination---");
List<Painting> list=XmlDataSource.getRawData();//首先获得全部xml数据
PageModel pageModel=new PageModel(list,page,rowsPerPage);//获取计算出来的分页数据,该数据被存放在自定义对象中
return pageModel;
}
//重写pagination(重载),实现分类功能
public PageModel pagination(int category,int page,int rowsPerPage) {
List<Painting> list=XmlDataSource.getRawData();//返回全部油画数据
List<Painting> categoryList=new ArrayList();//符合筛选条件的油画数据
for(Painting p:list) {//遍历全部油画数据
if(p.getCategory()==category) {
categoryList.add(p);//把符合条件的油画数据添加到categoryList中
}
}
PageModel pageModel=new PageModel(categoryList,page,rowsPerPage);//用符合条件的油画数据调用PageModel,以实现得到分页数据
return pageModel;//得到分页和分类后的数据,以PageModel类型保存
}
/**
* 数据新增
*/
public void create(Painting painting) {
XmlDataSource.append(painting);
}

/**
* 按编号查询油画
* @param id 油画编号
* @return 油画对象
*/
public Painting findById(Integer id) {
List<Painting> data=XmlDataSource.getRawData();
Painting painting=null;
for(Painting p:data) {
if(p.getId()==id) {
painting=p;
break;
}
}
return painting;//如果存在返回它,如果不存在返回空
}
/**
* 更新油画
* @param painting
*/
public void update(Painting painting) {
XmlDataSource.update(painting);
}

/**
* 删除油画
* @param id
*/
public void delete(int id) {
XmlDataSource.delete(id);
}
}

XmlDataSource.java

package mgallery.utils;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import mgallery.entity.Painting;

/**
* 数据源类,用于将XML文件解析为Java对象
*
*/
public class XmlDataSource {
//通过static静态关键字保证数据全局唯一
private static List<Painting> data=new ArrayList();//保存的油画数据
private static String dataFile;//保存的XML文件地址
static {
//得到painting.xml文件完整物理路径
//当文件路径中有空格,空格就会转换为%20
//URLDecoder将unicode64转化为普通字符串
dataFile=XmlDataSource.class.getResource("/painting.xml").getPath();
reload();
}

/**
*重新加载
*/
private static void reload() {
URLDecoder decoder=new URLDecoder();
//.decoder(待转换String,转换的编码);
try {
//System.out.println("---XmlDataSource.static---");
dataFile=decoder.decode(dataFile,"UTF-8");
System.out.println("dataFile:"+dataFile);
//利用Dom4j对xml进行解析
SAXReader reader=new SAXReader();
Document document=reader.read(dataFile);//获取xml文档
List<Node> nodes=document.selectNodes("/root/painting");//Xpath得到XML节点集合
data.clear();//清空后再追加
for(Node node:nodes) {//获取所有xml节点,从txt转换到Painting类
Element element=(Element)node;//Node是所有xml节点的统称
String id=element.attributeValue("id");//获取节点信息的方法1
String pname=element.elementText("pname");//获取节点信息的方法2
//System.out.println("id:"+id+" pname:"+pname);//现在只是把数据提取了出来,如何把数据转换成java对象呢,通过什么载体保存这些数据呢
//开发对应的javaBean来承载这些数据,javaBean放在entity下
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.setDescription(element.elementText("description"));
painting.setPreview(element.elementText("preview"));
data.add(painting);//从xml获取的文本转换为Painting类后的数组保存到本类数组List类对象data变量中
}
} catch (Exception e) {
e.printStackTrace();
}
}

//获取最原始的信息,也就是查询所有
public static List<Painting> getRawData(){
return data;
}

//在xml末尾追加一副油画
public static void append(Painting painting) {//油画信息来自前端已经封装好的油画数据painting
//1 读取XML文档,得到Document对象
Writer writer=null;
SAXReader reader=new SAXReader();
try {
Document document=reader.read(dataFile);//dataFile为保存xml文件的目录(路径)
//2 创建新的painting节点
Element root=document.getRootElement();//root节点
Element p=root.addElement("painting");//创建一个新的子节点
//3 创建painting节点的各个子节点
p.addAttribute("id",String.valueOf(data.size()+1));//添加属性,前属性,后属性值
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());
//内存中的document对象就形成了一个全新的数据,它只是保存在内存中,我们如何写入到XML中呢
//4 写入XML,完成追加操作
writer=new OutputStreamWriter(new FileOutputStream(dataFile),"UTF-8");//文件输出流,进行写操作,两个参数,第一个要写入的文件地址,第二个字符编码
document.write(writer);//用document的writer方法向目标xml文件中写入新节点,设置UTF-8编码不会出现中文乱码
//System.out.println(dataFile);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(writer!=null) {//说明它已经被实例化
try {
writer.close();//writer有开就有关
} catch (IOException e) {//关闭又抛出IO异常所以要捕捉一下
e.printStackTrace();
}
}
reload();//在所有的写入操作以后,无论是新增,修改,删除,都要对内存中的数据进行刷新,来保证准确性,一定在原有文件写入并且关闭后才可以对数据进行刷新
//无论执行成功还会执行失败,它都会进行数据的重新加载,进而保证内存与文件数据的一致
}

}

/**
* 更新油画
* @param painting
*/
public static void update(Painting painting) {
SAXReader reader=new SAXReader();//来读取XML文件
Writer writer=null;
try {
Document document=reader.read(dataFile);//读取XML文件
//对指定格式的节点筛选
// /root/painting[@id=x] 通过这个形式我们就可以得到指定id的painting节点了
List<Node> nodes=document.selectNodes("/root/painting[@id="+painting.getId()+"]");
//传入id不存在的异常处理
if(nodes.size()==0) {
throw new RuntimeException("id="+painting.getId()+"编号油画不存在");
}
Element p=(Element)nodes.get(0);//将唯一的element节点提取出来
p.selectSingleNode("pname").setText(painting.getPname());//完成了指定id油画名称更新的操作
p.selectSingleNode("category").setText(painting.getCategory().toString());//指定id油画类别更新
p.selectSingleNode("price").setText(painting.getPrice().toString());//指定id油画价格更新
p.selectSingleNode("preview").setText(painting.getPreview());//指定id油画地址更新
p.selectSingleNode("description").setText(painting.getDescription());//指定id油画描述更新
writer=new OutputStreamWriter(new FileOutputStream(dataFile),"UTF-8");//将writer实例化,指向我们原始数据文件,因为包含了中文,所以用字符集UTF-8
document.write(writer);//用文档对象的writer方法,对我们文件进行回写,将数据更新在xml文件中
}catch (Exception e) {
e.printStackTrace();
}finally {
if(writer!=null) {//没关才关
try {//try包裹,这个确实是麻烦一些
writer.close();//作为流来说,有开就有关
} catch (IOException e) {
e.printStackTrace();
}
}
//底层文件发生了修改,调用reload方法对原始的data集合进行重载更新
reload();
}

}

/**
* 删除油画
* @param id
*/
public static void delete(Integer id){
SAXReader reader=new SAXReader();//来读取XML文件
Writer writer=null;
try {
Document document=reader.read(dataFile);//读取XML文件
//对指定格式的节点筛选
// /root/painting[@id=x] 通过这个形式我们就可以得到指定id的painting节点了
List<Node> nodes=document.selectNodes("/root/painting[@id="+id+"]");
//传入id不存在的异常处理
if(nodes.size()==0) {
throw new RuntimeException("id="+id+"编号油画不存在");
}
Element p=(Element)nodes.get(0);//将唯一的element节点提取出来
Element pParent=p.getParent();//获取父节点
pParent.remove(p);//从父节点删除
writer=new OutputStreamWriter(new FileOutputStream(dataFile),"UTF-8");//将writer实例化,指向我们原始数据文件,因为包含了中文,所以用字符集UTF-8
document.write(writer);//用文档对象的writer方法,对我们文件进行回写,将数据更新在xml文件中
}catch (Exception e) {
e.printStackTrace();
}finally {
if(writer!=null) {//没关才关
try {//try包裹,这个确实是麻烦一些
writer.close();//作为流来说,有开就有关
} catch (IOException e) {
e.printStackTrace();
}
}
//底层文件发生了修改,调用reload方法对原始的data集合进行重载更新
reload();
}
}
public static void main(String[] args) {
//为什么只写new就可以,因为执行代码都在static块里,new就会执行static块
//new XmlDataSource();
//List<Painting> ps=XmlDataSource.getRawData();
//System.out.println("data:"+ps);
Painting p=new Painting();
p.setPname("测试油画");
p.setCategory(1);
p.setPrice(39);
p.setPreview("/upload/10.jpg");
p.setDescription("测试油画描述");
XmlDataSource.append(p);
//实现了向文件的写操作,当前的文件发生了变化,内存中之前读取的data这个数据集合没有产生任何的变更,这就会导致在内存中完整的数据和实际文件它的内容是不一致的
//写入文件后,还要执行读取文件的过程
}
}

PageModel.java

package mgallery.utils;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import mgallery.entity.Painting;

/**
* 数据源类,用于将XML文件解析为Java对象
*
*/
public class XmlDataSource {
//通过static静态关键字保证数据全局唯一
private static List<Painting> data=new ArrayList();//保存的油画数据
private static String dataFile;//保存的XML文件地址
static {
//得到painting.xml文件完整物理路径
//当文件路径中有空格,空格就会转换为%20
//URLDecoder将unicode64转化为普通字符串
dataFile=XmlDataSource.class.getResource("/painting.xml").getPath();
reload();
}

/**
*重新加载
*/
private static void reload() {
URLDecoder decoder=new URLDecoder();
//.decoder(待转换String,转换的编码);
try {
//System.out.println("---XmlDataSource.static---");
dataFile=decoder.decode(dataFile,"UTF-8");
System.out.println("dataFile:"+dataFile);
//利用Dom4j对xml进行解析
SAXReader reader=new SAXReader();
Document document=reader.read(dataFile);//获取xml文档
List<Node> nodes=document.selectNodes("/root/painting");//Xpath得到XML节点集合
data.clear();//清空后再追加
for(Node node:nodes) {//获取所有xml节点,从txt转换到Painting类
Element element=(Element)node;//Node是所有xml节点的统称
String id=element.attributeValue("id");//获取节点信息的方法1
String pname=element.elementText("pname");//获取节点信息的方法2
//System.out.println("id:"+id+" pname:"+pname);//现在只是把数据提取了出来,如何把数据转换成java对象呢,通过什么载体保存这些数据呢
//开发对应的javaBean来承载这些数据,javaBean放在entity下
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.setDescription(element.elementText("description"));
painting.setPreview(element.elementText("preview"));
data.add(painting);//从xml获取的文本转换为Painting类后的数组保存到本类数组List类对象data变量中
}
} catch (Exception e) {
e.printStackTrace();
}
}

//获取最原始的信息,也就是查询所有
public static List<Painting> getRawData(){
return data;
}

//在xml末尾追加一副油画
public static void append(Painting painting) {//油画信息来自前端已经封装好的油画数据painting
//1 读取XML文档,得到Document对象
Writer writer=null;
SAXReader reader=new SAXReader();
try {
Document document=reader.read(dataFile);//dataFile为保存xml文件的目录(路径)
//2 创建新的painting节点
Element root=document.getRootElement();//root节点
Element p=root.addElement("painting");//创建一个新的子节点
//3 创建painting节点的各个子节点
p.addAttribute("id",String.valueOf(data.size()+1));//添加属性,前属性,后属性值
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());
//内存中的document对象就形成了一个全新的数据,它只是保存在内存中,我们如何写入到XML中呢
//4 写入XML,完成追加操作
writer=new OutputStreamWriter(new FileOutputStream(dataFile),"UTF-8");//文件输出流,进行写操作,两个参数,第一个要写入的文件地址,第二个字符编码
document.write(writer);//用document的writer方法向目标xml文件中写入新节点,设置UTF-8编码不会出现中文乱码
//System.out.println(dataFile);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(writer!=null) {//说明它已经被实例化
try {
writer.close();//writer有开就有关
} catch (IOException e) {//关闭又抛出IO异常所以要捕捉一下
e.printStackTrace();
}
}
reload();//在所有的写入操作以后,无论是新增,修改,删除,都要对内存中的数据进行刷新,来保证准确性,一定在原有文件写入并且关闭后才可以对数据进行刷新
//无论执行成功还会执行失败,它都会进行数据的重新加载,进而保证内存与文件数据的一致
}

}

/**
* 更新油画
* @param painting
*/
public static void update(Painting painting) {
SAXReader reader=new SAXReader();//来读取XML文件
Writer writer=null;
try {
Document document=reader.read(dataFile);//读取XML文件
//对指定格式的节点筛选
// /root/painting[@id=x] 通过这个形式我们就可以得到指定id的painting节点了
List<Node> nodes=document.selectNodes("/root/painting[@id="+painting.getId()+"]");
//传入id不存在的异常处理
if(nodes.size()==0) {
throw new RuntimeException("id="+painting.getId()+"编号油画不存在");
}
Element p=(Element)nodes.get(0);//将唯一的element节点提取出来
p.selectSingleNode("pname").setText(painting.getPname());//完成了指定id油画名称更新的操作
p.selectSingleNode("category").setText(painting.getCategory().toString());//指定id油画类别更新
p.selectSingleNode("price").setText(painting.getPrice().toString());//指定id油画价格更新
p.selectSingleNode("preview").setText(painting.getPreview());//指定id油画地址更新
p.selectSingleNode("description").setText(painting.getDescription());//指定id油画描述更新
writer=new OutputStreamWriter(new FileOutputStream(dataFile),"UTF-8");//将writer实例化,指向我们原始数据文件,因为包含了中文,所以用字符集UTF-8
document.write(writer);//用文档对象的writer方法,对我们文件进行回写,将数据更新在xml文件中
}catch (Exception e) {
e.printStackTrace();
}finally {
if(writer!=null) {//没关才关
try {//try包裹,这个确实是麻烦一些
writer.close();//作为流来说,有开就有关
} catch (IOException e) {
e.printStackTrace();
}
}
//底层文件发生了修改,调用reload方法对原始的data集合进行重载更新
reload();
}

}

/**
* 删除油画
* @param id
*/
public static void delete(Integer id){
SAXReader reader=new SAXReader();//来读取XML文件
Writer writer=null;
try {
Document document=reader.read(dataFile);//读取XML文件
//对指定格式的节点筛选
// /root/painting[@id=x] 通过这个形式我们就可以得到指定id的painting节点了
List<Node> nodes=document.selectNodes("/root/painting[@id="+id+"]");
//传入id不存在的异常处理
if(nodes.size()==0) {
throw new RuntimeException("id="+id+"编号油画不存在");
}
Element p=(Element)nodes.get(0);//将唯一的element节点提取出来
Element pParent=p.getParent();//获取父节点
pParent.remove(p);//从父节点删除
writer=new OutputStreamWriter(new FileOutputStream(dataFile),"UTF-8");//将writer实例化,指向我们原始数据文件,因为包含了中文,所以用字符集UTF-8
document.write(writer);//用文档对象的writer方法,对我们文件进行回写,将数据更新在xml文件中
}catch (Exception e) {
e.printStackTrace();
}finally {
if(writer!=null) {//没关才关
try {//try包裹,这个确实是麻烦一些
writer.close();//作为流来说,有开就有关
} catch (IOException e) {
e.printStackTrace();
}
}
//底层文件发生了修改,调用reload方法对原始的data集合进行重载更新
reload();
}
}
public static void main(String[] args) {
//为什么只写new就可以,因为执行代码都在static块里,new就会执行static块
//new XmlDataSource();
//List<Painting> ps=XmlDataSource.getRawData();
//System.out.println("data:"+ps);
Painting p=new Painting();
p.setPname("测试油画");
p.setCategory(1);
p.setPrice(39);
p.setPreview("/upload/10.jpg");
p.setDescription("测试油画描述");
XmlDataSource.append(p);
//实现了向文件的写操作,当前的文件发生了变化,内存中之前读取的data这个数据集合没有产生任何的变更,这就会导致在内存中完整的数据和实际文件它的内容是不一致的
//写入文件后,还要执行读取文件的过程
}
}

Painting.java

package mgallery.entity;
//油画实体类
public class Painting {//JavaBean
private Integer id;//编号
private String pname;//名字
private Integer category;//种类1现实主义,2抽象主义
private Integer price;//价格
private String preview;//油画保存的地址
private String description;
public Painting() {
}
public Painting(Integer id, String pname, Integer category, Integer price, String preview, String description) {
super();
this.id = id;
this.pname = pname;
this.category = category;
this.price = price;
this.preview = preview;
this.description = description;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getPname() {
return pname;
}
public void setPname(String pname) {
this.pname = pname;
}
public Integer getCategory() {
return category;
}
public void setCategory(Integer category) {
this.category = category;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
public String getPreview() {
return preview;
}
public void setPreview(String preview) {
this.preview = preview;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}

}

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="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>


正在回答

登陆购买课程后可参与讨论,去登陆

1回答

同学你好,根据同学的报错信息,$并没有正确加载:

http://img1.sycdn.imooc.com//climg/612b498e090afab511100607.jpg

结合同学的描述,css样式也没有生效,在同学的项目目录结构中,js和css等文件也是在WEB-INF目录下

http://img1.sycdn.imooc.com//climg/612b49750990c71705410418.jpg

而同学的css、js的引入路径中,是直接通过绝对路径来加载的,比如

http://img1.sycdn.imooc.com//climg/612b4a1e0999352d06440088.jpg

此时可能出现浏览器直接访问WEB-INF安全目录的情况,不能正确访问,同学尝试将css、js等文件放在WebContent下,比如

http://img1.sycdn.imooc.com//climg/612b4a9509512ad302190280.jpg

同学参考如上形式重启项目试试。

祝学习愉快~

  • 慕函数4309305 提问者 #1

    http://img1.sycdn.imooc.com//climg/612c70630892875616000897.jpg

    http://img1.sycdn.imooc.com//climg/612c70c308d4711a19191035.jpg

    http://img1.sycdn.imooc.com//climg/612c70d008dca24716000900.jpg

    老师,这是按您指导意见修改后的结果,后面怎么弄啊

    2021-08-30 13:47:29
  • 慕函数4309305 提问者 #2

    http://img1.sycdn.imooc.com//climg/612c78ed08ea65bc16000900.jpg

    http://img1.sycdn.imooc.com//climg/612c78fb08d14f9019201080.jpg

    老师,点击删除后对话框已经正确显示pname了,但是删除后列表中图片还在,过几分钟删除结果才显现

    2021-08-30 14:22:55
  • 好帮手慕小班 回复 提问者 慕函数4309305 #3

    同学你好,1、在xml操作中,删除操作是否正确执行完成了,这个数据最后删除了吗?

    http://img1.sycdn.imooc.com//climg/612c9327095f487010590723.jpg

        ​2、过几分钟删除结果才显现,这里老师并没有理解同学的意思,同学可以具体描述下吗?另外,并没有及时更新是缓存导致的吗?同学可以尝试重启项目试试。

    祝学习愉快~

    2021-08-30 16:16:25
问题已解决,确定采纳
还有疑问,暂不采纳

恭喜解决一个难题,获得1积分~

来为老师/同学的回答评分吧

0 星
请稍等 ...
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号

在线咨询

领取优惠

免费试听

领取大纲

扫描二维码,添加
你的专属老师