JavaWeb就是在B/S模式下,使用Java开发综合性web服务网站的技术。
Tomcat
下载:tomcat官网https://tomcat.apache.org/
根据jdk和操作系统选择合适的版本
使用
下载成功后无需安装,解压到某个盘下即可
解压后的目录
目录 | 说明 |
bin | 保存tomcat中的可执行文件,如启动tomcat的startup.bat文件等 |
conf | 保存tomcat的配置文件,如server.xml文件可以修改默认的8080端口 |
lib | 保存tomcat运行时所需的jar文件 |
logs | 保存tomcat运行时产生的日志文件 |
temps | 保存tomcat运行时产生的临时文件 |
webapps | 保存发布在tomcat山歌应用程序 |
work | 保存tomcat运行时产生的编译文件 |
启动tomcat
打开bin目录下的start.bat文件,tomcat启动后的默认端口号为8080,。
在浏览器输入localhose:8080或127.0.0.0:8080,即可进入tomcat默认启动页面
该页面位于ROOT目录下,名为index.jsp,localhost:8080表示进入ROOT目录下访问index文件
Maven
用于管理项目的工具。如管理项目中所需的jar文件,打包项目等。通过在maven项目中加入某个jar文件所需的dependency(依赖),让其自动从Maven仓库中下载对应jar文件。
Maven依赖官网
地址https://mvnrepository.com/,在这个网站中查询所需的jar文件的依赖
Maven本地仓库
Maven默认的配置文件会从官网下载jar文件,速度较慢,并且下载的jar文件保存在c盘。
这里在D盘的根目录下新建了一个MavenRepository的本地仓库,用于保存下载后的jar文件,
设置国内镜像下载
配置文件
使用
下载配置Maven – Welcome to Apache Maven
使用IDEA自带:在新建项目时选择Maven项目即可,在设置中更改IDEA自带的Maven配置。
IDEA创建不同Maven项目
使用easyexcel实现读写excel文件
新建项目
导入所需依赖
在Maven依赖官网搜索easyexcel
easyexcel使用文档EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel
然后选择版本 复制依赖,粘贴到项目的pom.xml文件下的dependencies标签中
普通Maven项目没有dependencies标签,需要自己编辑。
pom文件更新后,需要刷新Maven,让其自动下载所需依赖。
编写实体类
属性上使用@ExcelProperty("列名")注解设置要读取的excel文件表头
/*
* 员工表对应实体类
* 这里省略
* get/set
* toString()
* 全参构造
* 无参构造
* */
public class Employee {
@ExcelProperty("编号")
private int id;
@ExcelProperty("姓名")
private String name;
@ExcelProperty("性别")
private String sex;
@ExcelProperty("身份证")
private String idcard;
@ExcelProperty("部门")
private String dept;
@ExcelProperty("入职时间")
private String joinDate;
@ExcelProperty("电话")
private String phone;
}
读excel
//要读取的文件,必须存在
File file = new File("C:\\Users\\Administrator\\Desktop\\员工信息.xlsx");
// EasyExcel.read(要读取的文件,要读取的实体类.class,PageReadListener对象,参数为一个lambda表达式).sheet().doRead();
//PageReadListener对象需要设置泛型为要读取的实体类型,lambda表达式参数为读取到的数据集合
EasyExcel.read(file, Employee.class, new PageReadListener<Employee>((emps) -> {
for (Employee emp : emps) {
System.out.println(emp);
}
})).sheet().doRead();
写excel
//要写入的文件,可以不存在
File target = new File("d:\\employee.xlsx");// EasyExcel.write(要写入的目标文件,要写入的实体类.class).sheet("表名").doWrite(要写入的数据集合)
EasyExcel.write(target, Employee.class).sheet("员工信息").doWrite(数据集合);
在IDEA中创建基于Maven的Web项目
1.新建webapp模板
2.在src目录下新建文件夹,之后在此创建java文件
3.修改项目中web.xml版本为4.0
打开project Structure 移除默认的2.3版本的web.xml文件,移除后点apply应用 重新添加web.xml文件,选择4.0版本
4.配置 tomcat服务器
5.将项目部署到tomcat中
并修改项目的访问路径:application context:xxxxxxx
6.启动项目:
启动tomcat:默认启动web页中自带的index.jsp文件。
web资源目录结构
webapp下如果有index文件,访问项目后会自动访问index文件,如果没有名为index的文件,就会出现404,表示index页面不存在。
项目上下文路径
域名+端口+项目名:称为项目上下文路径
如localhost:8080/web就是项目的上下文路径,可以理解为项目根目录
webapp目录直接通过项目上下文路径访问,无需显示写出
更新了项目中的内容后,根据需要选择重启还是重新部署
修改项目编译时的jdk版本
解决控制台中文乱码
Edit Custom VM Options中添加:-Dfile.encoding=utf-8
重启IDEA
Servlet
Servlet表示Server+Applet,意味运行在服务器上的程序。是一个被动运行程序,每次请求都要执行。
BS结构客户端访问服务器端的流程
编写Servlet的步骤
1.在项目中导入Servlet所需依赖
粘贴在pom.xml文件中的dependencies标签下
2.在项目的java目录下,新建一个类,继承HttpServlet,重写doget和dopost方法
/*
* 编写Servlet的步骤
* 1.导入依赖
* 2.创建一个类,继承HttpServlet,重写doGet和doPost方法
* 3.在web.xml中设置访问该类的url映射
* */
public class FirstServlet extends HttpServlet {
//普通的控制台项目,需要主动运行main方法执行程序
//web项目中的java程序,需要被动运行,在用户通过浏览器访问某个程序的url映射时执行
/*
* 当浏览器发送的是get类型的请求,执行这个方法
* */
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("get请求");
}
/*
* 当浏览器发送的是post类型的请求,执行这个方法
* */
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("post请求");
}
}
3.在web.xml文件下,配置Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--xml是一个配置文件,属于一种标记语言,通过标签定义内容-->
<!--声明Servlet-->
<servlet>
<!--servlet命名-->
<servlet-name>firstServlet</servlet-name>
<!--servlet类的全限定名(路径+文件名)-->
<servlet-class>FirstServlet</servlet-class>
</servlet>
<!--定义Servlet的请求映射-->
<servlet-mapping>
<!--要映射的servlet名,与上方定义的servlet-name一致-->
<servlet-name>firstServlet</servlet-name>
<!--请求映射url,必须以/开头-->
<!--之后通过项目上下文路径+该路径,就能访问FirstServlet类-->
<url-pattern>/first</url-pattern>
</servlet-mapping>
</web-app>
复制资源的相对路径
启动tomcat,访问"localhost:8080/项目路径/first"即可访问自定义servlet类中的doget方法
三层架构
在软件开发中,并不是将所有的功能集成到一个类中或文件中实现,而是要将其分层处理,从而达到高内聚,低耦合的目的
低耦合是指降低各个模块之间的关联程度,便于开发和维护,各个模块各司其职。高内聚指各个模块的功能不开再分。
- 数据访问层:用于连接操作数据库的类,对数据进行增删改查
- 业务逻辑层:用于处理业务逻辑,在适当的情况下访问数据访问层中的内容
- 视图表现层:用于展示和提供用户输入的渠道,在适当的情况下访问业务逻辑层中的内容
访问服务器某个URL的方式
在浏览器的地址栏输入对应的URL,属于get请求
使用a标签,在href中定义对应的URL,属于get请求
使用form表单,在action中定义对应的URL,根据method属性的值控制get或post请求
页面向服务器端提交数据的方式
- 使用form表单的name属性提交
提交的数据会暴露在浏览器的地址栏中
- 使用form表单中的name属性隐式提交
在原form基础上加上method="post"
提交的数据不会暴露在浏览器的地址栏中
- 通过"?参数1=值&参数2=值"的方式显示提交
在浏览器地址栏中输入:http://localhost:8080/web01/getOne?id=23
- 使用a标签
<a href="http://localhost:8080/web01/login?username=admin&password=123123">登录</a>
服务器端获取前端提交的数据
前端页面传递数据
<a href="URL?id=123">访问</a>
<form action="URL">
<input name="id">
<input type="submit">
</form>
在servlet的doget或dopost方法中
通过request.getParameter("name值或?后的参数")获取数据,返回值为String
解决请求和响应时的中文乱码
HTTP状态码
常见状态码 | 说明 |
200 | 成功 |
404 | 要访问的资源不存在 |
500 | 服务器内部错误 |
405 | 方法不允许 |
表单提交数据注意
表单如果是get方式提交,无法在action中使用?提交数据
表单如果是post提交,可以再action中使用?提交数据
使用表单的隐藏域提交,无论get还是post方式,都能提交数据
使用Servlet实现表单的增删改查
实体类,数据访问类dao层,业务逻辑类:service最后是servlet
首页
添加页
web.xml文件中常用标签
web.xml是web模块的配置文件
<?xml version="1.0" encoding="UTF-8"?>
配置过滤器,一个特殊的servlet
Servlet的生命周期
构造一次-->init()一次-->service()-->destory()一次
在第一次访问某个servlet时,执行:1.构造方法一次,2.init()方法一次;3.有Service()方法执行service()方法,没有则执行doxxx方法,如果都没有,报405状态码,表示请求不允许;4.当服务器停止时,执行destory()方法一次。
使用注解开发Serclet
JSP
java Server Pages:使用java开发,运行在服务器上的页面,称为jsp,jsp页面的后缀名为.jsp
jsp实际是一个java类,具体为一个Servlet,第一次访问某个jsp页面,相当于编译运行servlet
访问jsp的流程:编译对应的Servlet-->运行servlet
使用jsp时,可以导入该依赖,在jsp中方便提示
JSP的组成
1.HTML元素*
2.Java脚本*
<%
java代码
%>
3.表达式*
用于在页面中输出变量或字符串。无需分号
4.注释:jsp注释在访问jsp时不会再浏览器中看见,html注释在访问jsp时会在浏览器中看见
5.指令:<% 指令名 属性="值"%>
- page指令
用于设置当前页面的属性
- include指令
用于嵌入其他页面
- taglib指令
用于导入其他标签库
6.声明:可以在这里定义方法,全局变量等
<%!
void fun(){}
%>
7.动作:使用标签定义一些功能
<jsp:单词></jsp:单词>
//包含另一张页面
<jsp:include page="路径"></jsp:include>
路径问题
绝对路径:
完整路径
相对路径:
/:从项目根目录出发。即域名+端口号
./:从当前位置出发
../退出当前目录后出发
跳转
HTML to HTML/JSP
<a href="另一个页面的路径">跳转</a>
<form action="另一个页面的路径">
<input type="submit">
</form>
HTML to Servlet
<a href="某个servlet的URL映射">跳转</a>
<form action="某个servlet的URL映射">
<input type="submit">
</form>
Servlet/JSP to Servlet/JSP/HTML
请求转发
request.getRequestDispatcher("目的路径").forward(request,response)
使用请求转发跳转到目的地后,浏览器的地址栏是最初访问的路径
可以再请求转发时,在request对象中保存数据,如request.getParameter(),request.setAttribute()
在执行查询时,通常将查询后的结果保存在request对象中,使用请求转发跳转到页面中
请求转发,用户只发送一次请求,得到一次响应。在request中保存的数据会一直存在。浏览器的地址栏中显示的是最初访问的路径,而不是最终的目标路径。用于地址栏中的路径不会改变,所以刷新页面就表示访问对应的内容。所以在对数据的增删改后,不要使用请求转发,否则会重复提交,执行查询时建议使用请求转发
重定向
response.sendRedirect("目的路径")
使用重定向跳转到目的地后,浏览器的地址栏是最终访问的目的地路径
如果在重定向时使用request对象保存数据,后续无法获取保存的内容
在执行增删改后使用重定向跳转到目的地,防止重复提交。
重定向用户不止发送了一个请求,在第一次的请求中保存的数据,一旦中途有重定向,后续无法获取该数据
跳转到目的地后,浏览器地址栏会变成最终的地址。在执行增删改后,使用重定向防止重复提交。
跳转过程中传递参数
由页面发送数据到servlet或jsp,通常使用表单元素或在某个URL后使用"?参数=值"方式提交参数,获取时使用request.getParameter("参数")获取
<a href="xxxx?id=11">删除</a>
<form action="xxxx" >
<input type="hidden" name="id" value="11">
</form>
//返回值为String
String str=request.getParameter("id");
由servlet发送数据到页面,使用request.setAttributr(String str,Object obj)保存obj到请求对象中,命名为str,获取时使用request.getAttribute(String str)
<%
//返回值为Object类型
objcet obj=request.getAttribute("obj");
List list=(list)obj;
%>
分页和条件分页
核心SQL语句
dao
/*
* 条件分页查询
* 根据图书名的关键字分页查询
* */
public List<BookInfo> queryPageByCondition(int page, int size, String keyword) {
List<BookInfo> list = new ArrayList<>();
String sql = "select * from book_info where book_name like concat('%',?,'%') limit ?,?";
conn = DBHelper.getConn();
try {
pst = conn.prepareStatement(sql);
pst.setString(1, keyword);
pst.setInt(2, (page - 1) * size);
pst.setInt(3, size);
rs = pst.executeQuery();
while (rs.next()) {
int bookId = rs.getInt(1);
int typeId = rs.getInt(2);
int bookPrice = rs.getInt(5);
int bookNum = rs.getInt(8);
String bookName = rs.getString(3);
String bookAuthor = rs.getString(4);
String publisher = rs.getString(6);
String publishDate = rs.getString(7);
String bookImg = rs.getString(9);
BookInfo bookInfo = new BookInfo(bookId, typeId, bookName, bookAuthor, bookPrice, publisher, publishDate, bookNum, bookImg);
list.add(bookInfo);
}
} catch (SQLException e) {
System.out.println("查询所有异常" + e);
} finally {
DBHelper.release(conn, pst, rs);
}
return list;
}
servlet
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
String op = req.getParameter("op");
if (op == null) {
return;
}
switch (op) {
case "queryByPage":
//获取关键字
String keyword = req.getParameter("keyword");
//获取总记录数
double sumCount = bookInfoService.getSumCount(keyword);
//根据总数和size计算最大页
int maxPage = (int) Math.ceil(sumCount / 8);
//将最大页保存到请求中
req.setAttribute("maxPage", maxPage);
//获取当前页
String page = req.getParameter("page");
//访问该servlet,调用后续的查询,得到查询的集合
List<BookInfo> books = bookInfoService.queryPageByCondition(Integer.parseInt(page), 8,keyword);
//将查询出的集合保存在请求中,命名为list req.setAttribute(String str,Object obj);
req.setAttribute("list", books);
//跳转到jsp页面中,这里只能使用请求转发
req.getRequestDispatcher("pages/bookshop_index.jsp").forward(req, resp);
//resp.sendRedirect("pages/bookshop_index.jsp");
break;
}
}
页面
<%@ page import="com.hqyj.book_shop.entity.BookInfo" %>
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
//样式略
</style>
</head>
<body>
<%
//获取当前的关键字
String keyword = request.getParameter("keyword");
%>
<div class="tool">
<form action="http://localhost:8080/book_shop/book">
<input type="hidden" name="op" value="queryByPage">
<input type="hidden" name="page" value="1">
<input type="text" name="keyword" value="<%=keyword%>" placeholder="请输入书名关键字">
<input type="submit" value="搜索">
</form>
</div>
<div class="main">
<div class="left"></div>
<div class="right">
<%--获取servlet中保存的数据--%>
<%
int maxPage = (Integer) request.getAttribute("maxPage");
List<BookInfo> list = (List) request.getAttribute("list");
for (BookInfo book : list) {
%>
<div>
<a href="http://localhost:8080/book_shop/book?op=findById&id=<%=book.getBookId()%>">
<img src="/book_shop/pages/imgs/<%=book.getBookImg()%>">
</a>
<p class="info"><%=book.getBookAuthor()%>:《<%=book.getBookName()%>》</p>
<p><span class="price">¥<%=book.getBookPrice()%></span><span
class="oldPrice">¥<%=book.getBookPrice()%></span></p>
</div>
<%}%>
</div>
</div>
<div class="pageTool">
<%
/*获取当前页*/
int curPage = Integer.parseInt(request.getParameter("page"));
/*下一页*/
int nextPage = curPage == maxPage ? maxPage : curPage + 1;
/*上一页*/
int prevPage = curPage == 1 ? 1 : curPage - 1;
%>
<a href="http://localhost:8080/book_shop/book?op=queryByPage&keyword=<%=keyword%>&page=1">首页</a>
<a href="http://localhost:8080/book_shop/book?op=queryByPage&keyword=<%=keyword%>&page=<%=prevPage%>">上一页</a>
<a>共<%=maxPage%>页/第<%=curPage%>页</a>
<a href="http://localhost:8080/book_shop/book?op=queryByPage&keyword=<%=keyword%>&page=<%=nextPage%>">下一页</a>
<a href="http://localhost:8080/book_shop/book?op=queryByPage&keyword=<%=keyword%>&page=<%=maxPage%>">尾页</a>
</div>
</body>
</html>
四大作用域对象
作用域:共享数据的区域:
如request就是一个作用域对象,request.setAttributr(String str,Object obj)在请求作用域中保存数据。
request.getAttribute(String str)在请求作用域中获取数据。
pageContext
当前页对象,共享数据的范围为当前页面 如果不在一个页面,数据无法读取。
request:
请求对象,共享数据的范围为一次请求。只要请求不改变(不重定向)数据一直保存在请求对象中。
session
会话对象,共享数据的范围为指定的会话时长。 默认会话超时为30min 表示如果30分钟没有对该站点进行访问,自动销毁会话。
application;
应用程序(项目)对象。 共享数据的范围为整个项目中
作用域范围
由大到小:application>session>request>pageContext
以上的作用域对象,都有这几个方法。
作用域对象的使用:
在jsp页面中使用
在jsp中,作用域对象为内置对象,可以直接通过单词使用:
p1.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--在JSP中,作用域对象属于内置对象,无需定义,可以直接使用--%>
<%--向四个作用域中保存数据--%>
<%
pageContext.setAttribute("str","保存在pageContext中的字符串");
request.setAttribute("str","保存在request中的字符串");
session.setAttribute("str","保存在session中的字符串");
application.setAttribute("str","保存在application中的字符串");
%>
<%--当前页中获取数据的情况--%>
<h1><%=pageContext.getAttribute("str")%></h1>
<h1><%=request.getAttribute("str")%></h1>
<h1><%=session.getAttribute("str")%></h1>
<h1><%=application.getAttribute("str")%></h1>
<%--使用a标签跳转,相当于重定向--%>
<%--session和application中的数据可以获取--%>
<a href="p2.jsp">跳转到p2</a>
<%
//销毁session后,无法读取session中保存的数据
//session.invalidate();
//移除某作用域对象中的数据
application.removeAttribute("str");
//使用请求转发跳转,除pageContext之外的作用域中保存的数据都可以获取
request.getRequestDispatcher("p2.jsp").forward(request,response);
%>
</body>
</html>
p2.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>pageContext:<%=pageContext.getAttribute("str")%></h1>
<h1>request:<%=request.getAttribute("str")%></h1>
<h1>session:<%=session.getAttribute("str")%></h1>
<h1>application:<%=application.getAttribute("str")%></h1>
<hr>
<h1>来自于servlet中保存在session里的数据:<%=session.getAttribute("msg")%></h1>
</body>
</html>
在servlet中使用:
pageContext:servlet中不会使用pageContext,它本身就是一个类,定义成员变量即可
request:使用doGet()/doPost()/service()等方法的HttpServletRequest参数即可
protected void service(HttpServletRequest req, HttpServletResponse resp){
//req即是request对象
}
session:
protected void service(HttpServletRequest req, HttpServletResponse resp){
//通过req对象调用getSession()方法获取session对象
HttpSession session = req.getSession();
}
application
protected void service(HttpServletRequest req, HttpServletResponse resp){
//通过getServletContext()方法获取application对象
ServletContext application = getServletContext();
}
总结:
- pageContext对象用于jsp页面中,保存的数据只能在当前页面中使用。
- request对象常用于servlet中保存查询后的集合。使用请求转发跳转到jsp页面中输出集合
- session对象常用于登录后保存登录的用户,在其他页面中共享用户对象
- application对象保存共享于整个项目中的数据。
Ajax
Ajax=Asynchronous JavaScript and XML(异步的JavaScript 和XML)
AJAX最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容
任何的浏览器都支持ajax,通过原生的js使用ajax不方便,这里使用jquery封装后的ajax
使用
1.在页面引入jquery文件
<script src="jquery文件路径"></script>
2.给某个节点绑定事件后使用ajax提交数据
<script>
$("#btn").click(()=>{
$.ajax({
//访问的地址
url:'URL地址',
//提交的数据,以键值对的形式提交,也可以提交单个数据
data:{
id:123,
op:"xxx"
},
//提交方式
type:"post",
//访问成功后的回调函数
succuss:(res)=>{//res为访问URL地址后的返回值
},
//访问失败后的回调函数
error:(res)=>{}
});
});
</script>
登录
dao层:
public Customer findByPhoneAndPwd(String phone, String password) {
String sql = "select * from customer where phone=? and password=?";
conn = DBHelper.getConn();
try {
pst = conn.prepareStatement(sql);
pst.setString(1, phone);
pst.setString(2, password);
rs = pst.executeQuery();
while (rs.next()) {
int id = rs.getInt(1);
String nickName = rs.getString(4);
String trueName = rs.getString(5);
String address = rs.getString(6);
return new Customer(id, phone, password, nickName, trueName, address);
}
} catch (SQLException e) {
System.out.println("登录异常" + e);
} finally {
DBHelper.release(conn, pst, rs);
}
return null;
}
service层
public Customer login(String phone, String password) {
return customerDao.findByPhoneAndPwd(phone, password);
}
servlet层:
case "login":
String phone = req.getParameter("phone");;
String password = req.getParameter("password");
Customer login = customerService.login(phone, password);
if (login != null) {
//登录成功后,创建购物车对象,保存到session中
Cart cart = new Cart();
session.setAttribute("cart", cart);
//将登录的用户保存在session中
session.setAttribute("customer", login);
//跳转到查询首页数据的servlet
resp.sendRedirect("http://localhost:8080/book_shop/book?op=queryByPage&page=1&keyword=");
} else {
//错误时,给前端反馈的字符串
resp.getWriter().print("error");
}
break;
页面ajax部分
$("#sub").bind("click",()=>{
$.ajax({
url:"http://localhost:8080/book_shop/customer",//提交的路径
data:{//提交的数据data:{k:v,k:v} data:xxx
op:"login",
phone:$(".inp[name=phone]").val(),
password:$(".inp[name=password]").val()
},
type:"post",//提交方式
success:(res)=>{
if(res=="error"){
alert("用户名或密码错误")
}else{
location.href="http://localhost:8080/book_shop/book?op=queryByPage&page=1&keyword=";
}
}
});
})
注册
dao层:
/*
* 检测电话是否存在
* */
public int phoneExists(String phone) {
//查询该电话出现的次数
String sql = "select count(id) from customer where phone=?";
conn = DBHelper.getConn();
try {
pst = conn.prepareStatement(sql);
pst.setString(1, phone);
rs = pst.executeQuery();
//返回查询到的结果
if (rs.next()) {
return rs.getInt(1);
}
} catch (SQLException e) {
System.out.println("检测电话是否存在" + e);
} finally {
DBHelper.release(conn, pst, rs);
}
return 0;
}
/*真正添加*/
public int insert(Customer customer) {
String sql = "insert into customer values(null,?,?,?,?,?)";
conn = DBHelper.getConn();
try {
pst = conn.prepareStatement(sql);
pst.setString(1, customer.getPhone());
pst.setString(2, customer.getPassword());
pst.setString(3, customer.getNickName());
pst.setString(4, customer.getTrueName());
pst.setString(5, customer.getAddress());
return pst.executeUpdate();
} catch (SQLException e) {
System.out.println("注册异常" + e);
} finally {
DBHelper.release(conn, pst, rs);
}
return 0;
}
service层:
/*
* 注册业务流程
* 根据电话查询是否存在,不存在则添加,存在返回false
* */
public boolean register(String phone, String password) {
//检测电话是否存在
if (customerDao.phoneExists(phone) == 1) {
return false;
}
//真实注册
Customer customer = new Customer();
customer.setPhone(phone);
customer.setPassword(password);
customer.setNickName("用户" + phone.substring(7));
return customerDao.insert(customer) > 0;
}
servlet层:
case "register":
//注册,注册的电话号码不能重复
String regPhone = req.getParameter("phone");;
String resPwd = req.getParameter("password");
//调用注册
if (customerService.register(regPhone, resPwd)) {
resp.getWriter().print("注册成功");
} else {
resp.getWriter().print("该手机号码已注册");
}
case "checkphone":
String checkphone=req.getParameter("phone");
resp.getWriter().print(customerService.phoneExists(checkphone));
break;
页面js部分:
<script>
/*电话文本框内容改变事件*/
$("input[name=phone]").change(function () {
$.ajax({
url: "http://localhost:8080/book_shop/customer",
data: {
op: "checkPhone",
phone: $(this).val()
},
type: "post",
success: (res) => {
if (res == "true") {
//显示提示文字
$("#warning").show();
//禁用按钮,修改按钮文件
$("#sub").attr("disabled","true");
$("#sub").val("请重新输入手机号码");
} else {
//隐藏提示文字,恢复按钮功能
$("#warning").hide();
$("#sub").removeAttr("disabled");
$("#sub").val("注册");
}
}
});
});
/*表单提交事件*/
document.forms[0].onsubmit = () => {
var phone = document.querySelector(".inp[name=phone]").value;
var password = document.querySelector(".inp[name=password]").value;
//1开头的11位数字
var regexPhone = /^1\d{10}$/;
//非空格的至少6位密码
var regexPwd = /^[^\s]{6,}$/;
if (!regexPhone.test(phone)) {
alert("电话格式有误");
return false;
}
if (!regexPwd.test(password)) {
alert("密码中不能出现空格且至少6位");
return false;
}
}
</script>
登出
servlet:
case "logout":
//销毁session
session.invalidate();
//跳转登录页
resp.sendRedirect("/book_shop/pages/login.html");
break;
EL
Expression Language 表达式语言:是为了时jsp中输出写起来更加简便,替换jsp中的<%=%>,即表达式
主要输出保存在某个作用域中的数据
特点
- 减少代码(获取对象,转换对象,获取对象属性)
如果通过"某个作用域对象.setAttribute(String str,Object obj)"方法保存的对象。在jsp页面中,如果用表达式,写为<%=str>;如果使用EL,写为${str}.会依次从pageContext-->request-->session-->application中获取指定对象,如果一旦从某个作用域中获取到了对象,就不再判断后续作用域。也可以指定从某个作用域中获取对象,如${session.str}表示从session作用域中获取对象
- 免去非空判断
如果要使用的某个对象不存在,不会输出null,而是输出空字符串""
使用
1.获取保存在作用域中的对象
- 获取保存在某个作用域中的对象:${对象名}
- 从指定的作用域中获取对象
作用域单词 | 对应作用域 | 代码 |
---|---|---|
pageScope | 当前页pageContext | ${pageScope.对象} |
requestScope | 请求request | ${requestScope.对象} |
sessionScope | 会话session | ${sessionScope.对象} |
applicationScope | 项目application | ${applicationScope.对象} |
- 输出对象的属性
${对象名.属性名}
${对象名["属性名"]}
要保证该属性有对应的get方法
- 输出对象的方法返回值
${对象名.方法名()}
举例
//在servlet中,将一个集合保存在请求对象request中
//Person有name、sex和age属性
Person p1 = new Person("a","男",20);
Person p2 = new Person("b","男",20);
Person p3 = new Person("c","女",20);
List list = new ArrayList();
list.add(p1);
list.add(p2);
list.add(p3);
req.setAttribute("list",list);
使用请求转发跳转到页面中:
<!--传统JSP中的表达式-->
<%
List list = (List)request.getAttribute("list");
//需要获取、转换后才能遍历使用
%>
<table>
<%
for(Object obj in 集合){
%>
<td><%=obj.get属性()%></td>
<%}%>
</table>
<!--使用EL和JSTL后-->
<table>
<c:forEach items="${list}" var="p">
<!--使用EL-->
<td>${p.name}</td>>
<td>${p.sex}</td>>
<td>${p.age}</td>>
</c:forEach>
</table>
2.用户获取当前项目上下文路径(根目录+项目名)
如http://localhost:8080/book_shop是一个项目上下文路径。
在JSP中使用${pageContext.request.contetxPath}获取项目上下文路径
<form action="${pageContext.request.contextPath}/book?op=xxx"></form>
<a href="${pageContext.request.contextPath}/book?op=xxx"></a>
<img src="${pageContext.request.contextPath}/pages/imgs/xxx.jpg">
<script>
$.ajax({
url:"${pageContext.request.contextPath}/book?op=xxx"
});
</script>
3.在页面中获取请求中携带的参数
可以使用EL获取表单提交或地址中?后传递的数据,使用${param.参数}表示request.getParameter("参数")方法获取的数据
在xxx.jsp中:
${param.username}获取提交的数据
注意
- web.xml版本在4.0之后,在jsp中使用EL时,默认可以识别。
- 如果JSP中无法识别EL,原样输出${}符号时,在指令(<%@%>中加入isELignored="false"表示不忽略EL)
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false"%>
- 如果在使用EL的过程中,出现PropertyNotFoundException异常,表示未发现指定属性原因:1.缺少指定属性,2.指定属性没有对应的get方法。
JSTL
java Server Page Standarded Tag Library JSP标准标签库:可以使用JSTL中特定标签,来替换JSP中常见的java代码,如循环判断等。减少JSP中java代码,提高页面的可读性。
使用:
导入jstl依赖
在jsp页面中,加入标签库指令
<!--在当前页面中使用jstl,以c为标签前缀-->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
这句话可以在使用循环时自动生成
具体使用:1.定义变量或给变量赋值
<c:set var="变量名" value="值"></c:set>
<c:set var="name" value="admin"></c:set>
2.if判断
<c:if test="判断条件">
满足时执行
</c:if>
session.setAttribute("user",用户对象);
<c:if test="${empty user}">
<span>请登录</span>
</c:if>
<c:if test="${!empty user}">
<span>${user.username}</span>
</c:if>
3.遍历list集合
//如在servlet中,将查询到的集合保存在request对象中
request.setAttribute("list",查询到的集合)
<table>
<!--<c:forEach items="要遍历的集合" var="遍历出的每个对象">-->
<c:forEach items="${list}" var="obj">
<tr>
<td>${obj.属性}</td>
</tr>
</c:foreach>
</table>
4.遍历Map集合
<%
HashMap<String,String> hm = new HashMap();
hm.put("yyds","永远单身");
hm.put("awsl","阿伟死了");
hm.put("u1s1","有一说一");
application.setAttribute("hm",hm);
%>
<c:forEach items="${hm}" var="kv">
<%--${键值对.key} 获取键 ${键值对.value} 获取值 --%>
<h2>${kv.key}--${kv.value}</h2>
</c:forEach>
使用JSTL和EL输出购物车中的信息
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page import="com.hqyj.book_shop.util.Cart" %>
<%@ page import="com.hqyj.book_shop.entity.BookInfo" %>
<%@ page import="java.util.HashMap" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
background-color: #f5f5f5;
}
.main {
width: 1240px;
height: 628px;
margin: 20px auto 20px;
background-color: #fff;
}
table {
float: left;
width: 600px;
margin-top: 50px;
margin-left: 320px;
border-collapse: collapse;
}
th, td {
width: 120px;
height: 20px;
border: 1px solid skyblue;
}
</style>
</head>
<body>
<jsp:include page="top.jsp"></jsp:include>
<div class="main">
<table>
<tr>
<th>图书编号</th>
<th>图书名称</th>
<th>图书单价</th>
<th>购买数量</th>
<th>小计</th>
<th>操作</th>
</tr>
<c:if test="${empty cart.getCart()}">
<tr>
<td colspan="6">当前购物车中无商品</td>
</tr>
</c:if>
<c:if test="${!empty cart.getCart()}">
<%--定义变量表示总合--%>
<c:set var="sumPrice" value="0"></c:set>
<c:forEach items="${cart.getCart()}" var="kv">
<tr>
<td>${kv.key.bookId}</td>
<td>${kv.key.bookName}</td>
<td>${kv.key.bookPrice}</td>
<td>${kv.value}</td>
<%--el中可以使用算术运算--%>
<td>${kv.key.bookPrice*kv.value}</td>
<td>
<a href="${pageContext.request.contextPath}book?op=remove&bookId=${kv.key.bookId}">移除</a>
</td>
<%--每次循环将小计累加到总价中--%>
<c:set var="sumPrice" value="${sumPrice + kv.key.bookPrice * kv.value}"></c:set>
</tr>
</c:forEach>
<tr>
<td colspan="4">总计</td>
<td>${sumPrice}</td>
<td><a href="${pageContext.request.contextPath}/book?op=clear">清空购物车</a></td>
</tr>
</c:if>
</table>
</div>
</body>
</html>
带有外键关联的表的查询
表结构:主表book_type,从表book_info
实体类:
数据访问层:
方式一:定义连接查询的sql语句,适合关联字段比较少的情况
Dao层:
public class BookInfoDao{
private Connection conn;
private PreparedStatement pst;
private ResultSet rs;
/*
查询从表数据
*/
public List<BookInfo> queryAll() {
List<BookInfo> list = new ArrayList<>();
//定制连接查询的sql
String sql = "select bi.*,type_name from book_info bi,book_type bt where bi.type_id=bt.type_id";
conn = DBHelper.getConn();
try {
pst = conn.prepareStatement(sql);
rs = pst.executeQuery();
while (rs.next()) {
/*
1-9是bi.*表示从表中的数据
*/
int bookId = rs.getInt(1);
int typeId = rs.getInt(2);
int bookPrice = rs.getInt(5);
int bookNum = rs.getInt(8);
String bookName = rs.getString(3);
String bookAuthor = rs.getString(4);
String publisher = rs.getString(6);
String publishDate = rs.getString(7);
String bookImg = rs.getString(9);
//10是额外的主表中的数据type_name
String typeName= rs.getString(10);
//创建从表对象,不包含外键属性
BookInfo bookInfo = new BookInfo(bookId, typeId, bookName, bookAuthor, bookPrice, publisher, publishDate, bookNum, bookImg);
//给外键属性赋值
bookInfo.setBt(new BookType(typeId,typeName));
list.add(bookInfo);
}
} catch (SQLException e) {
System.out.println("查询所有异常" + e);
} finally {
DBHelper.release(conn, pst, rs);
}
return list;
}
}
方式二:分别编写不同表的查询,使用子查询。
两个Dao层:
public class BookTypeDao{
private Connection conn;
private PreparedStatement pst;
private ResultSet rs;
/*
根据类型编号查询类型对象
*/
public BookType findByTypeId(int typeId){
String sql = "select * from book_type where type_id=?";
conn = DBHelper.getConn();
try {
pst = conn.prepareStatement(sql);
pst.setInt(1,typeId);
rs = pst.executeQuery();
while (rs.next()) {
//返回查询到的图书类型对象
return new BookType(rs.getInt(1),rs.getString(2));
}catch (SQLException e) {
System.out.println("根据类型编号查询类型异常" + e);
} finally {
DBHelper.release(conn, pst, rs);
}
return null;
}
}
}
public class BookInfoDao{
private Connection conn;
private PreparedStatement pst;
private ResultSet rs;
//子查询时,需要用到另一个dao类中的方法
private BookTypeDao bookTypeDao = new BookTypeDao();
/*
查询从表数据
*/
public List<BookInfo> queryAll() {
List<BookInfo> list = new ArrayList<>();
//定制连接查询的sql
String sql = "select * from book_info";
conn = DBHelper.getConn();
try {
pst = conn.prepareStatement(sql);
rs = pst.executeQuery();
while (rs.next()) {
/*
1-9是bi.*表示从表中的数据
*/
int bookId = rs.getInt(1);
int typeId = rs.getInt(2);
int bookPrice = rs.getInt(5);
int bookNum = rs.getInt(8);
String bookName = rs.getString(3);
String bookAuthor = rs.getString(4);
String publisher = rs.getString(6);
String publishDate = rs.getString(7);
String bookImg = rs.getString(9);
//调用根据类型编号查询类型对象的方法
BookType bt= bookTypeDao.findByTypeId(typeId);
//创建从表实体对象
BookInfo bookInfo = new BookInfo(bookId, typeId, bookName, bookAuthor, bookPrice, publisher, publishDate, bookNum, bookImg);
//给外键属性赋值
bookInfo.setBt(bt);
list.add(bookInfo);
}
} catch (SQLException e) {
System.out.println("查询所有异常" + e);
} finally {
DBHelper.release(conn, pst, rs);
}
return list;
}
}
JSP内置对象
在jsp中,可以不用定义,就能直接使用的对象,称为内置对象。一共有9个内置对象
- pageContext:作用域对象,当前页面作用域
- request:作用域对象,请求作用域
- session:作用域对象,会话作用域
- application:作用域对象,项目作用域
- response:响应对象
- out:输出对象,相当于Servlet中的response.getWriter()方法的返回值对象。
- page:表示页面自身对象,相当于servlet中的this
- config:配置对象,获取servlet的配置信息。
- exception:异常对象,只能使用在有isErrorPage="true"声明的jsp页面中,用于获取异常对象
Session和Cookie
session:称为会话,是一个作用域,使用session.setAttribute()保存数据,使用session.getAttribute()获取数据
默认session会话有效期为30分钟,可以更改,超时或关闭浏览器,session失效。保存在session中的数据,可以在同一个站点下的不同页面中共享。
session共享数据的原理
访问任意JSP页面时,默认都会创建一个JSESSIONID(可以取消自动创建),是一段session编号,保存在一个cookie文件中
自动生成的cookie文件信息,可以看出,随着浏览器关闭,session到期
再次访问该页面时,会查询JSESSIONID是否存在,如果存在,直接使用,如果不存在,重新创建新的JSESSIONID
保存该JSESSIONID的cookie文件,有效期为浏览会话结束。所以关闭浏览器,session失效
session对象常用方法:
设置项目全局session有效时长
在web.xml中设置,单位为min
cookie:
cookie是一个用于保存数据的对象,实际是一个保存在客户本地的文件。关闭浏览器,cookie依然存在。手动清理或自动超时清理后,数据随之消失。
cookie通常用于更久地保存数据,即便关闭浏览器,也能一直存在。如登录信息、购物车信息等
cookie中保存的数据有上限(4kb),
cookie在浏览器中保存的数量也有上限(30~300根据浏览器不同略有变化)。
创建cookie
保存cookie:response.addCookie(ck);
遍历cookie:
session和cookie的对比:
-
session中保存的数据可以是任意类型,没有大小限制;cookie中保存的是键值对,单个值大小上限为4kb
-
session中保存的数据存在服务器中,cookie中保存的数据存在浏览器中
-
session到期或随着浏览器关闭而失效,cookie如果设置了有效时长,即使关闭了浏览器也会存在,到期或手动清理时失效
过滤器Filter
是一个特殊的servlet:
黑色线条表示在没有过滤器时的访问流程,红色线条表示在有过滤器后的访问流程,过滤器可以将指定的请求进行拦截,拦截后进行判断绝对是否放行执行后续的请求。通常用于权限控制或解决中文乱码。
使用:
1.新建一个类,继承HttpFilter
2.重写doFilter()方法,设置过滤条件
/*
* 自定义过滤器
* 1.创建一个类,继承HttpFilter
* 2.重写受保护的doFilter方法
* 3.配置过滤器
* */
public class MyFilter extends HttpFilter {
@Override
protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
//获取当前访问的uri(地址)
String uri = req.getRequestURI();
System.out.println(uri + "访问过滤器");
//如果登录成功后,session会保存customer。
//过滤器中判断当前session中是否存在customer
if (req.getSession().getAttribute("customer") != null) {
chain.doFilter(req, res);
return;
}
//如果session中没有customer,只能放行登录、注册页及其相关静态资源和customer模块
if (uri.contains("html") || uri.contains("jquery")|| uri.contains("customer")) {
chain.doFilter(req, res);
}else{
//否则跳转登录页
res.sendRedirect("http://localhost:8080/book_shop/pages/login.html");
}
}
}
3.配置过滤器,过滤一切请求:
<!--声明一个过滤器-->
<filter>
<!--过滤器名,无限制-->
<filter-name>myFilter</filter-name>
<!--过滤器类全限定名-->
<filter-class>com.hqyj.book_shop.filter.MyFilter</filter-class>
</filter>
<!--配置过滤器映射-->
<filter-mapping>
<!--过滤器名,与上方对应-->
<filter-name>myFilter</filter-name>
<!--过滤一切请求-->
<url-pattern>/*</url-pattern>
</filter-mapping>