用 Java 去开发基于 Web 的应用程序,Java EE基于Java开发企业解决方案
Java Web 是 Java EE 的主要分支
用Java 开发 Web 应用
需要一个 Web 应用服务器,专门对外提供服务的,将写好的 Java 程序部署到这个 Web 应用服务器上,从而完成与客户端的交互。
Web 应用服务器,它是一个产品,是一个可以安装到服务器计算机上的一个服务产品,我们可以在 Web 应用服务器上部署一些供客户端访问的资源,然后启动该服务,那么客户端就可以通过浏览器访问这些资源。
Web 应用服务器:Tomcat、Jboss、Weblogic、Jetty
Tomcat
bin:各个平台(Windows、Mac/Linux)启动/停止 Tomcat 服务的脚本
shell脚本就是mac和Linux
conf:Tomcat 的配置文件
lib:Tomcat 需要用到 jar
logs:Tomcat 启动日志
temp:Tomcat 运行的临时文件
webapps:Tomcat 中允许客户端访问的资源
work:Tomcat 将 JSP 生成的 Servlet 放在这里
Servlet 就是运行在 Tomcat 中,单独的 Servlet 无法运行,必须部署到 Tomcat 中才能运行,负责与客户端进行通信:
1、接收客户端的请求
2、创建并返回基于客户端请求的动态 HTML 页面
3、与数据库进行通信
Servlet 就是一个 Java 类,是一个接口,描述的功能是让某个 Java 类具备处理客户端请求的能力。
自定义一个 Java 类,实现 Servlet 接口,Java 类就可以处理客户端的请求。
1、当浏览器请求 Servlet 的时候,Tomcat 会进行检索,找到 url 中映射的 Servlet,判断当前 Servlet 对象是否存在,如果不存在则创建 Servlet 对象,执行第 2 步,如果存在,则直接调用,跳过第 2 步,执行第 3 步。
Servlet 是单例模式的,通过反射机制创建 Servlet 对象。
2、执行 init 完成初始化操作(第一次创建完成 Servlet 之后会调用,如果 Servlet 已经存在则不执行)。
3、执行 service 完成相关的业务逻辑。
4、执行 destroy 方法销毁 Servlet 对象,关闭 Tomcat 服务时调用。
1.构造函数只调用一次,第一次请求Servlet并且Servlet对象不存在的时候。
2.init只调用一次,Servlet创建完成之后,立即调用init。
3.service调用多次,每请求一次Servlet,就会执行一次service方法。
4.destory只调用一次,关闭tomcat服务的时候执行。
ServletConfig 属于某个特定的Servlet,ServletContext 是全局对象,可以表示所有的Servlet,可以用于整个Web应用,一个Web应用只有一个ServletContext,但是作为一个Web应用可以有多个ServletConfig。每创建一个Servlet就会有一个对应的ServletConfig。
一个是全局对象,一个是局部对象。
@Override
public void init(ServletConfig servletConfig) throws ServletException {
ServletContext servletContext = servletConfig.getServletContext();
System.out.println(servletContext.getContextPath());
System.out.println(servletContext.getRealPath("/"));
System.out.println(servletContext.getServerInfo());
System.out.println("init...");
}
直接实现 Servlet 接口的弊端是需要实现全部的 5 个方法,但是其中的 4 个都是没有必要的。
Servlet 如何帮助开发者屏蔽掉不需要的方法?
提供了一个 GenericServlet 实现 Servlet 接口,实现 5 个方法,利用继承机制,在下一层的类就不需要重复实现了。
实际开发中不会直接使用实现 Servlet 接口的方式,有一些通用的步骤需要处理。
1、需要实现 5 个方法,但是 4 个没有用。
2、request 和 response 需要进行类型转换,父类转子类。
3、需要根据请求类型进行不同的分发处理,GET、POST。
Servlet 提供了两个类,分别完成这些通用的工作。
1、GenericServlet,实现 5 个方法。
2、HttpServlet,在 GenericServlet 的基础上完成类型转换以及请求分发。
实际开发中直接继承 HttpServlet 即可。
框架的底层依然是servlet。
postman 模拟器,模拟客户端HTTP请求,下载安装就可以了。
@webServlet("/test")
public class TestServlet etextends HttpServlet{
@Override
protected void doGet(HttpServletRequest req,HttpServletResponse resp){
resp.getWriter().write("Hello
")
}
@Override
protected void doPost(HttpServletRequest req,HttpServletResponse resp){
}
}
资源都是加入到web-inf.
Java Server Page Java 服务页面
HTML CSS JavaScript
使用vue,用来Java动态生成的HTML页面
Java Web 是通过Servlet给客户端做出响应的,具体的操作就是调用response的write。
方法将要响应的内容输出给客户端。
如果响应的页面,通过response的write将所有的HTML代码一行一行的输出,工作量很大。而且很容易出错,如何解决?使用JSP。
JSP就是一个中间层的模板,允许以编写HTML代码的形式创建一个servlet。他会自动的将JSP转换为一个Servlet。自动的使用response将用户编写的HTML代码来进行输出。
index_jsp.java就是index.jsp转换之后的Servlet文件
9 个内置对象 Java 对象
常用的是 request、response、session、application
request 的常用方法:
1、String getParameter(String name) //获取前端传来的参数
2、void setAttribute(String name,Object value) //通过键值对的形式保存数据
3、Object getAttribute(String name) //通过 name 获取数据
4、String[] getParameterValues(String name) //获取前端传来的多个参数
5、void setCharacterEncoding(String charset) //设置请求的编码
6、RequestDispatcher getRequestDispatcher(String path) //返回一个 RequestDispatcher 对象,该对象的 forward() 方法用来完成转发
package com.southwind.servlet;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
@WebServlet("/test")
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、创建一个字符串对象
//2、跳转到test.jsp并且将字符串传给jsp
String str = "Hello World";
req.setAttribute("str", str);
RequestDispatcher requestDispatcher = req.getRequestDispatcher("test.jsp");
requestDispatcher.forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String name = req.getParameter("name");
System.out.println(name);
}
}
<%--
Created by IntelliJ IDEA.
User: ningn
Date: 2021/7/15
Time: 21:42
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
test.jsp
<%
String str = (String) request.getAttribute("str");
%>
<%=str%>
response 常用方法:
1、sendRedirect(String path) //重定向
2、resp.setContentType(“text/html;charset=UTF-8”) //设置响应编码
resp.sendRedirect("test.jsp");
String str = "张三";
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write(str);
转发和重定向都是完成资源跳转的,从某个资源跳转到另外一个资源
资源:Servlet、JSP(其实就是一个servlet)
区别:转发是同一个请求,重定向是两个不同的请求
转发是在服务器端将请求指定到另外一个资源,跳转是由服务器完成,客户端不需要进行处理,所以也叫服务器跳转,客户端的请求还是同一个请求,意味着地址栏不会发生改变。
重定向相当于服务器拒绝了当前的访问,让客户端发起一次新的请求来访问要跳转的资源,跳转是由客户端完成的,所以也叫客户端跳转,客户端是一次新的请求,意味着地址栏会发生改变。
如果使用 request 进行参数传递,那么必须是在转发的情况下才可以正常访问,重定向无法访问,因为转发是同一个请求,存取的是同一个对象,重定向是两个不同的请求,存取的不是同一个对象。
requestDispatcher.forward(req,resp)
一定要做这个动作。
会话
服务器无法识别每一次 HTTP 请求的来源,就需要有一种技术来区分不同的客户端,这就是会话。
会话:浏览器和服务器之间发生的一系列连续的请求和响应的过程,打开浏览器进行操作到关闭浏览器的全过程。
服务器通过会话可以区分不同的浏览器/客户端。
实现会话的技术:
1、session 服务器技术、存在于服务器
2、cookie 客户端技术、存在于客户端
session 是 JSP 提供的内置对象,什么是内置对象?
JSP 预先创建好的对象,开发者可以直接使用,而不需要再次创建的对象。
session常用的方法:
方法 | 解释 |
---|---|
void setMaxInactiveInterval(int interval) | 设置session的失效时间,秒为单位 |
int getMaxInactiveInterval() | 获取session的有效时间,默认有效时间是30分钟 |
String getId() | 获取sessionid,服务器就是通过sessionID来区分不同的客户端。 |
void invaliddate() | 设置session失效 |
void setAttribute(String key,Object value) | 通过键值对的形式来储存数据 |
Object getAttribute(String key) | 通过key来取出对应的value |
void removeAttribute(String key) | 通过key来删除对应的value |
String sessionid = session.getId();
session.setMaxInactiveInterval(60);
session.invalidate();
int time = session.getMaxInactiveInteral;
session.setAttribute("str");
//跳转
request.getRrequestDispatcher("test.jsp").forward(request,response);
response.sendRedict("test.jsp");
request 作为载体传值,那么必须要确保两个页面使用的是同一个request,转发就是同一个request,重定向是两个request,所以转发可以获取到值,重定向不行。
session一次会话,只要浏览器不关闭,无论页面怎么跳转,所有的页面都是使用的是同一个session。
Cookie是一个文本类型的文件,储存在浏览器中的,可以存在文本类型的数据。
Cookie的运行机制:Cookie是服务器在响应的时候附带传给浏览器的一个小文本文件。一旦流浏器保存了某个cookie,那么在以后的每次访问服务器的时候,就会把cookie再传回来,服务器在响应的时候,又会把cookie再传给浏览器。
Cookie就是这样不断地在服务器和浏览器之间来回传递,依次实现传输数据的作用。
javax.sevlet.http
java.lang
java.util
jdk中的api都是以Java开头的包名,都是在Java包的基础之上扩充出来的api
cookie的常用方法
方法 | 解释 |
---|---|
void setMaxAge(int age) | 设置Cookie的有效期,单位是秒 |
int getMaxAge() | 获取Cookie的有效期 |
void setVlaue(String value) | 对Cookie进行赋值。 |
String getName() | 获取cookie的name |
String getVlaue() | 获取Cookie的value值 |
Cookie[] cookies = request.getCookies() |
<%--
Created by IntelliJ IDEA.
User: ningn
Date: 2021/7/17
Time: 21:26
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
Cookie cookie = new Cookie("name", "tom");
cookie.setValue("cat");
cookie.setMaxAge(60);
int age = cookie.getMaxAge();
response.addCookie(cookie);
%>
<%=age%>
</body>
</html>
<%--
Created by IntelliJ IDEA.
User: ningn
Date: 2021/7/17
Time: 22:00
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
String name = cookie.getName();
String value = cookie.getValue();
out.write(name+"-"+value);
out.write("
");
}
%>
</body>
</html>
储存用户信息
Cookie和Session的区别
1.Session是保存在服务器端,JAVA对象,cookie是保存在客户端,跟Java程序无关。
2.Session可以储存任意类型的数据Object,cookie只能存文本类型。
3.Session会随着会话的结束而销毁,cookie可以长期保存在客户端。
4.Session存储重要信息(服务器端),Cookie存储不重要的信息。
超链接和浏览器发送的都是get请求,表单是自己选择的。
用户登录登出
session
一般是使用的重定向的方式,跳转就是跳转。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/sessionlogin" method="post">
<table>
<tr>
<td>用户名:</td>
<td>
<input type="text" name="username"/>
</td>
</tr>
<tr>
<td>密码:</td>
<td>
<input type="password" name="pwd"/>
</td>
</tr>
<tr>
<td>
<input type="submit" value="登录"/>
</td>
<td>
<input type="reset" value="重置"/>
</td>
</tr>
</table>
</form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<h1>首页</h1>
<%
String username = (String) session.getAttribute("username");
//判断是否登录
if(username == null){
response.sendRedirect("session_login.jsp");
}
%>
欢迎回来!<%=username%><a href="/sessionlogout">退出</a>
</body>
</html>
package com.southwind.servlet;
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 javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/sessionlogin")
public class SessionLoginServlet extends HttpServlet {
private final String USERNAME = "admin";
private final String PWD = "123";
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取参数
String username = req.getParameter("username");
String pwd = req.getParameter("pwd");
//验证
if(username.equals(USERNAME) && pwd.equals(PWD)){
//登录成功,存储用户信息
//获取session
HttpSession session = req.getSession();
session.setAttribute("username", username);
//跳转到首页
resp.sendRedirect("session_index.jsp");
}
}
}
package com.southwind.servlet;
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 javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/sessionlogout")
public class SessionLogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//销毁session
HttpSession session = req.getSession();
session.invalidate();
//跳转页面
resp.sendRedirect("session_login.jsp");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
Cookie
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
首页
<%
Cookie[] cookies = request.getCookies();
String username = null;
for (Cookie cookie : cookies) {
if(cookie!=null){
if (cookie.getName().equals("username")){
username = cookie.getValue();
}
}
}
if(username == null){
response.sendRedirect("cookie_login.jsp");
}
%>
欢迎回来!<%=username%>退出
package com.southwind.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/cookielogin")
public class CookieLoginServlet extends HttpServlet {
private final String USERNAME = "admin";
private final String PWD = "123";
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String pwd = req.getParameter("pwd");
if(username.equals(USERNAME) && pwd.equals(PWD)){
Cookie cookie = new Cookie("username", username);
//设置有效期10分钟
cookie.setMaxAge(600);
resp.addCookie(cookie);
resp.sendRedirect("cookie_index.jsp");
}
}
}
package com.southwind.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/cookielogout")
public class CookieLogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie[] cookies = req.getCookies();
for (Cookie cookie : cookies) {
if(cookie!=null){
if(cookie.getName().equals("username")){
cookie.setMaxAge(0);
resp.addCookie(cookie);
resp.sendRedirect("cookie_login.jsp");
return;
}
}
}
}
}
核心的话就是把cookie的值直接给他赋予成0了,就可以去销毁这个cookie了。
过滤器是服务端代码,用于拦截传入的请求和传出的响应,可以对请求/响应进行判断,比如是否登录,是否拥有权限等等。
验证通过之后再发送到目标资源,否则直接进行相应的处理。
package com.southwind.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebFilter("/session_index.jsp")
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//判断是否登录
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpSession session = request.getSession();
String username = (String) session.getAttribute("username");
if(username == null){
response.sendRedirect("session_login.jsp");
}else{
//放行
filterChain.doFilter(servletRequest, servletResponse);
}
}
}
Expression Language 表达式语言,简称 EL 表达式
可以替代 JSP 页面中数据访问时的复杂编码,简化代码
语法:${目标数据的key}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
<%-- <%--%>
<%-- String str = (String) request.getAttribute("str");--%>
<%-- %>--%>
<%-- <%=str%>
--%>
${str}
package com.southwind.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/el")
public class ELServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String str = "Hello World";
req.setAttribute("str", str);
req.getRequestDispatcher("el.jsp").forward(req, resp);
}
}
两类数据库:
1、关系型数据库:MySQL、Oracle、SQLServer
2、非关系型数据库 NoSQL Not Only SQL:Redis、MongoDB
数据库管理工具
是安装在电脑上的一个服务,3306
数据库管理工具提供了可视化的界面,用来管理数据库
SQLyog、Navicate、DataGrip
IDEA 集成了 DataGrip
DataGrip 连接失败常见原因:
1、MySQL 服务没有启动,手动开启/控制面板-管理工具-服务-MySQL-启动
2、缺少驱动 jar,下载即可
3、时区问题
set global time_zone='+8:00';
database 安装在计算机上的一个专门用来存储数据的仓库、也是一种服务
存储引擎是如何存储数据,如何为数据建立索引,如何更新,查询数据等技术的具体实现方法。
MySQL提供了多种存储引擎,默认的存储引擎?InnoDB
show engines;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xfRlGYTp-1650551559123)(C:\Users\村头\AppData\Roaming\Typora\typora-user-images\image-20210731142208579.png)]
MySQL 默认使用 InnoDB 存储引擎,InnoDB 对事务的处理能力非常强大,是其他存储引擎所不能比的。
事务是数据库中最重要的一个东西。
SQL 是一种编程语言,专门用来操作数据的
以命令行的形式完成对于数据的增删改查
CRUD
Create 增
Read 读
Update 更新
Delete 删除
执行运算符
加减乘除
select id/10 from student;
比较运算符
比大小,0表示false。1表示true
select id >= 6 from student;
逻辑运算符
与或非
只会对结果进行运算,不会对其他数据的进行运算。
select id >= && cid > 1 from student;
select id >= and cid > 1 from student;
1、is null 判断值是否为空
空值和null都不是真正的null。
select name is null from student;
2、between and 判断值是否在某个区间内,某个区间的话都是可以取到临界值
select id,id between 1 and 5 from student;
select id,id >= 1 and id <= 5 from student;
3、in 判断值是否在某个特定的集合内
select id,id in (1,2,3) from student;
select id,id = 1 or id = 2 or id = 3 from student;
4、like 模糊查询
要加上百分号
select name from student where name like '%三%';
查询 name 中包含”三“的所有数据
以”三“开头
select name from student where name like '三%';
以”三“结束
select name from student where name like '%三';
name 长度为 2
一个_表示一个长度
select name from student where name like '__';
name 长度为 3 ,同时中间的字是 三
select name from student where name like '_三_';
主要还是利用模糊查询。
abs() 求绝对值
select abs(score) from student;
floor() 返回小于参数的最大整数
select floor(score) from student;
ceil() 返回大于参数的最小整数
select ceil(score) from student;
curdate() 获取当前日期
select curdate();
curtime() 获取当前时间
select curtime();
now() 获取当前日期+时间
select now();
insert(s1,index,len,s2)
s1 中 index 位置开始,长度为 len 的字符串替换为 s2。
index 从 1 开始
SQL语言中正常使用的就是单引号。
select insert(name,2,3,'test') from student where id = 1;
upper() 将字母转为大写
select upper(name) from student where id = 1;
lower() 将字母转为小写
select lower(name) from student where id = 1;
left(s,len) 返回 s 字符串的前 len 个字符
select left(name,3) from student where id = 1;
right(s,len) 返回 s 字符串的后 len 个字符
select right(name,3) from student where id = 1;
substring(s,index,len) 截取 s 字符串,从 index 开始,长度为 len,index 从 1 开始
select substring(name,2,3) from student where id = 1;
reverse() 反序输出
select reverse(name) from student where id = 1;
通过 SQL 语句获取系统时间相关的信息
curdate() 获取当前日期
select curdate();
curtime() 获取当前时间
select curtime();
now() 获取当前日期+时间
select now();
adddate(d,n) 返回 d 日期 n 天之后的日期
select adddate('2021-6-22',100);
subdate(d,n) 返回 d 日期 n 天之前的日期
select subdate('2021-6-22',100);
datediff(d1,d2) 计算 d1 和 d2 之间相隔的天数
select datediff('2021-6-22','2018-1-1');
count() 根据某个字段统计数据库的总记录数
一般是通过id来查,MySQL中的索引是有用的,中用的是B+树
select count(id) from product_info;
sum() 计算某个字段值的总和
select sum(product_id) from product_info;
非数值字段 sum 的和是 0
avg() 计算某个字段值的平均值
select avg(product_id) from product_info;
max() 计算某个字段的最大值
select max(product_id) from product_info;
min() 计算某个字段的最小值
select min(product_id) from product_info;
1、创建数据库
create database 数据库名称
create database test1;
2、创建数据表
行和列
列就是用来描述数据表结构的
行就表示每一条不同的数据
create table 数据表名称(
//列信息
id int,
name varchar(11),
age int
)
create table user(
id int,
name varchar(11),
password varchar(11),
age int,
score double,
type int
);
3、对数据进行操作 增删改查 CRUD
新增
insert into 数据表(字段列表) values(值列表);
insert into user values (1,'张三','123',23,100,1);
如果添加全部的字段,则数据表之后的字段列表可以直接省略,不写就等于写全部字段。
修改
update 数据表 set 字段列表(字段名=字段值);
update user set name='张三',age=3 where id=2;
修改时需要慎重,一般要添加 where 条件,否则会把整张表的数据全部改掉,安全性较差。
删除
delete from 数据表;
delete from user where id = 1;
删除更是一个要慎之又慎的操作,一旦操作不当,就得删库跑路,一定要添加 where,否则就会清空所有的数据,后果非常严重。
实际开发中,一般不会直接使用 delete 物理删除,因为风险太大,为了稳妥起见,一般会采用逻辑删除的形式来完成删除业务。
给表中追加一个字段,用不同的值来表示是否为删除状态,deleted:0/1
0 表示未删除
1 表示删除
update user set deleted = 1 where id = 3;
update user set deleted = 0 where id = 3;
查询
select 字段列表 from 数据表 where 条件;
select * from user where id = 2;
‘*’ 表示全部字段进行查询
select * from user where deleted = 0;
数据结构、原理
MySQL 数据量很大的时候,如何来提高查询效率?
最有效直接的方式就是添加索引,索引的作用就是用来提高数据库的查询速度。
什么是索引?
索引就是帮助数据库快速查询数据的一种数据结构,索引是一种数据结构。
没有索引的情况下,select * from user where id = 11,逐行遍历,需要查找 9 次才能找目标数据,需要进行 9 次的磁盘读取,速度很慢,效率很低。
每一个字段都可以添加索引,给 id 添加索引,存储数据的同时去维护一个存储了 id 值的数据结构。
数据结构:二叉树、红黑树、B 树、B+ 树
数据结构在线画图工具:https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
二叉树
id = 11,5 次找到
索引树种存储的是 key-value 结构的数据,key 就是索引的值,11,value 存储的是 id = 11 这行数据在磁盘中的地址 data,查找的顺序就是先找到索引对应的 key 值,进而获取 value,拿到了地址,读取磁盘,把数据读到内存中,展示出来。
二叉树的劣势
此时对应的二叉树如下所示。
此时的二叉树就是个链表,id = 10 需要查找 10 次,和不建立索引直接逐行查找的效率是一样的,单纯使用二叉树,肯定不行。
红黑树
可以有效避免二叉树退化成链表的情况,红黑树生成数据。
id = 10,只需要查找 5 次就可以找到。
红黑树的弊端,当数据量很大的时候,千万级别数据量,红黑树虽然可以保持平衡,但是此时树的高度会非常大。
B 树
红黑树的弊端是树的高度太大了,限制它的高度,就可以解决问题。
1、需要存储大量的数据
2、限制树的高度
纵向没有办法扩展,只能横向扩展,在同一层上存储多个节点。
B 树
B+ 树
非叶子节点不存储 data,只存储索引值,为什么?
为了存储更多的索引,降低树的结构
B+ 树就是非叶子节点只存储索引值,叶子节点存储完整的索引+data,data 就是 MySQL 数据库中对应的数据行在磁盘中的地址。
MySQL 分配给每一级的空间是 16 KB,非叶子节点单位大小是 14 byte,非叶子节点可以存储的索引个数是 16 KB / 14 byte = 1170
叶子节点因为会存储 data,所以占用空间会较大,1 KB,一层叶子节点可以存储 16 个元素
非叶子节点可以存 1170 个,叶子节点可以存 16 个
构建 3 层的 B+ 树,那么可以存储多少个索引呢?
2 层非叶子节点,1 成叶子节点
1170*1170*16 = 2190W
B+树就是一个数据结构,MySQL就是一个B+树。
普通索引、唯一性索引、全文索引、单列索引、多列索引、空间索引
虽然索引可以加快查询的速度,但是创建和维护索引需要消耗物理空间,所以不能创建太多的索引,否则反而会降低数据库的检索速度。
任何事情都要有一个度。
唯一性索引是指索引值是唯一的,比如主键索引。
全文索引:只能创建在 char、varchar、text 数据类型的字段上,查询数据量较大的字符串类型的字段,使用全文索引可以提升效率。
InnoDB 不支持全文索引。
单列索引:把索引添加到一个字段上
多列索引:把索引添加到多个字段上
空间索引:只能建立在空间数据类型上,(GIS,地理信息系统)
InnoDB 不支持空间索引
索引的设计原则:
1、出现在 where 语句后面的字段,添加索引
2、索引的值,要尽量唯一,效率更高
3、不要添加太多索引,维护成本很高
SQL
alter table user add index id_index(id);
电脑中安装了 MySQL 服务 — 数据库 — 数据表 — 数据
SQL 分为四类
1、DML(数据操作语言)操作数据库中的数据(insert、update、delete)
2、DDL(数据定义语言)创建、 删除、修改数据库、数据表
3、DQL(数据查询语言)对数据库中的数据进行查询(select)
4、DCL(数据控制语言)用来提交数据库事务(commit、rollback)
学习数据库,需要从两方面入手
1、掌握数据库的使用(CRUD),在建立好数据库/数据表的基础上,对数据进行操作,程序员日常最多的工作。
2、设计数据库,根据项目的需求,设计数据表和数据表之间的关系,建立连接。
数据表由两部分组成:
1、表结构:表的设计 //列信息
2、表数据:表中保存的数据 //行信息
create database name;
create database mytest1 default character set utf8 collate utf8_general_ci;
create database mytest2 default character set utf8 collate utf8_bin;
order by name
校验字符集?查询的时候对字符数据进行排序
mytest1:utf8_general_ci a B
mytest2:utf8_bin B a
utf8_general_ci:不区分大小写,B 排在 a 后面,正常也是通过时间来进行排序的。
utf8_bin:按照字符的 asc 码的大小进行排序 a 排在 B 后面
B 66
a 97
drop database name;
create table name(
//字段信息
字段名 数据类型 主键/外键 默认值 是否为null
);
MySQL 数据类型
1、整数类型:tinyint(1个byte)、smallint(2个byte)、mediumint(3个byte)int(4个byte)、bigint(8个byte)主要就是用int。
2、浮点型/定点型
浮点型:float(4个byte)、double(8个byte)
定点型:decimal(14个byte)decimal(M,D) BigDecimal
M 数字的最大长度
D 小数点后的长度
3、日期和时间类型
year(1个byte) 表示年
time(3个byte) 表示时间
date(3个byte) 表示日期
datetime(8个byte) 日期+时间
timestamp(4个byte) 时间戳 1970年1月1号 00:00:00 到现在的毫秒数,不会有重复的。
4、字符串类型
char(2个byte)
varchar(4个byte)
text(8个byte)
5、二进制类型
bit(2个byte)
binary(4个byte)
tinyblob(255个byte)
mediumblob(2^24-1个byte)
longblob(2^32-1个byte)
create table sutdent
(
id int default 100 auto_increment comment '编号'
);
create unique index sutdent_id_uindex
on sutdent (id);
alter table sutdent
add constraint sutdent_pk
primary key (id);
自增和默认值不可以同时存在。
设置默认值 100
自动生成,每一行记录的值都是 100,
同时设置自增,每一行记录的值都是递增的
主键自增。
unique 设置该字段的值不能有重复的,每个值都必须是唯一的。
create table student
(
id int primary key auto_increment comment '编号',
name varchar(11) not null comment '姓名',
age int default 22 comment '年龄'
);
drop table name;
desc name;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tijtlsTZ-1650551559124)(C:\Users\村头\AppData\Roaming\Typora\typora-user-images\image-20210801195443061.png)]
修改表中的字段
添加字段
alter table student add title varchar(11);
删除字段
alter table student drop title;
修改字段
alter table student change age title varchar(11);
表中的一个字段,该字段的值是每一行数据的唯一标识。
一张表只能有一个主键。
主键是添加到字段上的,并不是独立存在的。
主键生成策略:采用代理主键,与业务无关,仅仅是用来标识一行数据。
一般定义为 int 类型,因为 int 类型存储空间小,同时可以设置自增,设置自增就可以保证数据的唯一性。
主键的值不能为 null。
主键必须是唯一的,主键自带索引,提高查询效率。
如何设置主键
create table test1(
id int primary key
);
create table test2(
id int
);
alter table test2
add constraint test2_pk
primary key (id);
删除主键
alter table test1 drop primary key;
create table test1(
id int primary key auto_incremen);
外键:将表中的某一个字段设置为外键,需要跟另外一张表的主键进行关联,从而构建不同数据表之间的某种关系。
外键需要和主键结合起来使用,A 表的外键需要和 B 表的主键结合起来使用,不能一张表的外键和自己的主键结合使用。
外键一定是两张表的关系。
数据表一共有三种关系:
1、一对一:A 表的一条记录对应 B 表的一条记录,B 表的一条记录对应 A 表的一条记录
2、一对多:A 表的一条记录对应 B 表的多条记录,B 表的一条记录对应 A 表的一条记录
3、多对多:A 表的一条记录对应 B 表的多条记录,B 表的一条记录对应 A 表的多条记录
一对一:人和身份证号,一个人只能有一个身份号,一个身份证号只能对应一个人
一对多:班级和学生,一个班级对应多个学生,一个学生只能对应一个班级
多对多:选课和学生,一门课程可以被多个学生选择,一个学生也可以选择多门课程
用户和订单 是什么关系?一对多
商品和订单/用户 是什么关系?多对多
实际开发中,使用更多的是 一对多和多对多关系,一对一一般不用
person:id name age
cardid:value
一对一的关系一般可以直接合成一张表。
1、创建两张数据表
2、通过某种设置构建两张表的一对多关系
逻辑:主从关系,一张主表、一张从表
一的一方是主表,多的一方是从表
班级是一,班级是主表
学生是多,学生是从表
关联两个表的关联字段要存储在多的一方(从表)
关联字段一般设置为外键,外键需要被主表中的主键所约束
外键可以取的值必须是主表中存在的值
实际开发中设计数据库的时候一般不建议使用外键,因为添加外键之后,会消耗额外的资源,必然会导致数据库性能降低。
从表是被别人约束的表。被表格约束的表格。
初学者可以添加外键,因为一旦建立外键关系,数据库层面就会自动进行校验,外键的值是否正确。
create table class(
id int primary key auto_increment,
name varchar(11)
);
create table student(
id int primary key auto_increment,
name varchar(11),
cid int
);
alter table student add foreign key(cid) references class(id);
因为维护主外键约束,需要耗费大量的资源,导致数据库速度变慢,所以一般为了提升速度,不推荐使用主外键约束,唯一一对多关系的任务完全交给业务逻辑,就对我们写代码提出了更高的要求。
删除外键
alter table student drop foreign key student_ibfk_1;
没有外键的话或者没有逻辑处理,出现了一堆脏数据。
多对多的关系,需要借助于一张中间表建立两张目标表的关系
中间表就是从表,添加两个外键
两张目标都是主表,同时约束中间表
create table course(
id int primary key auto_increment,
name varchar(11)
);
create table user(
id int primary key auto_increment,
name varchar(11)
);
create table course_user(
id int primary key auto_increment,
uid int,
cid int
);
alter table course_user add foreign key(uid) references user(id);
alter table course_user add foreign key(cid) references course(id);
MySQL 中的多对多关系其实就是由两个一对多关系来组成的。
将两个 SQL 语句进行嵌套查询
查询张三所在的班级
select * from class where id = (select cid from student where name='张三');
连接查询
查询的时候将两张表进行关联
查询张三的信息(包括班级信息)
笛卡尔积
笛卡尔积把数据中所有的情况都给他联系起来。就是进行1*3所有情况
select * from student inner join class where student.name='张三' and student.cid = class.id
进行简化
select * from student s,class c where s.name='张三' and c.id = s.cid
左表是指写到左边的表(写到前边的表)
右表是指写到右边的表(写到后边的表)
左连接:左表所有数据和右表满足条件的数据
左表:张三 李四 王五
右表:2班 3班
左表&右表
select * from student s left join class c on s.cid = c.id;
右连接:右表所有数据和左表满足条件的数据
右表:1班 2班 3班
左表:张三 李四
select * from student s right join class c on s.cid = c.id;
, 就是inner join的简写。
select s.id sid,s.name sname,c.id cid,c.name cname from student s,class c where s.cid = c.id and s.id = 1;
select s.id sid,s.name sname,c.id cid,c.name cname from student s,class c
where s.cid = c.id and c.id = 1;
select u.id uid,u.name uname,c.id cid,c.name cname from user u,course c,course_user cu where
u.id = cu.uid and c.id = cu.cid and u.id = 7;
select u.id uid,u.name uname,c.id cid,c.name cname from user u,course c,course_user cu where
u.id = cu.uid and c.id = cu.cid and c.id = 1;
去重:去掉重复的数据。使用一个distinct。
select distinct u.id uid,u.name uname,c.id cid,c.name cname from user u,course c,course_user cu where
u.id = cu.uid and c.id = cu.cid and u.id = 8;
所查询的结果集中,字段值的重复率是 100%,MySQL 才会认为是重复字段,distinct 才会生效。
分页:数据量很大的时候,分页展示数据。减少的只是一个后台交互的东西。
limit index(下标),length(长度)
index 起始下标
length 截取的长度
从多少开始,然后截取多少个。
select * from course limit 10,5;
length = 5;
page = ?
page 1 2 3
index 0 5 10
index = (page-1)*length
视图就是一张虚拟的表,专门用来做查询,自定义要展示的数据
创建视图
create view view_common as select * from user;
使用视图
select * from view_common;
删除视图
drop view view_common;
类似于一个监听器。
触发器定义了一系列操作,可以在对指定表进行插入、更新或者删除操作的同时自动执行这些操作。
触发器在数据库中所定义的一个方法。
触发器的优点:
1、开发更快,因为触发器是存储在数据库中的,不需要再应用程序中进行调用
2、更容易维护,定义触发器之后,访问目标表时,会自动进行调用
3、业务全局实现,如果修改业务,只需要修改触发器即可,不需要修改任何的业务代码
创建触发器
create trigger t_afterinsert_on_tab1
after insert on tab1
for each row
begin
insert into tab2(tab2_id) values(new.tab1_id);
end;
create trigger t_afterdelete_on_tab1
after delete on tab1
for each row
begin
delete from tab2 where tab2_id = old.tab1_id;
end;
删除
drop trigger t_afterinsert_on_tab1;
存储过程是一组为了完成特定功能的 SQL 语句的集合,存储在数据库中的,用户可以直接进行调用,类似于方法的调用。
一次编写,多次使用,避免开发人员重复编写相同的 SQL。
创建存储过程
参数:
1、输入输出类型:入参、出参
入参是指传到存储过程中的参数,类似于 Java 中的参数
出参是指存储过程返回的数据,类似于 Java 中的返回值
2、参数名称
3、参数类型
create procedure add_name(in target int)
begin
declare name varchar(20);
if target = 1 then
set name = 'MySQL';
else
set name = 'Java';
end if;
insert into user(name) values (name);
end;
调用存储过程
call add_name(2);
删除存储过程
drop procedure add_name;
出参
create procedure count_of_course(out num int)
begin
select count(*) into num from course;
end;
call count_of_course(@num);
select @num;
create procedure test(in num int,out val int)
begin
if num = 1 then
select id into val from user;
else
select money into val from user;
end if;
end;
call test(2,@val);
select @val;
if
create procedure example_if(in x int)
begin
if x = 1 then
select id from student;
elseif x = 2 then
select name from student;
end if;
end;
call example_if(2);
case-when
create procedure example_case(in x int)
begin
case x
when 1 then select id from student;
when 2 then select name from student;
else select cid from student;
end case;
end;
call example_case(6)
while
create procedure example_while(out sum int)
begin
declare i int default 1;
declare s int default 0;
while i<=100 do
set s = s+i;
set i = i+1;
end while;
set sum = s;
end;
call example_while(@sum);
select @sum;
java DataBase Connectivity 是一个独立于特定数据库的管理系统,通用的SQL数据库存储和操作的公共接口
独立于特定的数据库
公共接口
JDBC 就是一套由 Java 提供的可以作用大部分数据库的接口
定义了一组标准,为访问不同数据库提供了统一途径
JDBC接口包括两个层面:
1.面向应用的API,供程序员调用
2.面向数据库的API,供数据库开发厂商调用的驱动程序
1.JDBC API Java 官方提供,供程序员调用的接口方法
在 java.sql、javax.sql 包中
2.DriverManager Java 官方提供,管理各种不同的 JDBC
3.JDBC 驱动,数据库厂商提供,负责连接不同的数据库
1、加载数据库驱动
2、获取 Connection,表示一次连接
3、创建 Statement,由 Connection 产生,执行 SQL 语句
4、ResultSet 保存 Statement 执行查询后所产生的结果(增、删、改不需要)
增删改
//加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//获取连接
// url user password
String url = "jdbc:mysql://localhost:3306/test1?serverTimezone=UTC";
String user = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url,user,password);
String sql = "insert into student(name,age) values('JDBC',1)";
// 增删改 都是使用的executeUpdate
// 查询用的executeQuery
Statement statement = connection.createStatement();
int i = statement.executeUpdate(sql);
System.out.println(i);
查询
package com.southwind.test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class Test {
public static void main(String[] args) throws Exception {
//获取连接
//url user password
String url = "jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=UTF-8";
String user = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, user, password);
//SQL
// String sql = "insert into student(name,cid) values('JDBC1',1),('JDBC2',1),();";
// String sql = "update student set name = 'MySQL' where id = 4";
// String sql = "delete from student where cid = 2";
String sql = "select * from user";
Statement statement = connection.createStatement();
//增 删 改 都用 executeUpdate
//查询用 executeQuery
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()){
int id = resultSet.getInt(1);
String name = resultSet.getString(2);
int money = resultSet.getInt(3);
System.out.println(id + "-" + name + "-" + money);
}
// int i = statement.executeUpdate(sql);
// System.out.println(i);
}
}
execute 方法返回值是 boolean 类型,不是表示 SQL 是否执行成功,而是表示
SQL 执行的结果类型,如果是 ResultSet,则返回 true,否则返回 false。
实际开发中,并不会直接使用 Statement,而是会使用它的子类 PreparedStatement
1、提供占位符的功能,避免拼接字符串
2、防止 SQL 注入,安全性更高
package com.southwind.test;
import java.sql.*;
public class Test {
public static void main(String[] args) throws Exception {
test("Test", 2);
}
public static void test(String name,int cid) throws Exception{
//获取连接
//url user password
String url = "jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=UTF-8";
String user = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, user, password);
//SQL
String sql = "insert into student(name,cid) values("+name+","+cid+")";
sql = "insert into student(name,cid) values(?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, name);
preparedStatement.setInt(2, cid);
int i = preparedStatement.executeUpdate();
System.out.println(i);
// String sql = "update student set name = 'MySQL' where id = 4";
// String sql = "delete from student where cid = 2";
// String sql = "select * from user";
// Statement statement = connection.createStatement();
//增 删 改 都用 executeUpdate int
//查询用 executeQuery ResultSet
// boolean execute = statement.execute(sql);
// System.out.println(execute);
//boolean 表示statement返回值是否为 ResultSet 类型
// ResultSet resultSet = statement.executeQuery(sql);
// while (resultSet.next()){
// int id = resultSet.getInt(1);
// String name = resultSet.getString(2);
// int money = resultSet.getInt(3);
// System.out.println(id + "-" + name + "-" + money);
// }
// int i = statement.executeUpdate(sql);
// System.out.println(i);
}
}
SQL 注入:利用某些系统没有对用户输入的信息进行充分检查,从而在用户输入的信息中注入 SQL 语句,利用系统的 SQL 引擎完成恶意行为的操作
select * from student where name = 'aaa' or '1=1' and password = '111' or '1=1';
先进性拼凑
如何去规避风险。
使用prepared
开发中,并不会直接使用 Statement,而是会使用它的子类 PreparedStatement
1、提供占位符的功能,避免拼接字符串
2、防止 SQL 注入,安全性更高
package com.southwind.test;
import java.sql.*;
public class Test {
public static void main(String[] args) throws Exception {
test("Test", 2);
}
public static void test(String name,int cid) throws Exception{
//获取连接
//url user password
String url = "jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=UTF-8";
String user = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, user, password);
//SQL
String sql = "insert into student(name,cid) values("+name+","+cid+")";
sql = "insert into student(name,cid) values(?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, name);
preparedStatement.setInt(2, cid);
int i = preparedStatement.executeUpdate();
System.out.println(i);
// String sql = "update student set name = 'MySQL' where id = 4";
// String sql = "delete from student where cid = 2";
// String sql = "select * from user";
// Statement statement = connection.createStatement();
//增 删 改 都用 executeUpdate int
//查询用 executeQuery ResultSet
// boolean execute = statement.execute(sql);
// System.out.println(execute);
//boolean 表示statement返回值是否为 ResultSet 类型
// ResultSet resultSet = statement.executeQuery(sql);
// while (resultSet.next()){
// int id = resultSet.getInt(1);
// String name = resultSet.getString(2);
// int money = resultSet.getInt(3);
// System.out.println(id + "-" + name + "-" + money);
// }
// int i = statement.executeUpdate(sql);
// System.out.println(i);
}
}
SQL 注入:利用某些系统没有对用户输入的信息进行充分检查,从而在用户输入的信息中注入 SQL 语句,利用系统的 SQL 引擎完成恶意行为的操作
select * from student where name = 'aaa' or '1=1' and password = '111' or '1=1';
先进性拼凑
如何去规避风险。
使用prepared