JavaWeb

一、JDBC 原理及访问数据库
1. JDBC
JDBC:Java DataBase Connectivity 可以为多种关系型数据库 DBMS 提供统一的访问方式,用 Java 操作数据库
JDBC 实现原理:Java 通过 JDBC 操作 JDBC DriverManager 管理不同数据驱动。通过 JDBC 的API(包括接口、方法、类)
2.JDBC API 主要功能:
三件事,具体是通过以下类/接口实现:
DriverManager:管理 jdbc 驱动
Connection:连接(通过 DriverManager 产生)
Statement (PreparedStatement):增删改查 (通过 Connection 产生)
CallableStatement:调用数据库中的 存储过程/ 存储函数 (通过 Connection 产生)
Statement、PreparedStatement、CallableStatement 都是由 Connection 产生的
Result:返回的结果集 (上面的 Statement 等产生)
ResultSet:保存结果集 select * from xxx
next ():光标下移,判断是否有下一条数据;true/false
previous ():true/false
getXxx (字段值|位置):获取具体的字段值
2.1 Connection 产生操作数据库的对象:
Connection 产生 Statement 对象:createStatement ()
Connection 产生 PreparedStatement 对象:prepareStatement ()
Connection 产生 CallableStatement 对象:prepareCall ();
2.2 Statement 操作数据库:
增删改:executeUpdate()
查询:executeQuery();
2.3 PreparedStatement 操作数据库:
public interface PreparedStatement extends Statement
因此
增删改:executeUpdate()
查询:executeQuery();
赋值操作 setXxx();
2.4 PreparedStatement 与 Statement 在使用时的区别:
Statement:
sql
executeUpdate(sql)
PreparedStatement:
sql(可能存在占位符 ? )
在创建 PreparedStatement 对象时,将 sql 预编译 prepareStatement(sql)
executeUpdate()
setXxx()替换占位符
推荐使用 prepareStatement :原因如下:
编码更加简便(避免了字符串的拼接)
提高性能(因为有预编译操作,预编译只执行一次)
安全(可以有效防止 sql 注入)
sql 注入:将客户端输入的内容 和 开发人员的 SQL 语句 混为一体
2.5 CallableStatement:调用 存储过程、存储函数
connection.prepareCall(存储过程或存储函数名)
参数格式:
存储过程(无返回值 return,用 Out 参数代替):
{call 存储过程名(参数列表)}
存储函数(有返回值):
{ ? = call 存储函数名(参数列表)}
2.5.1 存储过程
构造数据库存储过程
create or replace procedure addTwoNum ( num1 in number, num2 in number, result out number)
as
begin
result:=num1+num2;
end;
/
cstmt = connection.prepareCall("call addTwoNum(?,?,?)"); |
JDBC 调用存储过程的步骤:
a. 产生 调用存储过程的对象(CallableStatement) cstmt = connection.prepareCall(“call addTwoNum(?,?,?)”);
b. 通过 setXxx()处理 输出参数值 cstmt.setInt(1,..);
c. 通过 registerOutParameter();处理输出参数类型
d. cstmt.execute()执行
e. 接收输出值(返回值)getXxx()
2.5.2 存储函数
调用存储函数
create or replace function addTwoNumfunction ( num1 in number, num2 in number, result reutnr number)
return number
as
result number;
begin
result:=num1+num2;
return result;
end;
/
JDBC 调用存储函数与调用存储过程的区别:
在调用时,注意参数 “? = call addTwoNumfunction(?,?)”
3. JDBC 访问数据库的具体步骤:
a. 导入驱动,加载具体的驱动类
b. 与数据库建立连接
c. 发送 sql,执行
d. 处理结果集(查询)
4. 数据库驱动
数据库驱动 | 驱动 jar | 具体驱动类 | 连接字符串 |
---|---|---|---|
MySQL | mysql-connector-java-x.jar | com.mysql.jdbc.Driver | jdbc:mysql://localhost:3306/数据库实例名 |
使用 jdbc 操作数据库时,如果对数据库进行了更换,只需要替换:驱动、具体驱动类、连续字符串、用户名、密码
5. 用 java 实现对数据库的增删改、查
public class JDBCDemo { |
6. 总结 JDBC(模板、八股文)
try{
a. 导入驱动包、加载具体驱动类 Class.forName(“具体驱动类”)
b. 与数据库建立连接 connection = DriverManager.getConnection(…);
c. 通过 connection,获取操作数据库的对象(Statement\preparedStatement\callablestatement)
d.(查询)处理结果集 rs = pstmt.executeQuery()
while(rs.next()) { rs.getXxx(…) };
} catch (ClassNotFoundException e) {
} catch(SQLException e){
} catch(Exception e){
} finally {
// 打开顺序与关闭顺序相反
if (rs!= null) rs.close();
if (stmt != null) stmt.close();
if (connection!= null) connection.close();
}
—jdbc 中,除了 Class.forName()抛出 ClassNotFoundException,其余方法全部抛 SQLException
7. 处理 CLUB[Text]/BLOB 类型
处理稍大型数据:
a. 存储路径 E:\
通过 JDBC 存储文件路径,然后根据 IO 操作处理
b.
CLOB:大文本类型 (小说-> 数据)
BLOB:二进制
clob:
存
- 先通过 pstmt 的 ? 代替小说内容(占位符)
- 再通过 pstmt.setCharacterStream(2, reader , (int) file.length() );将上一步的 ? 替换为小说流,注意第三个参数需要是 Int 类型
取
- 通过 Reader reader = rs.getCharacterStream(“NOVEL”); 将 cloc 类型的数据保存到 Reader 对象中
- 将 Reader 通过 Writer 输出即可。
blob 与 clob 操作类似
把 CharacterStream 变为 BinaryStream
二、JSP 访问数据库
JSP 就是在 html 中嵌套 java 代码,因此 java 代码可以写在 jsp 中(<%……%>)
导包操作:java 项目:1. Jar 复制到工程中 2. 右键该 Jar :build path
Web 项目:jar 复制到 WEB-INF 的 lib 里面
核心:将 java 代码中的 JDBC 代码,复制到 JSP 中的 <% … %>
注意:如果 jsp 出现错误:The import Xxx cannot be resolved…
尝试解决步骤:
a. (可能是 JDK、tomcat 版本不一样)
b. 清空各种缓存
c. 删除之前的 tomcat,重新配置 tomcat、重启电脑等
d. 如果类之前没有包,则将该类加入包中
1. JavaBean
我们将 jsp 中登录操作的代码 转移到了 LoginDao.java;其中 LoginDao 类 就称之为 JavaBean。
JavaBean 的作用:
a. 减轻了 jsp 的复杂度
b. 提高了代码的复用率(以后任何地方的 登录操作,都可以通过调用 LoginDao 实现)
JavaBean(就是一个类)的定义:满足以下两点,就可以称为 JavaBean
a. public 修饰的类,public 无参构造
b. 所有属性(如果有)都是 private,提供 set/get (如果 boolean 则 get 可以替换 is)
使用层面,Java 分为 2 大类:
a. 封装业务逻辑的 JavaBean (Login.java 封装了登录逻辑) —-逻辑
可以将 jsp 中的 JDBC 代码,封装到 Login.java 中(Login.java)
b. 封装数据的 JavaBean (实体类,Student.java Person.java) —-数据
对应于数据库中的一张表
Login login = new Login(username , upwd); 即用 Login 对象 封装了 2 个数据(用户名 和 密码)
封装数据的 JavaBean 对应于数据库一张表(Login(name, pwd))
封装业务逻辑的 JavaBean 用于操作 一个封装数据的 JavaBean
可以发现使用,JavaBean 可以简化 代码 (jsp->jsp+java)、提供代码复用(LoginDao.java)
三、MVC 设计模式:
M:Model 模型:一个功能。用 JavaBean 实现。
V:View 视图:用于请求,以及与用户交互。使用 html js css jsp jquery 等前端
C:Controller 控制器:接受请求,将请求跳转到模型进行处理;模型处理完毕后,再将处理的结果返回给 请求处。可以用 jsp 实现,但一般建议使用 Servlet 实现控制器。
JSP->Java(Servlet)->JSP
四、Servlet
Java 类必须符合一定的 规范:
a. 必须继承 javax.servlet.http.HttpServlet
b. 重写其中的 doGet()或 doPost()方法
doGet(): 接受 并处 所有 get 提交方式的请求
doPost(): 接受 并处 所有 post 提交方式的请求
Servlet 要想使用,必须配置
Servlet2.5: web.xml
Servlet3.0: @WebServlet
Servlet2.5: web.xml
项目的根目录:web、src
<a href="WelcomeServlet">WelcomeServlet</a> 所在的jsp是在web目录中 |
因此 发出的请求 WelcomeServlet 是去请求项目的根目录。
Servlet 流程:
请求 ->
<servlet> |
Servlet3.0 与 Servlet2.5 的区别:
Servlet3.0 不需要在 web.xml 中配置,但需要在 Servlet 类的定义处之上编写
注释 @WebServlet(“url-pattern 的值”)
匹配流程:请求地址 与 @WebServlet 中的值 进行匹配,如果匹配成功,则说明请求的就是该注解所对应的类
根路径:/:
项目根目录:web src(所有的构建路径)
例如:在 web 中有一个文件 index.jsp
src 中有一个 Servlet.java
如果:index.jsp 中请求 <a href=”abc” >…</a>,则 寻找范围:既会在 src 根目录中寻找 也会在 web 中寻找
如果:index.jsp 中请求 <a href=”a/abc” >…</a>,寻找范围:现在 src 或 web 中找 a 目录,然后再在 a 目录中找 abc
/:
web.xml 中的 / :代表的是项目路径 http://localhost:8888/Servlet4Project_war_exploded/
jsp 中的 / :代表服务器根路径 http://localhost:8888/
构建路径、web:根目录
Servlet 生命周期:5 个阶段
加载
初始化:init(),该方法会在 Servlet 被加载并实例化的以后 执行
服务:service() ->doGet() doPost
销毁:destroy(),Servlet 被系统回收时执行
卸载
init():
a. 默认第一次访问 Servlet 时会被执行 (只执行一次)
b. 可以修改为 Tomcat 启动时自动执行
Ⅰ.Servlet2.5: web.xml 中
<servlet> |
Ⅱ.Servlet3.0
@WebServlet(value=”/WebcomeServlet” , loadOnStartup=1 )
service() -> doGet() doPost():调用几次,则执行几次
destroy():关闭 tomcat 服务时,执行一次。
Servlet API:
由两个软件包组成:对应于 HPPT 协议的软件包、对应于除了 HPPT 协议以外的其他软件包即 Servlet API 可以适用于 任何 通信协议
我们学习的 Servlet,是位于 javax.servlet.http 包中的类和接口,是基础 HTTP 协议。
Servlet 继承关系:
ServletConfig:接口
getServletContext():获取 Servlet 上下文对象 applicatio
String getInitParameter(String name):在当前 Servlet 范围内,获取名为 name 的参数值(初始化参数)
a. ServletContext 中的常见方法(application):
getContextPath():相对路径
getRealPath():绝对路径
setAttribute()、getAttribute()
—->
String getInitParameter(String name):在当前 web 范围内,获取名为 name 的参数值(初始化参数)
HttpServletRequest 中的方法:(同 request),例如 setAttrite()、getCookies()、getMethod()
HttpServletResponse 中的方法:同 response
使用建议:
Servlet:一个 Servlet 对应一个功能,因此 如果有增删改查 4 个功能,则需要创建 4 个 Servlet
五、三层架构:
与 MVC 设计模式的目标一致:都是为了 解耦合、提高代码复用;
区别:二者对项目理解的角度不同。
三层组成:
表示层(USL,User Show Layer;视图层)
-前台:对应于 MVC 中的 View:用于和用户交互、界面的显示
jsp js html css jquery 等 web 前端技术
代码位置:web
-后台:对应于 MVC 中 Controller,用于 控制跳转、调用业务逻辑层
Servlet(SpringMVC Struts2)
代码位置:一般位于 xxx.servlet 包中
业务逻辑层(BLL,Business Logic Layer;Service 层)
-接受表示层的请求,调用
-组装数据访问层,逻辑性的操作(增删改查,删:查+删)
一般位于 xxx.service 包(也可以成为: xxx.manager,xxx.bll)
数据访问层(DAL,Data Access Layer;Dao 层)
-直接访问数据库的操作,原子性的操作(增删改查)
一般位于 xxx.dao 包
三层间的关系:
上层 将请求传递给下层,下层处理后返回给上层
上层依赖于下层,依赖:代码的理解,就是持有成员变量
数据库是由数据访问层去访问的。
六、MVC 与三层架构的区别
七、Servlet 中使用 jsp 功能
out
out 对象在 servlet 中使用
PrintWriter out = response.getWriter(); |
session
request.getSession(); |
application
request.getServletContext(); |
八、分页 SQL
1、分页
要实现分页,必须要知道 某一页的 数据 从哪里开始 到哪里结束
页面大小:每页显示的数据量
假设每页显示 10 条数据
sqlserver/oracle:从 1 开始计数
第 n 页 开始 结束
1 1 10
2 11 20
3 21 30
n (n-1)×10+1 n×10
mysql:从 0 开始计数
0 0 9
1 10 19
2 20 29
n n×10 (n+1)×10-1
结论:
分页: 第 n 页的数据: 第(n-1)×10+1 条 — 第 n×10 条
MySQL 实现分页的 sql:
limit 从第几页开始,每页显示多少条
第 0 页
select * from student limit 0, 10;
第 1 页
select * from student limit 10, 10;
第 2 页
select * from student limit 20, 10;
第 n 页
select * from student limit n×10, 10;
MySQL 的分页语句
select * from Student limit 页数 × 页面大小,页面大小;
oracle 分页:
sqlserver/oracle:从 1 开始计数
第 n 页 开始 结束
1 1 10
2 11 20
3 21 30
n (n-1)×10+1 n×10
select * from student where sno >=(n-1)×10+1 and sno <= n×10; —此种写法的前提:必须是 Id 值连续,否则无法显示
oracle 分页查询语句:
select * from
(
select rownum r, t. from (select s. from student s order by sno asc) t
)
where r >(n-1)×10+1 and r <= n×10;
优化:
select * from
(
select rownum r, t. from (select s. from student s order by sno asc) t
where rownum <= n×10
)
where r >(n-1)×10+1 and r <= n×10;
SQLServer 分页:
row_number() over(字段)
select * from
(
select row_number() over (sno order by sno asc) as r, * from student
where r <= n×10
)
where r >(n-1)×10+1 and r <= n×10;
SQLServer 分页 sql 与 oralce 分页 sql 的区别:1.rownum, row_number() 2.oracle 需要排序
分页实现:
5 个变量(属性)
1.数据总数 100 103 (查数据库,select count(*)…)
2.页面大小(每页显示的数据条数)20 (用户自定义)
3.总页数
总页数 = 100 / 20 = 数据总数 / 页面大小
页面数 = 103 / 20 = 数据总数 / 页面大小 + 1
—>
总页数 = 数据总数 % 页面大小 == 0 ? 数据总数 / 页面大小:数据总数 / 页面大小+1;
4.当前页(页码) (用户自定义)
5.当前页的对象集合(实体类集合):每页所显示的所有数据(10 个人信息)
List
// 分页帮助类 |
九、文件上传
1.上传
apache:从 https://mvnrepository.com 下载
commons-fileupload.jar 组件
commons-fileupload.jar 组件 依赖于 commons-io.jar
a.引入两个 jar 包
b.
代码:
前台 jsp:
<form action="UploadServlet" method="post" enctype="multipart/form-data"> |
表单提交方式必须是 post
在表单中必须增加一个属性 enctype=”multipart/form-data”
后台 servlet:
注意的问题:
上传到目录 upload;
1.如果修改代码,则在 tomcat 重新启动时 会被删除
原因:当修改代码的时候,tomcat 会重新编译一份 class 并且重新部署(重新创建各种目录)
2.如果不修改代码,则不会删除
原因:没有修改代码,class 仍然是之前的 class
因此,为了防止 上传目录丢失:a.虚拟路径 b.直接更换上传目录 到非 tomcat 目录
限制上传:类型、大小
类型:
String fileName = item.getName(); // a.txt a.docx a.png |
进行判断文件后缀名,如果不是指定照片格式,则终止。
大小:
DiskFileItemFactory factory = new DiskFileItemFactory(); |
注意:对文件的限制条件 写在 parseRequest 之前
2.下载
不需要依赖任何 jar 包
a.请求(地址 a form),请求 Servlet b.Servlet 通过文件的地址 将文件转为输入流 读到 Servlet 中
c.通过输出流 将 刚才 已经转为输入流的文件 输出给用户
注意:下载文件 需要设置 两个响应头
// 下载文件:需要设置 消息头 |
下载时,文件名乱码问题:
Edge 解决方法
URLEncoder.encode(fileName,"UTF-8") |
FireFox、等其他浏览器需用 if 判断处理
// 对于不同浏览器,进行不同的处理 |
十、EL 表达式语言
EL:Expression Language,可以替代 JSP 页面中的 JAVA 代码
servlet(增加数据)-》jsp(显示数据)
传统的 在 JSP 中用 java 代码显示数据的弊端:类型转化、需要处理 null、代码参杂 -》EL
EL 操作符:
点操作符 . —-使用方便
${requestScope.student.sno} |
中括号操作符 [“ “] 或 [‘ ‘] —-功能强大:可以包含特殊字符(. 、-),获取变量值,获取数组的元素
${requestScope["student"]["sno"]} |
当获取变量值时,不加引号,例如存在变量 name,则可以用
${requestScope[name]} |
可以获取数组的元素,例如定义一个数组 String[] hobbies = new String[] {“football”,”pingpang”,”basketball”};
${requestScope.hobbies[0]} |
获取 map 属性
Map<String,Object> map = new HashMap<>(); |
关系运算符 逻辑运算符
${3>2}、${3 gt 2}<br/> // 输出结果 true、true |
Empty 运算符:判断一个值 null、不存在 —> true
${empty requestScope["my-name"]}<br/> //输出结果 false |
EL 表达式的隐式对象:
隐式对象:(不需要 new 就能使用的对象,自带的对象)
a. 作用域的访问对象(EL 域对象):
pageScope、requestScope、sessionScope、applicationScope
如果不指定域对象,则会默认根据 从小到大的顺序 依次取值
b. 参数访问对象:获取表单数据(超链接中传的值、地址栏的值)
(request.getParameter()、 request.getParamterValues() )
${param} ${paramValues}
c. JSP 隐式对象:pageContext
在 JSP 中可以通过 pageContext 获取其他的 jsp 隐式对象;
因此如果要在 EL 中使用 JSP 隐式对象,就可以通过 pageCount 间接获取
可以使用此方法,级联获取对象
${pageContext.request.serverPort} |
使用方法: ${pageContext.方法名去掉() 和 get 并且将首字母小写}
例如要拿 getRequset( ) ${pageContext.request}
十一、JSTL:
比 EL 更加强大,但需要两个 jar 包:jstl-1.2.jar、standard-1.1.2. jar
引入 tablib:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> |
其中 prefix=”c” :前缀
核心标签库:通用标签库、条件标签库、迭代标签库
a. 通用标签库:
< c:set> 赋值
- 在某个作用域之中(4 个范围对象),给某个变量赋值
<%-- |
给普通对象赋值
在某个作用域之中(4 个范围对象),给某个对象的属性赋值(此种方法,不能指定 scope 属性)
给 map 对象赋值
注意 < c:set> 可以给不存在的变量赋值(但不能给不存在的对象赋值)
< c:out>
- 显示(default=”当 value 为空时,显示的默认值”)
传统EL:${requestScope.student} <br/> |
- 根据 escapexml 选择显示超链接方式
true:<c:out value='<a href="https://www.baidu.com">百度</a>' escapeXml="true" /> |
< c:remove> 删除属性
<c:remove var="a" scope="request" /> |
b. 条件标签库:选择
if(boolean)
单重选择
< c:if test=”” >
<c:if test="${10>2}" var="result" scope="request" > |
多重选择
if else if… esle if ….
< c:choose >
< c:when test=”…”> < /c:when>
< c:when test=”…”> < /c:when>
< c:otherwise> < /c:otherwise>
< /c:choose >
注意:在使用 test=“” 一定要注意是否有空格
例如:test=“${10>2}” true
test=“${10>2} ” 非 true
c. 迭代标签库:循环
for(int i=0; i<5; i++ )
<c:forEach begin="0" end="5" step="1" varStatus="status"> // 0-5,varStatus显示下标次数 |
for(String str : names)
<c:forEach var="name" items="${requestScope.names}" > |
十二、过滤器与监听器
过滤器:
实现一个 Filter 接口
init()、destroy()原理、执行时机同 Servlet
配置过滤器,类似 Servlet
通过 doFilter()处理拦截,并且通过 chain.doFilter(request, response); 放行请求
public class MyFilter implements Filter { // 过滤器 |
filter 映射
只拦截 访问 Myservlet 的请求
<url-pattern>/MyServlet</url-pattern> |
拦截一切请求(每一次访问,都会被拦截)
<url-pattern>/*</url-pattern> |
通配符
dispatcher 请求方式:
REQUEST:拦截 HTTP 请求 get post 常用
FORWARD:只拦截 通过 请求转发方式的请求 常用
INCLUDE:只拦截通过 request.getRequestDispatcher(“”).include()、通过< jsp:include page=”…”/>发出的请求
ERROR:只拦截< error-page>发出的请求
过滤器中 doFilter 方法参数:ServletRequest
在 Servlet 中的方法参数:HttpServletRequest
过滤器链
可以配置多个过滤器,过滤器的先后顺序 是由 web.xml 中
监听器:
开发步骤:
- 编写监听器,实现接口
- 配置 web.xml
监听对象:request session application
a. 监听对象的创建和销毁
request:ServletRequestListener
session:HttpSessionListener
application:ServletContextListener
每个监听器 各自提供了 2 个方法:监听开始、监听结束的方法
b. 监听对象中属性的变更
request:ServletRequestAttributeListener
session:HttpSessionAttributeListener
application:ServletContextAttributeListener
每个监听器 各自提供了 3 个方法:新增、替换、删除属性
例如:ServletRequest
@Override |
session 对象的四种状态:绑定解绑、钝化活化
监听绑定与解绑:HttpSessionBindingListener 不需要配置 web.xml
a. session.setAttribute(“a”,xxx) 对象 a【绑定】到 sesssion 中
b. session.removeAttribute(“a”)将对象 a 从 session 中【解绑】
监听 session 对象的钝化、活化:HttpSessionActivationListener 不需要配置 web.xml
c. 钝化:内存-》硬盘
d. 活化:硬盘-》活化
如何钝化、活化:配置 tomcat 安装目录/conf/context.xml
钝化、活化的本质 就是序列化、反序列化:序列化、反序列化需要实现 Serializable
总结:钝化、活化 实际执行 是通过 context.xml 中进行配置 而进行。
活化:session 中获取某一个对象时,如果该对象不存在,则直接尝试从之前钝化的文件中获取(活化)
HttpSessionActivationListener 只是负责 在 session 钝化和活化时 予以监听.
需要实现 Serializable
十三、Ajax,JQuery 与 JSON
Ajax:
异步 js 和 xml
异步刷新:如果网页中某个地方需要修改,异步刷新可以使:只刷新需要修改的地方,而页面中其他地方保持不变。
例如:百度搜索框、视频的点赞
实现:
js:XMLHttpReques 对象
XMLHttpReques 对象的方法:
open(方法名(提交方式 get|post),服务器地址,true):与服务端建立连接
send():
get:send(null)
post:send(参数值)
setRequestHeader(header,value):
get:不需要设置此方法
post:需要设置:
a. 如果请求元素中包含了 文件上传:
setRequestHeader(”Content-Type”,”multiparty/form-data”);
b. 不包含了 文件上传
setRequestHeader(”Content-Type”,”application/x-www-form-urlencoded”);
XMLHttpReques 对象的属性:
readyState:请求状态 只有状态为 4 代表请求完毕
status:响应状态 只有 200 代表响应正常
onreadystatechange:回调函数
responseTest:响应格式为 String
responseXML:响应格式为 XML
jquery:推荐
$.ajax({
url:服务器地址,
请求方式:get|post
data:请求数据,
success:function(result,testStatus)
{
},
error:function(xhr,errorMessage,e) {
}
});
<script type="text/javascript" src="js/jquery.js"></script> |
$.get(
服务器地址,
请求数据,
function(result) {
},
预期返回值类型(string\xml)
);
$.post(
服务器地址,
请求数据,
function(result) {
},
“xml” 或 “json” 或 “text”
);
十四、JNDI 与 tomcat 连接池
1. JNDI:
java 命名与目录接口
范围对象:4 大作用域对象、4 大范围对象
pageContext < request < session < application
pageContext:定义的变量有效期为当前页面,页面跳转后失效
request:一次请求有效
session:一次会话有效
application:一次项目运行期间都有效(项目打开后,只有不关闭,永远有效)
当我们需要多个项目都有效,除了数据库等,还可以使用 JDNI
JNDI:将某一个资源(对象),以配置文件(tomcat / conf / context.xml )的形式写入;
在配置文件(tomcat / conf / context.xml )中加入:
String jndiName = "jndiValue"; |
String:写在 type 中,需要写包,java.lang.String
jndiName:元素名写在 name 中
jndiValue:赋的值写在 value 中
2. JNDI 实现步骤:
tomcat / conf / context.xml 配置:
jsp 中使用:
<% |
注意:java:comp/env/… (固定格式)
3. 连接池
常见的连接池:Tomcat-dbcp、dbcp、c3p0、druid
可以用 数据源 (javax.sql.DataSource)管理连接池 (数据源包含数据库连接池)
连接池:怎么用?
不用连接池
Class.forName();
Connection connection = DriverManager.getConnection();
使用连接池的核心:将连接的指向改了,现在指向的是数据源 而不是数据库
… -> DataSource ds = …..
Connection connection = ds.getConnection; // 指向的是数据源的连接
Tomcat-dbcp 连接池:
a. 类似 jndi,在 context.xml 中配置数据库,加入以下代码
<Resource name="student" auth="Container" type="javax.sql.DataSource"
maxActive="400" maxIdle="20" maxWait="5000" username="root" password="159357"
driverClassName="com.mysql.cj.jdbc.Driver" url="jdbc:mysql://localhost:3306/login" />
b. 在项目 web.xml 中配置:
<resource-ref> |
c. 使用数据源
更改 连接对象 Connection 的获取方式:
传统 JDBC 方式:
connection = DriverManager.getConnection(URL, USER, PWD);
数据源方式:
Context ctx = new InitialContext(); // context.xml |
Tomcat - dbcp 数据源总结:
- 配置数据源(context.xml)
- 指定数据源(web.xml)
- 用数据源:通过数据源获取 Connection
dbcp 连接池:
需要引入 jar 包:commons-dbcp-1.4.jar、commons-pool-1.6.jar
BasicDataSource、BasicDataSourceFactory
BasicDataSource 方式:硬编码方式 BasicDataSource 对象设置各种属性
// 获取dbcp方式的ds对象 |
BasicDataSourceFactory 方式:配置方式(.properties 文件,编写方式 key = value ) 常用这种
dbcpconfig.properties
public static DataSource getDataSourceWithDBCPByProperties() throws Exception { |
DataSource 是所有 sql 是所有数据源的上级类。 BasicDataSource 是 dbcp 类型的数据源
ComboPooledDataSource 是 c3p0 的数据源;
c3p0 连接池:
核心类:ComboPooledDataSource
两种方式:硬编码与配置文件
-》合二为一,通过 ComboPooledDataSource 的构造方法参数区分:如果无参——硬编码;有参——配置文件
jar 包:c3p0.jar
如果无参——硬编码
public static DataSource getDataSourceWithC3P0() throws PropertyVetoException { |
有参——配置文件(c3p0-config.xml)文件
先写配置文件:
<?xml version="1.0" encoding="UTF-8"?> |
在代码中传入参数,参数为
public static DataSource getDataSourceWithC3P0ByXml() { |
总结:
所有连接池的思路:
硬编码,某个连接池数据源的 对象 ds = new XxxDataSource();
ds.setXxx();
return ds;
配置文件, ds = new XxxDataSource(); 加载配置文件,return ds;
数据源工具类:
可将 dbcp、c3p0 整合到一个类中,方便使用。
// 连接池帮助类 |
使用方法:
十五、ApacheDBUtil
下载 commons-dbutils-1.7.jar,其中包含以下几个重点类:
DbUtils、QueryRunner、ResultSetHandler
1. DbUtils:辅助
包含了关闭连接、关闭结果、关闭提交、提交事务、传入驱动名,加载并注册 JDBC 驱动程序
2. QueryRunner:增删改、查
update()
query()
3. ResultSetHandler 的实现类
如果是查询,则需要 ResultSetHandler 接口,有很多实现类,一个实现类对应于一种 不同的查询结果类型
——Object[] {1,zs}
实现类ArrayHandler:返回结果集中的第一行数据,并用 Object[] 接收
实现类ArrayListHandler:返回结果集中的多行数据,List
——Student (1,zs)
BeanHandler:返回结果集中的第一行数据,用对象(Student)接收
// 查询单行数据(放入对象中) |
反射会通过无参构造来创建对象
BeanListHandler:返回结果集中的多行数据,List
// 查询多行数据(放入对象中) |
BeanMapHandler:可以给每个数据加个 key 1:stu , 2:stu12 , 3:stu3
// 查询单行数据(放入map对象中) |
——Map 查询的结果为 {sno=1,sname=zs }
MapHandler:返回结果集中的第一行数据
{sno=1,sname=zs}
MapListHandler:返回结果集中的多行数据
sno=1,sname=zs sno=2,sname=ls
KeyedHandler:
zs=sno=1,sname=zs,ls=sno=2,sname=ls——
ColumListHandler:把结果集中的某一列 保存到 List 中
例如: 2,ls 3,ww
结果 {ls,ww}
ScalarHandler:单值结果集
问题:查询实现类的参数问题
query(…,Object…params )
其中 Object…params 代表可变参数:既可以写单值,也可以写数组
4. apache dbutil 增删改
增:
public static void add() throws PropertyVetoException, SQLException { |
删:
public static void delete() throws PropertyVetoException, SQLException { |
改:
public static void update() throws PropertyVetoException, SQLException { |
自动提交事务 update(sql,参数);update(sql);
手动提交事务 update(connection,sql,参数);
5. 手动提交事务
例如银行转账:zs-1000;li+1000; 需要一起提交
基础知识:
①如果既要保存数据安全,又要保证性能,可以考虑ThreadLocal
ThreadLocal:可以为每个线程 复制一个副本。每个线程可以访问自己内部的副本。 别名 线程本地变量
set():给 tl 中存放一个 变量
get():给 tl 中获取变量(副本)
remove():删除副本
②对于数据库来说,一个连接对应于一个事务,一个事务可以包含多个 DML 操作(增删改)
Service(多个原子操作)—》Dao(原子操作)
update:如果给每个 dao 操作 都创建一个 connection,则 多个 dao 操作对应于多个事务;
但是 一般来说,一个业务(Service)中的多个 dao 操作 应该包含在一个事务中。
——》解决:ThreadLocal,在第一次 dao 操作时,真正的创建一个 connnection 对象,然后
在其他几次 dao 操作时,借助于 ThreadLocal 本身特性,自动将该 connection 复制多个(connection 只创建了一个,因此该 connection 中的所有操作,必然对应于同一个事务;并且 ThreadLocal 将
connection 在使用层面复制了多个,因此可以同时完成多个 dao 操作)
事务流程:开始事务(将自动事务—>手动提交)—>进行各种 DML—>正常,将刚才所有 DML 全部提交(全部成功)
—>失败(异常),将刚才所有 DML 全部回滚(全部失败)
事务的操作 全部和连接 Connection 密切相关
十六、元数据
元数据:描述数据的数据
三类:数据库元数据、参数元数据、结果集元数据
1. 数据库元数据:DataBaseMetaData
Connection——》DataBaseMetaData
public static void databaseMetaData() { |
2. 参数元数据:ParameterMetaData
pstmt——》 ParameterMetaData
getParameterCount():获取 SQL 语句中,参数占位符的个数
因为带参数的 SQL 是通过 pstmt 执行的,因此需要从 pstmt 中获取参数元数据相关信息
public static void parameterMetaData() { |
3. 结果集元数据:ResultSetMetaData
ResultSet——》 ResultSetMetaData
Mysql必须在 url 中附加参数配置:
jdbc:mysql://localhost:3306/数据库名?generateSimpleParameterMetadata=true
private static final String URL = "jdbc:mysql://localhost:3306/login?generateSimpleParameterMetadata=true"; |
十七、自定义标签
验证码
防止恶意攻击
强制刷新:除了禁止缓存以外,还需要给服务端传递一个唯一的参数(没有实际用处)。随机数、时间
先通过 jsp 制作一个可以随机生成验证码图片
<%@ page import="java.awt.*" %> |
前台 jsp:
<html> |
以及 Servlet
public class CheckCodeServlet extends HttpServlet { |