一.网络编程
1.1网络编程的三要素
ip: 使用点分十进制法,中间使用.隔开
A类 国家大部门---->前一个号段是网络号段,后面三个主机号段
B类 校园/大公司服务器机房/:前面两个网络号段,后面使用两个注解号段
C类 私人地址:前面三个为网络号段,后面是主机号段
port端口:
port端口 360软件都可以查看你电脑所有客户端软件的端口号
范围:0~65535 里面0~1024属于保留端口
传输协议:
UDP协议--->数据报包(数据包的报文)
1)不需要建立连接通道
2)不可靠协议,不安全的
3)发送数据大小有限制
TCP协议--->最基本的字节流的方式发送数据
1)就必须连接通道
2)可靠协议,一种安全
3)发送数据大小无限制
1.2UDP发送端和接收端的实现(了解) —>不可靠连接
1.2.1基本UDP的发送端和接收端代码实现
发送端:
//1)创建接收端的Socket对象,绑定端口
DatagramSocket ds = new DatagramSocket(10086) ;
//2)创建一个接收容器--->数据包--->自定义字节缓冲区,将发送的数据包
byte[] bytes = new byte[1024] ;//1024或者1024整数倍
int length = bytes.length ;
DatagramPacket dp = new DatagramPacket(bytes,length) ; //将发送端数据缓冲到这个接收容器中
//3)接收,以上面这个接收容器来接收
ds.receive(dp);
//4)从接收容器中解析数据包的实际内容数据
//从接收容器中获取public byte[] getData() 实际缓冲区的对象(从上bytes分段取数据)
byte[] bytes2 = dp.getData();
//获取里面实际缓冲区的长度
// public int getLength()
int length2 = dp.getLength();
//展示数据---分段取数据,每次从0开始取实际长度
String msg = new String(bytes2,0,length2) ;
//数据包里面获取哪一个ip地址发来的--->ip地址字符串形式
String ip = dp.getAddress().getHostAddress() ;
System.out.println("data from --->"+ip+",发送内容是:"+msg);
//释放资源
ds.close();
接收端:
//1)创建接收端的Socket对象,绑定端口
DatagramSocket ds = new DatagramSocket(10086) ;
//2)创建一个接收容器--->数据包--->自定义字节缓冲区,将发送的数据包
byte[] bytes = new byte[1024] ;//1024或者1024整数倍
int length = bytes.length ;
DatagramPacket dp = new DatagramPacket(bytes,length) ; //将发送端数据缓冲到这个接收容器中
//3)接收,以上面这个接收容器来接收
ds.receive(dp);
//4)从接收容器中解析数据包的实际内容数据
//从接收容器中获取public byte[] getData() 实际缓冲区的对象(从上bytes分段取数据)
byte[] bytes2 = dp.getData();
//获取里面实际缓冲区的长度
// public int getLength()
int length2 = dp.getLength();
//展示数据---分段取数据,每次从0开始取实际长度
String msg = new String(bytes2,0,length2) ;
//数据包里面获取哪一个ip地址发来的--->ip地址字符串形式
String ip = dp.getAddress().getHostAddress() ;
System.out.println("data from --->"+ip+",发送内容是:"+msg);
//释放资源
ds.close();
1.2.2UDP发送端可以不断键盘录入数据,接收端不断展示数据,发送端可以自定义结束条件代码实现
发送端:
//创建发送端的Socket
DatagramSocket ds = null;
try {
ds = new DatagramSocket();
//键盘录入可以使用BufferedReader--->读一行
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = null;
System.out.println("请输入一个数据");
while ((line = br.readLine())!=null){
if ("over".equals(line)){
break;
}
//创建数据报包,将数据存储在数据包中
DatagramPacket dp = new DatagramPacket(line.getBytes(),
line.getBytes().length,
InetAddress.getByName("127.0.0.1"),
6666);
//发送数据报包
ds.send(dp);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(ds!=null){
ds.close();
}
}
接收端:
try {
//创建接收端的Socket对象
DatagramSocket ds = new DatagramSocket(6666);
while (true){
//创建一个接收容器
//自定义一个字节数组缓冲区
byte[] buffer = new byte[1024];
int bufferLength = buffer.length;
DatagramPacket dp = new DatagramPacket(buffer,bufferLength);
//接收数据容器
ds.receive(dp);
byte[] bytes = dp.getData();
int length = dp.getLength();
//每次0开始读取字节数---转成String
String receiveMsg = new String(bytes, 0, length);
//获取ip地址字符串
String ip = dp.getAddress().getHostAddress();
//展示数据
System.out.println("data from"+ ip + ",count is"+ receiveMsg);
}
} catch (IOException e) {
e.printStackTrace();
}
1.3TCP客户端和服务器端的实现---->安全—可靠连接
1.3.1基本TCP的服务端和客户端端代码实现
客户端:
//1)创建客户端的Socket对象,指定ip和端口
Socket socket = new Socket("10.35.162.121",1888) ;
//2)获取客户端通道内容字节输出流对象,写数据
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hello,TCP我来了".getBytes());
//客户端获取通道字节输入流对象,读服务器端的反馈的数据
InputStream in = socket.getInputStream();
byte[] bytes = new byte[1024] ;
int length = in.read(bytes);
String fkMsg = new String(bytes,0,length) ;
System.out.println(fkMsg);
//3)释放资源
outputStream.close();
服务端:
//1)创建服务器端的Socket对象
ServerSocket ss = new ServerSocket(1888) ;
System.out.println("服务器正在等待客户端连接请求...");
//2)监听客户端的连接
Socket socket = ss.accept();//阻塞式方法,没有客户端连接,一直等待
System.out.println("客户端已连接");
//3)获取监听到的客户端的通道内的字节输入流对象,读数据
InputStream inputStream = socket.getInputStream();
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int length = inputStream.read(bytes);
//转换成String
String receiveMsg = new String(bytes,0,length) ;
//获取ip地址对象,同时ip地址字符串形式
String ip = socket.getInetAddress().getHostAddress();
System.out.println("data from --->"+ip+",content is-->"+receiveMsg);
//服务器端反馈给客户端数据
//服务器端可以获取监听客户端通道内字节输出流,写数据
OutputStream out = socket.getOutputStream();
out.write("我这边已经收到数据!".getBytes());
//4)释放服务器端的资源
ss.close();
1.3.2TCP客户端不断键盘录入数据,服务器端不断展示数据
客户端:
//创建客户端Socket
Socket socket = new Socket("127.0.0.1",1111);
System.out.println("连接成功");
//键盘录入数据
System.out.println("请输入数据");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//获取通道字节输出流对象
OutputStream out = socket.getOutputStream();
//使用字符流把上面通道内的字节输出流包装
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));
String line = null;
while ((line = br.readLine()) != null){
bw.write(line);
bw.newLine();
bw.flush();
}
//释放资源
socket.close();
服务端:
//创建服务器端的Socket
ServerSocket ss = new ServerSocket(1111);
System.out.println("正在等待连接....");
while (true){
//监听客户端连接
Socket socket = ss.accept();
System.out.println("连接成功");
//获取监听客户端所在的通道内的字节输入流
InputStream in = socket.getInputStream();
//将字节输入流封装成字符流读
BufferedReader br = new BufferedReader(new InputStreamReader(in));
//读
String line = null;
while ((line = br.readLine()) != null){
System.out.println(line);
}
}
1.3.3TCP客户端文件,服务器端将文件复制到指定文件中, 服务器端加入反馈!(图片或者文本文件)
客户端
//创建客户端的Socket
Socket s = new Socket("127.0.0.1",2222);
//创建字符缓冲输入流对象
//BufferedReader br = new BufferedReader(new FileReader("a.txt"));
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("aa.jpg"));
//获取客户端通道的字节输出流
//BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
//读
byte[] bytes = new byte[1024];
int len = 0;
while ((len = bis.read(bytes))!=-1){
bos.write(bytes,0,len);
//刷新
bos.flush();
}
//关闭输出流
s.shutdownOutput();
//客户端要读取服务器反馈数据
//获取通道内的字节输入流
InputStream inputStream = s.getInputStream();
//读取
byte[] bytes1 = new byte[1024];
int len1 = inputStream.read(bytes1);
System.out.println("客户端收到了反馈数据"+new String(bytes1,0,len1));
//释放资源
s.close();
bis.close();
服务端:
//创建服务器端的Socket对象
ServerSocket ss = new ServerSocket(2222);
//创建客户监听连接
Socket s = ss.accept();
//获取监听客户端所在的通道内字节输入流对象,包装成字符缓冲输入流
//BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
//将监听客户端的通道内的字节流的内容,通过的字符缓冲输出流写入到文件中
//BufferedWriter bw = new BufferedWriter(new FileWriter("copy.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.jpg"));
//读,写
byte[] bytes = new byte[1024];
int len = 0;
while ((len = bis.read(bytes))!=-1){
bos.write(bytes,0,len);
bos.flush();
}
//加入反馈,服务器端反馈给客户端数据
//获取字节输出流
OutputStream out = s.getOutputStream();
out.write("文件复制完毕".getBytes());
out.flush();
//释放资源
ss.close();
bos.close();
二.反射
2.1定义
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
2.2获取类的字节码文件
三种
//对象名.getClass
Persion p = new Persion();
Class cls1 = p.getClass();
System.out.println(cls1);
//类名.Class
Class cls2 = Persion.class;
System.out.println(cls2);
//class.forName("类的全路径");
Class cls3 = Class.forName("com.reflect.Persion");
System.out.println(cls3);
2.3常用方法
//获取包名、类名
clazz.getPackage().getName()//包名
clazz.getSimpleName()//类名
clazz.getName()//完整类名
//获取成员变量定义信息
getFields()//获取所有公开的成员变量,包括继承变量
getDeclaredFields()//获取本类定义的成员变量,包括私有,但不包括继承的变量
getField(变量名)
getDeclaredField(变量名)
//获取构造方法定义信息
getConstructor(参数类型列表)//获取公开的构造方法
getConstructors()//获取所有的公开的构造方法
getDeclaredConstructors()//获取所有的构造方法,包括私有
getDeclaredConstructor(int.class,String.class)
//获取方法定义信息
getMethods()//获取所有可见的方法,包括继承的方法
getMethod(方法名,参数类型列表)
getDeclaredMethods()//获取本类定义的的方法,包括私有,不包括继承的方法
getDeclaredMethod(方法名,int.class,String.class)
//反射新建实例
clazz.newInstance();//执行无参构造创建对象
clazz.newInstance(222,"韦小宝");//执行有参构造创建对象
clazz.getConstructor(int.class,String.class)//获取构造方法
//反射调用成员变量
clazz.getDeclaredField(变量名);//获取变量
clazz.setAccessible(true);//使私有成员允许访问
f.set(实例,值);//为指定实例的变量赋值,静态变量,第一参数给null
f.get(实例);//访问指定实例变量的值,静态变量,第一参数给null
//反射调用成员方法
Method m = Clazz.getDeclaredMethod(方法名,参数类型列表);
m.setAccessible(true);//使私有方法允许被调用
m.invoke(实例,参数数据);//让指定实例来执行该方法
2.3.1通过构造方法创建对象
//方法一:
Persion p = new Persion();
System.out.println(p);
System.out.println("--------------------------");
//方法二:通过反射获取构造器(Constructor)对象--->创建当前类实例
//1.获取类字节码文件
Class cls = Class.forName("com.reflect.Persion");
//2.获取指定的构造方法所在的Constructor类对象
Constructor constructor = cls.getConstructor();
//3.通过它创建当前类实例
//public T newInstance(Object... initargs):参数就给构造函数中参数进行实际赋值
Object obj = constructor.newInstance();
System.out.println(obj);
2.3.2使用反射方式通过构造方法赋值
//1.获取当前类的字节码文件对象
Class cls = Class.forName("com.reflect.Persion");
//2.获取构造器Constructor类对象--->带两个参数的
Constructor constructor = cls.getDeclaredConstructor(String.class, String.class);
//3.提供功能:取消Java语言访问检查,暴力访问
//setAccessible(boolean flag) true:就是抑制Java语言访问检查功能
constructor.setAccessible(true);
//4.通过它创建当前类实例
Object obj = constructor.newInstance("高圆圆", "女");
System.out.println(obj);
2.3.3使用反射的方式调用成员方法
//1)获取类的字节码文件对象
Class clazz = Class.forName("com.qf.reflect_06.Person") ;
//2)通过无参构造器创建器Constructor创建当前类实例
Constructor con = clazz.getConstructor();
Object obj = con.newInstance() ;
System.out.println(obj);
//3)获取指定的成员方法所在的Method类对象
Method method = clazz.getMethod("show");//方法本身就是空参,获取所有可见的方法,包括继承的方法
System.out.println(method);
method.invoke(obj) ; //本身这个方法没有返回值,单独调用
System.out.println("----------------------------------------------------------");
Method m1 = clazz.getDeclaredMethod("method", String.class);//获取本类定义的的方法,包括私有,不包括继承的方法
//私有方法取消Java语言检查,暴力访问
m1.setAccessible(true) ;
m1.invoke(obj,"hello,高圆圆") ;
2.3.4通过反射获取一个类的成员变量的类的对象Field并去赋值
//获取字节码文件
Class cls = Class.forName("com.reflect.Person");
//创建实例对象
Object obj = cls.newInstance();
System.out.println(obj);
//通过字节码文件对象获取成员变量的Field类对象
Field namefield = cls.getDeclaredField("name");//获取本类定义的成员变量,包括私有
namefield.setAccessible(true);
namefield.set(obj,"张三");
System.out.println(obj);
2.4反射的应用
2.4.1ArrayList里添加字符串
//创建集合
ArrayList<Integer> arr = new ArrayList<>();
//添加数据
arr.add(12);
arr.add(122);
arr.add(22);
arr.add(42);
arr.add(13);
System.out.println(arr);
//获取字节码文件
Class cls = arr.getClass();
//获取add方法
Method addmethod = cls.getMethod("add", Object.class);
//添加其它类型数据
addmethod.invoke(arr,"张三");
2.4.2通过读取.properties文件的方式,
//读取配置文件
InputStream ips = ReflectDemo2.class.getClassLoader().getResourceAsStream("classname.properties");
//创建空集合列表
Properties properties = new Properties();
properties.load(ips);
System.out.println(properties);
//通过属性列表获取值
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
//获取字节码文件对象
Class cls = Class.forName(className);
//创建当前实例
Object obj = cls.newInstance();
//通过字节码文件获取方法
Method method = cls.getMethod(methodName);
System.out.println(method);
method.invoke(obj);
2.5反射—jdk动态代理
结论:使用接口实现方式,运行时,在内存中动态构建出一个类,然后编译,执行。这个类是一次性的,JVM停止,代理类就消失。
三.数据库
3.1什么是数据库
数据库就是存储数据的一种仓库
早期存储数据--->临时变量(局部变量)--->随着方法的调用而存在,随着方法调用的结束而消失
后面-------->成员变量(非静态)----->随着方法对象的创建而存在,随着对象的创建完毕等待垃圾回收器回收而消失
容器-------->数组---->长度固定(弊端)
--->StringBuffer--->字符串缓冲区中存储各种字符序列,它长度可变,但是stringBuffer使用完毕等待回收
--->集合---->长度可变,它可以存储任意引用类型数据,使用完毕---->也要被回收掉
--->Io流---->永久存储,但是IO流的太耗时了
数据库存储数据特点--->
1)存储空间非常大,可以存储百万条,千万条甚至上亿条数据,用户可以对数据库中数据进行新增,查询,更新,删除等操作
2)数据独立性高
3)实现数据共享
4)减少数据的冗余度
5)通过数据库里面"事务"--->实现数据的一致性以及维护性!
3.2市面上常见的数据库
数据库分为两大类:
关系型数据库:Mysql,SqlServer,Oracle
SqlServer,Oracle:收费
Oracle:中大型公司使用居多
Mysql:免费产品,中小型公司使用居多
非关系型数据库:Redis,Memcache,MongoDb,Hbase
3.3安装与卸载
3.4使用DOS窗口登录启动mysql
使用管理员打开命令提示符:
开启mysql: net start mysql;
关闭mysql: net stop mysql;
3.5DDL语句的基础语法
DDL语句:数据库定义语句 库和表的基本操作
3.5.1库的操作
1.查询所有库:show databases;
2.创建库的语法
2.1 create database if not exists 库名; 如果没有这个库则创建该库
2.2 create database 库名;
3.查看指定库的字符集格式
show create database 库名;
4.修改库的字符集
alter database 库名 default(可以省略) character set 字符集格式;
5.删除库
5.1 drop database if exists 库名; 如果有该库则直接删除
5.2 drop database 库名; 直接删除
6.使用库
use 库名;
3.5.2表的操作
1.创建表的语法
create table 表名(
字段名称1 字段类型1,
字段名称2 字段类型2,
字段名称3 字段类型3,
.....
字段名称n 字段类型n,
);
2.查询表的结构
desc 表名;
3.修改表的字段类型
alter table 表名 modify 字段名称 修改后的字段类型; -- 修改字段类型
alter table 表名 change 以前的字段名称 修改后的字段名称 字段类型;-- 修改字段名称
4.修改表
alter table 表名 add 字段名称 字段类型; 添加表的字段
alter table 表名 drop 字段名称; 删除表中某个字段
alter table 表名 rename to 新表名; 修改表名
rename table 表名 to 新表名; 修改表名
5.查看表的字符集
show create table 表名;
6.修改表的字符集
alter table 表名 character set 字符集格式;
7.复制表---快速去创建一个结构相同的表
create table 新表名 like 表名;
复制表的全数据
CREATE TABLE 新表名
AS
SELECT * FROM 表名;
8.删除表
drop table if exists 表名;如果存在删除表
drop table 表名;
3.6DML语句:数据库操作语句
3.6.1操作表的记录—插入,修改,删除,查询…
1.插入数据,没插入的值为null
插入一行:insert into 表名 values(值1,值2...);
插入多行:insert into 表名 values(值1,值2...),values(值1,值2...)..;
插入一行部分字段:insert into 表名(字段名称1,字段名称2.,等部分字段)values(值1,值2,值3...);
插入多行多条数据,部分字段:insert into 表名(字段名称1,字段名称2.,等部分字段)values(值1,值2,值3...),(值1,值2,值3...),(...);
2.修改数据
一次修改一个数据: update 表名 set 字段名称= 值 where 字段名称= 值;
一次修改多个数据: update 表名 set 字段名称= 值1,字段名称= 值2,.. where 字段名称= 值;
3.删除表的记录
带条件删除
delete from 表名 where 字段名称 = 值;
delete from 表名 where 字段名称1 = 值1 and 字段名称1 = 值1 ...;
删除全表数据
方式一:delete from 表名; 只删除 全表数据,表结构还在
方式二:truncate table 表名; 连表结构删除
4.表的查询
基本查询:
1.查询全表:select 全部字段名称 from表名; 使用 select * from 表名;
查询指定字段并给别名:select id as '编号',name '姓名' from 表名;
查询指定字段去重:select distinct 字段 from 表名;
2.带条件查询
2.1where 比较运算符 <=,>=,<,>,!=(在mysql使用 <>)
and && /or ||
between 值1 and 值2
例1:查询年龄是20的学生的编号,姓名,年龄,住址信息
SELECT id '编号',NAME '姓名',age '年龄',address '住址' FROM student2 WHERE age = 20 ;
例2:查询年龄在18到20岁之间学生的所有信息
方式一:SELECT *FROM student2 WHERE age >=18 && age<=20;
方式二:SELECT * FROM student2 WHERE age >=18 AND age<=20;
方式三:SELECT * FROM student2 WHERE age BETWEEN 18 AND 20;
2.2where 模糊条件 like关键字
语句格式:select 字段列表 from 表名 where 字段名称 like '%xx%';
%:表示任意字符
_:表示一字符
2.3排序查询 order by,升序是asc,降序是desc
单独使用语法:select 字段列表 from 表名 order by 字段名称 排序规则;
与where语句使用时先满足where条件,在排序
语法:select 字段列表 from 表名 where 条件 order by 字段名称 排序规则,字段名称2 排序规则2;
2.4聚合函数查询 结果是单行单列,可以sql语句嵌套
count():统计表的数据,如果字段为空不会统计
语法:select count(字段名称) from 表名;
max(列名称):最大值
语法:select max(列名称) from 表名;
min(列名称):最小值
语句:select min(列名称) from 表名;
avg(列名称):平均值
语句:select avg(列名称) from 表名;
sum(列名称);求和
语句:select sum(列名称) from 表名;
2.5分组查询:group by
语句:select 字段列表 from 表名 group by 分组的字段名称;
注意:分组查询里面可以select 查询分组字段
分组group by后面不能使用聚合函数
where条件和group by,where条件在group by前面;先满足where条件在参与分组
2.6筛选查询:having
having的后面可以跟聚合函数,having在group by后面,where在group by前面
2.7分页查询:limit where在limit前面
方式一;select 字段列表 from 表名 limit 起始行数,每页显示的条数;
方式二:select 字段列表 from 表名 limit 值;
复合查询:有where条件,还有limit,where在limit前面
起始行数= (当前页码数-1)*每页显示的条数;
3.7mysql常见的几种数据类型
int,int(int类型的字符数)
int:默认11位,
int(int类型的字符数):int(3): 1---->001
varchar(字符长度):字符串类型
姓名 varchar(20)---->最大取到20
在mysql中字符串写的时候可以使用双引号也可以是单引号
date:日期类型--->仅仅表示日期
datetime:日期+时间
timestap:时间戳-->当前插入数据或者修改/删除数据的即时时间
double:小数类型
double(3,2): 小数是3位数,小数点后保留2位
clob:大字符类型,某个表中某个字段--->使用clob来存储大文本
blob:答字节类型:存储大图片文件---大字节类型
3.8约束
约束:约束用户操作数据库的一种行为
3.8.1默认约束 default
特点:当没有插入这个字段,默认约束起作用
添加默认约束
创建表时:字段名称 字段类型 default '默认'
sql语句:alter table 表名 modify 字段名称 字段类型 default '默认';
删除默认约束:修改字段的类型
alter table 表名 modify 字段名称 修改后的字段类型;
3.8.2非空约束 not null
特点:当前这个值不能为null,不能直接添加数据给一个null
添加非空约束:
创建表的时候在指定字段后面加入not null
sql语句:alter table 表名 modify 字段名称 字段类型 not null;
除非空约束:修改字段的类型
alter table 表名 modify 字段名称 修改后的字段类型;
3.8.3唯一约束 unique
特点:当前值不能为空
添加唯一约束:constraint(声明)
创建表的时候在指定字段后面加入 unique
sql语句:alter table 表名 add constraint 唯一约束索引名称 unique(列名);
删除:
alter table 表名 drop index 索引名(没给就是默认列的名称)
3.8.4主键约束 primary key
特点:非空且唯一
添加:
创建表的时候在指定字段后面加入 primary key
sql语句:alter table 表名 add primary key(列名称);
删除:
alter table 表名 drop primary key;
3.8.5自增长约束 auto_increment
特点:一般自增长约束都是在主键字段上,保证唯一
指定插入数据的值,下次在之前的值上继续自增1.
ysql自带函数---查找数据库表中最后一次自增主键的值
select last_insert_id();
添加:
创建表时添加
注意:指定插入id值时会自增,修改后的值不会自增.
3.8.6外键约束 foreign key
特点:外键作用的表是从表,另一张表是主表.
添加:
创建表时添加:
constraint -- 声明
外键名 --起名 主表名_从表名_fk
foreign key(从表的字段名称)
references -- 关联
主表名(主键字段的名称)
sql语句添加:
alter table 表名 add constraint 外键名 foreign key(从表的字段名称) references 主表名(主键字段名称);
删除:
有外键时直接修改或者删除主表数据,前提需要让从表的数据跟主表没有关联,才可以删除该表
sql删除:
alter table 表名 drop foreign key 外键名
3.8.7级联操作 cascade
特点:当修改/删除主表的数据,从表数据随之改动
级联删除/级联修改 on delete cascade /on update cascade
添加:
创建表示添加:
constraint -- 声明
外键名 -- 起名 主表名_从表名_fk
foreign key(从表的字段名称)
references -- 关联
主表名(主键字段的名称)
on update cascade
on delete cascade
sql语句添加:
alter table 表名 add constraint 外键名 foreign key(从表的字段名称) references 主表名(主键字段名称) on update cascade on delete cascade;
3.9数据库表的关系问题—三大范式
3.9.1表与表之间的关系
表与表的关系 | 关系的维护 |
---|---|
一对多 | 主外键的关系 |
多对多 | 中间表,两个一对多 |
一对一 | 特殊一对多,从表中的外键设置为唯一,从表中的主键又是外键 |
3.9.2三大范式
什么是范式:
好的数据库设计对数据的存储性能和后期的程序开发,都会产生重要的影响。建立科学的,规范的数据库就需
要满足一些规则来优化数据的设计和存储,这些规则就称为范式。
第一范式(1NF):
数据库表的每一列都是不可分割的原子数据项,不能是集合、数组等非原子数据项。即表中的某个列有多个值
时,必须拆分为不同的列。简而言之,第一范式每一列不可再拆分,称为原子性。
第二范式(2NF):
在满足第一范式的前提下,表中的每一个字段都完全依赖于主键。
特点:1.一张表只能描述一件事情.
2.表中的每一列都完全依赖于主键
第三范式(3NF):
在满足第二范式的前提下,表中的每一列都直接依赖于主键,而不是通过其它的列来间接依赖于主键。
范式 | 特点 |
---|---|
1NF | 原子性 |
2NF | 不产生局部依赖,一张表只描述一件事情 |
3NF | 不产生传递依赖,表中每一列都直接依赖于主键.而不是通过其他列间接依赖于主键 |
3.10多表查询
笛卡尔乘积:
针对A表的记录和B表的记录数,两个表的记录相乘.
内连接:
隐式内连接:where条件
语句格式:select 字段列表 from 表名1,表名2,..表名n where 表之间的连接条件;
显示内连接:inner join
语句格式:select 字段列表 from 表名1 inner join 表名2 in 连接条件;
外连接:
左外连接:将A表(左表)的和B表(右表)的交集以及A表(左表)中所有数据全部查询!
语句格式:select 字段列表 from 左表名 left join 表名2 on 连接条件;
子查询:select 嵌套 select
情况1:使用where后面条件表达式=,<=,>=..
例:查询员工工资最高的员工信息以及他的部门信息
SELECT
e.*,
d.name '部门名称'
FROM
employee e,
dept d
WHERE e.dept_id = d.id
AND e.salary =
(SELECT
MAX(salary)
FROM
employee) ;
情况2:利用in集合语句
例:查询在"市场部"和销售部的员工信息
SELECT
e.*,
d.name '部门名称'
FROM
employee e,
dept d
WHERE e.dept_id = d.id
AND e.dept_id IN
(SELECT
id dept
WHERE NAME = '市场部'
OR NAME '销售部') ;
情况3:通过两个表的关系--查询出结果集--当做'虚表'在和其他表之间关联查询
例:查询入职日期大于"2021-11-11"后入职的员工信息以及部门名称信息
SELECT
t.name '员工姓名',
t.join_date '入职日期',
d.`name` '部门名称'
FROM
(SELECT
*
FROM
employee e
WHERE e.`join_date` > '2021-11-11') t LEFT JOIN
dept d
ON t.dept_id = d.id ;
3.11数据库的事务/事务的特点/事务的隔离级别
3.11.1数据库事务
数据库事务(Transaction):在一个业务中执行多个sql(多张表的sql),这多个语句要么同时执行成功,要么同事执行失败.
举例: 转账操作 一个转账,一个收款
提交订单
订单表和订单项 同时添加数据
mysql中如何管理事务-- 单独执行这些指令
start transaction 开启事务
rollback 回滚到操作sql之前的状态
commit 事务不提交,数据只是临时提交,数据无法永久更新
-- 查询级别
SELECT @@tx_isolation;
-- 更改级别
set global transaction isolation level 级别名;
3.11.2事务特点
ACID : 关系型数据库传统事务
原子性:在使用事务管理的时候,执行多个sql增删改,要么同时执行成功,要么同时执行失败
一致性:高并发的时候,需要保证事务多次读写,保证数据一致性
隔离性:事务和事务是独立的,相互不影响
持久性:事务一旦提交,对数据的影响是永久的,即使关机了,数据还是要更新
3.11.3事务的隔离级别
四个级别:从大到小,安全性:从低到高,效率性:从高到低
read uncommitted :读未提交 会造成问题'脏读'
'脏读':是事务管理最严重的问题,一个事务读取到另一个没有提交的事务
read committed :读已提交 有效防止脏读,出现了一个问题 '不可重复读'
事务多次读取数据不一致
repeatable read :可重复读(默认),防止脏读,不可重复读,会出现幻读(有更新操作影响)
serializable : 串行话
一个事务读取到另一个没提交事务,数据查不到,必须将其提交,才能操作数据