jsp学习笔记,不定时更新
- 一.JSP的页面元素:
- 二. jsp内置对象(自带的,不需要new也能用)
- 三. JDBC(连接java代码和数据库 )
- 四. MVC设计模式
- 五. 三层架构
- 六. 上传和下载
- 七. EL
- 八. JSTL
- 九. 过滤器(拦截器)
- 十. 监听器
- 十一. session绑定解绑、钝化活化
- 十二. Ajax
- 十三. 连接池
- 十四. ApacheDbutils
- 十五. 元数据(MetaData)
- 十六 . 自定义标签
- 十七. 集群
一.JSP的页面元素:
html、java代码(脚本Scriptlet)、指令、注释
1. 脚本Scriptlet
a. <% java语句;局部变量%>
b. <%! 方法;全局变量%>
c. <%= 输出表达式%>
= <%out.print("输出内容");%>
(tomcat中默认,Maven会报错但是可运行)
ps:out.println无法换行,要想换行:
ps:一般而言,修改web.xml、配置文件、java ,需要重启服务
但如果修改jsp、html、css、js,不需要重启
2.指令
JSP指令用来设置整个JSP页面相关的属性,如网页的编码方式和脚本语言。
语法格式如:<%@ page attribute="value" %>
JSP中的三种指令标签:
例如:<%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.Date"%>
3.注释
a. html注释<!-- 注释-->
,可以被客户在浏览器查看源码看到
b. jsp注释<%–注释–%>
c. java注释//注释
或/*注释*/
二. jsp内置对象(自带的,不需要new也能用)
jsp共有九个内置对象:request/out/pageContext/response//session/application/config/page/exception
1.out:输出对象,
向客户端输出内容
2.request:请求对象,
储存客户端向服务器发出的请求信息
a.常见方法:
- String getParameter(String name):根据请求字段名key,返回字段值value
- String[ ] getParameterValues(String name):根据请求字段名key,返回多个字段值value,常见的有:checkbox(多选按钮)
- setCharacterEncoding(“编码格式utf-8”),设置请求的编码,默认值看版本
- getRequestDispatcher(“b.jsp”).forward(request,response);请求转发的方式跳转页面 A —》B
- ServletContext getServerContext();获取项目的ServletContext对象
b.示例:
register.jsp👇
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="show.jsp">
用户名:<input type="text" name="uname" /><br/>
密码:<input type="password" name="upwd" /><br/>
年龄:<input type="text" name="uage" /><br/>
爱好:<br/>
<input type="checkbox" name="uhobbies" value="足球"/>足球、
<input type="checkbox" name="uhobbies" value="篮球"/>篮球、
<input type="checkbox" name="uhobbies" value="游戏"/>游戏<br/>
<input type="submit" value="注册">
</form>
</body>
</html>
show.jsp👇
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>展示</title>
</head>
<body>
<%
//设置编码
request.setCharacterEncoding("utf-8");
String name = request.getParameter("uname");
int age = Integer.parseInt(request.getParameter("uage"));
String upwd = request.getParameter("upwd");
String[] hobbies = request.getParameterValues("uhobbies");
%>
注册成功,信息如下:<br/>
姓名:<%=name %><br/>
年龄:<%=age%><br/>
密码:<%=upwd%><br/>
爱好:
<%
if (hobbies != null){
for (String hobby : hobbies) {
out.print(hobby + " ");
}
}
%>
</body>
</html>
http://localhost:8081/show.jsp?uname=123&upwd=q23&uage=1233&uhobbies=%E8%B6%B3%E7%90%83&uhobbies=%E7%AF%AE%E7%90%83&uhobbies=%E6%B8%B8%E6%88%8F
输入以上地址一样可以访问show.jsp,不需要register.jsp。
method="get"方式 、地址栏 、超链接 请求方式都是GET提交
c.post与get请求方式的区别
GET 在地址栏显示请求信息 POST不显示
文件上传必须是POST
d.统一请求的编码 request
1.get方式请求 如果出现乱码,解决:
i. 统一每一个变量的编码(不推荐)
ii. 修改server.xml
2.post方式请求 如果出现乱码,解决:
3.response:相应对象
a.response的方法:
返回值 | 方法 | 用处 |
---|---|---|
void | addCookie(Cookie cookie); | 服务端向客户端增加一个 cookie |
void | sendRedirect(“a.jsp”); | 重定向(页面跳转的一种方式) |
void | setContetType(String type); | 设置服务端响应的编码 |
. | . | . |
. | . | . |
. | . | . |
b.示例:
response.sendRedirect(“a.jsp”);重定向,数据丢失,地址栏改变
request.getRequestDispatcher(“a.jsp”);请求转发,数据还在,地址栏没变化
c.请求转发和重定向的区别:
具体可以看:请求转发和重定向
4.1.Cookie(客户端,不是内置对象):
由服务端产生再发送给客户端保存,相当于本地缓存。
提高访问效率,安全性差
a.生成Cookie :name=value
javax.servlet.http.Cookie
public Cookie ( String name , String value)
String getName()
String getValue()
void setMaxAge(int expiry);最大有效期(秒)
b.发送Cookie
1、服务器准备Cookie:response.addCookie(Cookie cookie)
2、转发Cookie:页面跳转(转发、重定向)
3、客户端获取Cookie:request getCookies();
要点:
a、获取cookie都是获取全部的,不能单独获取
b、服务端增加cookie:response对象 ; 客户端获取cookie:request对象
c.示例
Cookie.jsp👇
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>模拟服务端增加cookie</title>
</head>
<body>
<%
// 生成cookie
Cookie cookie1 = new Cookie("name","zs");
Cookie cookie2 = new Cookie("pwd","abc");
// 添加cookie
response.addCookie(cookie1);
response.addCookie(cookie2);
// 从页面跳转到客户端(转发/重定向)
response.sendRedirect("result.jsp");
%>
</body>
</html>
result.jsp👇
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>模拟客户端接收cookie</title>
</head>
<body>
<%
// 客户端接收所有的cookie
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
out.print(cookie.getName() + "--"+cookie.getValue() + "<br>");
}
%>
</body>
</html>
访问http://localhost:8081/Cookie.jsp后重定向自动跳转到result.jsp👇
可以看到除了自己设置的两个cookie外还有一个jessionid的cookie为系统自动产生
心态炸裂!这里两个小时的内容因为测试cookie功能,全被删除了!!!都是心血!!心好累啊!再简单再写一点点吧
😭
4.2 session:服务端(内置对象):
一次会话 | 👇 |
---|---|
浏览网站 | 开始→关闭 |
电子邮件 | 浏览→写邮件→发邮件→退出 |
a.session的机制
客户端第一次请求服务端时,(先匹配JSESSIONID - sessionID)
- 服务端会产生一个session对象(用于保存该客户的信息) ;
并且每个session对象都会有一个唯一的 sessionId( 用于区分其他session) ; - 服务端由会产生一个cookie,并且 该cookie的name= JSESSIONID,value=服务端sessionId的值;
- 然后服务端会在响应客户端的同时将该cookie发送给客户端,至此客户端就有了一个cookie (JSESSIONID);
- 因此,客户端的cookie就可以和服务端的session一一对应( JSESSIONID - sessionID)
客户端第二次请求服务端时,服务端会先用客户端的cookie中的JSESSIONID去服务端的session中匹配sessionID,如果匹配成功,说明不是第一次访问,就无需登录
b.session的方法:
返回值 | 方法 | 用处 |
---|---|---|
String | getId() : | 获取sessionId |
void | invalidate() : | 使session失效 (退出登录、注销) |
void | setAttribute() | 后面再写 |
object | getAttribute() | 后面再写 |
void | setMaxInactiveInterval(秒) | :设置最大有效非活动时间 |
int | getMaxInactiveInterval() | :获取最大有效非活动时间 |
c.示例
login.jsp👇
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登陆页面</title>
</head>
<body>
<form action="check.jsp">
用户名:<input type="text" name="uname" /><br/>
密码:<input type="password" name="upwd"/><br/>
<input type="submit" value="登陆"><br/>
</form>
</body>
</html>
check.jsp👇
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登陆后页面</title>
</head>
<body>
<%
//设置编码
request.setCharacterEncoding("utf-8");
String name = request.getParameter("uname");
String pwd = request.getParameter("upwd");
if(name.equals("zs")&&pwd.equals("123")) {
//只有登陆成功,session中才有值
session.setAttribute("uname",name);
session.setAttribute("upwd",pwd);
//看下sessionID
System.out.println("sessionID"+session.getId());
//创建发送cookie
Cookie cookie = new Cookie("uname",name);
response.addCookie(cookie);
//设置有效非活动时间(秒)
session.setMaxInactiveInterval(20);
//登陆成功转到欢迎页面
request.getRequestDispatcher("welcome.jsp").forward(request,response);
}else {
//登陆失败,返回登陆页面
response.sendRedirect("login.jsp");
}
%>
</body>
</html>
a.jsp👇
<html>
<head>
<title>其中一个页面</title>
</head>
<body>
<%
out.print(session.getAttribute("uname"));
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if (cookie.getName().equals("JSESSIONID")) {
System.out.println("JSESSIONID"+"--"+cookie.getValue());
}
}
%>
</body>
</html>
invalidate.jsp👇
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>注销功能</title>
</head>
<body>
<%
//session失效
session.invalidate();
response.sendRedirect("login.jsp");
//session.removeAttribute("uname");
%>
</body>
</html>
welcome.jsp👇
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>欢迎页面</title>
</head>
<body>
欢迎您:
<%
String name = (String) session.getAttribute("uname");
//如果用户没有通过登陆访问welcome.jsp,则name为null
//没有登陆跳回登陆页面
if (name != null) {
out.print(name);
System.out.println();
%>
<a href="invalidate.jsp">注销</a>
<%
} else {
response.sendRedirect("login.jsp");
}
%>
</body>
</html>
d. cookie和session的区别 (!)
区别 | session | cookie |
---|---|---|
保存的位置 | 服务端 | 客户端 |
安全性 | 较为安全 | 较不安全 |
保存的内容 | object | String |
5. application:(全局对象)
a.方法
返回值 | 方法 | 用处 |
---|---|---|
String | getContextPath(): | 虚拟路径 |
String | getRealPath(String name): | 绝对路径(虚拟路径相相对的绝对路径) |
虚拟路径、绝对路径👇
b.示例
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>显示页面</title>
</head>
<body>
<%="当前项目的虚拟路径:" + application.getContextPath() + "<br>"%>
<%="虚拟路径相对应的绝对路径:" + application.getRealPath("/applicationDemo") + "<br>"%>
</body>
</html>
6.1pageContext:
6.2 四种范围对象
a. 对比
四种范围对象 | (小->大) | 有效范围 |
---|---|---|
pageContext | JSP页面容器 | 当前页面有效 |
request | 请求对象 | 同一次请求有效 |
session | 会话对象 | 同一次会话有效 |
appliation | 全局对象 | 全局有效(整个项目有效) |
尽量使用最小范围
- pageContext 当前页面有效(页面跳转后无效)
- request 同一次请求有效;其他请求无效(请求转发后有效; 重定向后无效)
- session 同一次会话有效 (无论怎么跳转,都有效;关闭/切换浏览器后无效;从登陆->退出之间全部有效
- application 全局变量;整个项目运行期间都有效(切换浏览器仍然有效);关闭服务、其他项目无效
尽量使用最小范围
b. 共有方法
返回值 | 方法 | 用处 |
---|---|---|
Object | getAttribute (String name) | 根据属性名 获取 属性值 |
void | setAttribute(String name, 0bject obj) | 根据属性名 设置 属性值(新增,修改) |
void | removeAttribute(String name): | 根据属性名 删除 对象 |
7.config:
8.page:
9. exception:
三. JDBC(连接java代码和数据库 )
1. 概念
Java - DataBase - Connectivity
Java — 数据库 ------ 连接对象
可以为多种关系型数据库DBMS 提供统一的访问方式
目的:用java操作数据库
- JDBC API:提供各种操作访问接口,Connection、Statement、PreparedStatement、ResultSet
- JDBC DriverManager:管理不同的数据库驱动
- 各种数据库驱动(jar包):各种相应的数据库厂商提供(第三方公司提供),作用:连接、直接操作数据库
2. JDBC API
通过下列 类/接口 实现:
DriverManager :管理JDBC驱动
Connection:连接
Statement ( PreparedStatement ):增删改查
CallableStatement:调用数据库中的 存储过程/存储函数
Result:就是返回的结果集
a. Connection产生操作数据库的对象
- Connection产生Statement对象:createStatement();
- Connection产生PreparedStatement对象:prepareStatement();
- Connection产生CallableStatement对象:prepareCall();
b. 产生的对象怎么操控数据库的
Statement操作数据库
- 增删改:statement.executeAdd();
- 查询:statement.executeQuery();
PreparedStatement操作数据库
public interface PreparedStatement extends Statement {
因为继承接口,Statement有的PreparedStatement都有
- 增删改:statement.executeAdd();
- 查询:statement.executeQuery();
比statement更多的赋值操作
ResultSet保存结果集
select * from xxx
👆 * = ResultSet
方法 | 功能 |
---|---|
resultSet.next(); | 光标下移,判断是否有下一条数据:T/F |
resultSet.previous(); | 光标上移,判断是否有上一条数据:T/F |
resultSet.getxxx(字段名/位置); | 获取具体的字段值 |
c. PreparedStatement 和 Statement使用时的区别
PreparedStatement | Statement |
---|---|
sql(可能存在占位符?) | sql |
在创建PreparedStatement对象时候,将sql语句预编译PreparedStatement(sql) | 无预编译 |
executeUpdate() | executeUpdate(sql) |
setxxx()替换占位符 | 无 |
防止sql注入 | 会被sql注入 |
推荐使用PreparedStatement,有预编译,当需要重复录入数据时,PreparedStatement只需要预编译1次,而Statement需要编译相对多次
什么是sql注入?
例: | xxxx为任意值 |
---|---|
用户名: | xxxx ’ or 1=1 – |
密码: | xxxx |
select count(*) from login where uname = '"+name+" 'and upwd = '"+pwd+"'
结合起来即为:
select count(*) from login where uname = 'xxx ' or 1=1 --' and upwd = '"+pwd+"'
账号部分:因为or 1=1
永远为真;
密码部分:因为--
而被当成注释了
d. CallableStatement(调用储存过程、储存函数)
***connection.prepareCall(储存过程 / 储存函数) ***
d.1 调用储存过程、储存函数的区别
存储过程:(无返回值,用out参数代替) | 存储函数:(有返回值return) |
---|---|
{ call 存储过程名 (参数列表) } | { ?= call存储函数名 (参数列表) } |
d.2 JDBC调用 存储过程 的步骤
- 创建CallableStatement对象
-
调用CallableStatement对象
-
用set方法处理输入参数值(从1开始)
-
设置输出参数的类型 (无返回值这步省略)
-
cstmt.execute();
执行
-
获取返回值(无返回值这步省略)
存储过程示例:
sql语句👇
-- 1 + 1 -> 2
create or replace procedure addTwoNum (num1 in number,num2 in number,result out number)
as
begin
result := num1 + num2;
end;
/
JDBC_CallableStatement.java👇
import java.lang.reflect.Type;
import java.sql.*;
/**
* @Author: qsX
* @Time: 2020-04-27 16:22
* @Description:JDBC访问Oracle具体步骤(CallableStatement)
*/
public class JDBC_CallableStatement {
private static final String URL = "jdbc:oracle:thin:@localhost:1521:ORCL";
private static final String USERNAME = "scott";
private static final String PWD = "tiger";
/**
* @Description 调用存储过程
* @Param []
* @Return void
*/
public static void invokeProcedure() {
Connection connection = null;
Statement statement = null;
CallableStatement cstmt = null;
try {
// a. 导入驱动,加载具体的驱动类
Class.forName("oracle.jdbc.OracleDriver");//加载具体的驱动类
// b. 与数据库建立连接
connection = DriverManager.getConnection(URL, USERNAME, PWD);
// c. 发送sql,执行(增删改)
//存储过程:{ call存储过程名 (参数列表) }
cstmt = connection.prepareCall("{ call addTwoNum (?,?,?) }");
cstmt.setInt(1,10);
cstmt.setInt(2,10);
//设置输出参数的类型
cstmt.registerOutParameter(3, Types.INTEGER);
cstmt.execute();//num1+num2,execute()之前设置输入值、输出参数类型。之后接收输出参数
//获取返回值
int result = cstmt.getInt(3);
// d. 处理结果集(查询)(可选操作,没有返回就忽略)
System.out.println("result=" + result);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
//执行完后关闭
if (cstmt != null) {
cstmt.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
invokeProcedure();
}
}
结果👇
d.3 JDBC调用 存储函数 的步骤
- 在调用存储过程的基础上,将格式改变
- 参数索引的顺序也要改变
存储函数示例:
-- 1 + 1 -> 2
create or replace function addTwoNumfunction (num1 in number,num2 in number)
return number
as
result number;
begin
result := num1 + num2;
return result;
end;
/
invokeFunction()👇
/**
* @Description 调用存储函数
* @Param []
* @Return void
*/
public static void invokeFunction() {
Connection connection = null;
Statement statement = null;
CallableStatement cstmt = null;
try {
// a. 导入驱动,加载具体的驱动类
Class.forName("oracle.jdbc.OracleDriver");//加载具体的驱动类
// b. 与数据库建立连接
connection = DriverManager.getConnection(URL, USERNAME, PWD);
// c. 发送sql,执行(增删改)
//存储函数:{ ? = call存储函数名 (参数列表) }
cstmt = connection.prepareCall("{ ? = call addTwoNumfunction (?,?) }");
cstmt.setInt(2,10);
cstmt.setInt(3,10);
//设置输出参数的类型
cstmt.registerOutParameter(1, Types.INTEGER);
cstmt.execute();//num1+num2,execute()之前设置输入值、输出参数类型。之后接收输出参数
//获取返回值
int result = cstmt.getInt(1);
// d. 处理结果集(查询)(可选操作,没有返回就忽略)
System.out.println("result=" + result);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
//执行完后关闭
if (cstmt != null) {
cstmt.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
3. JDBC访问数据库的具体步骤
a. 导入驱动,加载具体的驱动类
数据库驱动:
数据库 | 驱动jar | 具体驱动类 | 连接字符串 |
---|---|---|---|
Oracle | ojdbc-x.jar | oracle.jdbc.OracleDriver | jdbc:oracle:thin:@localhost:1521:ORCL |
MySQL | mysql-connector-java-x.jar | com.mysql.jdbc.Driver | jdbc:mysql://localhost:3306/数据库实例名 |
Sqlsever | sqljdbc-x.jar | com.microsoft.sqlserver.jdbc.SQLSeverDriver | jdbc:microsoft:sqlsever:1443;databasename=数据库实例名 |
Oracle驱动jar安装到Maven库
- Oracle官方宣布的Oracle数据库11g的驱动jar包是ojdbc6.jar
ojdbc6.jar下载地址:https://www.oracle.com/database/technologies/jdbcdriver-ucp-downloads.html - 进入ojdbc6.jar目录看见MANIFEST.MF文件,打开后查看具体版本号,下步使用
- 打开cmd窗口,执行maven命令:
mvn install:install-file -Dfile==F:/Java/jar/ojdbc6.jar -DgroupId=com.oracle -DartifactId=ojdbc6 -Dversion=11.2.0.4.0 -Dpackaging=jar -DgeneratePom=true
- pom.xml依赖引入
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.4.0</version>
</dependency>
Mysql驱动jar安装到Maven库
- pom.xml依赖引入
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
b. 与数据库建立连接
- 通过
DriverManager.getConnection(URL, USERNAME, PWD)
👇
URL | 连接字符串中有:数据库名、IP、端口 |
USERNAME | 用户名 |
PWD | 密码 |
- 写完
DriverManager..getConnection(URL, USERNAME, PWD);
后ALT+ENTER👇
c. 发送sql语句,执行
c.1 Statement
- 创建statement
connection.createStatement();
👉ALT+ENTER👇
- 发送sql语句👇
- 查询:
statement.executeQuery(sql语句)
- 增删改:
statement.executeUpdate(sql语句)
- 返回成功否👇
c.2 PreparedStatement
- 先导入sql语句,connection.prepareStatement(sql);(可预编译)
- 对之前预编译的sql语句完善
- 发送命令,返回结果
d. 处理结果集(查询)(可选操作,没有返回就忽略)
这边PreparedStatement与Statement一样
-
创建返回结果集resultset
-
通过循环得到返回的查询结果,rs.next()有值为true
获取内容也可以直接写1,2,3,但是不推荐使用。
-
注意:层层抛出异常:
除了class.forName()
抛出ClassNotFoundException
其他的方法都抛出SQLException
最后保险起见,再抛出Exception
-
关流,后开的先关,先开的后关
注意:如果没开可能会报空指针异常,所以要先排空
4.1 示例(JDBC访问Oracle具体步骤)
用Statement方式实现
import java.sql.*;
/**
* @Author: qsX
* @Time: 2020-04-25 14:22
* @Description:JDBC访问Oracle具体步骤
*/
public class JDBCDemo1 {
private static final String URL = "jdbc:oracle:thin:@localhost:1521:ORCL";
private static final String USERNAME = "scott";
private static final String PWD = "tiger";
/**
* @Description 增删改
* @Param []
* @Return void
*/
public static void update(