网络编程
编程 : 使用java语言编写程序,实现不同计算机应用程序之间的数据传输。
组成部分
为什么可以在百度的平台上,查询一些数据?
1.百度也是一台计算机(服务器)----> 服务器一直开启
2.另外一台计算机请求一些数据---->百度服务器响应
https://www.baidu.com/: 百度服务器的路径
https: 协议
www.baidu.com: 域名 (花钱购买) 方便用户记忆== 计算机的ip+端口
协议
*** TCP/IP 协议族**: 传输控制协议/互联网协议 (打电话)—>三次握手
https: 超文本传输协议+s(数字安全证书)
http: 超文本传输协议
ftp: 文件传输协议
smtp: 简单邮件传输协议
pop3: qq邮箱 邮箱协议
UDP: 用户数据报协议 (发短信)—>丢包—> 直播(花屏)
程序项目开发:
- C/S : 全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件。
- *** B/S**: 全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等。
网络通信协议
-
**网络通信协议:**通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。
-
TCP/IP协议: 传输控制协议/因特网互联协议( Transmission Control Protocol/Internet Protocol),是Internet最基本、最广泛的协议。它定义了计算机如何连入因特网,以及数据如何在它们之间传输的标准。它的内部包含一系列的用于处理数据通信的协议,并采用了4层的分层模型,每一层都呼叫它的下一层所提供的协议来完成自己的需求。
上图中,TCP/IP协议中的四层分别是应用层、传输层、网络层和链路层,每层分别负责不同的通信功能。
链路层:链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤、网线提供的驱动。
网络层:网络层是整个TCP/IP协议的核心,它主要用于将传输的数据进行分组,将分组数据发送到目标计算机或者网络。
运输层:主要使网络程序进行通信,在进行网络通信时,可以采用TCP协议,也可以采用UDP协议。
应用层:主要负责应用程序的协议,例如HTTP协议、FTP协议等。
协议分类
通信的协议还是比较复杂的,java.net
包中包含的类和接口,它们提供低层次的通信细节。我们可以直接使用这些类和接口,来专注于网络程序开发,而不用考虑通信的细节。
java.net
包中提供了两种常见的网络协议的支持:
-
UDP:用户数据报协议(User Datagram Protocol)。UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。
由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议。UDP的交换过程如下图所示。
特点:数据被限制在64kb以内,超出这个范围就不能发送了。
数据报(Datagram):网络传输的基本单位
-
TCP:传输控制协议 (Transmission Control Protocol)。TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”。
- 三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。
- 第一次握手,客户端向服务器端发出连接请求,等待服务器确认。
- 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。
- 第三次握手,客户端再次向服务器端发送确认信息,确认连接。整个交互过程如下图所示。
- 三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。
完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛,例如下载文件、浏览网页等。
网络编程三要素
- 协议 2.IP地址 3.端口
IP地址
-
IP地址:指互联网协议地址(Internet Protocol Address),俗称IP。
-
IPv4:是一个32位的二进制数,通常被分为4个字节,表示成
a.b.c.d
的形式,例如192.168.65.100
。其中a、b、c、d都是0~255之间的十进制整数,那么最多可以表示42亿个。 -
IPv6:IPv6重新定义地址空间,采用128位地址长度,每16个字节一组,分成8组十六进制数,表示成
ABCD:EF01:2345:6789:ABCD:EF01:2345:6789
,号称可以为全世界的每一粒沙子编上一个网址,这样就解决了网络地址资源数量不够的问题。
常用命令
- 查看本机IP地址,在控制台输入:
ipconfig
- 检查网络是否连通,在控制台输入:
ping 空格 IP地址
ping 220.181.57.216
特殊的IP地址
- 本机IP地址:
127.0.0.1
、localhost
。
端口
0-65535 计算机软件应用程序制定端口(端口唯一) 0-1024 是系统保留 80本机端口
tomcat: 8080
mysql: 3306
oracl: 1521
netstat -ano
netstat -aon|findstr "49157"
常用类
java.net.*
InetAddtress —>
*** 代表的就是计算机的ip地址**。
Inet4Address , Inet6Address
此类表示Internet协议(IP)地址。
static InetAddress getLocalHost() 获得本机的ip地址
static InetAddress getByName(String host) 根据指定的计算机名称获得ip
public static void main(String[] args) {
try {
//创建对象
// InetAddress inetAddress = InetAddress.getLocalHost();
// System.out.println(inetAddress);//Lisa/192.168.12.75
// System.out.println("getHostName:" + inetAddress.getHostName());
// System.out.println("getHostAddress:" + inetAddress.getHostAddress());
// System.out.println("getAddress:" + Arrays.toString(inetAddress.getAddress()));
System.out.println(InetAddress.getByName("JAVA-20200618FB"));//192.168.12.228
InetAddress inetAddress = Inet6Address.getByName("Lisa");
System.out.println(inetAddress);
//fe80::a811:5ea3:f814:61dd%6
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
URL
https://www.baidu.com/ 路径 万维网的资源的指针 统一资源定位符
http://192.168.12.75:80/demo/a.html?name=jim&pass=1234#abc
URL(String spec)
//通过url可以访问服务(接口): 抓取小说内容
//http/https: 数据读写IO
public static void main(String[] args) {
String path = "http://www.javasm.cn";
try {
URL url = new URL(path);
System.out.println(url);
System.out.println(url.getPath());// /demo/a.html
System.out.println(url.getAuthority());// 192.168.12.75:80
System.out.println(url.getDefaultPort());//80
System.out.println(url.getPort());//8081
System.out.println(url.getProtocol());//http
System.out.println(url.getFile());// /demo/a.html?name=jim&pass=1234
System.out.println(url.getHost());//192.168.12.75
System.out.println(url.getQuery());// ? 参数的数据
System.out.println(url.getRef());// 锚记 #abc
System.out.println(url.getContent());//内容 IO的对象 输入流 sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@3d075dc0
// InputStream inputStream = url.openStream();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Socket
基于TCP协议的网络编程。
两个进程间可以通过一个双向的网络通信连接实现数据交换,这种通信链路的端点被称为“套接字”(Socket)
服务端
1.创建服务端应用程序
ServerSocket:
指定在哪一台机器上创建服务端程序,并监听指定端口 1025-65535
ServerSocket(int port)
ServerSocket serverSocket = new ServerSocket(6666);
- 服务端等待客户端的主动连接
Socket clientSocket = serverSocket.accept();//阻塞 clientSocket客户端
System.out.println("clientSocket:" + clientSocket);
- 服务端读写数据
while(true) {
Socket clientSocket = serverSocket.accept();//阻塞 clientSocket客户端
System.out.println("clientSocket:" + clientSocket);
//3.数据传输 IO读写
//服务端给客户端发送信息 ----> 将信息写给客户端---> 输出流
String msg = "server: 欢迎你,"+clientSocket.getInetAddress().getHostAddress();
//数据流: writeUTF() DataOutputStream
DataOutputStream dataOutputStream = new DataOutputStream(clientSocket.getOutputStream());
dataOutputStream.writeUTF(msg);
DataInputStream dataInputStream = new DataInputStream(clientSocket.getInputStream());
System.out.println(dataInputStream.readUTF());
}
客户端
- 创建客户端 主动连接服务端程序
Socket(InetAddress address, int port)
Socket(String host, int port)
//-----------------------------------------------
//1 创建客户端程序(主动发起连接)--->连接哪一台主机上面的端口为哪个的服务端程序
Socket socket = new Socket(InetAddress.getLocalHost(),6666);
//socket: 服务端程序
- 客户端读写数据
try {
//1 创建客户端程序(主动发起连接)--->连接哪一台主机上面的端口为哪个的服务端程序
Socket socket = new Socket(InetAddress.getLocalHost(),6666);
//socket: 服务端程序
//客户端读取服务端发送的信息----> readUTF
DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
System.out.println(dataInputStream.readUTF());
//客户端给服务发信息--->write
String msg = "client: 你好,服务器";
DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
dataOutputStream.writeUTF(msg);
} catch (IOException e) {
e.printStackTrace();
}
动态交互数据
public class Server {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
final String server = "server: ";
try {
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("------------------服务端启动-----------------");
while (true) {
Socket client = serverSocket.accept();
while (true) {
//读写数据
//1.写
DataOutputStream dataOutputStream = new DataOutputStream(client.getOutputStream());
String msg = input.nextLine();
if(msg.equals("bye")){
break;
}
dataOutputStream.writeUTF(server + msg);
//2.读
DataInputStream dataInputStream = new DataInputStream(client.getInputStream());
System.out.println(dataInputStream.readUTF());
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public class Client {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
final String server = "client: ";
try {
Socket socket = new Socket("192.168.12.75", 9999);
while (true) {
//读写数据
//2.读
DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
System.out.println(dataInputStream.readUTF());
//1.写
DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
String msg = input.nextLine();
dataOutputStream.writeUTF(server + msg);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
优化封装代码
----将读 写功能封装成类 并调用时可以多线程监听 这样就可以多次聊天
//服务端
public class Server {
public static void main(String[] args) {
final String server = "server: ";
try {
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("------------------服务端启动-----------------");
while (true) {
Socket client = serverSocket.accept();
//线程读写
new ReadThread(client, server).start();
new WriteThread(client, server).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//客户端
public class Client {
public static void main(String[] args) {
final String client = "client: ";
try {
Socket socket = new Socket("192.168.12.75", 9999);
new ReadThread(socket, client).start();
new WriteThread(socket, client).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//读重写
public class ReadThread extends Thread {
private Socket socket;//null
public ReadThread(Socket socket, String name) {
super(name);
this.socket = socket;
}
@Override
public void run() {
DataInputStream dataInputStream = null;
try {
dataInputStream = new DataInputStream(socket.getInputStream());
while (true) {
System.out.println(dataInputStream.readUTF());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//写
public class WriteThread extends Thread {
private Socket socket;
public WriteThread(Socket socket, String name) {
super(name);
this.socket = socket;
}
@Override
public void run() {
Scanner input = new Scanner(System.in);
DataOutputStream dataOutputStream = null;
while (true) {
try {
dataOutputStream = new DataOutputStream(socket.getOutputStream());
System.out.print(getName());
String msg = input.nextLine();
dataOutputStream.writeUTF(getName()+msg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
UDP
了解
发送数据:
发送者/接收者: DatagramSocket DatagramPacket
DatagramPacket(byte[] buf, int length, InetAddress address, int port) 发送数据
DatagramPacket(byte[] buf, int length) 接收数据
DatagramSocket(int port) 此类表示用于发送和接收数据报数据包的套接字。
DatagramSocket(int port, InetAddress laddr)
public class ReceiveMsgUDP {//服务端
public static void main(String[] args) {
try {
//创建接收数据socket
DatagramSocket socket = new DatagramSocket(6666,InetAddress.getLocalHost());
//存储接收的数据的
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf,buf.length);
//接收数据
socket.receive(packet);
// System.out.println(new String(buf));
// byte[] data = packet.getData();
// int length = packet.getLength();
System.out.println(new String( packet.getData(),0,packet.getLength()));
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class SendMsgUDP {//等同于客户端
public static void main(String[] args) {
//创建发送数据的socket
try {
DatagramSocket datagramSocket = new DatagramSocket();
String msg = "hello,world";
//将发送的数据存储到数据包中
byte[] bytes = msg.getBytes();
DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 6666);
//DatagramPacket(byte[] buf, int length, InetAddress address, int port)
datagramSocket.send(packet);
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
### MySQL
#### 数据库的基本概念
```sql
1. 数据库的英文单词: DataBase 简称 : DB
2. 什么数据库? ---->存储不同类型的数据
* 用于存储和管理数据的仓库。
3. 数据库的特点:
1. 持久化存储数据的。其实数据库就是一个文件系统
2. 方便存储和管理数据
3. 使用了统一的方式操作数据库
数据库管理系统(DBMS)
> 数据库管理系统是为管理数据库而设计的电脑软件。 Database Management System
> 一般具有存储、截取、安全保障、备份等基础功能.
> * Mysql Oracle Sqlserver
数据库的分类
mysql数据库管理系统: 数据库--->表--->字段 类型 约束 数据
> 1. 从存储位置:
1. 基于磁盘: ==Mysql== Oracle Sqlserver (IO 效率低)
2. 基于缓存(内存): redis mogodb hbase (数据不能完全保证持久化)
> 2. 从关系上划分:
1. 关系型数据库: 数据与数据有关系 ==Mysql== Oracle Sqlserver RDBMS
2. 非关系型数据库: key---value (json) redis (读多) NOSQL(not only sql)
redis(CAS)+mysql
Mysql
配置
* MySQL服务启动
1. 手动。
2. cmd--> services.msc 打开服务的窗口
3. 使用管理员打开cmd
* net start mysql : 启动mysql的服务
* net stop mysql:关闭mysql服务
* MySQL登录
1. mysql -uroot -p密码
2. mysql -hip -uroot -p连接目标的密码
3. mysql --host=ip --user=root --password=连接目标的密码
* MySQL退出
1. exit
2. quit
SQL
SQL: 结构化查询语言。 strtuctured query language
1.DDL: 数据定义语言 data definition language create drop alter等
2.DML: 数据操作语言 insert delete update
3.DQL: 数据查询语言 select
4.DCL:数据控制语言 grant commit rollback 等
mysql(数据库管理系统)---> 有很多数据库database
数据库(database)---> 表(table)----> 字段(属性) 字段类型 约束 数据
1 指令
mysql服务端应用程序: cmd(客户端) —> 主机(端口) root/密码
-- 1.连接mysql服务
mysql -hip -uroot -p密码 -- ip: localhost/127.0.0.1/192.168.12.75(root没有权限)
如果连接本机mysql服务。-hip 是可以省略。(打开了mysql的连接)---> max-connection:151
-- C:\Users\DELL>mysql -h127.0.0.1 -uroot -p
-- Enter password: ****
-- C:\Users\DELL>mysql -uroot -p
-- 2. 查看mysql所有的db
mysql> show databases;
-- 3. 选中指定的数据库
use 数据库名称;
mysql> use mysql;
-- 4. 查看指定数据库所有的table
mysql> show tables;
-- 5. 查看指定的表结构
mysql> desc user;
-- 6. 查看表数据 DQL
select * from 表名; select * from user;
--- mysql> select database(); 查看当前使用的数据库
-- 7. 查看创建数据库的信息
mysql> show create database java; -- latin1(只能存储数字和英文)
+----------+-----------------------------------------------------------------+
| Database | Create Database |
+----------+-----------------------------------------------------------------+
| java | CREATE DATABASE `java` /*!40100 DEFAULT CHARACTER SET latin1 */ |
2 DDL(了解)
-- 1. 创建数据库
create database 数据库名称; -- mysql> create database java; 数据库名称不允许更改
-- 2. 删除数据库
drop database 数据库名称; --- 数据无法回滚
-- 3.创建表 tb_userinfo t_userinfo userinfo (创建表结构---> 字段 类型 约束)
create table 表名(
字段1 类型 [约束],
字段2 类型 [约束],
字段3 类型 [约束],
....
字段n 类型 [约束]
);
整型: tinyint unsigned int (id) bigint(时间 ---> 毫秒数/id)
字符串: char(n) varchar(n)
char(3) varchar(3) 代表可以存储3个字符 char是个定长(查询 提前trim())
"ab " "ab"
-- 创建学生表(必不可少3个字段: id createtime updatetime)
create table tb_student(
id int(3),
stuname varchar(20),
gender char(1),
score float(4,1),
money decimal(4,1),
birthday date,
createtime datetime,
updatetime datetime
);
-- 修改表结构: alter
-- 1. 新增一个字段 age
alter table tb_student add age tinyint(2) unsigned; //unsigned不能为负数
-- mysql> alter table tb_student add age tinyint(2) unsigned after gender;
-- 2.删除字段
alter table tb_student drop age;
-- 3.修改字段名称/类型
mysql> alter table tb_student change age stuage tinyint(2) unsigned;
mysql> alter table tb_student change stuage age int(1) unsigned;
-- 4. 修改类型
mysql> alter table tb_student modify age tinyint(2) unsigned;
-- 5. 修改表名
alter table tb_student rename student;
-- 6. 删除表
drop table tb_student;
3 * DML
-- 1. 新增 insert(1行记录受影响)
1.1 语法: insert into 表名 values (数据1,数据2,...数据n),(); 对表的所有的字段赋值
insert into tb_student values (2, 'jim1', 'n',90,200,'2020-01-01','2020-01-01 12:00:00','2020-01-01 12:00:00');
1.2 语法: 指定字段新增(推荐)
insert into 表名 (字段1,字段2...字段n) values (数据1,数据2,...数据n);
mysql> insert into tb_student (id,stuname,score,createtime) values (4,'lucy',100,now());
1.3 添加多条数据
insert into
items(name,city,price,number,picture)
VALUES
('耐克运动鞋','广州',500,1000,'003.jpg'),
('耐克运动鞋2','广州2',500,1000,'002.jpg');
这样,就实现了一次性插入了2条数据
--2. 删除 delete(>=0条记录受影响的)
语法: delete from 表名 [where (字段)条件 and/or]; 条件删除
delete from tb_student;-- 清空表数据
mysql> delete from tb_student where id =3;
mysql> delete from tb_student where id =1 and stuname ='jim1';
-- 3. 修改 update(>=0条记录受影响的)
语法:
1. update 表名 set 字段名1=新值,字段名2=新值...字段名n=新值 [where (字段)条件 and/or];
update tb_student set gender ='f';
mysql> update tb_student set money=1000,birthday='2020-01-01', updatetime=now() where id=4;
mysql> show variables like '%character%';
+--------------------------+---------------------------------------------------------+
| Variable_name | Value |
+--------------------------+---------------------------------------------------------+
| character_set_client | gbk |
| character_set_connection | gbk |
| character_set_database | latin1 |
| character_set_filesystem | binary |
| character_set_results | gbk |
| character_set_server | latin1 |
| character_set_system | utf8 |
| character_sets_dir | C:\Program Files\MySQL\MySQL Server 5.7\share\charsets\ |
+--------------------------+---------------------------------------------------------+
mysql> alter database java character set utf8; -- 修改指定数据库的编码格式
-- 后续创建的数据库的编码还是latin。
-- 修改mysql的核心配置文件: my.ini
66 default-character-set=utf8
100 character-set-server=utf8
-- 重启mysql的服务。
4 * 约束
限定字段的数据。(新增/修改)
1. 空约束 null(行级约束)
-- (字段值可以为null)
create table a(
id int null,
`name` VARCHAR(10) null
);
-- insert into a (name) values ('abc');
-- update a set name = null where id = 123;
2. 非空约束 not null(行级约束)
-- 字段数据不允许为null(可以重复)
-- create table b(
-- id int not null,
-- `name` VARCHAR(10) not null,
-- age TINYINT(2)
-- );
-- insert into b values (1,'jim',20);
insert into b (id,name) values (2,'lucy');
-- Field 'id' doesn't have a default value
3. 唯一性约束 unique(行级约束)
-- 限定字段的数据唯一的不可重复的(排除null值) (唯一性索引--->index): 提升查询的效率
-- create table c(
-- id int not null,
-- `name` VARCHAR(10) unique,
-- age TINYINT(2) NOT NULL UNIQUE
-- );
-- insert into c values (1,'jim',20),(2,null,18);
-- > 1062 - Duplicate entry '20' for key 'age'
insert into c values (2,'tom',22);
4. 主键约束 primary key(行级/表级约束)
-- 等同于: not null + uinque (既不能为null又是唯一的)
-- 修饰一个列(字段): 一张表只有一个主键列(标识行记录的唯一性)
-- 任意类型的字段都可以充当主键列。常用: int/bigint/varchar id为主键列
-- create table c(
-- id int primary key,
-- `name` VARCHAR(10) unique,
-- age TINYINT(2)
-- );
-- insert into c values (1,'jim',20),(2,'tom',20);
insert into c (name,age)values('lucy',20);
-- 整型的字段充当主键列,主键列数据一般都是自增的。auto_increment
-- create table c(
-- id int primary key auto_increment,
-- `name` VARCHAR(10) unique,
-- age TINYINT(2)
-- );
-- insert into c values (101,'jim1',20),(102,'tom1',20);
-- insert into c (name,age)values('lucy8',20);
-- auto_increment: 初始值1 每次自增+1(步长)
-- 修改自增的初始值
-- alter table c auto_increment=1000;
-- 修改步长
-- set GLOBAL auto_increment_increment=1;
-- 主键列是字符串类型的varchar
-- create table d(
-- id varchar(100) primary key,
-- `name` VARCHAR(10) unique,
-- age TINYINT(2)
-- );
-- insert into d values ('1','jim1',20),('2','tom1',20);
insert into d values (UUID(),'jim',20),(UUID(),'tom',20);
-- 随机生成的字符串 UUID
-- SELECT UUID();
-- SELECT UUID();
5. 默认约束default
-- 5. 默认约束 default
-- 给字段设定默认值
-- create table d(
-- id varchar(100) primary key,
-- `name` VARCHAR(10) DEFAULT '无名氏',
-- age TINYINT(2),
-- gender char(1) not null default '男'
-- );
-- insert into d (id,age) values (uuid(),20);
-- 注释
create table d(
id varchar(100) primary key COMMENT '用户id',
`name` VARCHAR(10) DEFAULT '无名氏'COMMENT '用户姓名',
age TINYINT(2),
gender char(1) not null default '男'
);
6. 外键约束 foreign key(表级约束)
表示表与表有关系。 (与2张表有关)
-- 1. 一对一 (一个用户有一个角色)
-- create table tb_role(
-- id int PRIMARY key auto_increment,
-- rolename varchar(10) not null unique,
-- `desc` varchar(100),
-- createtime datetime,
-- updatetime datetime
-- );
-- alter TABLE tb_role auto_increment = 1000;
create TABLE tb_userinfo(
id int PRIMARY key auto_increment,
username varchar(20) not null,
age TINYINT(2) UNSIGNED,
gender TINYINT(1) UNSIGNED COMMENT '0 false 男 1 true 女',
createtime BIGINT(2),
updatetime BIGINT(2)
);
-- 用户是哪一个角色是要严格参照角色表的信息。
-- 主表: 角色表
-- 从表(子表): 用户表
-- 从表的数据要严格参照主表的数据。
-- 外键列(有一个字段充当)在从表里面。(用户表有一个列充当外键列(外键列的数据要严格参照主表的数据))
alter table tb_userinfo add CONSTRAINT fk_user_roleid
FOREIGN key(roleid) REFERENCES tb_role(id);
数据库的表设计:(3大范式)
1. 列的原子性(列不可再分)
2. 遵循第一范式基础之上,保证每行记录的唯一性。(主键)
3. 遵循第二范式基础之上,避免出现数据的冗余(外键列的数据除外(参照主表的主键列的))
需求为准
--1. fk_user_roleid roleid java tb_role id RESTRICT RESTRICT
-- 修改/删除主表数据的时候,查看子表的记录是否关联主表的数据。
-- 2. fk_user_roleid roleid java tb_role id CASCADE CASCADE
-- 删除主表数据的时候,子表关联的记录会一起删除。(不推荐)
-- 3.fk_user_roleid roleid java tb_role id SET NULL SET NULL
-- 删除主表数据的时候,子表外键列数据会设置为null(外键列可以为null)
-- 使用外键效率很低,弱外键。(所有的业务逻辑(表与表的关系)都在业务代码层面上进行解决)
2.一对多/多对多
tb_teacher
tb_student
* DQL(查询)
--DCL: grant all privileges on *.* to 'root'@'%' identified by 'root';
-- 将mysql数据库里面的user表root用户的host字段的数据改成%。
select * from 表名;-- *: 通配的所有的字段名称
语法:(只要是查询一般后面都会where/order by/limit)
select
字段名称1, 字段名称2,....
from 表1,表2,.....
[where 条件1 and/or 条件2]-- 过滤不符合条件的行记录
[group by 字段1] -- 分组
[having 条件]-- 对分组之后的数据进行过滤
[order by 字段 asc/desc ]-- 对行记录进行排序
[limit ?,?]-- 限定查询的行记录
1. 条件查询 where
条件查询就是在查询时给出WHERE子句,在WHERE子句中可以使用如下运算符及关键字:
=、!=、<>、<、<=、>、>=;
BETWEEN…AND;是否满足一个区间范围 >= <=
IN(set);条件的集合
IS NULL;
AND; 连接多个条件的查询
OR;or 满足其中一个条件就可以
NOT;
-- 查询学生性别为女,并且年龄15的记录
-- select * from stu where gender = 'female' and age = 15;
-- 查询学号为S_1001,S_1002,S_1003的记录
-- select * from stu where sid = 's_1001' or sid = 's_1002' or sid = 's_1003';
-- select * from stu where sid in ('s_1001','s_1002','s_1003');
-- 查询学号不是S_1001,S_1002,S_1003的记录
-- select * from stu where sid != 's_1001' and sid != 's_1002' and sid != 's_1003';
-- select * from stu where sid not in ('s_1001','s_1002','s_1003');
-- 查询年龄为null的记录(字段值null is)
-- select * from stu where age is null;
-- 查询年龄不为null的学生记录
-- select * from stu where age is not null;
-- 查询年龄在20到40之间的学生记录
-- select * from stu where age>=20 and age <=40;
-- select * from stu where age BETWEEN 20 and 40;
-- 查询性别非男的学生记录
-- select * from stu where gender !='male' or gender is null;
2. 模糊查询 like
-- show variables like '%character%'; 查看整个mysql所有字符编码格式
-- show variables like '%character%';
-- 查询姓名由5个字母构成的学生记录 (模糊通配字母: _ )
-- select * from stu where sname like '_____';
-- 查询姓名以“z”开头的学生记录(%: 通配任意量的内容)
-- select * from stu where sname like 'z%';
-- 查询姓名中第2个字母为“i”的学生记录
-- select * from stu where sname like '_i%';
-- 查询姓名中包含“a”字母的学生记录
-- select * from stu where sname like '%a%';
3.字段控制查询
-- 3.1 去重 distinct (单列)
-- 查询学生表里面所有的性别。
-- select DISTINCT gender from stu;
-- 3.2 null值运算
-- 把学生的年龄都+5(字段值null,算术运算 最后的结果都是null)
-- 需求: null-->0 ifnull(字段,新值)
-- select sid,sname,age, (ifnull(age,0)+5) from stu;
-- 查询员工的月薪和佣金之和
-- select empno,ename,sal,comm,sal+IFNULL(comm,0) from emp;
-- 3.3 别名查询 as(可以省略) 字段/表名 起别名查询(表名别名: 多表)
-- select e.empno,e.ename,e.sal,comm,sal+IFNULL(comm,0) as '月薪和佣金之和' from emp e;
4. 排序 order by
-- 按照多个字段值进行升序或者降序排列。 默认是升序 asc 降序: desc
-- 查询所有学生记录,按年龄降序排序
-- SELECT * FROM stu ORDER BY age desc;
-- 查询所有雇员,按月薪降序排序,如果月薪相同时,按编号降序排序
-- SELECT * FROM emp ORDER BY sal desc,empno desc;
5. 分组查询
COUNT():统计指定列不为NULL的记录行数;
MAX():计算指定列的最大值,如果指定列是字符串类型,那么使用字符串排序运算;
MIN():计算指定列的最小值,如果指定列是字符串类型,那么使用字符串排序运算;
SUM():计算指定列的数值和,如果指定列类型不是数值类型,那么计算结果为0;
AVG():计算指定列的平均值,如果指定列类型不是数值类型,那么计算结果为0;
-- count(参数) 统计表里面行记录(参数: 字段(一般都是主键列))
-- 查询emp表中记录数:
-- select count(empno) as total,count(*),count(1) from emp;
-- 查询emp表中有佣金的人数:注意,因为count()函数中给出的是comm列,那么只统计comm列非NULL的行数。
-- select count(comm) from emp;
-- 查询emp表中月薪大于2500的人数:
-- select count(*) from emp where sal>2500;
-- 统计月薪与佣金之和大于2500元的人数:
-- select count(*) from emp where sal+IFNULL(comm,0)>2500;
-- 查询最高工资和最低工资
-- select max(sal)'最高工资',min(sal) '最低工资' from emp;
-- 查询有佣金的人数,以及有领导的人数:
-- select count(comm),count(mgr) from emp;
-- 查询所有雇员月薪和,所有雇员佣金和,所有雇员月薪+佣金和 统计所有员工平均工资: :
-- select sum(sal),sum(IFNULL(comm,0)),sum(sal+IFNULL(comm,0)),avg(sal) from emp;
-- 查询每个部门的部门编号以及每个部门的人数:
-- select deptno,count(*) from emp GROUP BY deptno;
-- 查询每个部门的部门编号以及每个部门员工工资大于1500的人数:
-- select deptno,count(*) from emp where sal>1500 GROUP BY deptno;
-- select deptno,count(*) from (select * from emp where sal>1500) temp GROUP BY deptno ;
-- 查询工资总和大于9000的部门编号以及工资和:
-- where 不能与聚合函数使用 Invalid use of group function
-- 分组之后的数据进行顾虑 having
-- select deptno,sum(sal) sum from emp GROUP BY deptno having sum>9000;
-- where vs having
-- select * from emp having sal>1500;
-- 1. 位置
-- 2. 组函数运用 where 不可以 having 可以
6. 关联查询
-- 关联查询(多表之间数据查询)---> 推荐
-- 1.等值连接(连接的条件是=)
-- 查询员工信息,要求显示员工号,姓名,月薪,部门名称
-- 56= 14*4 (笛卡尔积数据)
-- 2张表 至少有1个条件 3张表 至少2个条件
-- select e.empno,e.ename,e.sal,d.dname,d.loc from emp e,dept d where e.deptno = d.deptno;
-- 2.不等值连接(连接的条件不是=)
-- 查询员工信息,要求显示:员工号,姓名,月薪,薪水的级别
-- select e.empno,e.ename,e.sal,s.GRADE from emp e,salgrade s where e.sal BETWEEN s.LowSAL and s.HISAL;
-- select e.empno,e.ename,e.sal,d.dname,d.loc,s.GRADE from emp e,dept d,salgrade s where e.deptno = d.deptno and e.sal BETWEEN s.LowSAL and s.HISAL;
-- 3.外连接
-- 查询每个部门的部门编号和每个部门的工资和: 基表: dept
-- 因为员工表没有员工在40号部门
-- select deptno,count(*),sum(sal) from emp GROUP BY deptno;
-- select d.*,count(*),sum(sal) from dept d,emp e where e.deptno = d.deptno GROUP BY e.deptno;
-- 1.内连接 inner join on/where ==> 等值连接
-- select d.*,count(*),sum(sal) from dept d INNER JOIN emp e where e.deptno = d.deptno GROUP BY e.deptno;
-- 2.左外连接 以左表为基准 右表没有的数据以null进行填充 LEFT JOIN on
-- select d.*,count(e.empno),IFNULL(sum(e.sal),0) from dept d LEFT JOIN emp e on e.deptno = d.deptno GROUP BY e.deptno ORDER BY d.deptno;
--
-- 3.右外连接 以右表为基准 左表没有的数据以null进行填充 RIGHT JOIN on
-- select d.*,count(e.empno),IFNULL(sum(e.sal),0) from emp e RIGHT JOIN dept d on e.deptno = d.deptno GROUP BY e.deptno ORDER BY d.deptno;
-- 4. 自连接(自己与自己关联查询 把1张表看成多张表使用)
-- 查询员工姓名和员工的老板的名称 emp: 员工表 e1 老板表 e2
-- select e1.empno,e1.ename,e1.mgr,e2.empno,e2.ename from emp e1, emp e2 WHERE e1.mgr=e2.empno ORDER BY e1.empno;
-- 一共14个员工 13条记录
-- select e1.empno,e1.ename,e1.mgr,e2.empno,e2.ename from emp e1 INNER JOIN emp e2 WHERE e1.mgr=e2.empno ORDER BY e1.empno;
-- select e1.empno,e1.ename,e1.mgr,e2.empno,e2.ename from emp e1 LEFT JOIN emp e2 on e1.mgr=e2.empno ORDER BY e1.empno;
-- 5. 子查询(查询条件的数据是未知的 级别最高)
-- 查询工资为800的员工信息
-- select * from emp where sal = 800;
-- 查询工资为20号部门平均工资的员工信息. 使用1条sql
-- select * from emp where sal = (select avg(sal) from emp where deptno = 20);
-- 6.集合查询 union vs union all
-- 分库分表 mycat: 用户表: tb_user1 tb_user2 DISTINCT(单列)
-- 去除重复的行记录(多个列的数据)
-- select * from tb_user1
-- UNION
-- select * from tb_user2
-- UNION
-- select * from tb_user3;
-- 通过1条sql语句 查询3张表的数据。 union去除重复的行记录 union all 不去重
7. 分页查询 limit
-- 6.分页查询(查询量的限定)--->100 每页30条记录 4页
-- limit mysql独有关键字
-- Limit size; 代表从第1条记录开始查询 查询size
-- limit start,size;代表从第start条记录开始查询 查询size 行记录有索引 0
-- 查询表的总记录数: select count(id) from 表;
-- pageSize = 30/20/10;
-- totalPage = count/pageSize
-- select count(empno) from emp;-- 14
-- 每页展示5条数据
-- 3页
-- 1页: 5条
-- SELECT * FROM emp ORDER BY empno LIMIT 0,5;
-- 2页 5条
-- SELECT * FROM emp ORDER BY empno LIMIT 5,5;
-- 3页 4
-- SELECT * FROM emp ORDER BY empno LIMIT 10,5;
-- 通用sql:
-- int pageSize = 5;
-- int page = 1/2/3/totalPage 第一个参数的数据完全取决于用户要查看第几页的数据
-- SELECT * FROM emp ORDER BY empno LIMIT (page-1)*pageSize,pageSize;
JDBC
目的: 通过java程序(编写sql)实现数据库表数据的CRUD. create read update delete
sql在哪里运行? 数据库的服务。
JAVA DATABASE Connectivity java数据库连接技术。(实现对不同dbms操作)
DBMS: 不同的数据库厂商提供的软件。
1.获得数据库连接
java.sql.Connection 接口
代表不同的数据库连接对象, mysql oracle 提高程序的扩展性。
public static void main(String[] args) {
//获得数据库连接
String username = "root";
String password = "root";
// String url = "jdbc:mysql://ip地址:端口/指定数据库名称?参数=值&参数=值";
String url = "jdbc:mysql://192.168.12.75:3306/exercise?characterEncoding=utf-8&useSSL=true";
String driver = "com.mysql.jdbc.Driver";//下载驱动文件 jar 类库文件
//mysql服务: mysql5.7 mysql8 下载安装相同版本
//JDBC 获得指定数据库的连接对象
Connection connection = null;
try {
//1. 注册驱动(将指定数据库的驱动对象加载jvm里面)--->加载Driver.class 创建Class的对象
//java项目: JDBC 4 注册驱动也可以省略。
//web,boot 运行服务器 这时候注册驱动就不可以省略了
Class.forName(driver);
//2. 获得连接对象 DriverManager
connection = DriverManager.getConnection(url, username, password);
//多态: mysql的连接对象
System.out.println("mysql的连接:"+connection);
//com.mysql.jdbc.JDBC4Connection@446cdf90
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
//资源(关闭) 151
try {
if(connection!=null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Class.forName(driver); 干了什么?
//在jvm中加载了com.mysql.jdbc.Driver.class 会执行静态代码块逻辑
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());//注册驱动
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
1.1 资源文件
#配置在property里
# 资源数据一般不可变,交给配置文件维护:
jdbc.username=root
jdbc.password=root
jdbc.url=jdbc:mysql://192.168.12.75:3306/test?characterEncoding=utf-8&useSSL=true
jdbc.driver=com.mysql.jdbc.Driver
// 配置文件内容只需要加载一次
public class PropUtil {
private PropUtil() {
}
private static final String FILE_NAME = "jdbc.properties";
private static Properties properties;
// 在静态代码块里 只加载一次资源配置文件
static {
properties = new Properties();
try {
properties.load(DBHelper.class.getClassLoader().getResourceAsStream(FILE_NAME));
Class.forName(getValue("jdbc.driver"));
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static String getValue(String key) {
Objects.requireNonNull(key);
return properties.getProperty(key);
}
}
public class DBHelper {
private DBHelper() {
}
public static Connection getConnection() {
Connection connection = null;
try {
connection = DriverManager.getConnection(
PropUtil.getValue("jdbc.url"),
PropUtil.getValue("jdbc.username"),
PropUtil.getValue("jdbc.password"));
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
public static void closeResource(Connection connection) {
try {
if (connection != null) {
connection.close();//成了一个无用的连接对象了
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
1.2 一个进程使用同一个对象
//在一个线程中 使用的应该是同一个连接对象 避免资源的浪费
// 使用静态代码块维护即可
public class DBHelper {
private DBHelper() {
}
private static Connection connection;
static { 多个线程里面使用同一个连接对象
try {
connection = DriverManager.getConnection(
PropUtil.getValue("jdbc.url"),
PropUtil.getValue("jdbc.username"),
PropUtil.getValue("jdbc.password"));
} catch (SQLException e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
return connection;
}
public static void main(String[] args) {
//在一个线程里面 即使调用多次获得连接操作 获得同一个连接对象
new Thread(() -> {
Connection connection1 = getConnection();
System.out.println("connection1:" + connection1);
Connection connection3 = getConnection();
System.out.println("connection3:" + connection3);
}).start();
new Thread(() -> {
Connection connection2 = getConnection();
System.out.println("connection2:" + connection2);
}).start();
}
//connection1/2/3 是同一个连接对象。
1.3 一个线程一个对象
//由于多个线程使用的同一个对象 有可能会出现另外一个线程关闭连接 也同样会导致另外一个线程无法使用连接的问题。
//需要为每个线程创建一个连接对象: ThreadLocal
public class DBHelper {
private DBHelper() {
}
//ThreadLocal 为每个线程创建一个副本
//CONNECTION_THREAD_LOCAL 维护Connection的对象
private static final ThreadLocal<Connection> CONNECTION_THREAD_LOCAL = new ThreadLocal<>();
//提供静态的方法 获得连接对象
public static Connection getConnection() {
Connection connection = null;
try {
connection = CONNECTION_THREAD_LOCAL.get();//第一次获得没有值 null
if (connection == null || connection.isClosed()) {
connection = DriverManager.getConnection(
PropUtil.getValue("jdbc.url"),
PropUtil.getValue("jdbc.username"),
PropUtil.getValue("jdbc.password"));
CONNECTION_THREAD_LOCAL.set(connection);
}
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
public static void closeResource(Connection connection) {
try {
if (connection != null) {
connection.close();//交给了CONNECTION_THREAD_LOCAL所以 close()成了一个无用的连接对象了
CONNECTION_THREAD_LOCAL.remove(); //这里要remove()
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
//在一个线程里面 即使调用多次获得连接操作 获得同一个连接对象
new Thread(() -> {
Connection connection1 = getConnection();
System.out.println("connection1:" + connection1);
Connection connection3 = getConnection();
System.out.println("connection3:" + connection3);
}).start();
new Thread(() -> {
Connection connection2 = getConnection();
System.out.println("connection2:" + connection2);
}).start();
}
//运行结果
//connection1:com.mysql.jdbc.JDBC4Connection@62a00bb6
//connection2:com.mysql.jdbc.JDBC4Connection@118f2ebb
//connection3:com.mysql.jdbc.JDBC4Connection@62a00bb6
2. CRUD
实现 java库 tb_userinfo的增删改查 DAO—> 数据访问对象
2.1 新增
2.1.1 新增所有字段
sql: insert into tb_userinfo (username,age,gender,createtime,roleid) values ();
Statement createStatement() 创建一个 Statement对象,用于将SQL语句发送到数据库。
===> sql 语句封装成了一个语句对象(Statement)
PreparedStatement prepareStatement(String sql) //推荐
创建一个 PreparedStatement对象,用于将参数化的SQL语句发送到数据库。
PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
创建一个默认的 PreparedStatement对象,该对象具有检索自动生成的密钥的能力。
PreparedStatement 接口是Statement子接口。
PreparedStatement: 参数化sql语句 可以支持占位符 预编译sql语句
Statement: 不支持占位符。可能引起sql注入
int executeUpdate()
执行在该SQL语句PreparedStatement对象,它必须是一个SQL数据操纵语言(DML)语句,如INSERT , UPDATE或DELETE ; 或不返回任何内容的SQL语句,例如DDL语句。
ResultSet executeQuery()
执行此 PreparedStatement对象中的SQL查询,并返回查询 PreparedStatement的 ResultSet对象。
ResultSet: 存储了临时表的数据(字段名称-字段值)
@Override
public int addUser() {
//1.获得数据库的连接 Connection---> 驱动类库文件中
connection = DBHelper.getConnection();
//2.准备sql语句 数据应该是动态传递过来的
sql = "insert into tb_userInfo (username,age,gender,createTime,roleId) values ('张三',20,0,UNIX_TIMESTAMP(),1001)";
int result = 0;
try {
//3.执行sql语句(sql语句在数据库的服务(管理系统))
//3.1 将sql语句发送(提交)到mysql的服务中 (sql: DDL DML DQL DCL)
ps = connection.prepareStatement(sql);// sql语句已经在ps里面
//3.2 在mysql服务中执行sql语句
result = ps.executeUpdate();// 代表表里面受影响的记录数
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBHelper.closeResource(connection, ps);
}
return result;
}
@Override
public int addUser(String name, byte age, boolean gender, int roleId) {
//1.获得数据库的连接 Connection---> 驱动类库文件中
connection = DBHelper.getConnection();
//2.准备sql语句 数据应该是动态传递过来的 使用占位符?表示参数
sql = "insert into tb_userInfo (username,age,gender,createTime,roleId) values (?,?,?,UNIX_TIMESTAMP(),?)";
int result = 0;
try {
//3.执行sql语句(sql语句在数据库的服务(管理系统))
//3.1 将sql语句发送(提交)到mysql的服务中 (sql: DDL DML DQL DCL) 参数化的sql语句
ps = connection.prepareStatement(sql);// sql语句已经在ps里面
//检查sql语句有没有占位符?
//有: 对占位符赋值 set
// ps.setInt(index,数据); 第几个占位符 ps.setObject(index,数据)
ps.setString(1, name);
ps.setByte(2, age);
ps.setBoolean(3, gender);
ps.setInt(4, roleId);
//3.2 在mysql服务中执行sql语句
result = ps.executeUpdate();// 代表表里面受影响的记录数
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBHelper.closeResource(connection, ps);
}
return result;
}
@Override
public int addUser(UserInfo userInfo) {
//1.获得数据库的连接 Connection---> 驱动类库文件中
connection = DBHelper.getConnection();
//2.准备sql语句 数据应该是动态传递过来的 使用占位符?表示参数
sql = "insert into tb_userInfo (username,age,gender,createTime,roleId) values (?,?,?,UNIX_TIMESTAMP(),?)";
int result = 0;
try {
//3.执行sql语句(sql语句在数据库的服务(管理系统))
//3.1 将sql语句发送(提交)到mysql的服务中 (sql: DDL DML DQL DCL) 参数化的sql语句
ps = connection.prepareStatement(sql);// sql语句已经在ps里面
//检查sql语句有没有占位符?
//有: 对占位符赋值 set
// ps.setInt(index,数据); 第几个占位符 ps.setObject(index,数据)
ps.setString(1, userInfo.getUsername());
ps.setByte(2, userInfo.getAge());
ps.setBoolean(3, userInfo.getGender());
ps.setInt(4, userInfo.getRoleId());
//3.2 在mysql服务中执行sql语句
result = ps.executeUpdate();// 代表表里面受影响的记录数
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBHelper.closeResource(connection, ps);
}
return result;
}
2.2 删除
delete from tb_userinfo where id=?
delete from tb_userinfo where id in (?,?,?)
2.2.1 删除单个
@Override
public int deleteUserById(int id) {
connection = DBHelper.getConnection();
sql = "delete from tb_userinfo where id = ?";
int result = 0;
try {
ps = connection.prepareStatement(sql);
ps.setObject(1, id);
result = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBHelper.closeResource(connection, ps);
}
return result;
}
@Test
public void deleteTest() {
UserInfoDao userInfoDao = new UserInfoDaoImpl();
int[] ids = {27,28,29};
for (int id : ids) {
if(userInfoDao.deleteUserById(id)>=1){
System.out.println("成功");
}else {
System.out.println("error");
}
}
}
2.2.2 删除多个
@Override
public int deleteUserByIds(int[] ids) {
connection = DBHelper.getConnection();
sql = "delete from tb_userinfo where id in ( ";
//delete from tb_userinfo where id in (1,2,3)
StringBuilder builder = new StringBuilder(sql);
for (int id : ids) {
builder.append(id);
builder.append(", ");
}
builder.deleteCharAt(builder.lastIndexOf(","));
builder.append(")");
System.out.println("sql:" + builder);
int result = 0;
try {
ps = connection.prepareStatement(builder.toString());
result = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBHelper.closeResource(connection, ps);
}
return result;
}
2.3 修改
// 修改所有字段
update tb_userinfo set username=?, age=?,gender=?,roleid=?,updatetime= where id = ?
2.3.1 修改所有字段
@Override
public int updateUserById(UserInfo userInfo) {
connection = DBHelper.getConnection();
sql = "update tb_userinfo set username=?, age=?,gender=?,roleid=?,updatetime=UNIX_TIMESTAMP() where id = ?";
int result = 0;
try {
ps = connection.prepareStatement(sql);
ps.setObject(1, userInfo.getUsername());
ps.setObject(2, userInfo.getAge());
ps.setObject(3, userInfo.getGender());
ps.setObject(4, userInfo.getRoleId());
ps.setObject(5, userInfo.getId());
result = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBHelper.closeResource(connection, ps);
}
return result;
}
2.3.2 指定字段修改(了解)
通过sql语句控制要修改的字段: sql拼接 ? 占位数据
update tb_userinfo set 字段名=新的值, where id=? key---value Map
@Override
public int updateUserByParams(Map<String, Object> params, int uid) {
connection = DBHelper.getConnection();
StringBuilder builder = new StringBuilder("update tb_userinfo set ");
params.forEach((key, value) -> {
builder.append(key);
builder.append("='");
builder.append(value);
builder.append("' ,");
});
builder.append(" updatetime =UNIX_TIMESTAMP() where id = ?");
int result = 0;
try {
ps = connection.prepareStatement(builder.toString());
ps.setInt(1, uid);
result = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBHelper.closeResource(connection, ps);
}
return result;
}
public static void updateTest2() {
Scanner input = new Scanner(System.in);
System.out.println("1.用户名 2.姓别 3. 年龄 4. 角色id");
System.out.println("请选择要修改的字端:(1,2)");
String str = input.nextLine();//1,2
String[] fields = str.split(",");
Map<String, Object> params = new HashMap<>(16);
for (String field : fields) {
switch (field) {
case "1":
System.out.println("请录入新的用户名");
params.put("username", input.nextLine());
break;
case "2":
System.out.println("请录入新的姓别");
params.put("gender", input.nextByte());
break;
case "3":
System.out.println("请录入新的年龄");
params.put("age", input.nextByte());
break;
case "4":
System.out.println("请录入新角色id");
params.put("roleid", input.nextInt());
break;
}
}
UserInfoDao userInfoDao = new UserInfoDaoImpl();
System.out.println(userInfoDao.updateUserByParams(params, 4));
}
2.4 查询
2.4.1 查询单个
select * from tb_userinfo where id=?
@Override
public UserInfoVO selectUserById(int uid) {
connection = DBHelper.getConnection();
sql = "select id,username,age,gender," +
"from_unixtime(createtime,'%Y-%m-%d %H:%i:%S')createtime, " +
"from_unixtime(updatetime,'%Y-%m-%d %H:%i:%S')updatetime," +
"roleid from tb_userinfo where id=?";
UserInfoVO userInfo = null;
try {
ps = connection.prepareStatement(sql);
ps.setInt(1, uid);
//执行sql: dql
rs = ps.executeQuery();// 查出的记录都存储在了 rs对象中
//Iterator 迭代器对象
//判断rs对象光标之后有没有更多的行记录需要遍历
if(rs.next()){
//获得属性的数据
userInfo = new UserInfoVO(uid,
rs.getString("username"),
rs.getByte("age"),
rs.getBoolean("gender"),
rs.getString("createTime"),
rs.getString("updateTime"),
rs.getInt("roleId"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBHelper.closeResource(connection, ps,rs);
}
return userInfo;
}
2.4.2 查询所有
select * from tb_userinfo
@Override
public List<UserInfoVO> selectUser() {
connection = DBHelper.getConnection();
sql = "select id,username,age,gender," +
"from_unixtime(createtime,'%Y-%m-%d %H:%i:%S')createtime, " +
"from_unixtime(updatetime,'%Y-%m-%d %H:%i:%S')updatetime," +
"roleid from tb_userinfo";
List<UserInfoVO> userInfoVOList = new ArrayList<>(10);
try {
ps = connection.prepareStatement(sql);
rs = ps.executeQuery();
//循环遍历获取光标之后的记录
while (rs.next()) {
//获得属性的数据
userInfoVOList.add(new UserInfoVO(rs));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBHelper.closeResource(connection, ps, rs);
}
return userInfoVOList;
}
2.4.3 条件查询
条件:(等值条件) 字段=值 and
select * from tb_userinfo where 字段=值 and 字段=值
@Override
public List<UserInfo> selectUserByParams(Map<String, Object> params) {
connection = DBHelper.getConnection();
StringBuilder builder = new StringBuilder("select * from tb_userinfo where ");
params.forEach((k, v) -> {
builder.append(k);
builder.append("='");
builder.append(v);
builder.append("'and ");
});
builder.delete(builder.lastIndexOf("and"), builder.length());
List<UserInfo> userInfoList = new ArrayList<>(10);
try {
ps = connection.prepareStatement(builder.toString());
rs = ps.executeQuery();
//循环遍历获取光标之后的记录
while (rs.next()) {
//获得属性的数据
userInfoList.add(new UserInfo(rs));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBHelper.closeResource(connection, ps, rs);
}
return userInfoList;
}
2.4.5 模糊查询
select * from tb_userinfo where username like '%数据%'
@Override
public List<UserInfo> selectUserByLike(String param) {
connection = DBHelper.getConnection();
// sql = "select * from tb_userinfo where username like '%"+param+"%'";
sql = "select * from tb_userinfo where username like ?";
List<UserInfo> userInfoList = new ArrayList<>(10);
try {
ps = connection.prepareStatement(sql);
ps.setString(1, "%" + param + "%");// %五%
rs = ps.executeQuery();
while (rs.next()) {
userInfoList.add(new UserInfo(rs));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBHelper.closeResource(connection, ps, rs);
}
return userInfoList;
}
2.4.6 分页查询
select * from tb_userinfo order by createtime desc limit ?,?
@Override
public List<UserInfo> selectUserByPage(int page) {
connection = DBHelper.getConnection();
sql = "select * from tb_userinfo order by createtime desc limit ?,?";
List<UserInfo> userInfoList = new ArrayList<>(10);
try {
ps = connection.prepareStatement(sql);
ps.setInt(1,(page-1)*UserInfoConst.PAGE_SIZE);
ps.setInt(2, UserInfoConst.PAGE_SIZE);
rs = ps.executeQuery();
while (rs.next()) {
userInfoList.add(new UserInfo(rs));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBHelper.closeResource(connection, ps, rs);
}
return userInfoList;
}
2.4.7 关联查询
查询多张表:
查询所有的用户信息 并展示用户的角色名称
select u.*,r.rolename from tb_userinfo u,tb_role r where u.roleid=r.id
order by u.id desc limit ?,?;
@Override
public List<Map<String, Object>> selectUserAndRole(int page) {
connection = DBHelper.getConnection();
sql = "select u.*,r.rolename from tb_userinfo u,tb_role r where u.roleid=r.id \n" +
" order by u.id desc limit ?,?";
List<Map<String, Object>> list = new ArrayList<>(10);
try {
ps = connection.prepareStatement(sql);
ps.setInt(1, (page - 1) * UserInfoConst.PAGE_SIZE);
ps.setInt(2, UserInfoConst.PAGE_SIZE);
rs = ps.executeQuery();
while (rs.next()) {
Map<String, Object> map = new LinkedHashMap<>(10);
map.put("id", rs.getInt("id"));
map.put("username", rs.getString("username"));
map.put("rolename", rs.getString("rolename"));
list.add(map);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBHelper.closeResource(connection, ps, rs);
}
return list;
}
面试
线程
- 等待当前线程死亡。 join 3个线程 : A B C ----> 有顺序执行
- wait 与sleep的区别
wait() wait(time)
1. 所属: sleep是Thread类方法 wait() Object
2. wait(time) sleep(time) 指定时间内处于等待
自己醒过来 (sleep 是继续执行以下逻辑)
wait(time) 处于就绪状态 重新抢占cpu
时间内: sleep无法唤醒 wait(time) 可以用个notify
调用wait会释放锁对象,sleep不会自动释放锁
调用wait,必须通过锁对象调用,thread对象调用sleep
3. wait()