01 MySQL基础

01.1 整体介绍

  1. 网页:展现数据
  2. 数据库:存储和管理数据
  3. JavaWeb程序:逻辑处理

课程安排

  1. 数据库
  2. MySQL
  3. JDBC
  4. Maven
  5. MyBatis
  6. 前端
  7. HTML + CSS
  8. JavaScript
  9. Ajax+ Vue+ ElementUI
  10. web核心
  11. Tomcat + HTTP + Servlet
  12. Request + Response
  13. JSP
  14. Cookies + Session
  15. Filter + Listener
  16. 综合案例

01.2 数据库相关概念

数据库:DataBase,简称DB
数据库管理系统:DataBase Management System,简称DBMS
结构化查询语言:Structured Query Language,简称SQL

01.3 MySQL安装

  • 下载
  • 安装(解压)
  • 配置
  • 登录MySQL
  • 卸载MySQL

01.4 SQL数据模型

关系型数据库

01.5 SQL

  1. SQL语句以分号结尾,可单行可多行
  2. SQL语句不分大小写,关键字建议使用大写
  3. 注释
  • 单行注释:-- 注释内容 或 #注释内容(MySQL特有)
  • 多行注释:/* 注释 */

SQL分类

  • DDL(Data Definition Language)数据定义语言,用来定义数据库对象:数据库,表,列等
  • DML(Data Manipulation Language)数据操作语言,用来对数据库中表的数据进行增删改
  • DQL(Data Query Language)数据查询语言,用来查询数据库中表的记录(数据)
  • DCL(Data Control Language)数据控制语言,用来定义数据库的访问权限和安全级别,及创建用户

01.5 DDL-操作数据库

  1. 查询

SHOW DATABASES;展示已有数据库

  1. 创建

CREATE DATABASE 数据库名;创建数据库

CREATE DATABASE IF NOT EXISTS 数据库名;在不报错的情况下进行创建

  1. 删除

DROP DATABASE 数据库名;删除数据库

DROP DATABASE IF EXISTS 数据库名;在不报错的情况下删除数据库

  1. 使用数据库

USE 数据库名;进入该数据库

SELECT DATABASE();查看当前数据库

01.6 DDL-操作表-查询表&创建表

查询表

  • 查询当前数据库下所有表的名称

    SHOW TABLES;
    
  • 查询表结构

    DESC 表名称;
    

创建表

CREATE TABLE 表名 (字段名1 数据类型1,字段名2 数据类型2,...字段名n 数据类型n
);

注意:最后一行末尾不加逗号

示例

CREATE TABLE tb_user (id int,username varchar(20),-- 长度不超过20的字符串password varchar(32)
)

01.7 数据类型

数据类型 大小 描述
数值类型
TINYINT 1 byte 小整数值
SMALLINT 2 bytes 大整数值
MEDIUMINT 3 bytes 大整数值
INT或INTEGER 4 bytes 大整数值
BIGINT 8 bytes 极大整数值
FLOAT 4 bytes 单精度浮点数值
DOUBLE 8 bytes 双精度浮点数值
DECIMAL 小数值
日期和时间类型
DATE 3 日期值
TIME 3 时间值或持续时间
YEAR 1 年份值
DATETIME 8 混合日期和时间值
TIMESTAMP 4 混合日期和时间值,时间戳
字符串类型
CHAR 0-255 bytes 定长字符串
VARCHAR 0-65535 bytes 变长字符串
TINYBLOB 0-255 bytes 不超过 255 个字符的二进制字符串
TINYTEXT 0-255 bytes 短文本字符串
BLOB 0-65 535 bytes 二进制形式的长文本数据
TEXT 0-65 535 bytes 长文本数据
MEDIUMBLOB 0-16 777 215 bytes 二进制形式的中等长度文本数据
MEDIUMTEXT 0-16 777 215 bytes 中等长度文本数据
LONGBLOB 0-4 294 967 295 bytes 二进制形式的极大文本数据
LONGTEXT 0-4 294 967 295 bytes 极大文本数据

案例

CREATE TABLE student (id int,-- 编号name varchar(10),-- 姓名gender char(1),-- 性别birth date,-- 生日score double(5,2),-- 入学成绩email varchar(64),-- 邮件地址tel varchar(15),-- 家庭联系电话status tinyint(1)-- 学生状态
);

01.8 DDL-操作表-修改&删除

删除表

  1. 删除表
DROP TABLE 表名;
  1. 删除表时判断表是否存在
DROP TABLE IF EXISTS 表名;

修改表

  1. 修改表名
ALTER TABLE 表名 RENAME TO 新的表名;
  1. 添加一列
ALTER TABLE 表名 ADD 列名 数据类型;
  1. 修改数据类型
ALTER TABLE 表名 MODIFY 列名 新数据类型;
  1. 修改列名和数据类型
ALTER TABLE 表名 CHANGE 列名 新列名 新数据类型;
  1. 删除列
ALTER TABLE 表名 DROP 列名;

01.9 DML-操作数据-添加&修改&删除

添加数据

给指定列添加数据

INSERT INTO 表名(列名1,列名2,...) VALUES(值1,值2,...);

给所有列添加数据,列名可以省略

INSERT INTO stu(id,name,sex,birth,score,email,tel,status) values(2,'李四','男','1999-11-11',88.88,'lisi@itcast.cn','13888888888',1);

可在一段代码中添加多行数据

INSERT INTO stu
VALUES(2,'李四','男','1999-11-11',88.88,'lisi@itcast.cn','13888888888',1),(2,'李四','男','1999-11-11',88.88,'lisi@itcast.cn','13888888888',1),(2,'李四','男','1999-11-11',88.88,'lisi@itcast.cn','13888888888',1);

修改数据

修改表数据

UPDATE 表名 SET 列名1=值1,列名2=值2,... [WHERE 条件];

修改语句中如果不加条件,则将所有数据都修改

示例

-- 将张三的性别改为女
UPDATE stu SET sex='女' WHERE name='张三';-- 将张三的生日改为1999-12-12,分数改为99.99
UPDATE stu SET birth='1999-12-12',score=99.99 WHERE name='张三';

删除数据

删除数据

DELETE FROM 表名 [WHERE 条件];

删除语句中如果不加条件,则将所有数据都删除

示例

-- 删除数据 DELETE FROM 表名 [WHERE 条件];
DELETE FROM stu WHERE name='张三';
DELETE FROM stu;

01.10 DQL-基础查询

SELECT字段列表
FROM表名列表
WHERE条件列表
GROUP BY分组字段
HAVING分组后条件
ORDER BY排序字段
LIMIT分页限定
  • 基础查询
  • 条件查询(WHERE)
  • 分组查询(GROUP BY)
  • 排序查询(ORDER BY)
  • 分页查询(LIMIT)
基础查询
  1. 查询多个字段
SELECT 字段列表 FROM 表名;
SELECT * FROM 表名;-- 查询所有数据
  1. 去除重复记录
SELECT DISTINCT 字段列表 FROM 表名;
  1. 起别名
AS-- AS也可以省略

示例

-- 查询name age两列
SELECT NAME,age FROM stu;
-- 查询地址信息
SELECT address FROM stu;
-- 查询地址信息(去除重复记录)
SELECT DISTINCT address FROM stu;
-- 查询姓名,数学信息,英语成绩
SELECT NAME AS '姓名',math AS '数学成绩',english AS '英语成绩' FROM stu;
条件查询(WHERE)
符号 功能
>
<
>=
<=
<> 或 != 不等于
BETWEEN … AND … 在某个范围之内(都包括)
IN(…) 多选一
LIKE 占位符 模糊查询 _单个任意字符 %d多个任意字符
IS NULL
IS NOT NULL
AND 或 &&
OR 或 \ \
NOT 或 !

示例

-- 1.查询年龄大于20岁的成员信息
SELECT id,NAME,age,sex,address,math,english,hire_date FROM stu WHERE age>20;
-- 2.查询年龄大于等于20岁的成员信息
SELECT id,NAME,age,sex,address,math,english,hire_date FROM stu WHERE age>=20;
-- 3.查询年龄大于等于20岁 并且 小于等于30岁的成员信息
SELECT id,NAME,age,sex,address,math,english,hire_date FROM stu WHERE age>=20&&age<=30;
-- 4.
SELECT id,NAME,age,sex,address,math,english,hire_date FROM stu WHERE hire_date>='1998-09-01'&&hire_date<='1999-09-01';
SELECT id,NAME,age,sex,address,math,english,hire_date FROM stu WHERE hire_date BETWEEN '1998-09-01' AND '1999-09-01';
-- 5.
SELECT id,NAME,age,sex,address,math,english,hire_date FROM stu WHERE age=18;
-- 6.
SELECT id,NAME,age,sex,address,math,english,hire_date FROM stu WHERE age!=18;
-- 7.
SELECT id,NAME,age,sex,address,math,english,hire_date FROM stu WHERE age=18||age=20||age=22;
SELECT id,NAME,age,sex,address,math,english,hire_date FROM stu WHERE age IN (18,20,22);
-- 8.
SELECT id,NAME,age,sex,address,math,english,hire_date FROM stu WHERE english IS NULL;
-- 1.
SELECT id,NAME,age,sex,address,math,english,hire_date FROM stu WHERE NAME LIKE '马%';
-- 2.
SELECT id,NAME,age,sex,address,math,english,hire_date FROM stu WHERE NAME LIKE '_花%';
-- 3.
SELECT id,NAME,age,sex,address,math,english,hire_date FROM stu WHERE NAME LIKE '%德%';
排序查询(ORDER BY)
SELECT 字段列表 FROM 表名 ORDER BY 排序字段名1 排序方式1,排序字段名2 排序方式2...;

排序方式:

  • ASC:升序排列(默认值)
  • DESC:降序排列

如果有多个排序条件,当前面的条件值一样时,才会根据第二条件进行排序

示例

-- 1.
SELECT * FROM stu ORDER BY age;
-- 2.
SELECT * FROM stu ORDER BY math DESC;
-- 3.
SELECT * FROM stu ORDER BY math DESC,english ASC;

正式工作中避免使用 *

聚合函数
SELECT 聚合函数名(列名) FROM 表;
函数名 功能
count(列名) 统计数量(一般选用不为null的列)
max(列名)
min(列名)
sum(列名)
avg(列名)
-- 1.
SELECT COUNT(NAME) FROM stu;
SELECT COUNT(NAME) AS '学生个数' FROM stu;
-- 2.
SELECT MAX(math) FROM stu;
-- 3.
SELECT MIN(math) FROM stu;
-- 4.
SELECT SUM(math) FROM stu;
-- 5.
SELECT AVG(math) FROM stu;
--6.
SELECT MIN(english) FROM stu;
分组查询(GROUP BY)
SELECT 字段列表 FROM 表名 [WHERE 分组前条件限定] GROUP BY 分组字段名 [HAVING 分组后条件过滤];

分组之后,查询的字段为聚合函数和分组字段,查询其他字段无任何意义

where和having区别:

  • 执行时机不一样:where时分组之前进行限定,不满足where条件,则不参加分组,而having是分组之后对结果进行过滤
  • 可判断的条件不一样:where不能对聚合函数进行判断,having可以

where > 聚合函数 > having

-- 1.
SELECT sex,AVG(math) FROM stu GROUP BY sex;
-- 2.
SELECT sex,AVG(math),COUNT(sex) FROM stu GROUP BY sex;
-- 3.
SELECT sex,AVG(math),COUNT(sex) FROM stu WHERE math>=70 GROUP BY sex;
-- 4.
SELECT sex,AVG(math),COUNT(sex) FROM stu WHERE math>=70 GROUP BY sex HAVING COUNT(sex)>2;
分页查询(LIMIT)
SELECT 字段列表 FROM 表名 LIMIT 起始索引,查询条目数;
  • 起始索引:从0开始

计算公式:起始索引=(当前页码-1) * 每页显示的条数

分页查询limit是MySQL的方言

Oracle分页时查询使用rownumber

SQL Server分页查询使用top

-- 1.从0开始,查询3条数据
SELECT*
FROMstu
LIMIT 0,3;-- 2.每页3条数据,查询第1页
SELECT*
FROMstu
LIMIT 3,3;-- 3.每页3条数据,查询第2页
SELECT*
FROMstu
LIMIT 6,3;

02 MySQL高级

02.1 约束

约束是作用于表中列上的规则,用于限制加入表中的数据

名称 描述 关键字
非空约束 保证所有数据不能有null值 NOT NULL
唯一约束 保证数据各不相同 UNIQUE
主键约束 主键是一行数据的唯一标识,要求非空且唯一 PRIMARY KEY
检查约束 保证列中的值满足某一条件 CHECK
默认约束 保存数据时,未指定值采用默认值 DEFAULT
外键约束 外键用来让两个表的数据之间建立链接 FOREIGN KEY

MySQL不支持检查约束

-- 主键约束:非空且唯一
INSERT INTO emp(id,ename,joindate,salary,bonus) VALUES (null,'张三','1999-11-11',8800,5000);
INSERT INTO emp(id,ename,joindate,salary,bonus) VALUES (1,'张三','1999-11-11',8800,5000);
-- 默认约束
INSERT INTO emp(id,ename,joindate,salary) VALUES (2,'李四','1999-11-11',8800)
INSERT INTO emp(id,ename,joindate,salary,bonus) VALUES (3,'王五','1999-11-11',8800,null);
-- 自增长
INSERT INTO emp(ename,joindate,salary,bonus) VALUES ('张三','1999-11-11',8800,null);
INSERT INTO emp(id,ename,joindate,salary,bonus) VALUES (null,'李四','1999-11-11',8800,null);

外键约束

  1. 添加约束
-- 创建表时添加外键约束
CREATE TABLE 表名(列名 数据类型,...[CONSTRAINT] [外键名称] FOREIGN KEY(外键列名) REFERENCES 主表(主表列名)
);
-- 建完表后添加外键约束
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY(外键字段名称) REFERENCES 主表名称(主表列名称);
  1. 删除约束
ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;

示例

-- 员工表中的dep_id关联部门表中的主键id,命名为fk_emp_dept
CONSTRAINT fk_emp_dept FOREIGN KEY(dep_id) REFERENCES dept(id)
ALTER TABLE emp DROP FOREIGN KEY fk_emp_dept;
ALTER TABLE emp ADD CONSTRAINT fk_emp_dept FOREIGN KEY(dep_id) REFERENCES dept(id);

02.2 数据库设计

软件的研发步骤

步骤 角色 成果
需求分析 产品经理 产品原型
设计 架构师 软件结构设计;数据库设计;接口设计;过程设计
编码 开发工程师
测试 测试工程师
安装部署 运维工程师

数据库设计概念

  • 根据业务系统的具体需求,结合选用的DBMS,为业务构造最优的数据存储模型
  • 建立数据库中表结构以及表与表之间关联关系的过程

数据库设计的步骤

  1. 需求分析
  2. 逻辑分析(通过ER图对数据库进行逻辑建模)
  3. 物理设计(创建表结构)
  4. 维护设计(对新需求进行设计、优化)

表关系

  • 一对一
  • 一对多(多对一)
  • 多对多

表关系之一对多

实现方式:在多的一方建立外键,指向一的一方的主键

表关系之多对多

实现方式:建立第三张中间表,中间表至少包括两个外键,分别关联两方主键

-- 订单表
CREATE TABLE tb_order(id int primary key auto_increment,payment double(10,2),payment_type TINYINT,status TINYINT
);-- 商品表
CREATE TABLE tb_goods(id int primary key auto_increment,title varchar(100),price double(10,2)
);-- 订单商品中间表
CREATE TABLE tb_order_goods(id int primary key auto_increment,order_id int,goods_id int,count int
);ALTER TABLE tb_order_goods ADD CONSTRAINT fk_order_id FOREIGN KEY(order_id) REFERENCES tb_order(id);
ALTER TABLE tb_order_goods ADD CONSTRAINT fk_goods_id FOREIGN KEY(goods_id) REFERENCES tb_goods(id);

表关系之一对一

实现方式:在任意一方加入外键,关联另一方主键,并且设置外键为唯一(UNIQUE)

练习

-- 专辑表
create table x_music(
title varchar(32),
alias varchar(32),
image varchar(64),
style varchar(8),
type varchar(4),
medium varchar(4),
publish_time date,
publisher varchar(16),
number tinyint,
barcode bigint,
summary varchar(1024),
artist varchar(16),
id int primary key
);-- 曲目表
create table x_song(
id int primary key,
name varchar(32),
serial_number tinyint,
music_id int
);-- 评论表
create table x_review(
content varchar(256),
rating tinyint,
review_time datetime,
music_id int,
user_id int
);-- 用户名表
create table x_user(
username varchar(16),
image varchar(64),
signature varchar(64),
nickname varchar(16),
id int primary key
);-- 用户评价的专辑
create table x_user_music(
id int primary key,
user_id int,
music_id int
);alter table x_song add constraint x_fk_music_song foreign key(music_id) references x_music(id);
alter table x_review add constraint x_fk_music_review foreign key(music_id) references x_music(id);
alter table x_review add constraint x_fk_user_review foreign key(user_id) references x_user(id);
alter table x_user_music add constraint x_fk_user_user_music foreign key(user_id) references x_user(id);
alter table x_user_music add constraint x_fk_music_user_music foreign key(music_id) references x_music(id);

02.3 多表查询-简介

消除无效数据

  • 连接查询

    • 内连接:相当于查询A和B的交集数据

    • 外连接

      • 左外连接:相当于查询A表所有数据和交集部分数据

      • 右外连接:相当于查询B表所有数据和交集部分数据

  • 子查询

02.4 多表查询-内连接&外连接

  1. 内连接查询语法
-- 隐式内连接
SELECT 字段列表 FROM 表1,表2... WHERE 条件;-- 显示内连接
SELECT 字段列表 FROM 表1 [INNER] JOIN 表2 ON 条件;

练习

-- 隐式内连接
-- 查询emp的name, gender, dept表的dname
select emp.name, emp.gender, dept.dname from emp, dept where emp.dep_id = dept.did;
-- 给表起别名
select t1.name, t1.gender, t2.dname from emp t1, dept t2 where t1.dep_id = t2.did;-- 显式内连接
select * from emp INNER JOIN dept on emp.dep_id = dept.did;
select * from emp JOIN dept on emp.dep_id = dept.did;
  1. 外连接
-- 左外连接
SELECT 字段列表 FROM 表1 LEFT [OUTER] JOIN 表2 ON 条件;-- 右外连接
SELECT 字段列表 FROM 表1 RIGHT [OUTER] JOIN 表2 ON 条件;

练习

-- 左外连接(用的更多)
select * from emp left join dept on emp.dep_id = dept.did;-- 右外连接
select * from emp right join dept on emp.dep_id = dept.did;

02.5 多表查询-子查询

  • 单行单列

    SELECT 字段列表 FROM 表 WHERE 字段名 = (子查询);
    
  • 多行单列

    SELECT 字段列表 FROM 表 WHERE 字段名 in (子查询);
    
  • 多行多列

    SELECT 字段列表 FROM (子查询) WHERE 条件;
    

示例

单行单列

-- 查询猪八戒的工资数据
SELECT salary FROM emp WHERE NAME = '猪八戒';
-- 查询工资高于猪八戒的员工姓名
SELECT NAME AS '姓名', salary AS '工资' FROM emp WHERE salary > 3600;-- 子查询
SELECT NAME AS '姓名', salary AS '工资' FROM emp WHERE salary > (SELECT salary FROM emp WHERE NAME = '猪八戒');

多行单列

SELECT * FROM emp WHERE dep_id IN (SELECT did FROM dept WHERE dname = '市场部' || dname = '财务部');

多行多列

SELECT * FROM (SELECT M emp WHERE join_date > '2011-11-11') t, dept WHERE t.dep_id = dept.did;

02.6 多表查询-案例

-- 1.查询所有员工信息。查询员工编号,员工姓名,工资,职务名称,职务描述
SELECT t1.id AS '员工编号', t1.ename AS '员工姓名', t1.salary AS '工资', t2.jname AS '职务名称', t2.description AS '职务描述' FROM emp t1, job t2 WHERE t1.job_id = t2.id;-- 2.查询员工编号,员工姓名,工资,职务名称,职务描述,部门名称,部门位置
SELECT t1.id AS '员工编号', t1.ename AS '员工姓名', t1.salary AS '工资', t2.jname AS '职务名称', t2.description AS '职务描述', t3.dname AS '部门名称', t3.loc AS '部门位置' FROM emp t1, job t2, dept t3 WHERE t1.job_id = t2.id && t1.dept_id = t3.id;-- 3.查询员工姓名,工资,工资等级
SELECT t1.ename AS '员工姓名', t1.salary AS '工资', t2.grade AS '工资等级' FROM emp t1, salarygrade t2 WHERE t1.salary < t2.hisalary && t1.salary > losalary;-- 4.查询员工姓名,工资,职务名称,职务描述,部门名称,部门位置,工资等级
SELECT t1.ename AS '员工姓名', t1.salary AS '工资', t2.jname AS '职务名称', t2.description AS '职务描述', t3.dname AS '部门名称', t3.loc AS '部门位置', t4.grade AS '工资等级' FROM emp t1, job t2, dept t3, salarygrade t4 WHERE t1.job_id = t2.id && t1.dept_id = t3.id && (t1.salary < t4.hisalary && t1.salary > t4.losalary);-- 5.查询出部门编号、部门名称、部门位置、部门人数
SELECT t1.id AS '部门编号', t1.dname AS '部门名称', t1.loc AS '部门位置', t2.count AS '部门人数' FROM (SELECT id, dname, loc FROM dept) t1, (SELECT dept_id, COUNT(dept_id) count FROM emp GROUP BY dept_id) t2 WHERE t1.id = t2.dept_id;

02.7 事务

事务是包含了一组数据库操作命令的一种机制、一种操作序列

-- 开启事务
START TRANSACTION;
或者 BEGIN;
-- 提交事务
COMMIT;
-- 回滚事物
ROLLBACK;

03 JDBC

03.1 JDBC 简介

Java DataBase Connectivity(Java 数据库连接)

步骤

  1. 注册驱动

  2. 获取连接

  3. 定义SQL语句

  4. 获取执行SQL对象

  5. 执行SQL

  6. 处理返回结果

  7. 释放资源

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;public class JDBCDemo {public static void main(String[] args) throws Exception {// 1.注册驱动Class.forName("com.mysql.jdbc.Driver");// 2.获取连接String url = "jdbc:mysql://127.0.0.1:3306/db1";String username = "root";String password = "1234";Connection conn = DriverManager.getConnection(url,username,password);// 3.定义SQLString sql = "update account set money = 2000 where id = 1";// 4.获取执行SQL的对象StatementStatement stmt = conn.createStatement();// 5.执行SQLint count = stmt.executeUpdate(sql); // 受影响的行数// 6.处理结果System.out.println(count);// 7.释放资源stmt.close();conn.close();}
}

03.2 JDBC API详解-DriverManager

  • DriverManager(驱动注册类)作用:

    • 注册驱动

    • 获取数据库连接

  1. 注册驱动

MySQL 5 以后Class.forName("com.mysql.jdbc.Driver")可不写

  1. 获取连接

参数

1. url:连接路径

语法:jdbc:mysql://ip地址(域名):端口号/数据库名称?参数键值对1&参数键值对2…
示例:jdbc:mysql://127.0.0.1:3306
细节:
如果本机服务器,端口3306,可简写为jdbc:mysql:///数据库名称?参数键值对1&参数键值对2…
配置useSSL = false参数,禁用安全连接方式,解决警告提示

2. user:用户名

3. password:密码

示例

String url = "jdbc:mysql:///db1?useSSL=false";
String username = "root";
String password = "1234";
Connection conn = DriverManager.getConnection(url,username,password);

03.3 JDBC API详解-Connection

作用

  1. 获取执行SQL的对象

  2. 管理事务

1. 获取执行SQL的对象

  • 普通执行SQL对象

    Statement createStatement()
    
  • 预编译SQL的执行SQL对象:防止SQL注入

    PreparedStatement prepareStatement(sql)
    
  • 执行存储过程的对象(不常用)

    CallableStatement prepareCall(sql)
    

2.管理事务

JDBC事务管理:Connection接口中定义了3个对应的方法

  • 开启事务:setAutoCommit(boolean autoCommit) autoCommit=true为自动提交;false为手动提交,即为开启事务

  • 提交事务:commit()

  • 回滚事务:rollback()

示例

import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;public class JDBCDemo3_Connection {public static void main(String[] args) throws Exception {String url = "jdbc:mysql:///db1?useSSL=false";String username = "root";String password = "1234";Connection conn = DriverManager.getConnection(url,username,password);// 定义sqlString sql1 = "UPDATE account SET money = 3000 WHERE id = 1";String sql2 = "UPDATE account SET money = 3000 WHERE id = 2";// 获取执行sql的对象StatementStatement stmt = conn.createStatement();try {// 开启事务conn.setAutoCommit(false);// 执行sql1int count1 = stmt.executeUpdate(sql1);System.out.println(count1);// 执行sql2int count2 = stmt.executeUpdate(sql2);System.out.println(count2);// 提交事务conn.commit();} catch (Exception throwables) {// 回滚事务conn.rollback();throwables.printStackTrace();}// 释放资源stmt.close();conn.close();}
}

03.4 JDBC API详解-Statement

int executeUpdate(sql):执行DML、DDL语句

  • 返回值:(1)DML语句影响的行数 (2)DDL语句执行后,执行成功也可能返回0
int count = stmt.executeUpdate(sql);
if (count > 0) {System.out.println("修改成功");
} else {System.out.println("修改失败");
}

ResultSet exexuteQuery(sql):执行DQL语句

  • 返回值:ResultSet结果集对象
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;public class JDBCDemo5_ResultSet {public static void main(String[] args) throws Exception {String url = "jdbc:mysql:///db1?useSSL=false";String username = "root";String password = "1234";Connection conn = DriverManager.getConnection(url,username,password);String sql = "SELECT * FROM account";Statement stmt = conn.createStatement();ResultSet rs = stmt.executeQuery(sql);while (rs.next()) {int id = rs.getInt(1);String name = rs.getString(2);double money = rs.getDouble(3);System.out.println(id);System.out.println(name);System.out.println(money);System.out.println("---------");}rs.close();stmt.close();conn.close();}
}

查询示例

对象

public class Account {private int id;private String name;private double money;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getMoney() {return money;}public void setMoney(double money) {this.money = money;}@Overridepublic String toString() {return "Account{" +"id=" + id +", name='" + name + '\'' +", money=" + money +'}';}
}

测试类

public class JDBCDemo5_ResultSet_Test {public static void main(String[] args) throws Exception {String url = "jdbc:mysql:///db1?useSSL=false";String username = "root";String password = "1234";Connection conn = DriverManager.getConnection(url,username,password);String sql = "SELECT * FROM account";Statement stmt = conn.createStatement();ResultSet rs = stmt.executeQuery(sql);ArrayList<Account> list = new ArrayList<>();while (rs.next()) {Account account = new Account();account.setId(rs.getInt(1));account.setName(rs.getString(2));account.setMoney(rs.getDouble(3));list.add(account);}for (int i = 0; i < list.size(); i++) {Account a = list.get(i);System.out.println(a.getId());System.out.println(a.getName());System.out.println(a.getMoney());}}
}

03.5 JDBC API详解-PreparedStatement-SQL注入

SQL注入:通过定义好的SQL语句实现执行代码对服务器进行攻击

String url = "jdbc:mysql:///db1?useSSL=false";
String username = "root";
String password = "1234";
Connection conn = DriverManager.getConnection(url,username,password);
String name = "aadwafqw";
String pwd = "' or '1' = '1";
String sql = "select * from tb_user where username='"+name+"' && password='"+pwd+"'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
if (rs.next()) {System.out.println("登陆成功");
} else {System.out.println("登陆失败");
}
rs.close();
stmt.close();
conn.close();

PrepareStatement

预编译SQL并执行SQL语句

  1. 获取 PreparedStatement 对象
// SQL语句中的参数值,使用 ? 占位符代替
String sql = "select * from user where username = ? and password = ?";

// 通过Connection对象获取,并传入对应的SQL语句
PreparedStatement pstmt = conn.preparedStatement(sql);

2. 设置参数值```java
PreparedStatement setXxx(?的位置编号, ?的值)
  1. 执行SQL
executeUpdate();/executeQuery();

示例

public static void main(String[] args) throws SQLException {String url = "jdbc:mysql:///db1?useSSL=false";String username = "root";String password = "1234";Connection conn = DriverManager.getConnection(url,username,password);String sql = "select * from tb_user where username = ? && password = ?";PreparedStatement pstmt = conn.prepareStatement(sql);String name = "张三";// String pwd = "' or '1' = '1";String pwd = "123";pstmt.setString(1,name);pstmt.setString(2,pwd);ResultSet rs = pstmt.executeQuery();if (rs.next()) {System.out.println("登陆成功");} else {System.out.println("登录失败");}rs.close();pstmt.close();conn.close();
}

03.6 练习

public static void main(String[] args) throws SQLException {// 数据库URLString url = "jdbc:mysql:///db1?useSSL=false";// 数据库用户名String username = "root";// 数据库密码String password = "1234";// 连接数据库Connection conn = DriverManager.getConnection(url,username,password);// SQL语句// String sqlCreate = "CREATE TABLE tb_brand(id INT, brand varchar(10))";// String sqlAdd = "INSERT INTO tb_brand VALUES(1, '华为'),(2, '小米');";// String sqlSelect = "SELECT * FROM tb_brand;";// String sqlChange = "UPDATE tb_brand SET brand = 'HUAWEI' WHERE id = 1";// String sqlDelete = "DELETE FROM tb_brand WHERE id = 1";/*// SQL语句配合 PreparedStatementString sqlPreparedStatement = "SELECT * FROM tb_brand WHERE brand = ?";PreparedStatement pStmt = conn.prepareStatement(sqlPreparedStatement);String name = "HUAWEI";pStmt.setString(1,name);ResultSet rs = pStmt.executeQuery();ArrayList<Brand> list = new ArrayList<>();while (rs.next()) {Brand b = new Brand();b.setId(rs.getInt(1));b.setBrand(rs.getString(2));list.add(b);}for (int i = 0; i < list.size(); i++) {Brand b = list.get(i);System.out.println(b.getId());System.out.println(b.getBrand());}*//*Statement stmt = conn.createStatement();*/// 改/*int count = stmt.executeUpdate(sqlChange);System.out.println(count);*/// 增/*int count = stmt.executeUpdate(sqlAdd);*/// 查/*ResultSet rs = stmt.executeQuery(sqlSelect);while (rs.next()) {System.out.println(rs.getInt(1));System.out.println(rs.getString(2));}rs.close();*/// 删/*int count = stmt.executeUpdate(sqlDelete);System.out.println(count);*/// stmt.close();// pStmt.close();conn.close();
}

04 Maven&MyBatis

04.1 Maven概述

功能:

  • 提供了一套标准化的项目结构

  • 提供了一套标准化的构建流程(编译、测试、打包、发布、…)

  • 提供了一套依赖管理机制

1. 提供了一套标准化的项目结构

所有IDE使用Maven构建的项目结构完全一样,可以通用

|maven-project // 项目名称
|--|src // 源代码和测试代码目录
|--|--|main // 源代码目录
|--|--|--|java // 源代码Java文件目录
|--|--|--|resources // 源代码配置文件目录
|--|--|--|webapp // Web项目核心目录
|--|--|test // 测试代码目录
|--|--|--|java // 测试Java文件目录
|--|--|--|resources // 测试配置文件目录
|--|pom.xml // 项目核心配置文件

3. 依赖管理

  1. 使用标准的坐标配置来管理各类依赖

  2. 只需要简单的配置就可以完成依赖管理

04.2 Maven简介

仓库分类:

  • 本地仓库:自己计算机上的一个目录

  • 中央仓库:由Maven团队维护的全球唯一的仓库

  • 远程仓库:一般由公司团队搭建的私有仓库

当项目使用坐标引入依赖jar包时,会先查询本地仓库,如果没有,再查询中央仓库

04.3 Maven安装&配置及基本使用

Maven安装配置

  1. 解压rar

  2. 配置环境变量

  3. 配置本地仓库

  4. 配置阿里云私服

<mirror><id>alimaven</id><name>aliyun maven</name><url>http://maven.aliyun.com/nexus/content/groups/public/</url><mirrorOf>central</mirrorOf>
</mirror>

Maven常用命令

  • compile:编译

  • clean:清理

  • test:测试

  • package:打包

  • install:安装

Maven生命周期

  • clean:清理工作

  • default:核心工作,例如编译、测试、打包、安装等

  • site:产生报告、发布站点等

04.4 IDEA配置Maven

  1. 选择IDEA中的设置

  2. 搜索maven

  3. 设置IDEA使用本地安装的Maven,并修改配置文件路径

Maven坐标

  • groupId:定义当前Maven项目隶属组织名称(通常是域名反写,例如:com.itheima)

  • artifactId:定义当前Maven项目名称(通常是模块名称,例如order-service、goods-service)

  • version:定义当前项目版本号

案例

<groupId>com.itheima</groupId>
<artifactId>maven-demo</artifactId>
<version>1.0-SNAPSHOT</version>

IDEA创建Maven项目

  1. 创建模块,选择Maven,点击Next

  2. 填写模块名称,坐标信息,点击finish,创建完成

IDEA导入Maven项目

  1. 选择右侧Maven面板,点击 + 号

  2. 选择对应项目的pom.xml文件,双击即可

04.5 依赖管理&依赖范围

  1. 在pom.xml中编写<dependencies>标签

  2. <dependencies>标签中使用<depency>引入坐标

  3. 定义坐标的groupId, artifactId, version

  4. 点击刷新按钮,使坐标生效

用Alt + Insert选择依赖可以快速选择jar包

依赖范围

依赖范围 编译 测试 运行 例子
compile Y Y Y logback
test - Y - Junit
provided Y Y - servlet-api
runtime - Y Y jdbc 驱动
system Y Y - 存储在本地的 jar 包
import

默认值:compile

04.1 MyBatis简介

MyBatis是一款优秀的持久层框架,用于简化 JDBC 开发

持久层

  • 负责将数据保存到数据库的那一层代码

  • JavaEE三层框架:表现层、业务层、持久层

框架

  • 半成品软件

  • 在框架的基础上更加高效、规范、通用、可拓展

04.2 快速入门

  1. 创建user表,添加数据

  2. 创建模块,导入坐标

  3. 编写 MyBatis 核心配置文件 --> 替换连接信息 解决硬编码问题

  4. 编写 SQL 映射文件 --> 统一管理sql语句,解决硬编码问题

  5. 编码

  6. 定义POJO类

  7. 加载核心配置文件,获取 SqlSessionFactory 对象

  8. 获取 SqlSession 对象,执行 SQL 语句

  9. 释放资源

04.4 Mapper代理开发

  1. 定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放置在同一目录下

Mapper接口:src/main/java中的com.cookies.mapper.XXX

SQL映射文件:src/main/resources中com.cookies.mapper.XXX.xml

  1. 设置SQL映射文件的namespace属性为Mapper接口全限定名

示例

<!--namespace:名称空间-->
<mapper namespace="com.cookies.mapper.UserMapper"><select id="selectAll" resultType="com.cookies.pojo.User">select *from tb_user;</select>
</mapper>
  1. 在Mapper接口中定义方法,方法名就是SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致

示例

public interface UserMapper {List<User> selectAll();
}
  1. 编码

  2. 通过SqlSession的getMapper方法获取Mapper接口的代理对象

*示例*```java
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
```
  1. 调用对应方法完成sql的执行
*示例*```java
List<User> users = userMapper.selectAll();
```

完整示例

public static void main(String[] args) throws IOException {// 1.加载mybatis的核心配置文件,获取SqlSessionFactoryString resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);// 2.获取SqlSession对象,用它来执行sqlSqlSession sqlSession = sqlSessionFactory.openSession();// 3.执行sql// List<User> users = sqlSession.selectList("test.selectAll");// 3.1 获取 UserMapper 接口的代理对象UserMapper userMapper = sqlSession.getMapper(UserMapper.class);List<User> users = userMapper.selectAll();System.out.println(users);// 4.释放资源sqlSession.close();
}

04.5 MyBatis核心配置文件

示例

<configuration><!--类型别名--><typeAliases><package name="com.cookies.pojo"/></typeAliases><!--environments:配置数据库连接环境信息,可以配置多个environment,通过default属性切换不同的environment--><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><!--数据库连接信息--><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/><property name="username" value="root"/><property name="password" value="1234"/></dataSource></environment></environments><mappers><!--加载SQL映射文件--><mapper resource="com/cookies/mapper/UserMapper.xml"/></mappers>
</configuration>

05 MyBatis

目标:分别通过配置和注解完成增删改查

功能列表清单

  1. 查询

  2. 查询所有数据

  3. 查看详情

  4. 条件查询

  5. 添加

  6. 修改

  7. 修改全部字段

  8. 修改动态字段

  9. 删除

  10. 删除一个

  11. 批量删除

MyBatis完成操作三步走

编写接口方法–>编写SQL–>执行方法

05.1 查询-查询所有&结果映射

查询所有数据

  1. 编写接口方法:Mapper接口
  • 参数:无

  • 结果:List<Brand>

List<Brand> selectAll();
  1. 编写SQL语句:SQL映射文件:
<select id="selectAll" resultType="brand">select * from tb_brand;
</select>
  1. 执行方法,测试

实体类属性名 和 数据库表列名 不一致时

<!--namespace:名称空间-->
<mapper namespace="com.cookies.mapper.BrandMapper"><!--数据库字段名称与实体类属性名称不同,不能自动封装数据方法一:起别名,缺点是重复编写方法二:sql片段方法三:resultMap--><!--原始代码<select id="selectAll" resultType="com.cookies.pojo.Brand">select * from tb_brand;</select>--><!--方法一<select id="selectAll" resultType="com.cookies.pojo.Brand">select id, brand_name as brandName, company_name as companyName, ordered, description, status from tb_brand;</select>--><!--方法二<sql id="brand_column">id, brand_name as brandName, company_name as companyName, ordered, description, status</sql><select id="selectAll" resultType="brand">select<include refid="brand_column"/>from tb_brand;</select>--><!--方法三--><!--id:唯一标识type:映射的类型,支持别名--><resultMap id="brandResultMap" type="brand"><!--id:完成主键字段的映射column:表的别名property:实体类的属性名result:完成一般字段的映射--><result column="brand_name" property="brandName"/><result column="company_name" property="companyName"/></resultMap><select id="selectAll" resultMap="brandResultMap">select * from tb_brand;</select>
</mapper>

05.2 查询-查看详情

  • 参数占位符:一般用#{ }

  • 参数类型:parameterType可以省略

  • 特殊字符处理:

    • 转义字符\ + 特殊字符

    • CDATA区CDATA区块中写上特殊字符

示例

main–java–com.cookies.mapper.BrandMapper.java

public interface BrandMapper {Brand selectById(int id);
}

main–resources–com.cookies.mapper.BrandMapper.xml

<mapper namespace="com.cookies.mapper.BrandMapper"><resultMap id="brandResultMap" type="brand"><result column="brand_name" property="brandName"/><result column="company_name" property="companyName"/></resultMap><select id="selectById" resultMap="brandResultMap">select * from tb_brand where id = #{id};</select>
</mapper>

test–java–com.cookies.test.MyBatisTest.java

public class MyBatisTest {@Testpublic void testSelectById() throws IOException {int id = 1;// 1.加载mybatis的核心配置文件,获取SqlSessionFactoryString resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);// 2.获取SqlSession对象SqlSession sqlSession = sqlSessionFactory.openSession();// 3.获取Mapper接口的代理对象BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);// 4.执行方法Brand brand = brandMapper.selectById(id);System.out.println(brand);// 5.释放资源sqlSession.close();}
}

05.3 查询-条件查询

SQL语句设置多个参数

  1. 散装参数:需要使用@Param("SQL中的参数占位符名称")

  2. 实体类封装参数

只需要保证SQL中的参数名 和 实体类属性名对应上,即可设置成功

  1. map集合

只需要保证SQL中的参数名 和 map集合的键的名称对应上,即可设置成功

示例-散装参数

  1. 编写Mapper接口方法 BrandMapper.java
public interface BrandMapper {List<Brand> selectByCondition(@Param("status")int status,@Param("companyName")String companyName,@Param("brandName")String brandName);
}
  1. 编写SQL映射文件 BrandMapper.xml
<mapper namespace="com.cookies.mapper.BrandMapper"><resultMap id="brandResultMap" type="brand"><result column="brand_name" property="brandName"/><result column="company_name" property="companyName"/></resultMap><select id="selectByCondition" resultMap="brandResultMap">select * from tb_brandwhere status = #{status} and company_name like #{companyName} and brand_name like #{brandName};</select>
</mapper>
  1. 测试
public class MyBatisTest {@Testpublic void testSelectById() throws IOException {// 接收参数int status = 1;String companyName = "华为";String brandName = "华为";// 处理参数companyName = '%' + companyName + '%';brandName = '%' + brandName + '%';// 1.加载mybatis的核心配置文件,获取SqlSessionFactoryString resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);// 2.获取SqlSession对象SqlSession sqlSession = sqlSessionFactory.openSession();// 3.获取Mapper接口的代理对象BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);// 4.执行方法List<Brand> brands = brandMapper.selectByCondition(status,companyName,brandName);System.out.println(brands);// 5.释放资源sqlSession.close();}
}

05.4 查询-动态条件查询

  • SQL语句会随着用户的输入或外部条件的变化而变化,成为 动态SQL

  • MyBatis对动态SQL有强大的支撑

    • if

    • choose(when,otherwise)

    • trim(where,set)

    • foreach

if

用于判断参数是否有值,使用test属性进行条件判断

  • 存在的问题:第一个条件不需要逻辑运算符

  • 解决方案:标签替换where关键字

示例

  1. Mapper接口
public interface BrandMapper {List<Brand> selectByCondition(Map map);
}
  1. SQL映射
<mapper namespace="com.cookies.mapper.BrandMapper"><resultMap id="brandResultMap" type="brand"><result column="brand_name" property="brandName"/><result column="company_name" property="companyName"/></resultMap><select id="selectByCondition" resultMap="brandResultMap">select *from tb_brand<where><if test="status != null">and status = #{status}</if><if test="companyName != null and companyName != ''">and company_name like #{companyName}</if><if test="brandName != null and brandName != ''">and brand_name like #{brandName}</if></where></select>
</mapper>
  1. 测试
public class MyBatisTest {@Testpublic void testSelectById() throws IOException {// 接收参数int status = 1;String companyName = "华为";String brandName = "华为";// 处理参数companyName = '%' + companyName + '%';brandName = '%' + brandName + '%';Map map = new HashMap();map.put("status",status);map.put("companyName",companyName);map.put("brandName",brandName);// 1.加载mybatis的核心配置文件,获取SqlSessionFactoryString resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);// 2.获取SqlSession对象SqlSession sqlSession = sqlSessionFactory.openSession();// 3.获取Mapper接口的代理对象BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);// 4.执行方法List<Brand> brands = brandMapper.selectByCondition(map);System.out.println(brands);// 5.释放资源sqlSession.close();}
}

单条件动态查询Choose

<select id="selectByConditionSingle" resultMap="brandResultMap">select *from tb_brand<where><choose><when test="status != null">status = #{status}</when><when test="companyName != null and companyName != ''">company_name like #{companyName}</when><when test="brandName != null and brandName != ''">brand_name like #{brandName}</when></choose></where>
</select>

05.5 查询-添加&修改功能

添加

  1. 编写接口方法:Mapper接口
void add(Brand brand);
  1. 编写SQL语句:SQL映射文件
<insert id="add">insert into tb_brand(brand_name, company_name, ordered, description, status)values (#{brandName},#{companyName},#{ordered},#{description},#{status})
</insert>
  1. 执行方法,测试

openSession()处可以更改事务设置(自动/手动)

// 2.获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();// 4.执行方法
brandMapper.add(brand);
sqlSession.commit();
// 2.获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);// 4.执行方法
brandMapper.add(brand);

添加-主键返回

在SQL映射中设置

<insert id="add" useGeneratedKeys="true" keyProperty="id">insert into tb_brand(brand_name, company_name, ordered, description, status)values (#{brandName},#{companyName},#{ordered},#{description},#{status})
</insert>
  1. 在测试中设置

修改-修改全部字段

步骤没太大变化

修改-修改动态字段

在SQL映射中设置

<update id="update">update tb_brand<set><if test="brandName != null and brandName != ''">brand_name = #{brandName},</if><if test="companyName != null and companyName != ''">company_name = #{companyName},</if><if test="ordered != null">ordered = #{ordered},</if><if test="description != null and description != ''">description = #{description},</if><if test="status != null">status = #{status},</if></set>where id=#{id};
</update>

05.6 查询-删除功能

删除单个

过程没太大变化

批量删除

  1. 编写接口方法:Mapper接口
  • 参数:id数组

  • 结果:void

void deleteByIds(@Param("ids")int[] ids);
  1. 编写SQL语句:SQL映射文件
<!--删除的个数为?的个数-->
<delete id="deleteByIds">delete from tb_brandwhere id in (?,?,?)
</delete>
<!--自动适应删除个数-->
<delete id="deleteByIds">delete from tb_brandwhere id in<foreach collection="ids" item="id" separator="," open="("close=")"> #{id}</foreach>
</delete>

mybatis会将数组参数,封装为一个Map集合

  • 默认:array = 数组

  • 使用@Param注解改变map集合的默认key的名称

05.7 参数传递

  • 单个参数

    1. POJO类型

    2. Map类型

    3. Collection

    4. List

    5. Array

    6. 其他类型

  • 多个参数:封装为Map集合

MyBatis提供了 ParamNameResolver 类来进行参数封装

建议:都使用@Param注解修改默认键名,并使用修改后的名称来获取值,增加可读性

05.8 使用注解完成增删改查

  • 查询:@Select

  • 添加:@Insert

  • 修改:@Update

  • 删除:@Delete

使用注解开发会比配置文件开发更加方便

@Select("select * from tb_user where id = #{id}")
public User selectById(int id);

一般用于简单语句的开发

07 JavaScript

本文涉及前端的部分主要是对 前端学习 专栏的补充

07.1 引入方式

外部脚本

定义外部 js 文件

在 html 中使用 script 标签的 src 属性指定 js 文件的 URL 路径

07.2 书写语法&输出语句

使用window.alert()写入警告框

使用document.write()写入HTML输出

使用console.log()写入浏览器控制台

07.3 变量&数据类型

var

  1. 作用域:全局变量

  2. 变量可以重复定义

let

  1. 作用域:只在let关键字所在代码块生效

  2. 不允许重复声明

const

用来声明一个只读变量,一经声明不能改变

数据类型

使用typeof获取变量类型

alert(typeof 变量名)

07.4 运算符

三元运算符:条件表达式?true_value:false_value

双等号和三等号

  • ==会先类型转换再比较

  • ===会直接判断,相当于全等于

类型转换

parseInt():String转number

07.7 自定义对象&Window对象

BOM

  • Browser Object Model 浏览器对象模型

  • JavaScript 将浏览器的各个组成部分封装为对象

  • 组成

    • Window:浏览器窗口对象

    • Navigator:浏览器对象

    • Screen:屏幕对象

    • History:历史记录对象

    • Location:地址栏对象

Window

  • Window:浏览器窗口对象

  • 获取:直接使用,window.可以省略

  • 属性:获取其他BOM对象

  • 方法

    方法名 说明
    alert() 显示带有一段消息和一个确认按钮的警告框
    confirm() 显示带有一段消息和一个确认、取消按钮的警告框
    setInterval() 按照指定的周期(以毫秒计)来调节函数或表达式
    setTimeout() 设置毫秒后的时延,只执行一次

示例

<script>// var flag = confirm("确认?");// alert(flag);// setTimeout(function() {//     alert("弹出")// },3000)setInterval(function () {alert("弹出" + i + "次")}, 3000)
</script>

07.8 定时器案例

<body><img src="../img/86010297_p0.png" alt="roon" id="image"><script>var flag = true;setInterval(function () {var img = document.getElementById("image");if (flag) {img.src = "../img/86010297_p1.png"} else {img.src = "../img/86010297_p0.png";}flag = !flag;}, 2000)</script><style>#image {width: 300px}</style>
</body>

07.9 Location对象

href控制界面跳转

<script>var i = 3;setInterval(function () {document.write("剩余" + i + "秒跳转到百度");document.write('\n');i--;}, 1000)setTimeout(function () {location.href = "http://www.baidu.com"}, 3000)
</script>

07.10 DOM

对于通过element操作style

只需要在得到的返回值后面加style即可

<body><img src="../img/99684713_p0.jpg" alt="" id="image"><script>var count = 0;setInterval(function () {let image = document.getElementById("image");switch (count % 3) {case 0:image.style.width = "auto";// image.style.setProperty('width', 'auto');break;case 1:image.style.setProperty('width', '700px');break;case 2:image.style.setProperty('width', '500px');break;}count++;}, 1000)</script>
</body>

07.11 事件监听

示例

<body><div><button id="button">点击切换</button></div><div><img src="../img/86010297_p0.png" alt="roon" id="image"></div><script>var flag = true;var button = document.getElementById("button");var img = document.getElementById("image");button.onclick = function () {if (flag) {img.src = "../img/86010297_p1.png"} else {img.src = "../img/86010297_p0.png";}flag = !flag;}</script><style>#image {width: 300px}</style>
</body>

07.12 表单验证

07.13 正则表达式

  1. 直接量:不加引号
var reg = /^\w{6,12}$/;
  1. 创建RegExp对象
var reg = new RegExp("^\\w{6,12}$");
正则表达式.test(被检测变量);

示例

<script>var string = "123";var reg = /^\w{6,12}$/;if (reg.test(string)) {console.log("符合");} else {console.log("不符合");}
</script>

08 HTTP&Tomcat&Servlet

08.1 Web核心介绍

JavaWeb技术栈

  • B/S架构:浏览器 / 服务器

  • 静态资源:HTML、CSS、JavaScript、图片等。负责页面展示

  • 动态资源:Servlet、JSP等。负责逻辑处理

  • 数据库:负责存储数据

学习安排

  1. HTTP、Tomcat、Servlet

  2. Request(请求)、Response(响应)

  3. JSP、会话技术(Cookie、Session)

  4. Filter(过滤器)、Listener(监听器)

  5. Ajax、Vue、ElementUI

  6. 综合案例

08.2 HTTP简介

特点

  1. 基于TCP:面向连接,安全

  2. 基于请求-响应模型:一次请求对应一次响应

  3. HTTP协议是无状态的协议:没有记忆能力,每次请求-响应都是独立的

  • 缺点:多次请求间不能共享数据,需要通过会话技术解决(Cookie、Session)

  • 优点:速度快

08.3 HTTP-请求数据格式

请求数据分为3部分

  1. 请求行:请求数据的第一行。

其中:

  • GET表示请求方式

  • /表示请求资源路径

  • HTTP/1.1表示协议版本

  1. 请求头:第二行开始,格式为key: value形式

  2. 请求体:POST请求的最后一部分,存放请求参数

GET和POST的区别

    • GET请求请求参数在请求行中,没有请求体
  • POST请求请求参数在请求体中
  1. GET请求请求参数大小有限制,POST没有

示例

<body><!-- <form action="#" method="get"><label for="">用户名:</label><input type="text" name="" id=""><br><label for="">密码:</label><input type="password" name="" id=""><br><input type="submit" value="确认"></form> --><form action="#" method="post"><label for="">用户名:</label><input type="text" name="" id=""><br><label for="">密码:</label><input type="password" name="" id=""><br><input type="submit" value="确认"></form>
</body>

查看开发者工具的网络即可

08.4 HTTP-响应数据格式

请求数据分为3部分

  1. 响应行:响应数据的第一行

其中:

  • HTTP/1.1表示协议版本

  • 200表示响应状态码,OK表示状态码描述

  1. 响应头:第二行开始,格式为key: value形式

  2. 响应体:最后一部分,存放响应参数

08.5 Tomcat-简介&基本使用

  • 开源的轻量级Web服务器,支持 Servlet / JSP 少量 JavaEE 规范

  • Tomcat也被称为Web容器、Servlet容器。Servlet需要依赖Tomcat才能运行

使用

双击startup.bat即可

关闭

Ctrl + C

08.6 Tomcat-配置和部署项目

修改启动端口号

在conf/server.xml中修改port即可

<Connector port="8080" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443" />

部署项目

  • 将项目放置到 webapps 目录下,即部署完成

  • 一般是打包成 war,会自动解压

08.7 Tomcat-Web项目结构

Maven Web 项目结构:开发中的项目

|--|hello(项目名称)
|--|--|src #主目录
|--|--|--|main
|--|--|--|--|java #Java代码
|--|--|--|--|resources #资源文件
|--|--|--|--|webapp #Web项目特有目录
|--|--|--|--|--|html #HTML文件目录(可自定义)
|--|--|--|--|--|WEB-INF #Web项目核心目录(必须叫这个名称)
|--|--|--|--|--|--|web.xml #Web项目配置文件
|--|--|--|test #测试目录
|--|--|--|pom.xml

部署的JavaWeb项目结构:开发完成,可以部署的项目

|--|hello #项目访问路径(虚拟目录)
|--|--|html #HTML文件目录(可自定义)
|--|--|WEB-INF #Web项目核心目录(必须叫这个名称)
|--|--|--|classes #java和resources
|--|--|--|lib #项目所需jar包
|--|--|--|web.xml #Web项目配置文件

08.8 Tomcat-创建MavenWeb项目

  • 使用骨架

    1. 选择web项目骨架,创建项目

    2. 删除pom.xml中多余的坐标

    3. 补齐缺失的目录结构

  • 不使用骨架

08.9 Tomcat-IDEA集成本地Tomcat

运行/调试配置

08.10 Tomcat-Tomcat的Maven插件

  1. pom.xml添加Tomcat插件
<build><plugins><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version></plugin></plugins>
</build>
  1. 使用Maven Helper插件快速启动项目,选中项目,右键 --> Run Maven -> tomcat7:run

可以增加设置,让浏览器访问更为方便

<build><plugins><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version><configuration><port>80</port><path>/</path></configuration></plugin></plugin

08.11 Servlet简介&快速入门

  • Java提供的一门动态web资源开发技术

  • 是JavaEE规范之一,其实就是一个接口,将来会定义Servlet类实现Servlet接口,并由web服务器运行Servlet

快速入门

  1. 创建web项目,导入Servlet依赖坐标
<dependencies><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.0.1</version><scope>provided</scope></dependency>
</dependencies>
<build><plugins><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version></plugin></plugins>
</build>
  1. 创建:定义一个类,实现Servlet接口,并重写接口中所有方法,并在service方法中输入一句话
public class ServletDemo1 implements Servlet {@Overridepublic void init(ServletConfig servletConfig) throws ServletException {}@Overridepublic ServletConfig getServletConfig() {return null;}@Overridepublic void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {System.out.println("servlet hello world");}@Overridepublic String getServletInfo() {return null;}@Overridepublic void destroy() {}
}
  1. 配置:在类上使用@WebServlet注解,配置该Servlet的访问权限
@WebServlet("/demo1")
public class ServletDemo1 implements Servlet {}
  1. 访问:启动Tomcat,浏览器输入URL访问该Servlet
localhost:8080/web-demo/demo1

08.12 Servlet执行流程&生命周期

由web服务器创建

生命周期

  1. 加载和实例化:创建Servlet对象

  2. 初始化:调用init()方法

  3. 请求处理:调用service()方法

  4. 服务终止:调用destory()方法

public void init(ServletConfig servletConfig) throws ServletException {System.out.println("init...");
}
public ServletConfig getServletConfig() {return null;
}
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {System.out.println("hello servlet");
}
public String getServletInfo() {return null;
}
public void destroy() {System.out.println("destroy...");
}

08.13 Servlet方法介绍&体系结构

将config传递给其他方法

private ServletConfig config;
@Override
public void init(ServletConfig servletConfig) throws ServletException {this.config = config;System.out.println("init...");
}
public ServletConfig getServletConfig() {return null;
}
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {System.out.println("hello servlet");
}
public String getServletInfo() {return config;
}
public void destroy() {System.out.println("destroy...");
}

通过HTTPServlet实现service

@WebServlet("/demo3")
public class ServletDemo3 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("get");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("post");}
}
<body>
<form action="/web-demo/demo3" method="post"><input name="username"><input type="submit">
</form>
</body>

08.15 Servlet urlPattern配置

  • Servlet想被访问,必须配置访问路径

    1. 一个Servlet可以配置多个urlPattern
    @WebServlet(urlPatterns = {"/demo4","/demo5"})
    public class ServletDemo4 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) {System.out.println("get...");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) {System.out.println("post...");}
    }
    
    1. 配置规则

    2. 精确匹配@WebServlet("/user/select")

    3. 目录匹配@WebServlet("/user/*")

    4. 扩展名匹配@WebServlet("*.do")

    5. 任意匹配@WebServlet("/")@WebServlet("*")

08.16 XML配置Servlet

老版本方法,现在不常用,步骤为:

  1. 编写Servlet类

  2. 在pom.xml中配置

09  Request&Response

09.1 Request继承体系

ServletRequest Java提供的请求对象根接口

HTTPServletRequest Java提供的对Http协议封装的请求对象接口

RequestFacade Tomcat定义的实现类

  1. Tomcat需要解析请求数据,封装为request对象,并且创建request对象传递到service方法中

  2. 使用request对象,查阅JavaEE API文档的HttpServletRequest接口

09.2 Request获取请求数据-请求行&请求头&请求体

  • 请求数据分为3部分

    1. 请求行:GET /request-demo/req1 HTTP/1.1
    • String getMethod():获取请求方式:GET

    • String getContextPath():获取虚拟目录(项目访问路径):/request-demo

    • StringBuffer getRequestURL():获取URL(统一资源定位符):http://localhost/request-demo/req1

    • String getRequestURI():获取URI(统一资源标识符):/request-demo/req1

    • String getQueryString():获取请求参数(GET方式):username=zhangsan&password=123

    1. 请求头:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/XXX.XX (KHTML, like Gecko) Chrome/XXX.X.XXXX.XX Safari/XXX.XX Edg/XXX.X.XXXX.XX
    • Strin getHeader(String name):根据请求头名称,获取值
    1. 请求体:username=zhangsan&password=123
    • BufferedReader getReader():获取字符输入流

    • ServletInputStream getInputStream():获取字节输入流

09.3 Request通用方法获取请求参数

Map

  • Map<String,String[]> getParameterMap():获取所有参数Map集合

  • String[] getParameterValues(String name):根据名称获取参数值(数组)

  • String getParameter(String name):根据名称获取参数值(单个值)

个人心得

  1. GET简易地请求,会在地址栏显示提交地内容,但是可以被记录

  2. POST适用于加密传输的请求方式,在请求同时给服务器传递一部分信息

案例

req.html

<form action="/request-demo/req2" method="post"><input type="text" name="username"><br><input type="password" name="password"><br><input type="checkbox" name="hobby" value="1">游泳<input type="checkbox" name="hobby" value="2">爬山<br><input type="submit">
</form>

RequestDemo2.java

@WebServlet("/req2")
public class RequestDemo2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) {// 1. 获取所有参数Map集合Map<String, String[]> map = req.getParameterMap();for (String key : map.keySet()) {// 获取键System.out.println(key + ":");// 获取值String[] values = map.get(key);for (String value : values) {System.out.println(value + " ");}}System.out.println("--------------");// 2. 根据key获取参数值,数组String[] hobbies = req.getParameterValues("hobby");for (String hobby : hobbies) {System.out.println(hobby);}System.out.println("---------------");// 3. 根据名称获取参数值,单个值String username = req.getParameter("username");System.out.println(username);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) {this.doGet(req,resp);}
}

09.4 IDEA模板创建Servlet

可以直接右键新建

09.5 Request请求参数中文乱码-POST解决方案

request.setCharacterEncoding("UTF-8"); // 设置下编码方式即可
String password = request.getParameter("username");
System.out.println(password);

09.6 Request请求参数中文乱码-GET解决方案

@WebServlet("/req3")
public class RequestDemo3 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {request.setCharacterEncoding("UTF-8");String username = request.getParameter("username");System.out.println("解决前");System.out.println(username);// GET中文乱码问题// 原因:tomcat进行URL编码时,默认字符集为ISO-8859-1// 1. 先对乱码数据编码,转为字节数组byte[] bytes = username.getBytes(StandardCharsets.ISO_8859_1);// 2. 字节数组编码username = new String(bytes, StandardCharsets.UTF_8);System.out.println("解决后");System.out.println(username);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

简化

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {request.setCharacterEncoding("UTF-8");String username = request.getParameter("username");System.out.println("解决前");System.out.println(username);// GET中文乱码问题// 原因:tomcat进行URL编码时,默认字符集为ISO-8859-1// 1. 先对乱码数据编码,转为字节数组// 2. 字节数组编码username = new String(username.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);System.out.println("解决后");System.out.println(username);
}

09.7 Request请求转发

  • 实现方式

    req.getRequestDispatcher("资源B路径").forword(req,resp);
    
  • 请求转发资源间共享数据:使用Request对象

    • void setAttribute(String name, Object o);存储数据到request域中

    • Object getAttribute(String name);根据key,获取值

    • void removeAttribute(String name);根据key,删除该键值对

示例

System.out.println("demo4...");
// 存储数据
request.setAttribute("msg", "hello");
// 请求转发
request.getRequestDispatcher("/req5").forward(request, response);
System.out.println("demo5...");
// 获取数据
Object msg = request.getAttribute("msg");
System.out.println(msg);
// 删除键值对
request.removeAttribute("msg");
msg = request.getAttribute("msg");
System.out.println(msg);

转发时地址栏不变

09.8 Response设置响应数据&重定向

响应数据分为3部分

  1. 响应行:
  • 设置响应状态码
  1. 响应头
  • 设置响应头键值对
  1. 响应体
  • 获取字符输出流

  • 获取字节输出流

重定向

一种资源跳转方式

示例

// 重定向
// // 1.设置响应状态码 302
// response.setStatus(302);
// // 2.设置响应头 Location
// response.setHeader("Location", "/request-demo/resp2");
// 简化方式完成重定向
response.sendRedirect("/request-demo/resp2");

重定向与请求转发的区别

  • 重定向

    • 可以重定向到任意位置

    • 两次请求,不能再多个资源使用request共享数据

  • 请求转发

    • 只能转发到当前服务器

    • 一次请求,可以在转发的资源间共享数据

09.9 资源路径问题

  • 浏览器使用:使用虚拟目录

  • 服务端使用:不需要加虚拟目录

// 动态获取虚拟目录
String contextPath = request.getContextPath();
// 简化方式完成重定向
response.sendRedirect(contextPath + "/resp2");

09.10 Response响应字符&字节数据

响应字符数据

// 设置文字类型与编码方式
response.setContentType("text/html; charset = utf-8");
// 调用getWriter
PrintWriter writer = response.getWriter();
// 写入writer
writer.write("你好");
writer.write("<h1>abc</h1>");

响应字节数据

// 1.读取文件
FileInputStream fileInputStream = new FileInputStream("");
// 2.获取response字节输出流
ServletOutputStream outputStream = response.getOutputStream();
// 3.完成流的copy
byte[] bytes = new byte[1024];
int length;
while ((length = fileInputStream.read(bytes)) != -1) {outputStream.write(bytes, 0, length);
}
fileInputStream.close();

引入commons-io后更方便

<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version>
</dependency>
// 1.读取文件
FileInputStream fileInputStream = new FileInputStream("");
// 2.获取response字节输出流
ServletOutputStream outputStream = response.getOutputStream();
// 3.完成流的copy
/* byte[] bytes = new byte[1024];
int length;
while ((length = fileInputStream.read(bytes)) != -1) {outputStream.write(bytes, 0, length);
}*/
IOUtils.copy(fileInputStream, outputStream);
fileInputStream.close();

09.11 案例-用户登录-准备环境&代码实现

准备环境

  1. 复制静态页面到项目的webapp目录下

  2. 创建db数据库,创建tb_user表,创建User实体类

  3. 导入MyBatis坐标,MySQL驱动坐标

  4. 创建mybatis-config.xml核心配置文件,UserMapper.xml映射文件,UserMapper接口

流程说明

  1. 用户填写用户名密码,提交到LoginServlet

  2. 在LoginServlet中使用MyBatis查询数据库,验证用户名密码是否正确

  3. 如果正确,响应“登陆成功”;如果错误,响应“登陆失败”

09.12 案例-用户注册

流程说明

  1. 用户填写用户名密码,点击注册,提交到RegisterServlet

  2. 在RegisterServlet中使用MyBatis保存数据

  3. 保存前,判断用户名是否已经存在

09.13 SqlSessionFactory工具类抽取

将重复的SqlSessionFactory抽取为一个工具类,避免重复

SqlSessionFactoryUtils

public class SqlSessionFactoryUtils {private static SqlSessionFactory sqlSessionFactory;static {// 静态代码块会随着类的加载而自动执行,且只执行一次String resource = "mybatis-config.xml";InputStream inputStream = null;try {inputStream = Resources.getResourceAsStream(resource);} catch (IOException e) {e.printStackTrace();}sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);}public static SqlSessionFactory getSqlSessionFactory() {return sqlSessionFactory;}
}

Servlet

// 2.调用MyBatis完成查询
// 2.1 获取SqlSessionFactory对象(直接从官网粘过来即可)
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();

后端学习 Java Web 上半部分(MySQL~RequestResponse)相关推荐

  1. 后台是怎么判断管理员用户还是普通用户_深入学习 Java Web(九) :用户管理系统...

    本文转自与博客园一杯凉茶的博客. 前面学习了一大堆,什么JSP,Servlet.jstl.el等等等,大多是一些死的东西,只要会其语法,知道怎么用就行了,所以做了一个小小的只有增删改查的小demo,为 ...

  2. 获取mysql可行方法_Mysql学习Java实现获得MySQL数据库中所有表的记录总数可行方法...

    <Mysql学习Java实现获得MySQL数据库中所有表的记录总数可行方法>要点: 本文介绍了Mysql学习Java实现获得MySQL数据库中所有表的记录总数可行方法,希望对您有用.如果有 ...

  3. java将xml中的标签名称转为小写_深入学习Java Web(七): JSTL标签库

    本文转自与博客园一杯凉茶的博客. 在之前我们学过在JSP页面上为了不使用脚本,所以我们有了JSP内置的行为.行为只能提供一小部分的功能,大多数的时候还是会用java脚本,接着就使用了EL表达式,基本上 ...

  4. 学习java web感想_学了近一个月的java web 感想

    对于每天学习的新知识进行一定的总结,是有必要的. 之前我学的每一门知识,我都没有怎么总结自己的问题,也没有怎么去想想该怎样才能学的更好,把知识掌握的更牢固.从现在开始呢,我会每半个月,或每一个月总结总 ...

  5. 小猿圈之学习java web需要有什么基础?

    有很多人想学java,因为java虽然已经火了多年,但是其发展前景还是很不错的,有很大一部分人选择走java web方向,那么java web应该提前掌握哪些知识呢?小猿圈加加下面说一下. 首先找准自 ...

  6. 根据实践经验,讲述些学习Java web能少走的弯路,内容摘自java web轻量级开发面试教程...

    在和不少比较上进的初级程序员打交道的过程中,我们总结出了一些能帮到合格程序员尽快进阶的经验,从总体上来讲,多学.多实践不吃亏.本文来是从 java web轻量级开发面试教程从摘录的. 1  哪些知识点 ...

  7. 学习(Java Web)编程技术要点及方向; 完成项目的要决

    本文亮点: 传统学习编程技术落后,应跟著潮流,要对业务聚焦处理. 要Jar, 不要War:以小为主,以简为宝,集堆而成. 去繁取简 Spring Boot,明日之春. 集堆综合技术如 jHipster ...

  8. 给学习java web新手们的建议和推荐一些书籍

    经常有人问我新手应该看哪些书籍,怎么样才行学好J2EE.其实各人都各人的学习方法,一个人的不一定适合另一个人,我在这里就我的学习路径,给大家列举一下,希望能提供一些参考.(申明:这里我不谈首先学习什么 ...

  9. javaweb课堂练习继承与重写怎么做_深入学习Java Web(一):深入了解Servlet

    本文转自与博客园一杯凉茶的博客. Servlet是一种古老的Java Web技术,在开发中除了祖传的项目,已经很少见到它的身影,但是作为Java Web的重要组成部分,Servlet还是值得深入学习的 ...

  10. java web项目——MySQL中文乱码问题解决

    在我们开发java web项目使用MySQL数据库时,常会遇到中文乱码的问题,比如:使用MySQL命令行查询数据库中的表显示中文乱码:使用jdbc访问数据库读取到中文乱码:在jsp页面显示中出现中文乱 ...

最新文章

  1. Python代码编写中的性能优化点
  2. ROM微型计算机是什么,在微型计算机中,ROM是().
  3. 解决打开虚拟机 VMware Workstation 报错无法改变虚拟机的电源状态 Operation inconsistent with current state问题
  4. 探寻浏览器渲染的秘密
  5. 模式匹配 怎么匹配减号_如何使您的应用导航与用户的思维模式匹配
  6. POJ2182-Lost Cows【树状数组,二分】
  7. matlab打开笔记本摄像头_matlab窗口调用摄像头
  8. 砥志研思SVM(二) 拉格朗日乘子法与KKT条件
  9. suse12安装详解
  10. 使用 ADO.NET连接SQL Azure
  11. Cocos2d-x 3.2:定时器的使用和原理探究(2)
  12. Storm Control
  13. 图片轮流翻转,一直循环
  14. Vue中使用Video标签播放 <解析后的短视频>去水印视频无响应
  15. Msql自定义函数和存储过程
  16. MATLAB--数字图像处理 图像直方图规定化
  17. 员工考勤软件用哪款比较好啊?快看这4款实用考勤软件
  18. 很舒服的几句话,心静,人就不会累了
  19. 天津达内可靠么 老员工揭秘真实的达内教育
  20. 云计算产品学习(1)

热门文章

  1. 2021-01-19第二次Sping Boot学习汇报--利用用Spring Initializr构建项目
  2. 京东商品详情查询接口V1新版接口
  3. 华为交换机配置ntp服务时间 自动同步不成功unsynchronized
  4. cannot import name 'izip_longest'
  5. C++ 游戏开发(一)图形库EasyX的安装及测试
  6. 已解决Python爬虫网页中文乱码问题
  7. ols残差_多元回归方程的OLS残差
  8. hdu 4311 4312 Meeting point 曼哈顿距离之和最小
  9. 数学_余弦距离不满足三角不定式简单证明
  10. Python报错解决:local variable ‘xxx‘ referenced before assignment