视频笔记

JavaScript 基础语法

一、面向对象编程

1.1 什么是面向对象

JavaScript 、Java、c#、、、、面向对象:JavaScript有区别!

  • 类:模板 原型对象
  • 对象:具体的实例

在JavaScript中这个需要大家换一下思维

 var xiaoming={
        name:'xiaoming'
    }
    var bird={
        fly:function () {
            console.log(this.name+"fly....");
        }
    }

    //小明的原型是user
    xiaoming.__proto__=bird;

Class继承

  • Class 关键字,在ES6引入的
  1. 定义一个类,属性,方法
  //定义一个全新得类
    class Student{
        constructor(name){
            this.name=name;
        }
        hello(){
            alert('hello')
        }
    }
    var xiaoming= new Student("xiaoming");
    var xiaohong= new Student("xiaohong");
    xiaoming.hello()
  1. 继承
class XiaoStudent extends Student{
        constructor(name,grade){
            super(name);
            this.grade=grade;
        }
        myGrade(){
            alert('我是一名小学生')
        }
    }
    var xiaoming= new Student("xiaoming");
    var xiaohong= new XiaoStudent("xiaohong",1);
  • 本质:查看对象原型
XiaoStudent {name: "xiaohong", grade: 1}
grade: 1
name: "xiaohong"
__proto__: Student
constructor: class XiaoStudent
myGrade: ƒ myGrade()
__proto__: Object

原型链

proto:

二、 操作BOM对象(重点)

浏览器介绍

  1. JavaScript和浏览器的关系?
  • JavaScript诞生就是为了能够让他在浏览器中运行

BOM:浏览器对象模型

  • IE 6-11
  • Chrome
  • Sarfari
  • FireFox Linux
  • Opera

三方浏览器

  • QQ浏览器
  • 360浏览器

window 代表 浏览器窗口

window.innerHeight
360
window.innerWidth
986
window.outerHeight
882

Navigator

  • 封装了浏览器信息
window.navigator.appName
"Netscape"
navigator.platform
"Win32"
navigator.userAgent
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.92 Safari/537.36"
  • 大多数时候我们不会使用navigator对象,因为会被人修改,不建议使用这些属性来判断和编写代码

screen

//代表屏幕尺寸
screen.width
1536
screen.height
864

location(重要)
location代表当前页面的URL信息

  • 主机:host: “www.baidu.com”
  • href: “https://www.baidu.com/”
  • 协议:protocol: “https:”
  • 刷新网页: reload: ƒ reload()
  • 设置新的地址:location.assign(‘http://blog.kuangstudy.com/’)

document

  • document代表当前的页面,HTML DOM文档树
  • 获取具体的文档树结点
<dl id="app">
    <dt>Java</dt>
    <dd>JavaSE</dd>
    <dd>JavaEE</dd>

</dl>
<script>
    var dl=document.getElementById('app');
</script>
  • 获取cookie
document.cookie
  • 劫持cookie原理
<script src="aa.js"></script>
<!-- 恶意人员:获取你的cookie上传到他的服务器-->
  • 服务器端可以设置cookie: httpOnly

history(不建议使用)

  • history代表浏览器的历史记录

三、操作对象DOM对象(重点)

  • DOM:文档对象模型

核心

  • 浏览器网页就是一个Dom树形结构
  • 更新:更新Dom节点
  • 遍历Dom节点:得到Dom节点
  • 添加:添加一个Dom节点
  • 要操作一个Dom节点。就必须要先获得这个Dom节点

获得Dom节点

   var h1=document.getElementsByTagName('h1');
    var h1=document.getElementById('p1');
    var h1=document.getElementsByClassName('p2');
    var father=document.getElementById('father');
    var childerns=father.children;//获取父节点下的所有子节点
    //father.firstChild;
  • 这是原生代码,之后用jQuery

更新节点

<div id="id1">

</div>
<script>
    var id1=document.getElementById('id1');

操作文本

- id.innertText='456' 修改文本的值
- id.innerHTML='<strong>123</strong>' 可以解析html

操作css

id1.style.color='red'
"red"
id1.style.fontSize='20px'
"20px"
id1.style.fontSize='200px'
"200px"

删除节点

  • 删除节点的步骤
  • 先获取父节点,在删除自己
<div id="father">
    <h1>标题一</h1>
    <p id="p1">p1</p>
    <p class="p2">p2</p>
</div>
<script>
    var self=document.getElementById('p1');
    var father=p1.parentElement;
    father.removeChild(self)
</script>
  • 注意:删除多个节点的时候,children属性是时刻变化的,删除节点的时候一定要注意

插入节点

  • 我们获得了某个节点,假设这个dom节点是空的,我们通过innerHtml就可以增加一个元素了,但这个Dom节点已近存在元素了,我们就不能着呢干!会产生覆盖

追加:

<p id="js">JavaScript</p>
<div id="list">
    <p id="se">JavaSE</p>
    <p id="ee">JavaSE</p>
    <p id="me">JavaME</p>
</div>
<script>
     var js= document.getElementById('js');
     var list=document.getElementById('list');
     list.appendChild(js);
</script>

创建一个新标签

 var js= document.getElementById('js');
    var list=document.getElementById('list');
    //通过js创建一个新的节点
    var newP =document.createElement('p');//创建一个p标签
    newP.id='newP';
    newP.innerText='Hello,zxh';
    list.appendChild(newP);

四、操作表单(验证)

表是什么 form DOM树

  • 文本框: text
  • 下拉框:
  • 单选框: radio
  • 多选框: checkbox
  • 隐藏框: hidden
  • 密码框: password

表单的目的:提交信息

<form action="post">
   <p>
       <span>用户名:</span><input type="text"id="username">
   </p>
    <p>
        <span>性别:</span>
        <input type="radio" name="sex" value="man" id="boy"><input type="radio" name="sex" value="woman" id="girl"></p>
</form>
<script>
   var input_text= document.getElementById('username');
   var boy_radio= document.getElementById('boy');
   var girl_radio= document.getElementById('girl');
   //对于单选框,多选框等等固定的值,boy_radio.value只能收到当前的值
   //girl_radio.checked;查看返回值的结果,是否为true,如果为true,则被选中
   //girl_radio.checked=true;赋值
   //得到输入框的代码
    //修改输入框代码
   input_text='123'
</script>

提交表单 MD5加密 及代码优化

    <script src="https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.min.js"></script>
</head>
<body>
<!--表单绑定提交事件
onsubmit=绑定的一个提交函数,true false
将这个结果返回给表单,使用onsubmit接收!
onsubmit="return aaa{}-->
<form action="https://hao.360.com" method="post" onsubmit="return aaa()">
    <p>
        <span>用户名:</span><input type="text" id="username" name="username">
   </p>
    <p>
        <span>密码:</span><input type="password" id="input-password">
    </p>
    <input type="hidden" id="md5-password" name="password">
    <!--绑定一些事件-->
    <button type="submit" >提交</button>
</form>
<script>

    function aaa() {
        alert(1);
       var uname=document.getElementById('username');
       var pwd=document.getElementById('input-password');
       var md5pwd=document.getElementById('md5-password');
    /*   console.log(uname.value);
        console.log(pwd.value);
        //MDS算法
        pwd.value=md5(pwd.value);
       console.log(pwd.value);*/
    md5pwd.value=md5(pwd.value);
    //可以校验判断表单内容,true就是通过验证,false:阻止提交
    return true;
    }

五、jQuery

  • JavaScript
  • jQuery 库,里面存在着大量的JavaScript函数

获取jQuery

  <script src="jquery-3.2.1.js"></script>

选择器

<script>
    //原生的js,选择器少,麻烦不好记
    //标签
    document.getElementsByTagName();
    //id
    document.getElementById();
    //类
    document.getElementsByClassName();
    //jQuery
    $('p').click();//标签选择器
    $('#id1').click();//id选择器or
    $('.class').click();//类选择器

</script>

事件

操作DOM

  • 节点文本操作
$('#test-ul li[name=python]').text();//获得值
"python"
$('#test-ul li[name=python]').text('12323');//设置值
jQuery.fn.init [li, prevObject: jQuery.fn.init(1)]
  • css 操作
 $('#test-ul li[name=python]').css("color","red");
  • 元素的显示和隐藏
  • $(’#test-ul li[name=python]’).hide();

Ajax

数据库

一、数据库介绍

数据库是按照一定的数据结构来组织、存储管理数据的仓库

数据库的发展史

  • 从早期的数据库到数据库管理系统DBMS的应运而生

数据库管理系统DBMS:

  • 是一种操纵和管理数据库的大型软件,用于建立、使用和维护数据库,简称DBMS
  • 它是数据库进行统一的管理机制,以保证数据库的安全和完整性
  • 数据库管理系统是数据库系统的核心,是管理数据库的软件
  • 我们一般说的数据库就是指数据库管理系统

常见的数据库

  • Oracle
  • DB2
  • MySQL
  • SQL Server

二、专业术语

  • 表:具有固定的列数和任意行数
  • 列:一个数据项的字段
  • 行:一条记录
  • 数据库:一些关联表得集合
  • 主键:主键是唯一的,一个数据表中只能包含一个主键,你可以使用主键来查询数据
  • 外键:用于关联两各表
  • 索引:

三、关系型数据库

MySQL是一个关系型数据库管理系统,关联数据库将数据保存在不同的表中,而不是将所有的数据放在大仓库中,这样就增加了速度并提高了灵活性

  • MySQL可以
  • 端口号:3306

字符集

  • ASSCLL码:一套文字符号及其编码比较规则的集合。美国标准化组织ANSI发布了第一个字符集,ASSCLL,后来进一步成了国际化标准。
  • unicode:为了统一字符编码,国际化标准组织制定,后来被IOS收编
  • utf-16:任何字符对应的数字都用两个字节来保存,但很显然如果都用两个字节来表示会浪费资源。
  • utf-8:用utf-8表示时一个字符是可变的,有可能用一个字节来表示一个字符,也可以是两三个,根据字符对应的数字大小来确定
    -汉字的一些常用字符集:GBK、GB2312

MySQL存储引擎

  • 什么是存储引擎:
  • MySQL中的数据用各种不同的技术存储在文件(或内存)中,这些技术中的每一种技术都使用不同的存储机制、索引技巧、锁定水平并且最终提供广泛的不同的功能和能力。通过选择不同的技术,你能够获得意外的熟读或者功能,从而改变应用的整体功能。
  • 不同的存储引擎性能是不一样的
  • 存储引擎分类:
    • MYISAM:它不支持事物,也不支持外键,尤其是访问速度快,对事物完整性没有要求或者以select、insert、为主的应用基本都可以使用这个引擎创建。在磁盘上存在三个文件,其中文件名和表明都相同,但扩展名分别为(.frm(存储定义)、.MYD(MYData,存储数据)、MY(MYINDEX,存储索引))
    • INNODB:innoDB存储引擎提供了具有提交、回滚、和崩溃恢复能力的事务安全,但是对比MYSAM的存储引擎,innoDB的写入效率差一些并且会占用更多的磁盘空间以保留数据和索引
    • MEMORY:memory使用内存中的内容来创建表,每个MEMORY表实际对应一个磁盘文件,格式是.frm;MEMORY类型的表访问非常快,因为它到数据是放在内存中的,但一旦服务器关闭,表中数据就会丢失,但表还会继续存在。

什么是SQL?

  • SQL是Structured Query Lanuage(结构化查询语言)的缩写。
  • SQL是专门为数据库而建立的操作命令集,是一种功能齐全的数据库语言。
  • 在使用他时,只需要发出操作命令即可

SQL的功能分类:

  • DDL:数据定义语言—用来定义数据库对象:创建库、表、列等
  • DML:数据操作语言—用来操作数据库中的记录
  • DQL:数据查询语言—用来查询数据
  • DCL:数据控制语言—用来定义访问权限和安全级别

SQL数据类型

  • MySQL支持所有标准SQL数值的数据类型
  • MySQL支持多种类型,大致可以分为三类:
    • 数值类型
    • 字符串类型
    • 日期和时间类型
  • 常用的数据类型:
    • double:浮点型,例如double(5,2)表示最多5为,其中必须有两位小数,即最大值为999.99
    • char:固定长度字符串类型:char(10) 'abc ’
    • varchar:可变字符串类型:varchar(10)‘abc’
    • text:字符串类型
    • blob:二进制类型
    • date:日期类型:格式为:yyy-MM-dd;
    • time:时间类型,格式为:hh:mm:ss
    • datetime:日期时间类型:yyy-MM-dd hh:mm:ss
  • 在mysql中字符串类型和日期类型都需要用单引号引起来

四、数据库操作

DDL

  • 创建数据库
create database 数据库名 charcter set  utf8;
  • 修改数据库
alert database 数据库名 character set gbk;
  • 创建学生表
CREATE TABLE student(
 id bigint,
 stu_name varchar(50),
 stu_age int 
);
  • 添加一列
ALTER TABLE 表名 ADD 列名 数据库类型  
  • 修改一个表的字段类型
alter TABLE student modify stu_name  varchar(30);
  • 修改表名:
rename table student to newstu;
  • 修改表的字符集为gbk:
alert table 表名 character set 字符集名称
  • 修改表的列名:
alert table 表名 change  原始列名 新列名 数据类型
alter table student change stu_name  s_name varchar(30);
  • 查看表的字段信息:
 DESC 表名;
  • 删除一列
alter table student  drop stu_gender;
  • 查看表的创建细节
show create table 表名
  • 删除表
drop table 表名

DML

  • 查询表中所有数据—Select * from 表名
    -DML是对表中的数据进行增删改查的操作
  • 插入操作
    • INSERT INTO 表名(列名1,列名2…) VALUE (列值1,列值2)
    insert into student (id,stu_name,stu_age) value(3,'wangwu',22);
    insert into student value (6 ,'lx',10);
    
    • 注意事项:
      • 列名与列值得类型个数,顺序等要一致
      • 值不要超出列定义得长度
      • 插入的日期和字符一样,都使用引号括起来
    • 批量插入:
    insert into student (id,stu_name,stu_age) values(4,'wangwu',22),(5,'zxh',23);
    
  • 更新操作
    • UPDATE 表名 SET 列名1==值列值1,列名2=列值2… where 列名
    • 把所有学生的成绩改为90;
      • update student set stu_score =90;
    • 把zhangsan的成绩改为60;
      • UPDATE student set stu_score =60 where stu_name=‘zhangsan’;
    • 把姓名为lisi的年龄改为20分数改为70
      • update student set stu_age=20 ,stu_score=70 where stu_name =‘lisi’;
    • 把zxh年龄在原来基础上加1岁
      • update student set stu_age=stu_age+1 where stu_name=‘zxh’;
    • 修改是数据库密码
      • mysql8
        -ALERT USER ‘root’@'localhost’IDENTIFIED BY ‘123456’;
  • 删除操作
    • DELETE FROM 表名{where 列名=值}
    • delete from student where id=6;
    • TRUNCATE TABLE 表名;
    • truncate table student;//删除多有数据 不能删除指定数据
    • DELETE与TRUNCATE的区别:
      • delete删除表中的数据,表结构还在;
      • truncate 删除是把表直接Drop掉,然后再创建一个同样的新表,执行速度比delete快

DQL

  • 查询所有列:select *from 表名
  • 结果集:
    • 数据库执行DQL语句不会对数据进行改变,而是让数据库发送结果集给客户端。
    • 查询返回的是一张虚拟的结果表。
    • 虚拟结果集存放在内存中
  • 查询指定的列
    • select stu_name,stu_age from student;
  • 条件查询
    • 条件查询就是时给出where 子句,在where子句中可以使用一些运算符及关键字;
    • 条件查询运行符及关键字:
      • = > < != <>(不等于)
      • BETWEEN …AND :值在什么范围:
      • IN(set):查询固定范围内的值
      • IS null:(为空) is notNull(不为空) 等查询
      • and :与
      • or:或
      • NOT:非
    • 使用:
      • 查询性别为男,并且年龄为20的学生记录
        • SELECT *from student where stu_gender=‘男’ and stu_age=20;
      • 查询序号为3或4名为 wangwu的记录
        • SELECT *from student where id=3 or stu_name=‘wangwu’;
      • 查询编号为 1、2、3的记录
        • SELECT *from student where id=3 or id=1 or id=2;
        • SELECT *from student where id in(1,2,3,4);
      • 查询表中年龄为空的记录
        • SELECT *from student where stu_name is null;
      • 查询性别为非男的记录
        • SELECT *from student where stu_gender !=‘男’;
      • 查询性别不为null的学生记录
        • SELECT *from student where stu_gender is not null;
      • 年龄在21到23 的记录
        • SELECT *from student where stu_age >=20 and stu_age<=23;
        • SELECT *from student where stu_age between 20 and 23 ;
  • 模糊查询:
    • 根据指定的关键字查询
    • 使用link关键字后的通配符
    • 通配符
      • _:任意一个字符
      • %:任意0-n个字符
    • 使用
    • 查询姓名由三个字母构成的记录
      • select stu_name from student where stu_name like ‘___’;
    • 查询姓名由3个字母组成,并且第三个字母为‘h’的记录
      • select stu_name from student where stu_name like ‘__h’;
    • 查询以‘z’开头的记录
      • select * from student where stu_name like ‘z%’;
    • 查询姓名中第二个字母为’a’的记录
      • select * from student where stu_name like ‘_a%’;
    • 查询姓名中包含‘s’的记录
      • select * from student where stu_name like ‘%s%’;
      • select * from student where stu_name like ‘%张%’;
  • 字段控制查询
    • 去除重复记录
      • select DISTINCT stu_name from student;
    • 把查询字段的结果进行运算,必须是数据型
      • select *,stu_age+stu_score from student;
      • 年龄和分数为null时把他们变为0 相加
      • select *,IFNULL(stu_age,0)+ IFNULL(stu_score,0) from student;
    • 对查询结果起别名
      • select *,IFNULL(stu_age,0)+ IFNULL(stu_score,0) as total from student;
  • 排序
    • 使用关键字升序 (默认)
      • SELECT *from student order by stu_score asc;
    • 降序:
      SELECT *from student order by stu_score desc;
    • 一个条件相同时 选取另一个条件
      SELECT *from student order by stu_score desc,stu_age desc;
  • 聚合函数
    • 对查询结果进行统计运算
    • 常用的聚合函数
      • COUNT():统计指定列不为空的记录行数
        • SELECT COUNT(*) from student; 为空着不同记
      • MAX():计算指定列最大值,如果是字符串类型,则使用字符串排序运算;
      • MIN():计算指定最小值,如果是字符串类型,则使用字符串排序运算;
      • SUM():计算指定列数值和,如果指定类型不是数值类型,那么结果计算为0 ;
      • AVG():计算指定列平均值,如果指定类型不是数值类型,那么结果计算为0
    • 使用
      • 统计不为空的列数
        • SELECT COUNT(stu_score) from student;
      • 统计成绩大于60的
        • SELECT COUNT(*)from student where stu_score>60;
      • 统计年龄和分数和大于110的记录
        • SELECT COUNT(*)from student where IFNULL(stu_score,0)+IFNULL(stu_age,0)>110;
      • 同时统计有分数和年龄的人数
        • SELECT COUNT(stu_age),COUNT(stu_score)from student ;
      • 查询所有人成绩的总和
        • SELECT SUM(stu_score) from student ;
      • 查询成绩和分数的和
        • SELECT SUM(stu_score+stu_age) from student ;
      • 平均值
        • SELECT AVG(stu_age) from student ;
      • 最大值 最小值:
        • SELECT max(stu_age),MIN(stu_age) from student ;

SQL进阶部分1

  • 分组查询
    • 将查询的结果按照一个或多个字段进行分组,字段相同的为一组
    • 分组使用:
      • SELECT * from student GROUP BY stu_gender;
      • 根据gender字段来分组,gnder字段的全部值只有两个(男和女)所以分为了两组,当groupby单独使用时,只能显示出每组的第一条记录,所以group by 实际意义不大
    • 分组事项:在使用分组时,select后面直接跟字段以般都出现在group by 后
    • SELECT stu_name,stu_gender from student GROUP BY stu_gender,stu_name ;
    • group by + group_concat():
      • group_concat(字段名)可以作为一个输出字段
      • 表示分组之后,根据分组结果,使用group_concat()来放置一组某字段的值的集合
      • SELECT stu_gender,GROUP_CONCAT(stu_name) from student GROUP BY stu_gender;
    • 分组聚合函数
      • SELECT stu_gender,GROUP_CONCAT(stu_score) ,SUM(stu_score)from student GROUP BY stu_gender;
    • group+having:
      • 用来分组查询后指定一些条件输出查询结果
      • having作用和where一样,但having只能用于group by
      • SELECT stu_gender,GROUP_CONCAT(stu_score),SUM(stu_score) from student GROUP BY stu_gender HAVING SUM(stu_score)>160;
    • having和where的区别:
      • having在分组后对数据进行过滤
      • where是在分组前对数据进行过滤
      • having后面可以使用分组函数
      • where后面不可以使用
      • where是对分组前记录的条件进行条件约束,如果某行记录没有满足where子句条件,那么这行记录不会参加分组,而having是对分组的数据约束
    • 查询分数大于70的,分数综合大于160的学生的性别以及分数和。
      • SELECT stu_gender ,GROUP_CONCAT(stu_score),SUM(stu_score),COUNT(*)from student where stu_score >=70 GROUP BY stu_gender HAVING SUM(stu_score)>160;
  • 分页查询 LIMIT
    • 从哪一行开始查,总共查几行
    • limit 参数1,参数2 :
      • 参数1:从哪行开始
      • 参数2:一共查几行
    • 角标是从0开始
    • 格式:select * from student LIMIT 0,3 ;
    • 分页思路:
      • int curPage=1;—当前页
      • int pageSize=3;—每页多少条数据
      • 当前页为1 第一页从0开始 (1-1)*3=0;
      • 当前页为2 第二页从3开始 (2-1)*3=3
      • 当前页为3 第三页从6开始 (3-1)*3=6
      • select * from student limit (curPage-1)*pageSize,pageSize
  • 数据完整性:
    • 保证用户输入的数据保存到数据库中是正确的。
    • 在创建表的时候给表中数据添加约束
    • 完整性分类
      • 实体完整性:
        • 什么是实体完整性:表中的一行(一条记录)代表一个实体(entity)
        • 实体完整性的作用:标识每一行数据不重复,行级约束
        • 约束类型:
          • 主键约束:
            • 特点:
              • 每个表中要有一个主键
              • 数据唯一,且不能为null
            • 添加方式:
              • CREATE TABLE person (ID BIGINT PRIMARY KEY, NAME VARCHAR(50));
              • CREATE TABLE person (ID BIGINT , NAME VARCHAR(50),PRIMARY KEY(ID));
              • 联合主键:两个字段数据同时相同,才违反联合主键约束
                • CREATE TABLE stu (ID BIGINT , snum BIGINT, sname VARCHAR(50),PRIMARY KEY(ID,snum));
              • 添加主键约束:
              • ALTER TABLE student ADD CONSTRAINT PRIMARY KEY(id);
          • 唯一约束:
            • 特点:指定列的数据不能重复,但可以为空值
            • 格式:CREATE TABLE stu (id int PRIMARY KEY,name VARCHAR(50) UNIQUE);
          • 自动增长列:
            • 特点:指定列的数据自动增长,即使数据删除,还是从删除的数据继续往下
            • 格式:
            • CREATE TABLE stu (
              id int PRIMARY KEY auto_increament,
              name VARCHAR(20) UNIQUE
              );
      • 域完整性:
        • 使用:
          • 限制此单元格的数据正确,不对照此列的其他单元格比较
          • 域代表当前单元格
        • 域完整性约束:
          • 数据类型:数值类型、日期类型、字符串类型
          • 非空约束:(not null)
          • 默认值约束:(default)
          • gender CHAR(1) DEFAULT ‘男’
      • 参照完整性:
        • 定义:
          • 表与表之间一一对应关系
          • 通常情况下可以通过设置两表的主键、外键关系或者编写两表的触发器来实现
          • 有对应参照完整性的两张表格,在对他们进行数据插入、更新、删除的过程中系统都会将被修改的表格与另一张表格进行对照,从而阻止一些不正确的数据的操作
        • 数据库的主键和外键的类型一定要一致
        • 两个表必须是InnoDB操作引擎
        • 完整性的添加:
          • CONSTRAINT sc_st_fk FOREIGN KEY (sid) REFERENCES stu1(id)

SQL进阶2

  • 表之间的关系:
    • 一对一
    • 一对多
    • 多对多
      • 一个老师有多个学生,一个学生也可以有多个老师
      • 创建老师表
      • 创建学生表
      • 创建学生与老师关系表
      • 添加外键
      CREATE TABLE teacher(
       tid int PRIMARY KEY ,name VARCHAR(50)
        );
      CREATE TABLE stu(
        sid int PRIMARY KEY,name VARCHAR(50)
      );
      CREATE TABLE tea_stu_rel(
        tid int,sid int
      )
      
      ALTER TABLE tea_stu_rel ADD  CONSTRAINT FOREIGN KEY(tid) REFERENCES teacher     (tid);
       ALTER TABLE tea_stu_rel ADD CONSTRAINT FOREIGN KEY(sid) REFERENCES stu(sid);
      
  • 拆分表:
    • 避免大量冗余信息
  • 多表查询:
    • 合并结果集
      • 合并结果集就是把两个select语句的查询结果语句合并到一起
      • 合并结果集的两种方式:
        • union:合并时结果去除重复记录
          SELECT * FROM A UNION SELECT * FROM B;
        • union all:合并时不去除重复记录
          SELECT * FROM A UNION all SELECT * FROM B;
      • 格式:
      • 示列:
      • 注意:别合并的表列数和列类型必须相同
    • 连接查询
      • 也可以叫跨表查询,需要关联多个进行查询
      • 笛卡尔集:
        • 假设A={a,b} B={0,1,2}
        • 则集合的笛卡尔集为(a,0),(a,1),(a,2),(b,0),(b,1),(b,2)
      • 同时查询两个表,出现的就是笛卡尔集的结果
      • 查询时起别名:
      • 多表联查,如何保证数据正确:
        • 在查询时要把主键和外键保持一致 SELECT * FROM stu ,score where stu.sid=score.sid;
        • 主表当中的数据参照子表当中的数据
        • 原理:
      • 根据连接方式分类:
        • 内连接
          • 等值连接:两个表同时出现的id值才显示
            • SELECT * FROM stu st INNER JOIN score sc ON where st.sid = sc.sid;
            • 与多表连接约束主外键一样,只是写法改变了
            • 如果有条件直接在后面加where
          • 多表连接:
            • 建立学生,分数,科目表
            • SELECT st.name,sc.score,c.NAME FROM stu1 st,score sc,course c where st.id=sc.sid and sc.cid=c.cid;
          • 非等值连接
            • 后面条件不一定必须时=是等于号,只要满座条件即可
        • 外连接:
          • 左连接:把左边的数据全部查出来,右边只查满足条件的数据
            -SELECT * FROM stu1 st LEFT JOIN score sc ON where st.id = sc.sid;
          • 右连接:
          • SELECT * FROM stu1 st RIGHT JOIN score sc ON where st.id = sc.sid;
        • 自然连接:
          • 连接查询时会产生笛卡尔集,我们通常使用主外键来去去除它,而自然连接无需你去给出主外键等式,他会自动找到这一等式,也就是说不用写条件
          • 条件:
            • 两张连接的表中的列名称和类型完全一致时才会去除相同的列。
    • 子查询:
      • 一个select语句中包含另一个完整的select语句 或两个以上Select语句就是子查询语句了
      • 子查询出现的位置:
        • where后,把select查询处的结果当作另一个select的条件值
          • 查询与1号同学选择同一课程的同学:
          SELECT  sid ,cid FROM score
          WHERE cid=(SELECT cid FROM  score WHERE sid='1');
          
        • from后:把查询出的结果当作一个新表
          • 查询选2号课程成绩大于80的学生
          SELECT sid ,cid, score FROM
          (SELECT sid, cid ,score FROM score where cid='2')  s
          WHERE s.score>80;
        
      • 示列表:
      • 使用:
      • 多表查询
        • – 多表查询 查询员工编号为2的学生的选修课程以及成绩
          SELECT * FROM stu1 s,score c,course o WHERE s.id=c.sid AND o.cid=c.cid AND s.id=2;
    • 自连接:
      • 自己连接自己 ,起别名

SQL进阶部分3

  • 常用函数:

    • 有对象调用的称为方法
    • 没有对象调用的称为函数
    • 函数介绍:
      • 事先提供好的一些功能可以直接使用
      • 函数可以用在select语句及其子句
      • 也可以用在UPDATE ,DELEETE语句当中
    • 函数分类:
      • 字符串函数:
        • concat(s1,s2,…sn):将传入的字符连接成一个字符串,任何字符串与null连接的结果都是null SELECT CONCAT(‘aaa’,‘bbb’,null);
        • insert(strert,3,2,‘ee’):把字符串从第三个位置开始,2个字符长的字符串替换为指定字符SELECT INSERT(‘aaa’,2,1,‘b’);
        • LOWER(Str)和UPPER(Str):将字符转成大写或小写
        • LEFT(Str,x)和RIGHT(Str,x):分别返回字符串最左边的x个字符和最右边的x个字符
        • LPAD(Str,n,pad)和RPAD(Str,n,pad):用字符串pad对Str最左边和最右边进行填充,直接到长度为n个字符长度 SELECT LPAD(‘my’,6,1234);
        • LTRIM(Str)和RTRIM(str):去掉字符串当中最左侧和最右侧的空格
        • TRIM(Str):去掉字符串左右的空格
        • REPEAT(Str,x):返回str重复x次的结果
        • REPLACE(str,a,b):用字符串b替换str中所有出现的字符串a --SELECT REPLACE( ‘abcdefg’,‘c’,’**’);
        • SUBSTRING(str,x,y):截取字符串,从x位置起截取到y位置的字符串
      • 数值函数
        • ABS(X) :返回x的绝对值
        • CEIL(X):小数不为0部分上取整,即向上取最近整数 SELECT CEIL(2.3);
        • FLOOR(X):小数向下取,也就是舍弃小数部分
        • MOD(X,Y):返回X/Y的模(余数)
        • RAND():返回0-1内容的随机值 SELECT FLOOR(RAND()*10) ;
      • 日期和时间函数
        • CURDATE():返回当前日期,只包含年月日
        • CURETIME():返回当前时间,只含时分秒
        • NOW():包含当前时间 年月日 时分秒
        • UNIX_TIMESTAMP():返回当前日期的时间戳
        • FROM_UNIXTIME(UNIXTIME):将以一个时间戳转换为日期
        • WEEK(DATE):返回当前第几周
        • YEAR(DATE):返回当前日期是那年
        • HOUR(TIME):返回当前时间的小时
        • MINUTE(TIME):返回当前时间的分钟
        • DATE_FORMAT(date,fmt):对日期进行格式化
        • DATE_ADD(date,interval expr type):计算日期
        • DATEDIFF(date1,date2):date1到date2隔了多少天
      • 流程函数
        • IF(VALUE,t,f):如果value是真,返回t否则返回f
        • IFNULL(VALUE1,VALUE2):如果value不为空,返回value1值否则返回2
        • CASE WHEN THEN END:
      • 其它函数
        • DATABASE():返回当前数据库名
        • VERSION():返回数据库版本
        • USER():返回当前登录用户名
        • PASSWORD(STR):对str进行加密
        • MD5():返回str的MD5值
  • 事务:

    • 概念:
      • 1、可分割的操作,假设该操作有ABCD四个步骤组成:若ABCD四个步骤都成功完成,则认为事务成功,若四个步骤其中任何一个失败,则认为事物失败
      • 2、每条sql语句都是一个事物
      • 3、事务只对DMl操作有效,对DQL无效
    • 事务的ACID:
      • 原子性(Atomicity):原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚
      • 一致性(Consistency):让数据保证一定的合理性,一个商品出库时,对应用户的购物车中的商品加1
      • 隔离性(lsolation):
      • 持久性(Durability):
    • 事务的使用:
      • 开启事务: start transaction:
      • 回滚事务:rollback: 回到事务开始前
      • 提交事务:commit: 所有事务全部执行完毕,没有发生异常,提交事务,更新到数据库当中
    • 事务的并发问题:
      • 脏读:用户看到的结果和真实达到的数据不同,后台管理者再提交事务时数据进行回滚。
        • 解决办法:Read committed! 读提交,能解决脏读问题。
      • 不可重复读:一个事务范围内两个相同的查询却返回了不同数据,这就时不可重复读
      • 重复读:事务开启时不允许其他事务执行修改操作
      • 幻读:
      • 对应关系:
    • 事务隔离级别:
      • Read uncommitted:就是一个事务可以读数据另一个未提交事务的数据
      • Read committed:一个事务要等另一个事务提交后才能读取数据
      • Repeatable read:重复读
      • Serializable:
  • 权限操作:

    • 限制一个用户能够作什么事情,再MySQL中,可以设置全局权限,指定数据库权限,指定表权限,指定字段权限
    • 创建用户:CREATE user ‘zxh’@‘localhost’ IDENTIFIED by ‘123456’;
    • 删除用户:drop user ‘zxh’
    • 分配权限:
      • 给自己创建的用户分配管理员权限:GRANT ALL PRIVILEGES on . TO zxh@localhost
        IDENTIFIED by ‘123456’
        with GRANT OPTION;
    • 查看权限:show grants;
  • 索引:

    • 概念:
      • 索引用于快速找出在某个列中有一特定的行
      • 不使用索引,MySQL必须从第一条记录开始扫扫描整个表,直到找出相关行
      • 表越大,查询数据花费的时间就越多
      • 如果表查询的列中有一个索引,MySQL能够快速到达一个位置去搜素文件,而不必查看所有的数据省下了很多时间。
    • 索引的优势与劣势:
      • 优势:
        • 提高了检索效率,降低了数据库的io成本
        • 通过索引对数据进行排序,降低数据排序的成本,降低了CPU的消耗的消耗
      • 劣势:
        • 实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表记录,所以索引也是要占空间的
        • 虽然索引大大提高了查询速度,同时却会降低更新表得速度。
      • 索引的分类
        • 胆单值索引
        • 唯一索引
        • 复合索引
        • 全文索引
        • 空间索引
      • 索引操作:
        • 创建索引:create index 索引名称 on table
        • 删除索引:drop index 索引名称 on 表名
        • 查看索引:show index from 表名
        • 自动创建索引:
          • 在表上定义了一个主键时,会自动创建一个对应的索引
          • 在表上定义一个外键时,会自动创建一个普通索引
        • 解析查询语句:
          • explain select * from emp where id=101\G;
      • 索引结构
        • btree索引: B+树
        • hash索引:一次性到位,经过算法算出位置
      • 索引的创建和选择:
        • 创建索引:
          • 主键自动建立唯一索引
          • 频繁作为查询条件的字段作为索引
          • 查询中与其他表关联二得字段,外键关系建立索引
          • 提高查询速率
        • 不需要创建索引:
          • 表太少
          • 经常进行增删改
  • 视图:

    • 概念:
      • 视图就是一个虚拟表,其内容由查询定义
      • 同真实表一样,视图包含一系列带有名称的行和列数据。
      • 行和列数据来自定义视图的查询所引起的表,并且在引用视图时动态生成
      • 简单的说视图由select结果组成
    • 视图的特性:
      • 视图是对若干张基本表的引用,一张虚表,查询语句执行的结果,
      • 不存储具体数据(基本表数据发生了改变,视图也会跟着改变)
      • 可以跟基本表一样,进行增删改操作有条件的权限
    • 视图的作用:
      • 安全性:创建一个视图,定义好该视图所操作数据,之后将用户权限与视图绑定
        ,这样的方式使用到了一个特性:grant 语句可以针对视图进行授予权限
      • 查询性提高
      • 提高了数据的独立性
    • 视图创建、修改、删除
      • CREATE VIEW stu_score_view AS (SELECT * FROM score where score>47);
    • 视图机制:
      • 替换式:操作试图时,视图名直接被视图定义给替换掉
      • 具化式:mysql先得到了视图执行结果,该结果形成一个中间结果暂时存在内存中,外面的select语句就调用了这个中间结果。(临时表)
      • 两者区别:
        • 替换式:将视图公式替换后,当成一个整体sql进行处理了
        • 具体化方式:先处理视图结果,后处理外面的查询需求
    • 视图不可更新部分:
      • 只要视图当中的数据不来源于基表,是不能直接修改的
  • 存储过程:

    • 定义
      • 一组编程语言, 是为了完成特定的mysql语句集
      • 存储过程就是具有名字的一断代码,用来完成一个特定的功能
      • 创建存储过程保存在数据库的数据字典中,
    • 为什么要有存储过程?
      • 将重复性很高的一些操作,封装到一个过程中,简化了对这些SQL的调用
      • 批量处理
      • 统一接口,确保数据安全
      • 相对于oracle数据库来说,MySQL的存储过程使用较少
    • 存储过程的创建和调用
      • delimiter : 它 与 存 储 过 程 语 法 无 关 , d e l i m i t e r 语 句 标 准 分 割 符 − − 分 号 改 为 : 它与存储过程语法无关,delimiter语句标准分割符--分号改为 :delimiter,
        因为我们想将存储过程作为整体传递给服务器
       delimiter $$
       CREATE PROCEDURE show_stu1()
       BEGIN
       SELECT * FROM stu1;
       END$$
       delimiter ;
       CALL show_stu1();
      
      • 查看存储过程:
        show PROCEDURE STATUS WHERE db=‘zxh’;
      • 删除:
        drop procedure show_stu1;
  • 自定义函数

        随机生成一个指定个数字符串
        delimiter $$
        CREATE FUNCTION rand_str(n int ) returns VARCHAR(255)
      BEGIN
      -- -声明一个str 个字母
      DECLARE str VARCHAR (100) DEFAULT  'abcdefghijklmnopqrstuvwxyz';
      -- 记录当前是第几个
      DECLARE i int DEFAULT 0;
      -- 生成结果
      declare res_str VARCHAR(255) DEFAULT '';
      while i<n do 
      -- 随机生成一个指定个数字符串
      -- 1+RAND()*26 FLOOR(1+RAND()*26)
      -- substr(str,FLOOR(1+RAND()*26,1);
        -- 拼接字符串
       set res_str=CONCAT(res_str,substr(str,floor(1+RAND()*26),1));
        set i=i+1;
      end while;
      return res_str;
      END$$
      delimiter ;
        SELECT rand_str(5);
    

JDBC

一、JDBC概述

  • 什么时JDBC?
    • 是一种用于执行SQL语句的JAVA API
    • 它由Java语言编写的类和接口组成
    • JDBC提供了一种操作数据标准
    • JDBC的目标是使Java程序员使用JDBC可以连接任何提供JDBC驱动程序的数据库系统
  • 通过驱动连接
    • JAVA程序 通过JDBC(包含了数据库操作规范、类、接口、方法自己并没有提供实现) 连接数据库
  • jdbc的API
    • Java.sql 包装的就是API
    • 各大数据库厂商就会对JDBC的API提供实现类 驱动包
    • 注意不要引错包,不用引用这个com.mysql.xxx l类

二、连接MySQL

  • 添加驱动:
    • 导入jar包
    • build pash
  • 创建连接
    • 1、加载驱动
      // 把com.mysql.jdbc.Driver这份字节码加载进JVM
        // 当一份字节码被 加载进JVM时,就会执行该字节码中的静态代码块
        Class.forName("com.mysql.jdbc.Driver");
      
    • 2、获取连接对象
      // 2、获取连接对象
           //url 数据库地址
         String url="jdbc:mysql://localhost:3306/zxh";
           //user 用户名
         String user="root";
          // password: 密码
         String password ="123456";
          
          Connection conn = DriverManager.getConnection(url, user, password);
          System.out.println(conn);
      
  • 操作MySQL创建表
    • 创建要执行行的Sql语句:
      • Statement接口:用来执行SQL语句对象
      • 把SQL语句发送到数据库中去执行,并返回执行结果
        • 对于DQl返回查询结果集
        • 对于DML返回受影响的行数
        • 对于DDL返回0
      • executeUpdate(String):执行DDL、和DML
    • 执行SQL
    • 释放数据资源
      • 为什么释放资源:
        • connection 连接就相当于Java和到mysql之间建立管道
        • 连接只连接到数据,Statement就相当于从数据库又接了一个管道连接MySQL的执行程序
      • 把管道撤掉
    • 实例
      	// 1、加载驱动
         // 把com.mysql.jdbc.Driver这份字节码加载进JVM
         // 当一份字节码被 加载进JVM时,就会执行该字节码中的静态代码块
         Class.forName("com.mysql.jdbc.Driver");
         // 2、获取连接对象
         // url 数据库地址
         String url = "jdbc:mysql://localhost:3306/zxh";
         // user 用户名
         String user = "root";
         // password: 密码
         String password = "123456";
      
         Connection conn = DriverManager.getConnection(url, user, password);
         System.out.println(conn);
         //3、编写SQl语句
          String sql="create table student(id int,name varchar(50),age int)";
          //创建接口 获取连接对象
          Statement st=conn.createStatement();
          //4、执行sql
          int row=st.executeUpdate(sql);
          //5、释放资源
          st.close();
          conn.close();
      
  • 异常处理

三、执行DML操作

  • 执行DML操作和DDL操作是一样,只是sql语句发生了变换

四、查询操作DQL

  • 结果集:ResubltSet:表示数据库查询的结果的集合,在查询语句时就会得到这样一个结果
  • 常用方法:
    • boolean next():是否有下一行数据
  • 执行sql:
    String url = "jdbc:mysql://localhost:3306/zxh";
      String user = "root";
      String password = "123456";
      // 1、加载驱动
      Class.forName("com.mysql.jdbc.Driver");
      // 2、数据库连接
      Connection conn = DriverManager.getConnection(url, user, password);
      // 3、编写sql
      String sql = "SELECT COUNT(*) FROM emp";
      // 创建连接接口
      Statement st = conn.createStatement();
      // 4、执行sql 得到一个结果集
      ResultSet res = st.executeQuery(sql);
      // 取出列一个数据
      if (res.next()) {
      	// int count = res.getInt(1);
      	int count = res.getInt("COUNT(*)");
      	System.out.println(count);
      }
      // 5、释放资源断开连接
      st.close();
      conn.close();
    

五、DAO设计

  • 没有DAO时存在的问题:
    • 多个地方都要同时做CRUD操作时,重复代码会很多
  • 什么是DAO
    • Data Access Object (数据存储对象)
    • 位于业务逻辑层(test层)和持久层(数据库)之间
    • 实现对数据持久化的访问
  • ORM
    • ORM-- 对象关系映射
      • 将关系型数据库中的记录映射成为对象,以对象的形式展现
      • 因此ORM的目的是为了方便开发人员以面向对象的思想来实现数据库的操作
    • 对应关系:
      • 类—》表
      • 对象—》一行记录
      • 属性—》字段
    • 示意图:
  • domain
    • 什么是domain
      • 就是一个类,符合JavaBeand的规范,一个类当中有字段和该字段的setter和getter方法
    • 作用:用户与数据库的核心中转站
    • 示例
      • 创建一个domain类
        • 先创建一个domain中转
      • 保存数据
      • 获取数据
  • DAO设计规范
    • 编写DAO组件
      • 1、定义dao接口
      • 2、编写dao实现类
    • 定义接口的原因
      • 接口只给函数声明,但是没有函数体类,函数体在实现类中给出
      • 面向接口编程:
        • 根据客户提出的需求,定义接口,业务具体实现通过实现类完成
        • 当客户提出新的需求时,只需要编写业务逻辑的实现类
        • 好处:
          • 业务逻辑更加清晰
          • 增强代码的扩展性,可维护性
          • 接口和实现组分类,适合团队协作开发
          • 降低耦合度,便于以后升级扩展
      • 举例:
    • 包名的规范:
      • 整体规范:域名倒写.模块名称.组件名称
      • DAO包名规范:
        • domain包:存储所有的domain
        • dao包:存储所有的dao接口
        • dao.impl:存储的所有Dao接口的实现类
        • test包:存储测试类
      • 类名规范:
        • domain类:存在domain包中,用于描述一个对象,是一个JavaBean,写时要见名知意
        • dao接口:存储在dao包中,用于表示某一个对象的CRUD声明 起名规范:iDomainimpl包中 - 接口.domain.dao
        • dao实现类:存储在dao.impl包中,用于表示DAO接口的实现类,要实现DAO接口 - DomainDAOimpl
  • DAO开发步骤
    • 1、创建表
    • 2、建立domain包和domain类
    • 3、建立dao包和dao接口
    • 4、建立dao.impl包和dao实现类
    • 5、根据dao接口创建dao测试类
    • 6、编写一个dao方法,进行测试功能是否正确
    • 7、创建包
    • 8、内部结构
    • 9、编写实现类方法体:
      • 保存方法
      • 删除方法
      • 修改方法
      • 获取一个学生
      • 获取所有学生
  • DAO代码重构:
    • 1、每一个DAO中都会写驱动名称、url、用户名密码
    • 2、每个DAO中都会有共同的代码模
    • 3、每个dao方法中每次操作只需要connection对象,但每次不要注册驱动 将驱动注册封装为静态代码块
      • 把创建的connection代码抽取到jdbcutil中
      • 并提供一个getConnection就能获得连接对象
    • 4、每个DAO方法需要关闭资源
    • 5、DAO中拼接SQL太忙烦
      • 使用预编译语句
  • Statement接口
    • 接口:
    • 作用:用于进行java程序和数据库之间的传输
    • 具体类有3个实现:
      • Statement:用于对数据库进行通用访问,使用的是静态sql
      • PreparedStatement:用于预编译模板SQL语句,在运行时直接输入参数
      • CallabStatement:
  • 预编译语句:
    • 没有预编译语句时,所有的sql都是进行拼接的
    • 有了预编译语句,在性能和代码的灵活性上有显著提高
    • PreparedStatement:对象使用?作为占位符,即参与标记
    • 使用setXXX(index,value)方法将值绑定在参数中
    • PreparedStatement 对象执行sql语句
      • executeQuery();
      • executeUpdate();
    • 编译语句去连接池中查询,在设置参数
    • 更安全 能防止SQL注入
    • 什么是SQL注入:
      • 就是通过把sql命令插入到web表单提交或输入域名或页面请求的查询字符,最终达到欺骗服务器执行sql命令。
    • 为什么预编译语句能防止sql注入?
      • 它能把单引号转义,变成’;
      • 这样一来,就无法截断sql语句基本上没有办法注入了
  • 调用存储过程
    • 1、在数据库中定义一个存储过程
    • 2、JDBC调用一个参数的存储过程
    • 3、编写输入参数和输出参数的存储过程
    • 4、JDBC调用第二个参数的储存过程

六、事务处理

  • 事务的使用
    • 事务问题:
    • 处理事务:
      • 默认情况下,事务是自动提交的,
      • 要设置为手动提交
      • 处理事务过程:
        • 关闭自动提交: conn.setAutoCommit(false);
        • 没有问题时提交事务:conn.commit();
        • 出现异常时进行回滚操作:conn.rollback():
          • 回滚之后,没有提交,释放资源
          • 出现异常,没有提交,也不会更新数据库,但是会占用资源
          • 所以在出现异常时,进行事务回滚操作
        • 只有增删改才需要事务,查询不需要事务
        • 以后发现自己写的代码是正确的,
        • InnoDB才支持外键和事务,MylSAM不支持外键和事务
    • 出现异常:一个钱少了,一个没加钱,
    • 事务的隔离级别:

七、批处理

  • 什么时批处理
    • 一次性执行多条sql语句,允许多条语句一次性提交给数据库批量处理
    • 比单独处理效率高
  • 批处理方法
    • addBatch(String):添加需要批处理的sql语句
    • exexcuteBatch():执行批处理
  • 支持情况
    • mysql默认情况下不支持批处理
    • 在后面添加一个参数
  • 示例

八、储存图片

  • 数据库中的BLOB
    • 存储图片、音频、视频等多媒体信息
    • 以二进制的形式
  • 真正开发当中
    • 不会把二进制的文件存放到数据库当中
    • 把文件存储的路径存储到数据库中,以后再取出路径,到该路径下读取信息
  • BLOB类型:
  • mediumBlob 16M
  • 操作:
    • 往表中添加一个字段,设置类型为blob类型
    • 通过代码存入一张图片到数据库中

九、获取自动生成的主键

  • 在我们设计表时,会设置主键,自动增长的主键
  • 有时候我们插入数据时,要想知道生成的主键是多少,
  • 需求场景
    • 用户注册时:添加用户,密码后插入到数据库中
    • 跳转到完善个人信息
    • 实列图:
  • 获取方法

十、连接池

  • 没有连接池情况
    • 每次crud 操作都要使用数据库时,都要创建一个数据库连接对象
    • 普通的jdbc数据库连接使用DriverManager来获取
    • 每次向数据库连接时都需要将connection加载到内存中
    • 每次CRUD操作就向数据库要一个连接执行完成后再断开连接,这样的方式将会消耗很多资源和时间。
  • 数据库连接池:
    • 池:保存对象的容器
    • 连接池:保存数据库连接对象的容器
    • 作用:
      • 初始化时创建一定数量的对象,需要时直接从池中取出一个空闲对象
      • 用完后并不选择直接释放对象,而是再放入对象池中以防便下一次对象请求时直接使用
      • 池技术的优势是,可以消除对象创建所带来的延迟,从而提高性能
    • 数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”
    • 预先再缓冲池中放入一定的数量的连接,当需要建立数据库连接时
    • 只需从“缓冲池”中取出一个,使用完毕后再放回去
    • 我们可以通过设定连接池最大连接来防止系统无尽的与数据库连接
    • 可以通过连接池的管理机制监视数据库的连接数量,使用情况,为系统开发,测试及性能提供依据
  • 示意图:
  • 连接池属性
  • 连接池的使用:
    • 连接池是使用javax.sql.DataSouce接口来表示连接池
    • DataSource 和jdbc连接池一样他在lib目录中
    • 常用连接池;
      • Druid:阿里
      • C3P0:
      • DBCP:
    • 使用连接池与不使用连接池的区别:
      • 1、获取方式
      • 2、释放资源的方式
    • 连接池操作
      • 主要是学如何创建DataSource对象,再从DataSource对象中获取Connection对象
      • 这些都是第三方提供的
  • 创建DataSource:
    • 使用DBCP:1、导入相关jar包 2、再项目中使用连接池来获取连接 3、实列R
  • 配置文件
    • 资源文件,是以properties作为扩展名的文件
    • 再上面使用的数据库过程当中,我们把库的连接地址,用户名,密码都写再代码当中
    • 不便于后期维护
    • 配置文件的书写
      • db.properties
  • DAO代码重构
    • 重构原则
    • 同一类中:
      • 同一个类中有多个方法当中有太多的代码相同
      • 不同地方当作参数传进去
      • 把他们抽到方法中
    • 不同类中:
      • 不同类中有共同的代码抽取到一个类中
      • 共享类中内容
    • DML抽取:
      • 1、设计一个方法
      • 2、要求传入两个参数
        • 一个sql语句
        • 一个参数:可变参数
      • 3、返回值
        • 返回值为int 受影响行数
    • 封装处理结果集:
      • 每一个都封装成了student对象,写死了
      • 解决方法
        • 把处理结果交给每个DAO
        • 为了规范每个DAO的处理结果集,大家都叫同样的名字,这样在模板中就可以调用同一个名称
        • 定义一个处理结果集的接口
      • 代码实现:
        • 1、定义一个接口
        • 2、在具体DAO中实现接口
        • 3、创建一个接口实现对象传给查询方法
        • 4、在查询方法当中调用处理结果集的方法
      • 结果集处理器:
        • 存在的问题:
          • 该类只能将结果集中的一行数据封装程一个Student对象
          • 我们如果有多个domain,就得提供多个结果集处理器
        • 想法:
          • 自动的把一行行数据封装成对象,自动设置属性,结果集处理器
          • 处理不同表时,干的事都一样:1、给我一个要封装的类2、自动设置属性
    • 内省
      • Class类型:
        • 字节码类型
        • 通过字节码创建对象
      • 内省:
        • 用于查看和操作JavaBean的属性
        • 类:introspector
    • DBUtils:
      • 1、什么是DBUtis:
        • DBUtils是Apach公司编写的数据库操作实用工具,小巧,简单,实用
        • 封装了对JDBC的操作,简化了JDBC的操作
      • 2、QueryRunner:
        • QueryRunner(DataSource ds):提供数据源连接池,会自动为你创建连接
        • Update(String sql, Object…obj):执行更新数据
        • query(String sql,ResultSetHandler rsh,object…param):执行查询
      • 3、ResultHandler:

WEB 服务器开发

一、客户端与服务器

C/S与B/S:软件使用上的两种方式的划分

  • C/S:客户端/服务器架构
    • 特点:在服务器当中有一个主要数据库,把所有的业务逻辑以及页面都交给客户端完成
    • 优点:较为安全、用户界面丰富、用户体验好
    • 缺点:每次升级多要重新安装,针对于其他操作系统的开发,可移值性差
  • B/S:浏览器/服务器架构
    • 特点:基于浏览器访问的应用,把业务层交给服务器来完成,客户端仅仅作页面的渲染来和数据交换。
    • 优点:只开发服务器端,可以跨平台,移植性高
    • 缺点:安全性比较低,用户体验差

Web资源

  • web:WEB网页,它用于表示网络主机上供外界访问的资源
  • Web资源分类:
    • 静态web资源:用web页面中提供人们浏览的数据资源始终不变
    • 动态web资源:用web页面中供人们浏览的数据是由程序产生的,不同的时间点访问web页面看到的内容不同。
  • web资源存在哪里:
    • 所有web资源都存放在一个web服务器中
    • web服务器是可以供外界访问web资源的一个软件
    • web资源放到指定目录当中,就可以通过对应的端口在浏览器当中访问
  • URL地址:
    • 协议://主机地址:端口号/资源地址
    • http://www.zxh.com:80/index.html
    • f访问协议 服务器的一个域名 、端口号、服务器上的目录或文件

资源访问流程

  • 客户端:浏览器、Android程序、iOS程序、微信小程序
  • 服务器:pah服务器、tomcat服务器、
  • 当我们在浏览器中访问一个一个网址时,为什么就能看到一个页面?
    • 1、 一个网址对应的其实就是一个IP地址
      • 一个IP地址对应一个一台电脑
      • 通过IP地址找到对应的电脑
      • 电脑当中安装的有web服务器,通过端口号就能找到服务器
    • 2、找到对应的服务器,服务器页面返回给你
    • 3、这样的一个过程就是http请求过程
  • BS结构流程图:
    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9JOHfOHd-1598243947085)(E:/dream_work/p2.png)]
  • 请求与响应
    • 请求:把客户请求发送给服务器
    • 响应:服务器把你的数据发送给客户端
    • 请求与响应都要一定格式
      • 约定好客户以什么格式访问服务器
      • 约定好服务器以什么的格式把数据给客户端
      • 这个约定使用的就是http协议

Http协议

  • 协议:约束双方的一个准则
  • Http协议:
    • http超文本传输协议是互联网上应用最为广泛的一种网络协议
    • 所有的www文件都必须是这个标准
    • 设计HTTP最初的目的是为了提供一种发布和接受html页面的方法
    • 约束请求与响应的规则
  • http组成部分:
    • 请求:
    • 响应:
    • 请求与响应是成对出现的
  • 请求的发送方式
    • 1、通过浏览器地址栏
    • 2、通过html当中的form表单
    • 3、通过a链接的href
    • 4、src属性
  • Http请求
    • 请求行:
      • 请求方式:Post 、get
      • 请求资源:/蚂蚁商城项目/html/login.html/?username=zxh&password=123
      • 协议版本:HTTP/1.0
        HTTP/1.1
    • 请求头:
      • 请求头是客户端发送给服务器的的以些信息
      • 使用键值对表示Key:value
      • 常见请求头:
        • Referer:浏览器通知服务器,当前的请求来自何处,如果是直接访问就不会有这个头,常用于:放到链
        • Cookie:用于存放浏览器缓冲的cookie信息
        • User_Agent:浏览器通知服务器,客户端浏览器与操作系统的相关信息
    • 请求体:
      • 当请求方式是post,请求体会有请求的参数
      • 如果请求方式为get,那么请求参数不会出现在请求体中,会拼接在url地址后
    • 示意图:
  • HTTP响应:
    • 响应行
      • Http协议:
      • 状态码:
        • 200:请求成功
        • 302:请求重定向
        • 304:请求资源没有改变,访问本地缓存
        • 404: 请求资源不存在,通常是用户路径编写错误,也可能是服务器资源以删除
        • 500:服务器内部错误,通常程序异常。
      • 其他状态码:
        • 2 开始一般请求成功
        • 3 开头代表重定向
        • 4 代表资源不存在
        • 5 代表资源内部有错误
    • 响应头
      • 服务器端将信息以键值对的形式返回给客户端
      • 常见请求头
    • 响应体
    • 图示
  • 请求方式
    • 8种请求类型:
      • 1、OPTIONS
      • 2、HEAD:请求页面信息,返回头部信息
      • 3、GET: 请求指定页面信息,并返回实体主体
      • 4、POST:向指定的资源提交数据进行处理请求
      • 5、PUT:
      • 6、DELETE:
      • 7、TRACE
      • 8、CONNECT:
    • 常见的两种请求:
      • GET:GET方法向页面请求发送参数
        • 地址和参数信息中间用?字符分隔:https://www.taobao.com/?username=zxh&password=123
        • 查询字符串会显示在地址栏的URL中,不安全,一般不是用get提交数据
        • GET方法有大小限制:请求字符串中最多只能有1024个字符
        • GET请求能够被缓存
        • GET请求会保存在浏览器的浏览记录中
        • 可以添加书签
        • 编码类型为
        • GET只允许传输ASCll字符类型,不能用二进制
        • 点击刷新时GET请求没反应
        • GET请求主要用于获取数据
      • POST: 向页面请求发送参数
        • 使用POST方法,查询字符串在POST信息中单独存在,和HTTP请求一起发送到服务器
        • 在传二进制流的时候一般使用post
        • 没有历史记录
        • 参数类型没有限制,可以是字符串也可以是二进制流
        • 数据不会再地址栏中显示,也不会缓存下来,或保存在浏览记录中,所以有POST请求比GET安全,但也不是最安全的方式,如需要传送敏感数据,则使用加密传输
        • 查询字符串不会显示在地址栏中
        • POST传输分数据量大,可以达到2M,
        • POST主要是为了将数据传送到服务器的,而get主要为了从服务器中取出数据

Tomcat服务器

  • Java分类:
    • JavaSE:Java的标准版,一般都用来开发桌面应用程序,但在开发桌面应用程序上相对于VB、Delphl、VC++并没有什么优势
    • JavaEE:Java企业版,开发javaweb应用程序,初级的一般使用jsp+servlet+JavaBean来开发的,大型网站一般用框架来开发的,Struts、spring、Mybatis
    • JavaME :Java的微型版,手机应用的开发
  • JavaEE的规范:13个技术规范,Sun公司定义了13个标准,其他公司的人可以根据规范来开发JavaEE程序
  • 常用的JavaWeb应用服务器:Tomcat(Apache开源组织开发免费应用)、weblogic、websphere
  • 下载与安装Tomcat
  • 启动tomcat:
    • 1、进入到解压目录
    • 2、找到bin目录
    • 3、找到startup.bat双击启动脚本
    • 4、通过浏览器访问:
    • 5、shutdown.bat停止脚本:
  • web应用目录结构
    • webName:
      • html、jsp、js文件
    • WEB-INF:
      • class目录:Java类字节码文件
      • lib目录:Java类运行时需要的jar包
      • web.xml文件:当前整个web应用的核心配置文件,可以从ROOT中复制一个
    • WEB-INF目录下当前的页面的资源不能直接通过浏览器访问,是保护的,外界不能直接访问
  • 使用Eclipse发布项目
    • 发布工程:
      • 右键项目,run Server:1、将未发布的工程、发布到webapps下面 2、启动tomcat
    • 说明:
      • 在发布时
      • 会在tomcat的webapps目录下创建一个名称为myprocject的目录
      • 并且把webcontent当中的所有内容放到webapps目录下,myproject当中
      • 不会把写的java代码给放到webapps当中,运行时只要字节码
  • 发布程序详解:
    • Context:docBase:web应用的文件路径
    • path:URL入口
    • reloadable:reloadable:字节码变化服务器是否重新加载web应用
  • tomcat服务器体系结构
    • Server整个Servlet容器组合,可以包含一个或多个
    • service:它由一个或者多个Connector组成,以及一个Engine,负责处理所有Connection所获得的客户请求
    • connector:客户端与程序交互组件:负责�������收请求以及向客户端返回响应
    • Engine:处理接收到的请求
    • Host:虚拟主机
    • Context:一个Context对应于一个Web Application
  • 虚拟主机:
    • 1、输入网址时,就会先到host文档中查找有对应的IP地址,如果有及直接访问该IP地址
    • 2、如果没有,就会到外网去找DNS服务器进行域名与IP地址的解析,查找
    • 3、多个域名可以访问同一个IP
    • 4、一个IP对应一台电脑

Servlet与Servlet服务器

  • 什么是Servlet?
    • Servlet运行在服务器端的Java小程序,是sun公司提供的一套规范(接口)
    • 用来处理客户端请求、响应给浏览器的动态资源
    • Servlet的实质就是Java代码,通过Java的API动态向客户端输出内容
    • 以后程序就不在本地执行了,而是编译成字节码,放到服务器上去执行
    • 编写程序时,不需要有main函数,因为写完后,就把编写的程序编译成了字节码,放到服务器上面。当前发送一个请求的时候,服务器就会按照一定的规则编写代码。
  • servlet 快速入门
    • 1、创建一个Web项目
    • 2、在JavaResource中src下创建一个包名称为com.zxh.servlet
    • 3、在创建的servlet包当中创建一个class文件起名为FirstServlet
    • 4、进入该class实现一个Servlet接口,实现它为实现的方法
      • 重点看service方法
      • 在该方法当中写入一句话进行输出
    • 5、在web.xml当中进行配置

反射

  • 类的加载时机:
    • 当程序要使用某个类时,如果该类还未被加载到内存中
    • 系统会通过加载,连接,初始化三部来实现对这个类进行初始化
      • 加载:就是指将class文件读入内存,并为之创建一个Class对象,任何类使用时系统都会建立一个Class对象
      • 连接:
        • 验证是否有正确的内部结构,并和其他类协商一致
        • 准备负责为类的静态成员分配内存,并设置默认的初始化值
      • 初始化:初始化变量
    • 加载时机:
      • 创建类的实例
      • 访问类的静态变量,或者为静态变量赋值
      • 调用类的静态方法
      • 初始化某类的子类
      • 使用反射的方式来强制创建某个类或接口对应的java.lang.Class对象
  • 类加载器
    • 什么是类加载器classLoader:
      • 负责将.Class文件加载到内存中,并为之生成对应的Class对象
      • 虽然不需要关心类加载机制,但是了解这个加载程序我们就能更好的理解程序的运行
    • 类加载器分类:
      • 根类加载器:
        • 也被称为引导类加载器,负责Java核心类加载
        • 比如System,String等,在JDK中JRE的lib目录下的rt.jar文件中
      • 扩展类加载器:
        • 负责JRE的扩展目录中jar包加载
        • 在JDK中JRE的lib目录下ext目录
      • 系统类加载器:
        • 负责在JVM启动时加载来自Java命令的class文件
        • 以及classpath环境变量所指定的jar包和类路径
  • 什么是反射
    • 创建一个对象的三个阶段:1、源文件阶段.java的文件2、字节码阶段.class 3、创建对象阶段 new 对象名称
    • 内省:在运行时能够获取JavaBean当中的属性名和get和set方法
    • 反射:
      • Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法
      • 这种动态获取的信息以及动态调用的对象的方法的功能称为Java语言的反射机制
      • 想要使用反射,就必须得要获取字节码文件
    • 获取字节码文件:
      • object类的getClass()方法
      • 静态属性class
      • Class类中静态方法forName()
      • 示例:
  • 通过字节码创建对象
  • 获取字段:
    • 获取公共字段
    • 获取私有字段
  • 获取方法:
  • 通过数组越过集合泛型检测

Servlet访问流程

  • 生命周期
    • Servlet什么时候创建
    • init
    • service
    • destory
    • load-on-staetup
  • Servlet配置信息
    • :
    • 名称
    • config参数:
      • 该servlet的配置信息
      • 获得web.xml当中参数
      • 初始化参数
      • 获取servletContext对象
  • url-patten:
    • 1、完全匹配
    • 2、目录匹配
    • 3、扩展名匹配
  • 缺省Servlet
  • 欢迎页面

httpServlet

  • 使用注解创建servlet

    • 注解就相当于在代码上添加了一个小插件,贴上这个注解,会在内部自动帮你去做
    • 注意点:在web.xml中要有一个配置 metadata-complete=“false”,是否扫描类当中定义的注解。true代表不扫描
  • 直接new 的Servlet它是一个httpServlet

  • 他是GenericeServlet的子类

  • HttpServlet方法

  • 方法:

    • service:每一次发送请求时候会调用,当写了service就不会在调用get和post
    • doGet:当发送get请求时间调用
    • dopost:当发送post请求的时候调用
  • 内部方法调用过程

    • 当接收到一个请求时,tomact就会找对应的service方法
    • 如果当中的service当中没有存在service方法,就会到它的父类当中去找
    • 在父类当中找到service是参数为ServletRequest,在会内部把参数转为httpServlet
    • 转还完毕后会在继续调用参数为httpServletRequest的service方法
    • 在此方法当中会获取参数类型,根据不同的参数类型再去调用不同方法
  • 驱动案例:

    • 用户输入用户的密码到数据库连接数据库,验证登录是否正确
    • 步骤:
      • 1、到数据库中建立一个用户表:id、name、password、email
      • 2、书写html登录界面
      • 3、创建loginServlet
      • 4、接收到请求时+

上下文对象–请求对象-响应对象

ServletContext

  • ServletContext:
    • 定义:ServletContext代表是一个web应用的上下文对象(web应用对象) 、里面封装的都是web应用信息、一个Servlet对应一个应用
    • servletContext的生命周期:在服务器一启动时就会创建、在服务器关闭时销毁
    • 如何获得上下文:
      • 1、通过init方法当中一个参数ServletConfig获取
      • 2、直接在HttpServlet当中获取
    • 获取全局的初始化参数:
    • 获取web应用中某一个资源的绝对路径:
      • context.getRealPath(“文件”)
      • 相对的是web应用目录
    • ServletContext是一个域对象
      • 什么是域:能够存储数据
      • 域对象:能够存取数据的对象
      • ServletContext域对象的作用范围(整个web应用、所有的web资源都可以进行存取数据、数据是可以共享的)
      • 获取完ServletContext之后向里面写数据Context.setAttribute (String name,Object value);
      • 获取完ServletContext之后,通过name取出存放数据、context.getAttribute(String name);
      • 删除指定名的值:Context.removeAttribute(String name);
      • 只要是一个域对象,就有这几个方法

Response

  • response响应过程
    • 1、在去发送一个请求时,会找到tomcat引擎
    • 2、引擎会找到对应的web应用,并且会创建response和request对象
    • 3、找到应用后,会执行应用的web.xml再去根据url-pattern的内容创建Servlet对象
    • 4、并且会调用servlet对象的service方法,并且把创建的request对象和response对象传入到方法中
    • 5、拿到response对象后自己可以往响应中写入一些自己给客户的内容
    • 6、写的内容是存到response的缓冲区当中
    • 7、当方法执行结束之后,tomcat从response缓冲区中取出数据
  • 学习响应
    • 通过response设置响应行,响应头,响应体
    • 设置响应行
      • 设置状态码:response。setStatus(Code)
    • 设置响应头:
      • add(添加):add代表添加额外的信息
      • set(设置):修改响应头信息
      • 重定向:
        • 什么是重定向:
          • 到服务器中去找servlet1,
          • servlet1当中有这个资源,告诉你去找servlet2
          • 再发送一个请求到servlet2
        • 状态码:302
        • 特点:
          • 要访问两次服务器:第一次人为的去访问,第二次自动访问,
          • 浏览器地址栏发生变化。
      • 设置重定向;
        • 设置响应码
          response.setStatus(302);
        • 设置响应头
          response.setHeader(“location”,)
      • 封装的重定向
        • 每次都要写状态码,和location比较麻烦,就封装了一个方法,
        • response.sendRedirect("/26_Servlet/LocationServlet");
      • 定时刷新重定向
    • 设置响应体:
      • 1、通过write的方法来写
        • 默认情况下写中文内容会乱码
          • 把写的内容存到缓冲区中使用的是ISO8859
          • ISO8859不支持中文,所以会乱码
        • 在存之前设置
      • 2、通过OutPutStream来写
        • FileinputStream:
        • FileOutputStream:
      • response注意点:getWrite()和OutputStream不可同时使用
    • 下载功能
      • 使用a标签去下载:有些内容会自动解析,浏览器不能解析的文件才会被下载
      • 通过发送Servlet请求来下载:
        • 通过发送一个Servlet请求,把文件名发送给服务器
        • 发送给服务器后,接收到文件名参数,获取文件的绝对地址
        • 通过流的形式写到浏览器
        • 还得要告诉文件是什么类型
        • 设置响应头告诉浏览器不要去解析是以附件的形式打开
        • 步骤
        • 示例代码
      • 解决中文名称的乱码问题
        • 获取中文参数报错问题
        • 1、把获取的字符串参数的字节码获取,再重新使用utf-8编码
        • 2、在设置以前以附件的形式打开时,不同的浏览器会对默认的名字进行解码
        • 所以根据不同的浏览器,要对名称进行编码之后,在放入文件名
        • 对文件名进行解码
        • 步骤
    • 注册验证码功能
      • 为什么要有验证码?
        • 防止黑客恶意注册
      • 1、把浏览器写好的验证码Servlet拿到程序当中
      • 2、编写静态页面
      • 3、编写判断验证码是否正确的servlet

Request

  • 获取请求行
    • 获取请求方法
    • 获取请求资源:String getRequestURL() String getRequestURI()
    • 获取应用名称: String getContextPath()
    • 获取get查询参数:String getQueryString()
  • 获取请求头
    • 1、获取所有的请求头名称
    • 2、获取指定头的消息
    • 3、打印所有的请求头和请求头内容
    • referer:
      • 告诉服务器是从哪个页面链接过来的
      • 注意事项:防盗猎
        • 通过以下方式发送请求时才会获取referer
          • 直接用
          • 用sumbit或 提交的表单(POST或GET)
          • 用JavaScript提交的表单
        • 通过以下方式不会有referer
          • 从收藏夹链接
          • 单击主页或自定义的地址
          • 在浏览器中直接输地址
  • 获取请求体
    • 获取一个值
    • 获取多个值
    • 获取所有请求参数的名称
    • 获取所有请求参数
    • 解决中文乱码问题?

  • 请求转发
  • 客户端地址与服务器地址

n",)
- 封装的重定向
- 每次都要写状态码,和location比较麻烦,就封装了一个方法,
- response.sendRedirect("/26_Servlet/LocationServlet");
- 定时刷新重定向
- 设置响应体:
- 1、通过write的方法来写
- 默认情况下写中文内容会乱码
- 把写的内容存到缓冲区中使用的是ISO8859
- ISO8859不支持中文,所以会乱码
- 在存之前设置
- 2、通过OutPutStream来写
- FileinputStream:
- FileOutputStream:
- response注意点:getWrite()和OutputStream不可同时使用
- 下载功能
- 使用a标签去下载:有些内容会自动解析,浏览器不能解析的文件才会被下载
- 通过发送Servlet请求来下载:
- 通过发送一个Servlet请求,把文件名发送给服务器
- 发送给服务器后,接收到文件名参数,获取文件的绝对地址
- 通过流的形式写到浏览器
- 还得要告诉文件是什么类型
- 设置响应头告诉浏览器不要去解析是以附件的形式打开
- 步骤
- 示例代码
- 解决中文名称的乱码问题
- 获取中文参数报错问题
- 1、把获取的字符串参数的字节码获取,再重新使用utf-8编码
- 2、在设置以前以附件的形式打开时,不同的浏览器会对默认的名字进行解码
- 所以根据不同的浏览器,要对名称进行编码之后,在放入文件名
- 对文件名进行解码
- 步骤
- 注册验证码功能
- 为什么要有验证码?
- 防止黑客恶意注册
- 1、把浏览器写好的验证码Servlet拿到程序当中
- 2、编写静态页面
- 3、编写判断验证码是否正确的servlet

Request

  • 获取请求行
    • 获取请求方法
    • 获取请求资源:String getRequestURL() String getRequestURI()
    • 获取应用名称: String getContextPath()
    • 获取get查询参数:String getQueryString()
  • 获取请求头
    • 1、获取所有的请求头名称
    • 2、获取指定头的消息
    • 3、打印所有的请求头和请求头内容
    • referer:
      • 告诉服务器是从哪个页面链接过来的
      • 注意事项:防盗猎
        • 通过以下方式发送请求时才会获取referer
          • 直接用
          • 用sumbit或 提交的表单(POST或GET)
          • 用JavaScript提交的表单
        • 通过以下方式不会有referer
          • 从收藏夹链接
          • 单击主页或自定义的地址
          • 在浏览器中直接输地址
  • 获取请求体
    • 获取一个值
    • 获取多个值
    • 获取所有请求参数的名称
    • 获取所有请求参数
    • 解决中文乱码问题?

  • 请求转发
  • 客户端地址与服务器地址
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值