数据库&JDBC(知识点整理)

文章目录

Mysql数据库表引擎与字符集

1、服务器处理客户端请求

其实不论客户端进程和服务器进程是采用哪种方式进行通信,最后实现的效果都是:客户端进程向服务器进程发送一段文本(MySQL语句),服务器进程处理后再向客户端进程发送一段文本(处理结果)。那服务器进程对客户端进程发送的请求做了什么处理,才能产生最后的处理结果呢?客户端可以向服务器发送增删改查各类请求,我们这里以比较复杂的查询请求为例来画个图展示一下大致的过程:

在这里插入图片描述

虽然查询缓存有时可以提升系统性能,但也不得不因维护这块缓存而造成一些开销,比如每次都要去查询缓存中检索,查询请求处理完需要更新查询缓存,维护该查询缓存对应的内存区域。从MySQL 5.7.20开始,不推荐使用查询缓存,并在MySQL 8.0中删除。

2、存储引擎

MySQL 服务器把数据的存储和提取操作都封装到了一个叫 存储引擎 的模块里。我们知道 表 是由一行一行的记录组成的,但这只是一个逻辑上的概念,物理上如何表示记录,怎么从表中读取数据,怎么把数据写入具体的物理存储器上,这都是 存储引擎 负责的事情。为了实现不同的功能, MySQL 提供了各式各样的 存储引擎 ,不同 存储引擎 管理的表具体的存储结构可能不同,采用的存取算法也可能不同。

存储引擎以前叫做 表处理器 ,它的功能就是接收上层传下来的指令,然后对表中的数据进行提取或写入操 作。

为了管理方便,人们把 连接管理 、 查询缓存 、 语法解析 、 查询优化 这些并不涉及真实数据存储的功能划分为 MySQL server 的功能,把真实存取数据的功能划分为 存储引擎 的功能。各种不同的存储引擎向上边的 MySQL server 层提供统一的调用接口(也就是存储引擎API),包含了几十个底层函数,像"读取索引第一条内容"、“读取索引下一条内容”、"插入记录"等等。

所以在 MySQL server 完成了查询优化后,只需按照生成的执行计划调用底层存储引擎提供的API,获取到数据后返回给客户端就好了。

MySQL 支持非常多种存储引擎:
在这里插入图片描述

MyISAM和InnoDB表引擎的区别

1) 事务支持

MyISAM不支持事务,而InnoDB支持。

事物:访问并更新数据库中数据的执行单元。事物操作中,要么都执行要么都不执行

2) 存储结构

MyISAM:每个MyISAM在磁盘上存储成三个文件。

.frm文件存储表结构。

.MYD文件存储数据。

.MYI文件存储索引。

InnoDB:主要分为两种文件进行存储

.frm 存储表结构

.ibd 存储数据和索引 (也可能是多个.ibd文件,或者是独立的表空间文件)

3) 表锁差异

MyISAM:只支持表级锁,用户在操作myisam表时,select,update,delete,insert语句都会给表自动加锁,如果加锁以后的表满足insert并发的情况下,可以在表的尾部插入新的数据。

InnoDB:支持事务和行级锁,是innodb的最大特色。行锁大幅度提高了多用户并发操作的效率。但是InnoDB的行锁,只是在WHERE的主键是有效的,非主键的WHERE都会锁全表的。

4) 表主键

MyISAM:允许没有任何索引和主键的表存在,索引都是保存行的地址。

InnoDB:如果没有设定主键或者非空唯一索引,就会自动生成一个6字节的主键(用户不可见),数据是主索引的一部分,附加索引保存的是主索引的值。 InnoDB的主键范围更大,最大是MyISAM的2倍。

5) 表的具体行数

MyISAM:保存有表的总行数,如果select count() from table;会直接取出出该值。

InnoDB:没有保存表的总行数 (只能遍历),如果使用select count() from table;就会遍历整个表,消耗相当大,但是在加了wehre条件后, myisam和innodb处理的方式都一样。

6) CRUD操作

MyISAM:如果执行大量的SELECT,MyISAM是更好的选择。

InnoDB:如果你的数据执行大量的INSERT或UPDATE,出于性能方面的考虑,应该使用InnoDB表。DELETE 从性能上InnoDB更优,但DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的删除,在innodb上如果要清空保存有大量数据的表,最好使truncate table这个命令。

7) 外键

MyISAM:不支持

InnoDB:支持

8) 查询效率

MyISAM相对简单,所以在效率上要优于InnoDB,小型应用可以考虑使用MyISAM。推荐考虑使用InnoDB来替代MyISAM引擎,原因是InnoDB自身很多良好的特点,比如事务支持、存储 过程、视图、行级锁定等等,在并发很多的情况下,相信InnoDB的表现肯定要比MyISAM强很多。

另外,任何一种表都不是万能的,只用恰当的针对业务类型来选择合适的表类型,才能最大的发挥MySQL的性能优势。如果不是很复杂的Web应用,非关键应用,还是可以继续考虑MyISAM的,这个具体情况可以自己斟酌。

9)MyISAM和InnoDB两者的应用场景

MyISAM管理非事务表。它提供高速存储和检索,以及全文搜索能力。如果应用中需要执行大量的SELECT查询,那么MyISAM是更好的选择。 InnoDB用于事务处理应用程序,具有众多特性,包括ACID事务支持。如果应用中需要执行大量的INSERT或UPDATE操作,则应该使用InnoDB,这样可以提高多用户并发操作的性能。现在默认使用InnoDB。

字符集和乱码

字符集简介

我们知道在计算机中只能存储二进制数据,那该怎么存储字符串呢?当然是建立字符与二进制数据的映射关系了,

建立这个关系最起码要搞清楚两件事儿:

  1. 你要把哪些字符映射成二进制数据?

也就是界定清楚字符范围。

  1. 怎么映射?

将一个字符映射成一个二进制数据的过程也叫做 编码 ,将一个二进制数据映射到一个字符的过程叫做 解码 。

人们抽象出一个 字符集 的概念来描述某个字符范围的编码规则

我们看一下一些常用字符集的情况:

ASCII 字符集

共收录128个字符,包括空格、标点符号、数字、大小写字母和一些不可见字符。由于总共才128个字符,所以可以使用1个字节来进行编码,我们看一些字符的编码方式:

‘L’ -> 01001100(十六进制:0x4C,十进制:76)

‘M’ -> 01001101(十六进制:0x4D,十进制:77)

ISO 8859-1 字符集

共收录256个字符,是在 ASCII 字符集的基础上又扩充了128个西欧常用字符(包括德法两国的字母),也可以使用1个字节来进行编码。这个字符集也有一个别名 latin1 。

GB2312 字符集

收录了汉字以及拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母。其中收录汉字6763个,其他文字符号682个。同时这种字符集又兼容 ASCII 字符集,所以在编码方式上显得有些奇怪:如果该字符在 ASCII 字符集中,则采用1字节编码。否则采用2字节编码。

这种表示一个字符需要的字节数可能不同的编码方式称为 变长编码方式 。比方说字符串 ‘爱u’ ,其中 ‘爱’ 需要用2个字节进行编码,编码后的十六进制表示为 0xCED2 , ‘u’ 需要用1个字节进行编码,编码后的十六进制表示为 0x75 ,所以拼合起来就是 0xCED275 。

小贴士: 我们怎么区分某个字节代表一个单独的字符还是代表某个字符的一部分呢?别忘了 ASCII 字符集只收录128个字符,使用0~127就可以表示全部字符,所以如果某个字节是在0~127之内的,就意味着一个字节代表一个单独的字符,否则就是两个字节代表一个单独的字符。

GBK 字符集

GBK 字符集只是在收录字符范围上对 GB2312 字符集作了扩充,编码方式上兼容 GB2312 。

utf8 字符集

收录地球上能想到的所有字符,而且还在不断扩充。这种字符集兼容 ASCII 字符集,采用变长编码方式,编码一个字符需要使用1~4个字节,比方说这样:

‘L’ -> 01001100(十六进制:0x4C)

‘啊’ -> 111001011001010110001010(十六进制:0xE5958A)

小贴士: 其实准确的说,utf8只是Unicode字符集的一种编码方案,Unicode字符集可以采用utf8、 utf16、utf32这几种编码方案,utf8使用1~4个字节编码一个字符,utf16使用2个或4个字节编码一个字符,utf32使用4个字节编码一个字符。更详细的Unicode和其编码方案的知识不是本书的重点,大家上网查查哈~ MySQL中并不区分字符集和编码方案的概念,所以后边唠叨的时候把utf8、utf16、utf32 都当作一种字符集对待。

对于同一个字符,不同字符集也可能有不同的编码方式。比如对于汉字 ‘我’ 来说, ASCII 字符集中根本没有收录这个字符, utf8 和 gb2312 字符集对汉字"我"的编码方式如下:

utf8编码:111001101000100010010001 (3个字节,十六进制表示是:0xE68891)

gb2312编码:1100111011010010 (2个字节,十六进制表示是:0xCED2)

MySQL中的utf8和utf8mb4

我们上边说 utf8 字符集表示一个字符需要使用1~4个字节,但是我们常用的一些字符使用1~3个字节就可以表示了。而在 MySQL 中字符集表示一个字符所用最大字节长度在某些方面会影响系统的存储和性能,所以设计 MySQL 的大叔偷偷的定义了两个概念:

utf8mb3 :阉割过的 utf8 字符集,只使用1~3个字节表示字符。

utf8mb4 :正宗的 utf8 字符集,使用1~4个字节表示字符。

有一点需要大家十分的注意,在 MySQL 中 utf8 是 utf8mb3 的别名,所以之后在 MySQL 中提到 utf8 就意味着使用1~3个字节来表示一个字符,如果大家有使用4字节编码一个字符的情况,比如存储一些emoji表情啥的,那请使用 utf8mb4 。

字符集的查看

MySQL 支持好多好多种字符集,查看当前 MySQL 中支持的字符集可以用下边这个语句:

show charset;

数据库基础

简介

数据库(DataBase,DB):指长期保存在计算机的存储设备上,按照一定规则组织起来,可以被各种用户或应用共享的数据集合。

数据库管理系统(DataBase Management System,DBMS):指一种操作和管理数据库的大型软件,用于建立、使用和维护数据库,对数据库进行统一管理和控制,以保证数据库的安全性和完整性。用户通过数据库管理系统访问数据库中的数据。

数据库软件应该为数据库管理系统,数据库是通过数据库管理系统创建和操作的。

数据库:存储、维护和管理数据的集合。

常见的数据库管理系统

- Oracle:Oracle数据库被认为是业界目前比较成功的关系型数据库管理系统。Oracle数据库可以运行在UNIX、Windows等主流操作系统平台,完全支持所有的工业标准,并获得最高级别的ISO标准安全性认证。

- MySQL:MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS(Relational Database Management System,关系数据库管理系统) 应用软件。

- DB2:DB2是IBM公司的产品,DB2数据库系统采用多进程多线索体系结构,其功能足以满足大中公司的需要,并可灵活地服务于中小型电子商务解决方案。

- Microsoft SQL Server:SQL Server 是Microsoft 公司推出的关系型数据库管理系统。具有使用方便可伸缩性好与相关软件集成程度高等优点。

三大范式(规范)

什么是三大范式:

第一范式:无重复的列。 当关系模式R的所有属性都不能在分解为更基本的数据单位时,称R是满足第一范式的,简记为1NF。满足第一范式是关系模式规范化的最低要求,否则,将有很多基本操作在这样的关系模式中实现不了。

第二范式:属性完全依赖于主键 [ 消除部分子函数依赖 ]。 如果关系模式R满足第一范式,并且R得所有非主属性都完全依赖于R的每一个候选关键属性,称R满足第二范式,简记为2NF。第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个实例或行必须可以被唯一地区分。为实现区分通常需要为表加上一个列,以存储各个实例的唯一标识。这个唯一属性列被称为主关键字或主键、主码。

第三范式:属性不依赖于其它非主属性 [ 消除传递依赖 ]。设R是一个满足第一范式条件的关系模式,X 是R的任意属性集,如果X非传递依赖于R的任意一个候选关键字,称R满足第三范式,简记为3NF. 满足第三范式(3NF)必须先满足第二范式(2NF)。第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。

注:关系实质上是一张二维表,其中每一行是一个元组,每一列是一个属性

第二范式(2NF)和第三范式(3NF)的概念很容易混淆,区分它们的关键点在于,2NF:非主键列是否完全依赖于主键,还是依赖于主键的一部分;3NF:非主键列是直接依赖于主键,还是直接依赖于非主键列。

SQL语言

概述

SQL:Structure Query Language(结构化查询语言),SQL被美国国家标准局(ANSI)确定为关系型数据库语言的美国标准,后来被国际化标准组织(ISO)采纳为关系数据库语言的国际标准。

各数据库厂商都支持ISO的SQL标准,普通话

各数据库厂商在标准的基础上做了自己的扩展,方言

SQL 是一种标准化的语言,它允许你在数据库上执行操作,如创建项目,查询内容,更新内容,并删除条目等操作。

Create, Read, Update, and Delete 通常称为CRUD操作。

SQL语句分类

- DDL(Data Defifinition Language):数据定义语言,用来定义数据库对象:库、表、列等。

- DML(Data Manipulation Language):数据操作语言,用来定义数据库记录(数据)增删改。

- DCL(Data Control Language):数据控制语言,用来定义访问权限和安全级别。

- DQL(Data Query Language):数据查询语言,用来查询记录(数据)查询。

注意:sql语句以;结尾

mysql中的关键字不区分大小写

mysql注释符有三种:

1、#…

2、"-- …" --后面有个空格

3、/*…*/

DDL操作数据库

1、创建

CREATE DATABASE语句用于创建新的数据库:

编码方式:gb2312,utf-8,gbk,iso-8859-1

//create database 数据库名 
CREATE DATABASE mydb1; 
//create database 数据库名 character set 编码方式 
CREATE DATABASE mydb2 character SET GBK; 
//create database 数据库名 set 编码方式 collate 排序规则 
CREATE DATABASE mydb3 character SET GBK COLLATE gbk_chinese_ci; 
2、查看数据库

查看当前数据库服务器中的所有数据库

show databases;

查看前面创建的mydb2数据库的定义信息

//show create database 数据库名;
Show CREATE DATABASE mydb2;
3、修改数据库
alter database 数据库名 character set 编码方式 

查看服务器中的数据库,并把mydb2的字符集修改为utf8;

ALTER DATABASE mydb2 character SET utf8;
4、删除数据库
drop database 数据库名
5、其他语句

查看当前使用的数据库

Select database();

切换数据库: use 数据库名

USE mydb2;

DDL操作表

CREATE TABLE语句用于创建新表。

语法:

CREATE TABLE 表名( 
列名1 数据类型 [约束], 
列名2 数据类型 [约束], 
列名n 数据类型 [约束] 
);

说明:表名,列名是自定义,多列之间使用逗号间隔,最后一列的逗号不能写

[约束] 表示可有可无

示例:

CREATE TABLE Employees( 
	id INT , 
	age INT , 
	first VARCHAR(255), 
	last VARCHAR(255) 
);
常用数据类型

int:整型

double:浮点型,例如double(5,2)表示最多5位,其中必须有2位小数,即最大值为999.99;默认支持四舍五入

char:固定长度字符串类型; char(10) 'aaa ’ 占10位

varchar:可变长度字符串类型; varchar(10) ‘aaa’ 占3位

text:字符串类型,比如小说信息;

blob:字节类型,保存文件信息(视频,音频,图片);

date:日期类型,格式为:yyyy-MM-dd;

time:时间类型,格式为:hh:mm:ss

timestamp:时间戳类型 yyyy-MM-dd hh:mm:ss 会自动赋值

datetime:日期时间类型 yyyy-MM-dd hh:mm:ss

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

其他表操作

drop table 表名;

DROP TABLE table_name;

当前数据库中的所有表

show tables;

SHOW TABLES;

查看表的字段信息

desc 表名;

DESC employee;

增加列:在上面员工表的基本上增加一个image列。

alter table 表名 add 新列名 新的数据类型

ALTER TABLE employee ADD image blob;

修改job列,使其长度为60。

alter table 表名 change 旧列名 新列名 新的数据类型

ALTER TABLE employee MODIFY job varchar(60); 
ALTER TABLE employee change job job varchar(60);

列名name修改为username

ALTER TABLE user CHANGE name username varchar(100);

删除image列,一次只能删一列。

alter table 表名 drop 列名

ALTER TABLE employee DROP image;

修改表名,表名改为users。

alter table 旧表名 rename 新表名;

alter table user rename users;

查看表格的创建细节

show create table 表名;

SHOW CREATE TABLE user;

修改表的字符集为gbk

alter table 表名 character set 编码方式

ALTER TABLE user CHARACTER SET gbk;

练习:

表名 card(会员卡表)

列名 数据类型

cardid int

cardnum varchar(20)

regDate date

需求:

(1)创建该表

在这里插入图片描述

(2)将card表名修改为CardInfo

在这里插入图片描述

(3)添加delDate(注销时间) 列到表中
在这里插入图片描述

(4)将cardnum改为varchar(30)

在这里插入图片描述

(5)删除regDate列

在这里插入图片描述

(6)删除cardInfo表

在这里插入图片描述

DML操作

DML是对表中的数据进行增、删、改的操作。不要与DDL混淆了。

主要包括:INSERT 、UPDATE、 DELETE

小知识:

在mysql中,字符串类型和日期类型都要用单引号括起来。

空值:null

(1)插入操作:INSERT:

insert into 表名(列名) values(数据值);

insert into student(stuname,stuage,stusex,birthday) values('张三1',18,'a','2000-1-1');

– 注意:1多列和多个列值之间使用逗号隔开 2.列名要和列值一一对应

– 非数值的列值两侧需要加单引号

常见错误: Data too long for column ‘stusex’ at row 1

– 添加数据的时候可以将列名省略->当给所有列添加数据的时候

– 此时列值的顺序按照数据表中列的顺序执行

insert into student values('李四',12,'1111',189.98,'2000-1-1','男','2007-1-1');

– 同时添加多行

insert into 表名(列名) values(第一行数据),(第二行数据),(),();

insert into student(stuname,stuage,stusex,birthday) 
values('张三3',18,'a','2000-1-1'), 
('张三4',18,'a','2000-1-1'), 
('张三5',18,'a','2000-1-1'), 
('张三6',18,'a','2000-1-1'), 
('张三7',18,'a','2000-1-1'), 
('张三8',18,'a','2000-1-1');

注意:列名与列值的类型、个数、顺序要一一对应。

参数值不要超出列定义的长度。

如果插入空值,请使用null

插入的日期和字符一样,都使用引号括起来。

练习准备 :

create table emp( 
	id int primary key, 
	name varchar(100) not null, 
	gender varchar(10) not null, 
	birthday date, 
	salary float(10,2), 
	entry_date date, 
	resume text 
);
INSERT INTO emp(id,name,gender,birthday,salary,entry_date,resume) 
VALUES(1,'zhangsan','female','1990-5-10',10000,'2015-5-5-','goodgirl'); 
INSERT INTO emp(id,name,gender,birthday,salary,entry_date,resume) 
VALUES(2,'lisi','male','1995-5-10',10000,'2015-5-5','good boy'); 
INSERT INTO emp(id,name,gender,birthday,salary,entry_date,resume) 
VALUES(3,'你好','male','1995-5-10',10000,'2015-5-5','good boy');

sql中的运算符:

(1)算术运算符:+,-,*,/(除法),求余(%)

示例:

5/2

5%2

2/5

2%5

(2)赋值运算符:=

注:赋值方向:从右往左赋值

示例: name=‘张三’

(3) 逻辑运算符:and(并且),or(或者),not(取非)

作用:用于连接多个条件时使用

(4)关系运算符: >,<,>=,<=,!=(不等于),=(等于),<>(不等于)

补充:查询所有数据:select * from 表名

(2)修改(更新)操作:UPDATE:

语法:UPDATE 表名 SET 列名1=列值1,列名2=列值2 … WHERE 列名=值

练习:

在这里插入图片描述

将所有员工薪水修改为5000元。

update emp set salary=5000;

将姓名为’zs’的员工薪水修改为3000元。

update emp set salary=3000 where name='zs';

将姓名为’aaa’的员工薪水修改为4000元,resume改为ccc。

update emp set salary=4000,resume='ccc' where name='aaa';

将wu的薪水在原有基础上增加1000元。

update emp set salary=salary+1000 where name='wu';

将姓名name为’zhangsan’,出生日期birthday为’1990-5-10’的员工性别gender更改为boy

update emp set gender='boy' where name='zhangsan' and birthday='1995-5-10';

(3)删除操作:DELETE:

语法 : DELETE from 表名 【WHERE 列名=值】

练习 :

删除表中名称为’zs’的记录。

delete from emp where name='zs';

删除表中所有记录。

delete * from emp

使用truncate删除表中记录。

TRUNCATE TABLE emp; 

- DELETE 删除表中的数据,表结构还在;删除后的数据可以找回

- TRUNCATE 删除是把表直接DROP掉,然后再创建一个同样的新表。 删除的数据不能找回。执行速度比DELETE快。

练习题:

Manager(管理员表):

mid 编号 int (主键)

mname 名字 varchar(20)

age 年龄 int

sex 性别 char(2)

password 密码 varchar(20)

address 地址 varchar(20)

phone 电话 varchar(20)

数据:

1 王子 18 男 123 北京 110

2 公主 20 女 456 上海 220

3 太子 23 男 789 南京 330

需求:

(1)创建表

在这里插入图片描述

(2)将数据插入到表中

在这里插入图片描述

在这里插入图片描述

(3)将王子的年龄修改为24

update manager set age=24 where mname='王子';

(4)将地址是南京的管理员改为天津

update manager set address='天津' where address='南京';

(5)将性别是女,并且年龄大于30的用户密码改为888888

update manager set password='888888' where set='女' and age>30;

(6)将所有用户的密码恢复最初设置111111

update manager set password='111111';

(7)将员工的电话中不是110的电话号码改为7654321

update manager set phone='7654321' where phone<>'110'

(8)将王子的年龄改为18,地址改为承德,性别改为女

update manager set age=18,address='承德',set='女'

(9) 删除王子的信息

delete from manager where mname='王子';

(10)删除地址在南京并且年龄大于60的员工信息

delete from manager where address='南京' and age>60;

(11)删除不在北京的员工信息

delete from manager where address<>'北京';

(12)删除地址在北京或上海的员工信息

delete from manager where address='北京' or address='上海';

(13)删除电话号码是空的员工信息

delete from manager where phone='' and phone is null;

小结:

为空的条件:列名 is null or 列名=’’

注:两个单引号表示空字符串

日期类型值的区别:

date:yyyy-MM-dd (年月日)

time:hh:mm:ss (时分秒)

datetime:yyyy-MM-dd hh:mm:ss (年月日时分秒)

获取当前系统时间:now()

select now();

DCL

1、创建用户

create user 用户名@指定ip identifified by 密码;

create user test123@localhost IDENTIFIED by 'test123'

create user 用户名@客户端ip identifified by 密码; 指定IP才能登陆

create user test456@10.4.10.18 IDENTIFIED by 'test456'

create user 用户名@‘% ’ identifified by 密码; 任意IP均可登陆

create user test7@'%' IDENTIFIED by 'test7'
2、用户授权

grant 权限1,权限2,…,权限n on 数据库名.* to 用户名@IP; 给指定用户授予指定指定数据库指定权限

grant select,insert,update,delete,create on chaoshi.* to 'test456'@'127.0.0.1';

grant all on . to 用户名@IP 给指定用户授予所有数据库所有权限

grant all on *.* to 'test456'@'127.0.0.1'
3、用户权限查询

show grants for 用户名@IP;

show grants for 'root'@'%';
4、撤销用户权限

revoke 权限1,权限2,…,权限n on 数据库名.* from 用户名@IP;

REVOKE SELECT ON *.* FROM 'root'@'%' ;
5、删除用户

drop user 用户名@IP;

drop user test123@localhost;

DQL数据查询

DQL数据查询语言(重要)

数据库执行DQL语句不会对数据进行改变,而是让数据库发送结果集给客户端。

查询返回的结果集是一张虚拟表。

查询关键字:SELECT

语法: SELECT 列名 FROM 表名 【WHERE --> BROUP BY–>HAVING–> ORDER BY】

* 表示所有列

SELECT 要查询的列名称 
FROM 表名称 
WHERE 限定条件 /*行条件*/ 
GROUP BY grouping_columns /*对结果分组*/ 
HAVING condition /*分组后的行条件*/ 
ORDER BY sorting_columns /*对结果分组*/ 
LIMIT offset_start, row_count /*结果限定*/

示例操作:

1>创建学生表并添加数据

#创建表stu 
CREATE TABLE stu ( 
	sid CHAR(6), 
	sname VARCHAR(50), 
	age INT, 
	gender VARCHAR(50) 
);
#添加数据 
INSERT INTO stu VALUES('S_1001', 'liuYi', 35, 'male'); 
INSERT INTO stu VALUES('S_1002', 'chenEr', 15, 'female'); 
INSERT INTO stu VALUES('S_1003', 'zhangSan', 95, 'male'); 
INSERT INTO stu VALUES('S_1004', 'liSi', 65, 'female'); 
INSERT INTO stu VALUES('S_1005', 'wangWu', 55, 'male'); 
INSERT INTO stu VALUES('S_1006', 'zhaoLiu', 75, 'female'); 
INSERT INTO stu VALUES('S_1007', 'sunQi', 25, 'male'); 
INSERT INTO stu VALUES('S_1008', 'zhouBa', 45, 'female'); 
INSERT INTO stu VALUES('S_1009', 'wuJiu', 85, 'male'); 
INSERT INTO stu VALUES('S_1010', 'zhengShi', 5, 'female'); 
INSERT INTO stu VALUES('S_1011', 'xxx', NULL, NULL);

2>创建雇员表并添加数据

#创建雇员表 
CREATE TABLE emp2( 
	empno INT, 
	ename VARCHAR(50), 
	job VARCHAR(50), 
	mgr INT, 
	hiredate DATE, 
	sal DECIMAL(7,2), 
	comm decimal(7,2), 
	deptno INT 
) ; 
#添加数据 
INSERT INTO emp2 values(7369,'SMITH','CLERK',7902,'1980-12-17',800,NULL,20); 
INSERT INTO emp2 values(7499,'ALLEN','SALESMAN',7698,'1981-02-20',1600,300,30); 
INSERT INTO emp2 values(7521,'WARD','SALESMAN',7698,'1981-02-22',1250,500,30); 
INSERT INTO emp2 values(7566,'JONES','MANAGER',7839,'1981-04-02',2975,NULL,20); 
INSERT INTO emp2 values(7654,'MARTIN','SALESMAN',7698,'1981-09-28',1250,1400,30); 
INSERT INTO emp2 values(7698,'BLAKE','MANAGER',7839,'1981-05-01',2850,NULL,30); 
INSERT INTO emp2 values(7782,'CLARK','MANAGER',7839,'1981-06-09',2450,NULL,10); 
INSERT INTO emp2 values(7788,'SCOTT','ANALYST',7566,'1987-04-19',3000,NULL,20); 
INSERT INTO emp2 values(7839,'KING','PRESIDENT',NULL,'1981-11-17',5000,NULL,10);
INSERT INTO emp2 values(7844,'TURNER','SALESMAN',7698,'1981-09-08',1500,0,30); 
INSERT INTO emp2 values(7876,'ADAMS','CLERK',7788,'1987-05-23',1100,NULL,20);

3>创建部门表并添加数据

#创建部门表
CREATE TABLE dept( 
	deptno INT, 
	dname varchar(14), 
	loc varchar(13) 
);
#添加数据 
INSERT INTO dept values(10, 'ACCOUNTING', 'NEW YORK'); 
INSERT INTO dept values(20, 'RESEARCH', 'DALLAS'); 
INSERT INTO dept values(30, 'SALES', 'CHICAGO'); 
INSERT INTO dept values(40, 'OPERATIONS', 'BOSTON'); 
简单查询

查询所有列

SELECT * FROM stu;

查询指定列

SELECT sid, sname, age FROM stu;
条件查询

条件查询就是在查询时给出WHERE子句,在WHERE子句中可以使用如下运算符及关键字:

=、!=、<>、<、<=、>、>=; BETWEEN…AND; IN(set); IS NULL; AND;OR; NOT;

(1)查询性别为女,并且年龄50以内的记录

SELECT * FROM stu WHERE gender='female' AND age<50;

(2)查询学号为S_1001,或者姓名为lisi的记录

SELECT * FROM stu WHERE sid ='S_1001' OR sname='liSi';

(3)查询学号为S_1001,S_1002,S_1003的记录

列名 in (列值1,列值2)

SELECT * FROM stu WHERE sid IN ('S_1001','S_1002','S_1003');

(4)查询学号不是S_1001,S_1002,S_1003的记录

SELECT * FROM tab_student WHERE sid NOT IN('S1001','S1002','S_1003');

(5)查询年龄为null的记录

SELECT * FROM stu WHERE age IS NULL;

(6) 查询年龄在20到40之间的学生记录

SELECT * FROM stu WHERE age>=20 AND age<=40;

或者:列名 between 开始值 and 结束值;//注意:1.开始值<结束值 2.包含临界值的

SELECT * FROM stu WHERE age BETWEEN 20 AND 40;

(7) 查询性别非男的学生记录

SELECT * FROM stu WHERE gender!='male';

或者

SELECT * FROM stu WHERE gender<>'male';

或者

SELECT * FROM stu WHERE NOT gender='male';

(8) 查询姓名不为null的学生记录

SELECT * FROM stu WHERE NOT sname IS NULL;

或者

SELECT * FROM stu WHERE sname IS NOT NULL;
模糊查询

当想查询姓名中包含a字母的学生时就需要使用模糊查询了。模糊查询需要使用关键字LIKE。

语法: 列名 like ‘表达式’ //表达式必须是字符串

通配符:

_(下划线): 任意一个字符

%:任意0~n个字符,‘张%’

(1)查询姓名由3个字构成的学生记录

SELECT * FROM stu WHERE sname LIKE '___';

模糊查询必须使用LIKE关键字。其中 “匹配任意一个字,3”表示3个任意字。

(2)查询姓名由5个字母构成,并且第5个字母为“i”的学生记录

SELECT * FROM stu WHERE sname LIKE '____i';

(3)查询姓名以“z”开头的学生记录

SELECT * FROM stu WHERE sname LIKE 'z%';

其中“%”匹配0~n个任何字母。

(4)查询姓名中第2个字母为“i”的学生记录

SELECT * FROM stu WHERE sname LIKE '_i%';

(5)查询姓名中包含“a”字母的学生记录

SELECT * FROM stu WHERE sname LIKE '%a%';
字段控制查询

(1)去除重复记录

去除重复记录(两行或两行以上记录中系列的上的数据都相同),例如emp表中sal字段就存在相同的

记录。当只查询emp表的sal字段时,那么会出现重复记录,那么想去除重复记录,需要使用

DISTINCT:

SELECT DISTINCT sal FROM emp;

(2)查看雇员的月薪与佣金之和

因为sal和comm两列的类型都是数值类型,所以可以做加运算。如果sal或comm中有一个字段不是数值类型,那么会出错。

SELECT *,sal+comm FROM emp;

comm列有很多记录的值为NULL,因为任何东西与NULL相加结果还是NULL,所以结算结果可能会出现NULL。下面使用了把NULL转换成数值0的函数IFNULL:

SELECT *,sal+IFNULL(comm,0) FROM emp;

(3)给列名添加别名

在上面查询中出现列名为sal+IFNULL(comm,0),这很不美观,现在我们给这一列给出一个别名,为

total:

SELECT *, sal+IFNULL(comm,0) AS total FROM emp;

给列起别名时,是可以省略AS关键字的:

SELECT *,sal+IFNULL(comm,0) total FROM emp;
排序

语法: order by 列名 asc/desc(Ascend/Descend)

//asc 升序 desc 降序 默认不写的话是升序,从第一行往下开始数值变小就是降序,否则就是升序。

(1) 查询所有学生记录,按年龄升序排序

SELECT * FROM stu ORDER BY age ASC;

或者

SELECT * FROM stu ORDER BY age;

(2) 查询所有学生记录,按年龄降序排序

SELECT * FROM stu ORDER BY age DESC;

(3) 查询所有雇员,按月薪降序排序,如果月薪相同时,按编号升序排序

多列排序:当前面的列的值相同的时候,才会按照后面的列值进行排序

SELECT * FROM emp ORDER BY sal DESC,empno ASC;
聚合函数

聚合函数是用来做纵向运算的函数:

COUNT(列名):统计指定列不为NULL的记录行数;

MAX(列名):计算指定列的最大值,如果指定列是字符串类型,那么使用字符串排序运算;

MIN(列名):计算指定列的最小值,如果指定列是字符串类型,那么使用字符串排序运算;

SUM(列名):计算指定列的数值和,如果指定列类型不是数值类型,那么计算结果为0;

AVG(列名):计算指定列的平均值,如果指定列类型不是数值类型,那么计算结果为0;

(1) COUNT

当需要纵向统计时可以使用COUNT()。

l 查询emp表中记录数:

SELECT COUNT(*) AS cnt FROM emp;

l 查询emp表中有佣金的人数:

SELECT COUNT(comm) cnt FROM emp;

注意,因为count()函数中给出的是comm列,那么只统计comm列非NULL的行数。

l 查询emp表中月薪大于2500的人数:

SELECT COUNT(*) FROM emp WHERE sal > 2500;

l 统计月薪与佣金之和大于2500元的人数:

SELECT COUNT(*) AS cnt FROM emp WHERE sal+IFNULL(comm,0) > 2500;

l 查询有佣金的人数,以及有领导的人数:

SELECT COUNT(comm), COUNT(mgr)FROM emp;

(2) SUM和AVG

当需要纵向求和时使用sum()函数。

l 查询所有雇员月薪和:

SELECT SUM(sal) FROM emp;

l 查询所有雇员月薪和,以及所有雇员佣金和:

SELECT SUM(sal), SUM(comm) FROM emp;

l 查询所有雇员月薪+佣金和:

SELECT SUM(sal+IFNULL(comm,0))FROM emp;

l 统计所有员工平均工资:

SELECT AVG(sal) FROM emp;

(3) MAX和MIN

l 查询最高工资和最低工资:

SELECT MAX(sal), MIN(sal) FROM emp;
分组查询

当需要分组查询时需要使用GROUP BY子句,例如查询每个部门的工资和,这说明要使用部分来分组。

注意:如果查询语句中有分组操作,则select后面能添加的只能是聚合函数和被分组的列名

l 查询每个部门的部门编号和每个部门的工资和:

SELECT deptno, SUM(sal) FROM emp GROUP BY deptno;

l 查询每个部门的部门编号以及每个部门的人数:

SELECT deptno,COUNT(*) FROM emp GROUP BY deptno;

l 查询每个部门的部门编号以及每个部门工资大于1500的人数:

SELECT deptno,COUNT(*) FROM emp WHERE sal>1500 GROUP BY deptno;
HAVING子句

l 查询工资总和大于9000的部门编号以及工资和:

SELECT deptno, SUM(sal) FROM emp GROUP BY deptno HAVING SUM(sal) > 9000;

注:having与where的区别:

  1. having是在分组后对数据进行过滤,where是在分组前对数据进行过滤

  2. having后面可以使用分组函数(统计函数或聚合函数),where后面不可以使用分组函数。

  3. WHERE是对分组前记录的条件,如果某行记录没有满足WHERE子句的条件,那么这行记录不会参加分组;而HAVING是对分组后数据的约束。

补充: 多列分组

– 统计出stu表中每个班级的男女生各多少人

select gradename,gender ,count(*) from stu group by gradename,gender
LIMIT

LIMIT用来限定查询结果的起始行,以及总行数。

limit 开始下标,显示条数;//开始下标从0开始

limit 显示条数;//表示默认从0开始获取数据

1.查询5行记录,起始行从0开始

SELECT * FROM emp LIMIT 0, 5;

注意,起始行从0开始,即第一行开始!

2.查询10行记录,起始行从3开始

SELECT* FROM emp LIMIT 3, 10;

分页查询:

如果一页记录为10条,希望查看第3页记录应该怎么查呢?

l 第一页记录起始行为0,一共查询10行; limit 0,10

l 第二页记录起始行为10,一共查询10行;limit 10,10

l 第三页记录起始行为20,一共查询10行; limit 20,10

pageIndex 页码值 pageSize 每页显示条数

limit (pageindex-1)*pagesize,pagesize;

查询语句书写顺序:select – from- where- groupby- having- order by-limit

查询语句执行顺序:from - where -group by -having - select - order by-limit

练习

练习1

Manager(管理员表):

mid 编号 int (主键)

mname 名字 varchar(20)

age 年龄 int

sex 性别 char(2)

password 密码 varchar(20)

address 地址 varchar(20)

phone 电话 varchar(20)

数据:

1 王子 18 男 123 北京 110

2 公主 20 女 456 上海 220

3 太子 23 男 789 南京 330

create table manager(
	mid int key,
    mname varchar(20),
    age int,
    sex char(2),
    password varchar(20),
    address varchar(20),
    phone varchar(20)
)
insert into manager (mid,mname,age,sex,password,address,phone) values (1,'王子',18,'男','123','北京','110'),
(2,'公主',20,'女','456','上海','220'),
(3,'太子',23,'男','789','南京','330')

(14)查询公主的所有信息

select * from manager where mname='公主';

(15)查询年龄在18-30之间的管理员姓名

select mname from manager where age between 18 and 30;

(16)查询表中所有的用户名和电话

select mname,phone from manager;

(17)查询性别是男,名字是王子的个人信息

select * from manager where sex='男' and mname='王子';

(18)查询出地址在北京和上海的员工信息

select * from manager where address in('北京','上海');

练习2

scores

stuid int 学生id

java int java成绩

mysql int mysql成绩

stuname varchar(20) 学生姓名

数据:

1 67 78 张三

2 87 55 李四

3 66 90 王五

4 98 78 赵六

5 80 88 田七

create table scores(
	stuid int primary key comment '学生id',
    java int comment 'java成绩',
    mysql int comment 'mysql成绩',
    stuname varchar(20) comment '学生姓名'
)
insert into scores values (1,67,78,'张三'),
(2,87,55,'李四'),
(3,66,90,'王五'),
(4,98,78,'赵六'),
(5,80,88,'田七')

需求:

(1)对java成绩进行降序排序

select java from scores order by java desc;

(2)得到mysql成绩前三名

select * from scores order by mysql desc limit 0,3;

(3)得到java学生中最后一名的学生信息

select * from scores order by java asc limit 0,1;

(4)查询出两门成绩都优秀(>=80)的学生姓名

select stuname from scores where java >=80 and mysql>=80;

(5)查询出成绩在90分以上(>=90)的学生信息

select * from scores where java>90 or mysql>90;

(6)查询出每名学员的java,mysql,总成绩

select java,mysql,java+mysql as 总成绩 from scores;

(7)显示出每名学生的总分以及姓名

select java+mysql 总分,stuname from scores;

练习3

测试数据:

郭敬明 1371234567 北京 java S1101 89 1979-04-05

张三丰 1372839201 上海 数据库 S1102 67 1967-09-07

赵敏 1387839201 山东 mysql S1103 99 1987-09-07

Student2

stuname 姓名 varchar(20)

telphone 电话 varchar(20)

address 住址 varchar(20)

subject 科目 varchar(20)

stuNo 学号 varchar(20)

score 成绩 int

birthday 出生日期 date

create table student2(
	stuname varchar(20) comment '姓名',
    telphone varchar(20) comment '电话',
    address varchar(20) comment '住址',
    subject varchar(20) comment '科目',
    stuno varchar(20) comment '学号',
    score int comment '成绩',
    birthday date comment '出生日期'
)
insert into student2 values('郭敬明','1371234567','北京','java','S1101',89,'1979-04-05'),
('张三丰','1372839201','上海','数据库','S1102',67,'1967-09-07'),
('赵敏','1387839201','山东','mysql','S1103',99,'1987-09-07')

1.要查询列 2.条件

a.查询住址为“山东”的学生姓名、电话、住址

select stuname,telphone,address from student2 where address='山东';

b.查询名称中含有“数据库”字样科目信息

select * from student2 where subject like '%数据库%';

c.查询电话中以“1387”开头的学生信息

select * from student2 where telphone like '1387%';

d.查询姓姜的,三个字的学生信息

select * from student2 where stuname like '姜__';

e.查询学号为S1101的指定java,mysql科目考试成绩

select score from student2 where stuno='S1101' and (subject='java' or subject='mysql');

f.查询出80后学员信息

select * from student2 where birthday between '1980-01-01' and '1989-12-31';

g.查询出家庭住址在北上广的学生名字

select stuname from student2 where address in('北京','上海','广州');

h.显示成绩在第5-10名的学生名字和电话

select stuname,telphone from student2 order by score desc limit 4,6;

i.查询分数在80-90之间并且在北京的学生

select * from student2 where (score between 80 and 90) and address='北京';

练习4:聚合函数练习

表:scores2

年级 grade varchar(10)

学号 stuno varchar(20)

考试时间 examDate date

科目 subject varchar(20)

成绩 score int

学期 xueqi int

数据:

S1 S1101 2015-02-03 C 89 1

S2 S1103 2015-03-03 JAVA 90 2

S3 S1102 2015-07-03 C 100 1

create table scores2(
	grade varchar(10) comment '年级',
    stuno varchar(20) comment '学号',
    examdate date comment '考试时间',
    subject varchar(20) comment '科目',
    score int comment '成绩',
    xueqi int comment '学期'
)
insert into scores2 values('S1','S1101','2015-02-03','C',89,1),
('S2','S1103','2015-03-03','JAVA',90,2),
('S3','S1102','2015-07-03','C',100,1)

1.查询学生总人数

select count(*) from scores2;

2.学号为S1101的学生第一学期考试总成绩,平均分

select sum(score) 总成绩,avg(score) 平均分 from scores2 where stuno='S1101' and xueqi=1;

3.查询2013年3月22日科目“C”的最高分、最低分、平均分

select max(score),min(score),avg(score) from scores2 where subject='C' and examdate='2013-03-22';

4.查询2013年3月22日科目“C”及格学生的平均分

select avg(score) from scores2 where score>=60 and examdate='2013-03-22';

5.查询所有参加“C”科目考试的平均分

select avg(score) from scores2 where subject='C';

6.查看考java的人数

select count(*) from scores2 where subject='JAVA';

练习5:分组练习

表名:student

年级(grade) varchar(10)

学生姓名(name) varchar(10)

学时(xueshi) int --每人单个学时

参加考试(isexam) char(1) 是/否、

课程(subject) varchar(10)

分数(score) int

数据:

1 张三 10 是 java 99

1 李四 10 否 java 0

2 王五 20 是 mysql 88

2 赵六 20 是 mysql 77

2 王五 20 是 java 99

2 赵六 20 否 java 0

1 张三 10 是 mysql 88

create table student(
	grade varchar(10) comment '年级',
    name varchar(10) comment '学生姓名',
    xueshi int comment '学时',
    isexam char(1) comment '参加考试',
    subject varchar(10) comment '课程',
    score int comment '分数'
)
insert into student values('1','张三',10,'是','java',99),
('1','李四',10,'否','java',0),
('2','王五',20,'是','mysql',88),
('2','赵六',20,'是','mysql',77),
('2','王五',20,'是','java',99),
('2','赵六',20,'否','java',0),
('1','张三',10,'是','mysql',88)

练习:

a:查询每个年级的总学时数,并按照升序排列

select grade,sum(xueshi) 总学时 from student group by grade order by 总学时 asc;

b:查询每个参加考试的学员的平均分*

select name,avg(score) 平均分 from student where isexam='是' group by name;

c:查询每门课程的平均分,并按照降序排列

select subject,avg(score) 平均分 from student group by subject order by 平均分 desc;

练习6:综合练习

Student

科目名称 subjectName varchar(20)

学生姓名 stuname varchar(20)

学生地址 address varchar(20)

学生性别 sex char(2)

电子邮件 email varchar(30)

年级 grade varchar(10)

出生日期 birthday date

考试日期 examDate date

成绩 scores int

数据:

JAVA 张三 北京 男 [email protected] S1 1990-03-04 2013-5-6 89

html 李四 上海 男 S2 1993-08-04 2014-5-6 87

html 王五 北京 男 [email protected] S2 1990-03-04 2015-4-6 90

create table student3(
	subjectname varchar(20) comment '科目名称',
    stuname varchar(20) comment '学生姓名',
    address varchar(20) comment '学生地址',
    sex char(2) comment '学生性别',
    email varchar(30) comment '电子邮件',
    grade varchar(10) comment '年级',
    birthday date comment '出生日期',
    examdate date comment '考试日期',
    scores int comment '成绩'
)
insert into student3 values ('JAVA','张三','北京','男','[email protected]','S1','1990-03-04','2013-5-6',8),
('html','李四','上海','男',null,'S2','1993-08-04','2014-5-6',87),
('html','王五','北京','男','[email protected]','S2','1990-03-04','2015-4-6',90)

1.查询S2的科目名称

select subjectname from student3 where grade='S2';

2.查询S2男同学的姓名和住址

select stuname,address from student3 where grade='S2' and sex='男';

3.查询无电子邮件的学生姓名和年级信息

select stuname,grade from student3 where email is null or email='';

4.查询出生日期在1993年之后的S2的学生姓名和年级信息

select stuname,grade from student3 where birthday > '1993-01-01' and grade='S2';

5.查询参加了日期为2013年2月15日的“HTML” 科目考试的成绩信息

select scores from student3 where examdate='2013-02-15' and subjectname='HTML';

数据库高级

数据库的完整性

用来保证存放到数据库中的数据是有效的,即数据的有效性和准确性

确保数据的完整性 = 在创建表时给表中添加约束

完整性的分类:

- 实体完整性(行完整性);

- 域完整性(列完整性);

- 引用完整性(关联表完整性);

主键约束:primary key

唯一约束:unique [key]

非空约束:not null

默认约束:default

自动增长:auto_increment

外键约束: foreign key

建议这些约束应该在创建表的时候设置

多个约束条件之间使用空格间隔

示例:

create table student( 
	studentno int primary key auto_increment, 
	loginPwd varchar(20) not null default '123456', 
	studentname varchar(50) not null, 
	sex char(2) not null, 
	gradeid int not null, 
	phone varchar(255) not null, 
	address varchar(255) default '学生宿舍', 
	borndate datetime, 
	email varchar(50) 
);

实体完整性

实体:即表中的一行(一条记录)代表一个实体(entity)

实体完整性的作用:标识每一行数据不重复。

约束类型:

主键约束(primary key)

唯一约束(unique)

自动增长列(auto_increment)

主键约束(primary key)

注:每个表中要有一个主键。

特点:数据唯一,且不能为null

示例:

第一种添加方式:

CREATE TABLE student( id int primary key, name varchar(50) );

第二种添加方式:此种方式优势在于,可以创建联合主键

CREATE TABLE student( id int, name varchar(50), primary key(id) ); 
CREATE TABLE student( classid int, stuid int, name varchar(50), primary key(classid,stuid) ); 

第三种添加方式:

CREATE TABLE student( id int, name varchar(50) ); 
ALTER TABLE student ADD PRIMARY KEY (id); 

唯一约束(unique)

特点:数据不能重复。

CREATE TABLE student( Id int primary key, Name varchar(50) unique );

自动增长列(auto_increment)

sqlserver数据库 (identity-标识列)

oracle数据库(sequence-序列)

给主键添加自动增长的数值,列只能是整数类型

CREATE TABLE student( Id int primary key auto_increment, Name varchar(50) ); 
INSERT INTO student(name) values(‘tom’);

域完整性

域完整性的作用:限制此单元格的数据正确,不对照此列的其它单元格比较

域代表当前单元格

域完整性约束:数据类型 非空约束(not null) 默认值约束(default)

check约束(mysql不支持)check(sex=‘男’ or sex=‘女’)

数据类型

数值类型

| 类型 | 大小 | 范围(有符号) | 范围(无符号 ) | 用途 |

| ------------- | ---------------- | ----------------------- | ---------------------- | ---------- |

| tinyint | 1 字节 | (-128,127) | (0,255) | 小整数值 |

| smallint | 2 字节 | (-32 768,32 767) | (0,65 535) | 大整数值 |

| mediumint | 3 字节 | (-8 388 608,8 388 607) | (0,16 777 215) | 大整数值 |

| INT| 4 字节 | (-2 147 483 648,2 147 483 647) | (0,4 294 967 295) | 大整数值 |

| bigint | 8 字节 | (-9 233 372 036 854 775 808,9 223 372 036 854 775 807) | (0,18 446 744 073 709 551 615) | 极大整数值 |

| flfloat | 4 字节 | (-3.402 823 466 E+38,-1.175 494 351 E-38),0,(1.175 494 351 E-38,3.402823 466 351 E+38) | 0,(1.175 494 351 E-38,3.402 823 466 E+38) | 单精度浮点数值 |

| double | 8 字节 | (-1.797 693 134 862 315 7 E+308,-2.225 073 858 507 201 4 E-308),0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) | 0,(2.225 073 858 507 201 4E-308,1.797 693 134 862 315 7 E+308) | 双精度浮点数值 |

日期类型:

表示时间值的日期和时间类型为DATETIME、DATE、TIMESTAMP、TIME和YEAR。

每个时间类型有一个有效值范围和一个"零"值,当指定不合法的MySQL不能表示的值时使用"零"值。

TIMESTAMP类型有专有的自动更新特性

| 类型 | 大小(字节) | 范围 | 格式 | 用途 |

| --------- | ------ | ---------------------------------------- | ------------------- | ------------ |

| DATE | 3 | 1000-01-01/9999-12-31 | YYYY-MM-DD | 日期值 |

| TIME | 3 | ‘-838:59:59’/‘838:59:59’ | HH:MM:SS | 时间值或持续时间 |

| YEAR | 1 | 1901/2155 | YYYY | 年份值 |

| DATETIME | 8 | 1000-01-01 00:00:00/9999-12-31 23:59:59 | YYYY-MM-DD HH:MM:SS | 混合日期和时间值 |

| TIMESTAMP | 4 | 1970-01-01 00:00:00/2038 结束时间是第 2147483647秒,北京时间 2038-1-19 11:14:07,格林尼治时间 2038年1月19日 凌晨 03:14:07 | YYYYMMDD HHMMSS | 混合日期和时间值,时间戳 ,当更新数据的时候自动添加更新时间

字符串类型:

字符串类型指CHAR、VARCHAR、BINARY、VARBINARY、BLOB、TEXT、ENUM和SET

| 类型 | 大小 | 用途 |

| ---------- | ----------------- | ------------------ |

| CHAR | 0-255字节 | 定长字符串 |

| VARCHAR | 0-65535 字节 | 变长字符串 |

| TINYBLOB | 0-255字节 | 不超过 255 个字符的二进制字符串 |

| TINYTEXT | 0-255字节 | 短文本字符串 |

| BLOB | 0-65 535字节 | 二进制形式的长文本数据 |

| TEXT | 0-65 535字节 | 长文本数据 |

| MEDIUMBLOB | 0-16 777 215字节 | 二进制形式的中等长度文本数据 |

| MEDIUMTEXT | 0-16 777 215字节 | 中等长度文本数据 |

| LONGBLOB | 0-4 294 967 295字节 | 二进制形式的极大文本数据 |

| LONGTEXT | 0-4 294 967 295字节 | 极大文本数据 |

CHAR和VARCHAR类型类似,但它们保存和检索的方式不同。它们的最大长度和是否尾部空格被保留等方面也不同。在存储或检索过程中不进行大小写转换。

BINARY和VARBINARY类类似于CHAR和VARCHAR,不同的是它们包含二进制字符串而不要非二进制字符串。也就是说,它们包含字节字符串而不是字符字符串。这说明它们没有字符集,并且排序和比较基于列值字节的数值值。

BLOB是一个二进制大对象,可以容纳可变数量的数据。有4种BLOB类型:TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB。它们只是可容纳值的最大长度不同。

有4种TEXT类型:TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT。这些对应4种BLOB类型,有相同的最大长度和存储需求。

非空约束(not null)

CREATE TABLE student( 
    Id int primary key, 
    Name varchar(50) not null, 
    Sex varchar(10) 
);
INSERT INTO student values(1,’tom’,null);

默认值约束(default)

CREATE TABLE student( 
    Id int primary key, 
    Name varchar(50) not null, 
    Sex varchar(10) default '男' 
); 
insert intostudent1 values(1,'tom','女'); 
insert intostudent1 values(2,'jerry',default);

引用完整性(参照完整性)

外键约束:FOREIGN KEY

示例:

CREATE TABLE student(
    id int primary key, 
    name varchar(50) not null, 
    sex varchar(10) default '男' 
); 
create table score( 
	id int primary key, 
	score int, 
	sid int , 
	constraint fk_score_sid foreign key(sid) references student(id)
);

constraint 自定义外键名称 foreign key(外键列名) references 主键表名(主键列名)

外键列的数据类型一定要与主键的类型一致

第二种添加外键方式。

ALTER TABLEscore1 ADD CONSTRAINT fk_stu_score FOREIGN KEY(sid) REFERENCES stu(id);

多表查询

多个表之间是有关系的,那么关系靠谁来维护?

多表约束:外键列

多表的关系

一对多/多对一关系

客户和订单,分类和商品,部门和员工。

一对多建表原则:在多的一方创建一个字段,字段作为外键指向一的一方的主键。

多对多关系

学生和课程

多对多关系建表原则:需要创建第三张表,中间表中至少两个字段,这两个字段分别作为外键指向各自一方的主键。

一对一关系

在实际的开发中应用不多,因为一对一可以创建成一张表。

两种建表原则:

唯一外键对应:假设一对一是一个一对多的关系,在多的一方创建一个外键指向一的一方的主键,将外键设置为unique。

主键对应:让一对一的双方的主键进行建立关系。

多表查询

多表查询有如下几种:

  1. 合并结果集:UNION 、 UNION ALL

  2. 连接查询

    2.1内连接 [INNER] JOIN ON

    2.2外连接 OUTER JOIN ON

    • 左外连接 LEFT [OUTER] JOIN
    • 右外连接 RIGHT [OUTER] JOIN
    • 全外连接(MySQL不支持)FULL JOIN

    2.3 自然连接 NATURAL JOIN

3.子查询

合并结果集

作用:合并结果集就是把两个select语句的查询结果合并到一起!

合并结果集有两种方式:

UNION:去除重复记录,例如:

SELECT* FROM t1 UNION SELECT * FROM t2;

UNION ALL:不去除重复记录,例如:

SELECT * FROM t1 UNION ALL SELECT * FROM t2;

在这里插入图片描述

在这里插入图片描述

注意:被合并的两个结果:列数、列类型必须相同。

连接查询

连接查询就是求出多个表的乘积,例如t1连接t2,那么查询出的结果就是t1*t2。

在这里插入图片描述

连接查询会产生笛卡尔积,假设集合A={a,b},集合B={0,1,2},则两个集合的笛卡尔积为{(a,0),(a,1),(a,2),(b,0),(b,1),(b,2)}。可以扩展到多个集合的情况。

那么多表查询产生这样的结果并不是我们想要的,那么怎么去除重复的,不想要的记录呢,当然是通过条件过滤。通常要查询的多个表之间都存在关联关系,那么就通过关联关系去除笛卡尔积。

示例 1:现有两张表

emp-员工表,dept-部门表

CREATE TABLE dept1( 
	deptno int primary key, 
	dname varchar(14), 
	loc varchar(13) 
);
insert into dept1 values(10,'服务部','北京'); 
insert into dept1 values(20,'研发部','北京'); 
insert into dept1 values(30,'销售部','北京'); 
insert into dept1 values(40,'主管部','北京'); 
CREATE TABLE emp1( 
	empno int, 
	ename varchar(50), 
	job varchar(50), 
	mgr int, 
	hiredate date, 
	sal double, 
	comm double, 
	deptno int 
);
insert into emp1 values(1001,'张三','文员',1006,'2019-1-1',1000,2010,10);
insert into emp1 values(1002,'李四','程序员',1006,'2019-2-1',1100,2000,20); 
insert into emp1 values(1003,'王五','程序员',1006,'2019-3-1',1020,2011,20); 
insert into emp1 values(1004,'赵六','销售',1006,'2019-4-1',1010,2002,30); 
insert into emp1 values(1005,'张猛','销售',1006,'2019-5-1',1001,2003,30); 
insert into emp1 values(1006,'谢娜','主管',1006,'2019-6-1',1011,2004,40); 
select * from emp,dept;

在这里插入图片描述

使用主外键关系做为条件来去除无用信息

SELECT * FROM emp,dept WHERE emp.deptno=dept.deptno;

在这里插入图片描述

上面查询结果会把两张表的所有列都查询出来,也许你不需要那么多列,这时就可以指定要查询的列了。

SELECT emp.ename,emp.sal,emp.comm,dept.dname 
FROM emp,dept 
WHERE emp.deptno=dept.deptno;

在这里插入图片描述

1、内连接

上面的连接语句就是内连接,但它不是SQL标准中的查询方式,可以理解为方言!内连接是查询出两个或多个表中共有的部分。

语法:

select 列名 
from1 
inner join2 
on1.列名=2.列名 #外键列的关系 
where.....

等价于:

select 列名 
from1,2 
where1.列名=2.列名 and ...(其他条件)

注:

<1>表1和表2的顺序可以互换

<2>找两张表的等值关系时,找表示相同含义的列作为等值关系。

<3>点操作符表示“的”,格式:表名.列名

<4>可以使用as,给表名起别名,注意定义别名之后,统一使用别名

示例:

#查询学生表中的学生姓名和分数表中的分数 
select name,score 
from student as s 
inner join scores as c 
on s.studentid=c.stuid 
#等价于: 
select name,score 
from student as s,scores as c 
where s.studentid=c.stuid

三表连接:

语法:

 select 列名 from1
 inner join2 on1.列名=2.列名
 inner join3 on1或表2.列名=3.列名 where

等价于:

 select 列名 from1,2,3
 where1.列名=2.列名 and1/2.列名=3.列名

SQL标准的内连接为:

SELECT * 
FROM emp e 
INNER JOIN dept d 
ON e.deptno=d.deptno;

内连接的特点:查询结果必须满足条件。

练习:

student2

stuid 学员id int 主键,自增长

stuname 学员名字 varchar(10)

password 登录密码 varchar(10)

birthday 出生日期 date

数据:

1 花儿 111111 1990-02-09

2 少年 222222 1989-03-12

3 小胡 333333 1989-09-12

create table student2(
	stuid int primary key auto_increment comment '学员id',
    stuname varchar(10) comment '学员名字',
    password varchar(10) comment '登录密码',
    birthday date comment '出生日期'
);
insert into student2 values (1,'花儿','111111','1990-02-09'),
(2,'少年','222222','1989-03-12'),
(3,'小胡','333333','1989-09-12');

subject2 —科目表

subjectid 科目编号 int 主键,自增长

subjectName 科目名称 varchar(10)

数据:

1 java

2 mysql

3 html

create table subject2 (
	subjectid int primary key auto_increment comment '科目编号',
    subjectname varchar(10) comment '科目名称'
);
insert into subject2 values(1,'java'),
(2,'mysql'),
(3,'html');

scores2

sid 分数主键 int 主键,自增长

score 分数 int

subject 科目 int

studentid 学生编号 int

数据:

1 89 1 1

2 90 2 1

3 87 2 2

4 98 3 3

create table scores2 (
	sid int primary key auto_increment comment '分数主键',
    score int comment '分数',
    subjectid int comment '科目',
    studentid int comment '学生编号'
);
insert into scores2 values(1,89,1,1),(2,90,2,1),(3,87,2,2),(4,98,3,3);

需求:

1.显示出花儿的考试成绩以及对应科目

select s1.subjectname 科目,s2.score 考试成绩 from subject2 s1,scores2 s2,student2 s3 where s1.subjectid=s2.subjectid and s2.studentid=s3.stuid and s3.stuname='花儿';
#或
select s1.subjectname 科目,s2.score 考试成绩 from subject2 s1 
inner join scores2 s2 on s1.subjectid=s2.subjectid 
inner join student2 s3 on s2.studentid=s3.stuid 
where s3.stuname='花儿';

2.显示出所有考试学生的信息

select * from student2 s1,scores2 s2,subject2 s3 where s1.stuid=s2.studentid and s2.subjectid=s3.subjectid;
#或
select * from student2 s1 
inner join scores2 s2 on s1.stuid=s2.studentid
inner join subject2 s3 on s2.subjectid=s3.subjectid;

3.查询出mysql的考试信息

select * from subject2 s1,scores2 s2 where s1.subjectid=s2.subjectid and s1.subjectname='mysql';
#或
select * from subject2 s1
inner join scores2 s2 on s1.subjectid=s2.subjectid 
where s1.subjectname='mysql';

4.查询出考试学员的总分

select s1.stuname 姓名,sum(s2.score) 总分 from student2 s1,scores2 s2 where s1.stuid=s2.studentid group by s2.studentid;
#或
select s1.stuname 姓名,sum(s2.score) 总分 from student2 s1
inner join scores2 s2 on s1.stuid=s2.studentid group by s2.studentid;

5.查询每科的平均分

select s1.subjectname 科目名称,avg(s2.score) 平均分 from subject2 s1,scores2 s2 where s1.subjectid=s2.subjectid group by subjectname;
#或
select s1.subjectname 科目名称,avg(s2.score) 平均分 from subject2 s1
inner join scores2 s2 on s1.subjectid=s2.subjectid group by subjectname;
2、外连接

包括左外连接和右外连接,外连接的特点:查询出的结果存在不满足条件的可能。

– 显示还没有员工的部门名称?

– 外连查询

– 左外连:select 列名 from 主表 left join 次表 on 主表.列名=次表.列名

– 1.主表数据全部显示,次表数据匹配显示,能匹配到的显示数据,匹配不成功的显示null

– 2.主表和次表不能随意调换位置

使用场景:一般会作为子查询的语句使用

select depname,name from 
(select e.*,d.depname from department d left join employee e 
on e.depid=d.depid 
) aa where aa.name is null;

– 右外连:select 列名 from 次表 right join 主表 on 主表.列名=次表.列名

a.左外连接

SELECT * FROM emp e 
LEFT OUTER JOIN dept d 
ON e.deptno=d.deptno;

左连接是先查询出左表(即以左表为主),然后查询右表,右表中满足条件的显示出来,不满足条件的显示NULL。

insert into emp values(1007,'何炅','主管',1006,'2019-6-1',1011,2004,50);

我们还是用上面的例子来说明。其中emp表中“张三”这条记录中,部门编号为50,而dept表中不存在部门编号为50的记录,所以“张三”这条记录,不能满足e.deptno=d.deptno这条件。但在左连接中,因为emp表是左表,所以左表中的记录都会查询出来,即“张三”这条记录也会查出,但相应的右表部分显示NULL。

在这里插入图片描述

b.右外连接

右连接就是先把右表中所有记录都查询出来,然后左表满足条件的显示,不满足显示NULL。例如在dept表中的40部门并不存在员工,但在右连接中,如果dept表为右表,那么还是会查出40部门,但相应的员工信息为NULL。

insert into dept values(60,'颜值部','成都');
SELECT * FROM emp e 
RIGHT OUTER JOIN dept d 
ON e.deptno=d.deptno;

在这里插入图片描述

连接查询心得

连接不限与两张表,连接查询也可以是三张、四张,甚至N张表的连接查询。通常连接查询不可能需要整个笛卡尔积,而只是需要其中一部分,那么这时就需要使用条件来去除不需要的记录。这个条件大多数情况下都是使用主外键关系去除。

两张表的连接查询一定有一个主外键关系,三张表的连接查询就一定有两个主外键关系,所以在大家不是很熟悉连接查询时,首先要学会去除无用笛卡尔积,那么就是用主外键关系作为条件来处理。如果两张表的查询,那么至少有一个主外键条件,三张表连接至少有两个主外键条件。

内连接是查询出两个或多个表共有的部分,而外连接是查询出两个或多个表全部的内容

3、自然连接

自然连接(NATURAL INNER JOIN):自然连接是一种特殊的等值连接,他要求两个关系表中进行连接的必须是相同的属性列(名字相同),无须添加连接条件并且在结果中消除重复的属性列。下面给出几个例子。

语句:

select * from emp e natural join dept d;

练习题:

在这里插入图片描述

选C

在这里插入图片描述

在这里插入图片描述

选D

子查询

一个select语句中包含另一个完整的select语句。

子查询就是嵌套查询,即SELECT中包含SELECT,如果一条语句中存在两个或两个以上SELECT,那么就是子查询语句了。

l 子查询出现的位置:

a. where后,作为被查询的条件的一部分;

b. from后,作表;

l 当子查询出现在where后作为条件时,还可以使用如下关键字:

a. any

b. all

l 子查询结果集的形式:

a. 单行单列(用于条件)

b. 单行多列(用于条件)

c. 多行单列(用于条件)

d. 多行多列(用于表)

示例:

1.工资高于JONES的员工

分析:

查询条件:工资>JONES工资,其中JONES工资需要一条子查询。

第一步:查询JONES的工资

SELECT sal FROM emp WHERE ename='JONES';

第二步:查询高于JONES工资的员工

SELECT * FROM emp WHERE sal > (第一步结果);

结果:

SELECT * FROM emp WHERE sal > (SELECT sal FROM emp WHERE ename='JONES');

2. 查询与SCOTT同一个部门的员工

l 子查询作为条件

l 子查询形式为单行单列

分析:

查询条件:部门=SCOTT的部门编号,其中SCOTT 的部门编号需要一条子查询。

第一步:查询SCOTT的部门编号

SELECT deptno FROM emp WHERE ename='SCOTT';

第二步:查询部门编号等于SCOTT的部门编号的员工

SELECT * FROM emp WHERE deptno = (SELECT deptno FROM emp WHERE ename='SCOTT');

3. 工资高于30号部门所有人的员工信息

分析:

SELECT * FROM emp WHERE sal>(SELECT MAX(sal) FROM emp WHERE deptno=30);

查询条件:工资高于30部门所有人工资,其中30部门所有人工资是子查询。高于所有需要使用all关键字。

第一步:查询30部门所有人工资

SELECT sal FROM emp WHERE deptno=30;

第二步:查询高于30部门所有人工资的员工信息

SELECT * FROM emp WHERE sal > ALL (第一步);

结果:

SELECT * FROM emp WHERE sal > ALL (SELECT sal FROM emp WHERE deptno=30);

l 子查询作为条件

l 子查询形式为多行单列(当子查询结果集形式为多行单列时可以使用ALL或ANY关键字)

综合练习

某网上商城数据库如下图所示

在这里插入图片描述

#一对多的实现 

#创建分类表 
create table category(
	cid varchar(32) PRIMARY KEY , # 分类id 
	cname varchar(100) #分类名称 
);
# 商品表 
CREATE TABLE `products` ( 
	`pid` varchar(32) PRIMARY KEY , #商品id 
	`name` VARCHAR(40) , #商品名称 
	`price` DOUBLE, # 价格 
	category_id varchar(32) # 分类id,外键列 
);
#多对多的实现 
#用户表 
create table users( 
	userid int, 
	username varchar(20), 
	upass varchar(20) 
)
#订单表 
create table `orders`( 
	`oid` varchar(32) PRIMARY KEY , #订单id
	totalprice double, #总计 
	uid int # 用户id 
);
# 订单项表 
create table orderitem( 
	oid varchar(50), #订单id 
	pid varchar(50) #商品id 
);
#初始化数据 
#给分类表初始化数据 
insert into category values('c001','电器'); 
insert into category values('c002','服饰'); 
insert into category values('c003','化妆品'); 
insert into category values('c004','书籍'); 
#给商品表初始化数据 
insert into products(pid,name,price,category_id) values('p001','联 想',5000,'c001'); 
insert into products(pid,name,price,category_id) values('p002','海 尔',3000,'c001'); 
insert into products(pid,name,price,category_id) values('p003','雷 神',5000,'c001'); 
insert into products(pid,name,price,category_id) values('p004','JACK JONES',800,'c002'); 
insert into products(pid,name,price,category_id) values('p005','真维 斯',200,'c002'); 
insert into products(pid,name,price,category_id) values('p006','花花公 子',440,'c002'); 
insert into products(pid,name,price,category_id) values('p007','劲 霸',2000,'c002'); 
insert into products(pid,name,price,category_id) values('p008','香奈 儿',800,'c003'); 
insert into products(pid,name,price,category_id) values('p009','相宜本 草',200,'c003'); 
insert into products(pid,name,price,category_id) values('p010','梅明 子',200,null);

多表查询

1>查询用户的订单,没有订单的用户不显示

select * from users u,orders o where u.userid=o.uid;
#或
select * from users u
inner join orders o on u.userid=o.uid;

2>查询所有用户的订单详情

select * from users u left join orders o on u.userid=o.uid;

3>查询所有订单的用户详情

select * from users u right join orders o on u.userid=o.uid;

子查询

1>查看用户为张三的订单详情

select * from products where pid=(select o2.pid from orders o,users u,orderitem o2 where o.uid=u.userid and o.oid=o2.oid and u.username='张三');#此处最好使用in判断pid的范围,因为子查询可能会有多个结果。
#或
select * from products where pid in (select o2.pid from orders o,users u,orderitem o2 where o.uid=u.userid and o.oid=o2.oid and u.username='张三');
#或
select p.* from orders o,users u,orderitem o2,products p where o.uid=u.userid and o.oid=o2.oid and u.username='张三' and p.pid=o2.pid;

2>查询出订单的价格大于300的所有用户信息。

select * from users where userid in (select uid from orders where totalprice>300);
#或
select u.* from users u,orders o where u.userid=o.uid and o.totalprice>300;

3>查询订单价格大于300的订单信息及相关用户的信息。

select * from users u,orders o,orderitem o2,products p,category c where u.userid=o.uid and o.oid=o2.oid and o2.pid=p.pid and p.category_id=c.cid and o.totalprice>300;

分页查询

1>查询所有订单信息,每页显示5条数据

select * from orders o,orderitem o2,products p,category c where o.oid=o2.oid and o2.pid=p.pid and p.category_id=c.cid and o.totalprice>300 limit 0,5;

扩展

多行新增

insert into 表名(列名) values (列值),(列值),(列值)

多表更新

(1)update 表1,表2 set 列名=列值 where 表1.列名=表2.列名 and 其他限定条件

(2)update 表1

inner join 表2 on 表1.列名=表2.列名

set 列名=列值

where 限定条件

示例:

update employee e,salary s
set title='助工',salary=1000
where e.empid=s.empid and name='李四'

多表删除

语法:

delete 被删除数据的表 from 删除操作中使用的表

where 限定条件

注:多张表之间使用逗号间隔

示例:

 #删除人事部的信息
 delete d,e,s from department d,employee e,salary s
 where d.depid=e.depid and s.empid=e.empid and depname='人事部'

日期运算函数

now() 获得当前系统时间

year(日期值) 获得日期值中的年份

date_add(日期,interval 计算值 计算的字段);

注:计算值大于0表示往后推日期,小于0表示往前推日期

示例:

date_add(now(),interval -40 year);#40年前的日期

数据库优化

1.对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引

2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:

select id from t where num is null;

最好不要给数据库留NULL,尽可能的使用 NOT NULL填充数据库。

备注、描述、评论之类的可以设置为 NULL,其他的,最好不要使用NULL。

3.应尽量避免在 where 子句中使用 != 或 <> 操作符,否则引擎将放弃使用索引而进行全表扫描。

4.应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描,如:

select id from t where num=10 or Name = 'admin';

可以这样查询:

select id from t where num = 10 
union all 
select id from t where Name = 'admin';

5.in 和 not in 也要慎用,否则会导致全表扫描,如:

select id from t where num in(1,2,3);

对于连续的数值,能用 between 就不要用 in 了:

select id from t where num between 1 and 3;

很多时候用 exists 代替 in 是一个好的选择

练习

练习1

#数据库结构 
#创建四张表 分别存储 学生信息 课程信息 分数 讲师信息表 存储相应数据 

#学生信息表 Student 
#字段名 字段类型 字段约束 / 含义 
#Sno Varchar(3) Not null / 学员编号 
#Sname Varchar(4) Not null / 学员姓名 
#Ssex Varchar(2) Not null / 性别 
#Sbirthday Datetime 生日 
#Classnum Varchar(5) 班级号 
CREATE TABLE STUDENT(
	SNO VARCHAR(3) NOT NULL, 
	SNAME VARCHAR(4) NOT NULL, 
	SSEX VARCHAR(2) NOT NULL, 
	SBIRTHDAY DATETIME, 
	CLASS VARCHAR(5) 
);
#课程信息表 course 
#字段名 字段类型 字段约束 / 含义
#Cno Varchar(5) Not null / 课程编号 
#Cname Varchar(10) Not null / 课程名称 
#Tno Varchar(10) Not null / 授课老师编号 
CREATE TABLE COURSE(
    CNO VARCHAR(5) NOT NULL, 
	CNAME VARCHAR(10) NOT NULL, 
	TNO VARCHAR(10) NOT NULL
); 
#成绩表score 
#字段名 字段类型 字段约束 / 含义 
#Sno Varchar(3) Not null / 学员编号 
#Cno Varchar(5) Not null / 课程编号 
#Degree Double(3,1) Not null / 分数 
CREATE TABLE SCORE (
    SNO VARCHAR(3) NOT NULL, 
	CNO VARCHAR(5) NOT NULL, 
	DEGREE NUMERIC(10, 1) NOT NULL
);
#讲师表teacher 
#字段名 字段类型 字段约束 / 含义 
#Tno Varchar(3) Not null / 讲师编号 
#Tname Varchar(4) Not null / 讲师姓名 
#Tsex Varchar(2) Not null / 讲师性别 
#Tbirthday Datetime Not null / 出生日期 
#Prof Varchar(6) 等级 
#Depart Varchar(10) 所属院系 
CREATE TABLE TEACHER (
    TNO VARCHAR(3) NOT NULL, 
	TNAME VARCHAR(4) NOT NULL, 
    TSEX VARCHAR(2) NOT NULL, 
	TBIRTHDAY DATETIME NOT NULL, 
    PROF VARCHAR(6), 
	DEPART VARCHAR(10) NOT NULL
); 
#向表中存储数据 
INSERT INTO STUDENT (SNO,SNAME,SSEX,SBIRTHDAY,CLASS) VALUES (108 ,'曾华' ,'男','1977-09-01',95033); 
INSERT INTO STUDENT (SNO,SNAME,SSEX,SBIRTHDAY,CLASS) VALUES (105 ,'匡明' ,'男','1975-10-02',95031); 
INSERT INTO STUDENT (SNO,SNAME,SSEX,SBIRTHDAY,CLASS) VALUES (107 ,'王丽' ,'女','1976-01-23',95033); 
INSERT INTO STUDENT (SNO,SNAME,SSEX,SBIRTHDAY,CLASS) VALUES (101 ,'李军' ,'男','1976-02-20',95033); 
INSERT INTO STUDENT (SNO,SNAME,SSEX,SBIRTHDAY,CLASS) VALUES (109 ,'王芳' ,'女','1975-02-10',95031); 
INSERT INTO STUDENT (SNO,SNAME,SSEX,SBIRTHDAY,CLASS) VALUES (103 ,'陆君' ,'男','1974-06-03',95031); 
INSERT INTO COURSE(CNO,CNAME,TNO)VALUES ('3-105' ,'计算机导论',825); 
INSERT INTO COURSE(CNO,CNAME,TNO)VALUES ('3-245' ,'操作系统' ,804); 
INSERT INTO COURSE(CNO,CNAME,TNO)VALUES ('6-166' ,'数据电路' ,856);
INSERT INTO COURSE(CNO,CNAME,TNO)VALUES ('9-888' ,'高等数学' ,100); 
INSERT INTO SCORE(SNO,CNO,DEGREE)VALUES (103,'3-245',86); 
INSERT INTO SCORE(SNO,CNO,DEGREE)VALUES (105,'3-245',75); 
INSERT INTO SCORE(SNO,CNO,DEGREE)VALUES (109,'3-245',68); 
INSERT INTO SCORE(SNO,CNO,DEGREE)VALUES (103,'3-105',92);
INSERT INTO SCORE(SNO,CNO,DEGREE)VALUES (105,'3-105',88); 
INSERT INTO SCORE(SNO,CNO,DEGREE)VALUES (109,'3-105',76); 
INSERT INTO SCORE(SNO,CNO,DEGREE)VALUES (101,'3-105',64); 
INSERT INTO SCORE(SNO,CNO,DEGREE)VALUES (107,'3-105',91); 
INSERT INTO SCORE(SNO,CNO,DEGREE)VALUES (108,'3-105',78); 
INSERT INTO SCORE(SNO,CNO,DEGREE)VALUES (101,'6-166',85); 
INSERT INTO SCORE(SNO,CNO,DEGREE)VALUES (107,'6-106',79); 
INSERT INTO SCORE(SNO,CNO,DEGREE)VALUES (108,'6-166',81); 
INSERT INTO TEACHER(TNO,TNAME,TSEX,TBIRTHDAY,PROF,DEPART) 
VALUES (804,'李诚','男','1958-12-02','副教授','计算机系'); 
INSERT INTO TEACHER(TNO,TNAME,TSEX,TBIRTHDAY,PROF,DEPART) 
VALUES (856,'张旭','男','1969-03-12','讲师','电子工程系'); 
INSERT INTO TEACHER(TNO,TNAME,TSEX,TBIRTHDAY,PROF,DEPART) 
VALUES (825,'王萍','女','1972-05-05','助教','计算机系'); 
INSERT INTO TEACHER(TNO,TNAME,TSEX,TBIRTHDAY,PROF,DEPART) 
VALUES (831,'刘冰','女','1977-08-14','助教','电子工程系'); 

1、 查询Student表中的所有记录的Sname、Ssex和Class列。

select sname,ssex,class from student;

2、 查询教师所有的单位即不重复的Depart列。

select distinct depart from teacher;

3、 查询Student表的所有记录。

select * from student;

4、 查询Score表中成绩在60到80之间的所有记录。

select * from score where degree between 60 and 80;

5、 查询Score表中成绩为85,86或88的记录。

select * from score where degree in (85,86,88);

6、 查询Student表中“95031”班或性别为“女”的同学记录。

select * from student where class='95031' or ssex='女';
#或
select * from student where class='95031'
union
select * from student where ssex='女';

7、 以Class降序查询Student表的所有记录。

select * from student order by class desc;

8、 以Cno升序、Degree降序查询Score表的所有记录。

select * from score order by cno asc,degree desc;

9、 查询“95031”班的学生人数。

select count(*) 人数 from student where class='95031'; 

10、查询Score表中的最高分的学生学号和课程号。

select sno,cno from score order by degree desc limit 0,1;

11、查询‘3-105’号课程的平均分。

select avg(degree) from score where cno='3-105';

12、查询Score表中至少有5名学生选修的并以3开头的课程的平均分数。

select cno,avg(degree) from score group by cno having count(*)>=5 and cno like '3%';

13、查询最低分大于70,最高分小于90的Sno列。

select sno from score where degree>70 and degree<90;
#或
select sno from score group by degree having min(degree)>70 and max(degree)<90;

14、查询所有学生的Sname、Cno和Degree列。

select s1.sname,s2.cno,s2.degree from stuent s1,score s2 where s1.sno=s2.sno;
#或
select s1.sname,s2.cno,s2.degree from student s1
inner join score s2
on s1.sno=s2.sno;

15、查询所有学生的Sno、Cname和Degree列。

select s1.sno,c.cname,s1.degree from score s1,course c where s1.cno=c.cno;
#或
select s1.sno,c.cname,s1.degree from score s1
inner join course c on s1.cno=c.cno;

16、查询所有学生的Sname、Cname和Degree列。

select s1.sname,c.cname,s2.degree from student s1,score s2,course c where s1.sno=s2.sno and s2.cno=c.cno;
#或
select s1.sname,c.cname,s2.degree from student s1
inner join score s2 on s1.sno=s2.sno
inner join course c on s2.cno=c.cno;

17、查询“95033”班所选课程的平均分。

select avg(s2.degree) from student s1,score s2 where s1.class='95033' and  s1.sno=s2.sno;
#或
select avg(s2.degree) from student s1
inner join score s2 on s1.sno=s2.sno
where s1.class='95033';

18、查询选修“3-105”课程的成绩高于“109”号同学成绩的所有同学的记录。

select s1.* from student s1,score s2 where s1.sno=s2.sno and s2.cno='3-105' and s2.degree> all(select degree from score where sno=109);
#或
select s1.* from student s1
inner join score s2 on s1.sno=s2.sno
where s2.cno='3-105' and s2.degree>all(select degree from score where sno=109);

19、查询score中选学一门以上课程的同学中分数为非最高分成绩的记录。

select max(degree) from score group by sno having count(sno)>1 order by max(degree) desc limit 1,1;

20、查询成绩高于学号为“109”、课程号为“3-105”的成绩的所有记录。

select * from score where sno=109 and cno='3-105';

21、查询和学号为108的同学同年出生的所有学生的Sno、Sname和Sbirthday列。

select sno,sname,sbirthday from student	where sno!=108 and sbirthday in(select sbirthday from student where sno=108);

22、查询“张旭“教师任课的学生成绩。

select * from teacher t,course c,score s where t.tname='张旭' and t.tno=c.tno and c.cno=s.cno;

23、查询选修某课程的同学人数多于5人的教师姓名。⭐

select t.tname from teacher t,course c,score s where s.cno=c.cno and c.tno=t.tno group by s.cno having count(*)>5;

24、查询95033班和95031班全体学生的记录。

select * from student where class='95033' or class='95031';
#或
select * from student where class='95033'
union all
select * from student where class='95031';

25、查询存在有85分以上成绩的课程Cno.

select cno from score where degree>85;

26、查询出“计算机系“教师所教课程的成绩表。

select t.depart,c.cname,s.* from teacher t,course c,score s where t.depart='计算机系' and t.tno=c.tno and s.cno=c.cno;
#或
select t.depart,c.cname,s.* from teacher t
inner join course c on t.tno=c.tno
inner join score s on c.cno=s.cno
where t.depart='计算机系';

27、查询“计算机系”与“电子工程系“不同职称的教师的Tname和Prof。

select tname,prof from teacher where depart='计算机系' or depart='电子工程系';
#或
select tname,prof from teacher where depart='计算机系'
union all
select tname,prof from teacher where depart='电子工程系';

28、查询选修编号为“3-105“课程且成绩至少高于选修编号为“3-245”的同学的Cno、Sno和Degree,并按Degree从高到低次序排序。

select cno,sno,degree from score where cno='3-105' and degree > all(select degree from score where cno='3-245') order by degree desc;

29、查询选修编号为“3-105”且成绩高于选修编号为“3-245”课程的同学的Cno、Sno和Degree.

select cno,sno,degree from score where cno='3-105' and degree > all(select degree from score where cno='3-245');

30、查询所有教师和同学的name、sex和birthday.⭐

select sname 姓名,ssex 性别,sbirthday 生日 from student
union all
select tname,tsex,tbirthday from teacher;

31、查询所有“女”教师和“女”同学的name、sex和birthday.

select sname 姓名,ssex 性别,sbirthday 生日 from student where ssex='女'
union all
select tname,tsex,tbirthday from teacher where tsex='女';

32、查询成绩比该课程平均成绩低的同学的成绩表。⭐

select * from (select cno,avg(degree) 平均分 from score group by cno) a left join score s on a.cno=s.cno where degree<平均分 group by s.cno,s.sno;

33、查询所有任课教师的Tname和Depart.

select tname,depart from teacher;

34、查询所有未讲课的教师的Tname和Depart.

select tname,depart from teacher where tno not in(select tno from course);

35、查询至少有2名男生的班号。

select class from student group by class,ssex having count(*)>=2;

36、查询Student表中不姓“王”的同学记录。

select * from student where sname not like '王%';

37、查询Student表中每个学生的姓名和年龄。⭐

select sname,year(now())-year(sbirthday) 年龄 from student;

38、查询Student表中最大和最小的Sbirthday日期值。

select max(sbirthday),min(sbirthday) from student;

39、以班号和年龄从大到小的顺序查询Student表中的全部记录。

select *,year(now())-year(sbirthday) 年龄 from student order by class desc,年龄 desc;

40、查询“男”教师及其所上的课程。

select * from teacher t,course c where t.tno=c.tno and t.tsex='男';
#或
select * from teacher t
inner join course c on t.tno=c.tno
where t.tsex='男';

41、查询最高分同学的Sno、Cno和Degree列。

select sno,cno,degree from score order by degree desc limit 0,1;

42、查询和“李军”同性别的所有同学的Sname.

select sname from student where ssex in(select ssex from student where sname='李军') and sname!='李军';

43、查询和“李军”同性别并同班的同学Sname.

select sname from student where ssex in(select ssex from student where sname='李军') and sname!='李军' and
class in(select class from student where sname='李军');

44、查询所有选修“计算机导论”课程的“男”同学的成绩表

select s2.* from student s1,score s2,course c where c.cname='计算机导论' and s1.ssex='男' and s1.sno=s2.sno and s2.cno=c.cno;
#或
select s2.* from student s1
inner join score s2 on s1.sno=s2.sno
inner join course c on s2.cno=c.cno
where c.cname='计算机导论' and s1.ssex='男';

练习2

#部门表
create table department (
	depid int primary key auto_increment comment '主键',
	depname varchar(10) comment '部门名称'
);
#雇员表
create table employee(
	empid int primary key auto_increment comment '主键',
	name varchar(10) comment '姓名',
	sex varchar(1) comment '性别',
	title varchar(10) comment '职称',
	birthday date comment '出生日期',
	depid int comment '所属部门',
	constraint fk_employee_depid foreign key(depid) references department(depid) on delete cascade on update cascade
);
#工资表
create table salary (
	empid int comment '雇员编号',
	basesalary int comment '基本工资',
	titlesalary int comment '职务工资',
	deduction int comment '扣除',
	constraint fk_salary_empid foreign key(empid) references employee(empid) on delete cascade on update cascade
);
#部门表数据导入
insert into department values(1,'人事部'),(2,'人力资源部'),(3,'销售部'),(4,'产品开发部'),(5,'公关部');
#雇员表数据导入
insert into employee values(1,'张三','男','总经理','1997-03-25',1),
(2,'李四','男','人力资源总监','1995-02-24',2),
(3,'王五','男','营销总监','1994-01-23',3),
(4,'王二','女','生产总监','1989-07-15',4),
(5,'秀儿','女','公关总监','1998-05-22',5);
#工资表数据导入
insert into salary values(1,3000,4000,500),(2,3200,4000,300),(3,4000,4000,200),(4,3800,3000,400),(5,3000,5000,500);

1、修改表结构,在部门表中添加部门简介字段

alter table department add introduction varchar(50);

2、将李四的职称改为“工程师”,并将她的基本工资改成 2000,职务工资为 700

update employee e,salary s 
set e.title='工程师',s.basesalary=2000,s.titlesalary=700 
where e.name='李四' and e.empid=s.empid;

3、删除人事部门的部门记录

delete d,e,s from department d,employee e,salary s 
where d.depname='人事部' and e.depid=d.depid and s.empid=e.empid;

4、查询出每个雇员的雇员编号,实发工资,应发工资

select empid,basesalary+titlesalary-deduction 实发工资,basesalary+titlesalary 应发工资 from salary;

5、查询姓张且年龄小于 40 的员工记录

select *,year(now())-year(birthday) 
from employee 
where (year(now())-year(birthday))<40 and name like '张%';

6、查询雇员的雇员编号,姓名,职称,部门名称,实发工资

select e.empid 编号,e.name 姓名,e.title 职称,d.depname 部门名称,s.basesalary+s.titlesalary-s.deduction 实发工资 
from department d,employee e,salary s 
where d.depid=e.depid and e.empid=s.empid;
#或者
select e.empid 编号,e.name 姓名,e.title 职称,d.depname 部门名称,s.basesalary+s.titlesalary-s.deduction 实发工资 
from department d
inner join employee e on d.depid=e.depid
inner join salary s on e.empid=s.empid;

7、查询销售部门的雇员姓名,工资

select e.name,s.* 
from department d,employee e,salary s 
where d.depname='销售部' and d.depid=e.depid and e.empid=s.empid;

8、统计各职称的人数

select title,count(*) from employee group by title;

9、统计各部门的部门名称,实发工资总和,平均工资

select d.depname,sum(s.basesalary+s.titlesalary-s.deduction) 实发工资和,avg(s.basesalary+s.titlesalary-s.deduction) 平均工资 
from department d,employee e,salary s 
where d.depid=e.depid and e.empid=s.empid group by d.depname;
#或者
select d.depname,sum(s.basesalary+s.titlesalary-s.deduction) 实发工资和,avg(s.basesalary+s.titlesalary-s.deduction) 平均工资 
from department d
inner join employee e on d.depid=e.depid 
inner join salary s on e.empid=s.empid 
group by d.depname;

10、查询比销售部门所有员工基本工资都高的雇员姓名

select e.name 
from department d,employee e,salary s 
where s.basesalary > all(
    select s.basesalary 
    from department d,employee e,salary s 
    where d.depname='销售部' and d.depid=e.depid and e.empid=s.empid
) and d.depname!='销售部' and d.depid=e.depid and e.empid=s.empid;

数据库进阶

事务

事务(Transaction)是由一系列对系统中数据进⾏访问与更新的操作所组成的⼀个程序执行逻辑单元。

(1) 事务的语法

(2) 事务的特性

(3) 事务并发问题

(4) 事务隔离级别

(5) 不同隔离级别的锁的情况(了解)

(6) 隐式提交(了解)

在这里插入图片描述

事务的语法

  1. start transaction; begin;

  2. commit; 使得当前的修改确认

  3. rollback; 使得当前的修改被放弃 ,在提交之前都是可以回滚的。

注意:当前数据库的引擎为InnoDB,其他类型不支持事务。

在事务提交之前可以在navicat中看到数据,但是在实际的数据库中查不到,只有提交了事务,在实际的数据库中才能查到。

事务的ACID特性

  1. 原⼦性(Atomicity)

事务的原⼦性是指事务必须是⼀个原子的操作序列单元。事务中包含的各项操作在⼀次执⾏过程中,只允许出现两种状态之一。

(1)全部执行成功

(2)全部执行失败

事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执⾏过程中出错, 会回滚到事务开始前的状态,所有的操作就像没有发⽣一样。也就是说事务是⼀个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。

  1. ⼀致性(Consistency)

事务的一致性是指事务的执⾏不能破坏数据库数据的完整性和一致性,一个事务在执⾏之前和执行之后,数据库都必须处以⼀致性状态。

比如:如果从A账户转账到B账户,不可能因为A账户扣了钱,⽽B账户没有加钱。

  1. 隔离性(Isolation)

事务的隔离性是指在并发环境中,并发的事务是互相隔离的。也就是说,不同的事务并发操作相同的数据时,每个事务都有各自完整的数据空间。

⼀个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务是不能互相干扰的。 隔离性分4个级别,下面会介绍。

  1. 持久性(Duration)

事务的持久性是指事务⼀旦提交后,数据库中的数据必须被永久的保存下来。即使服务器系统崩溃或服 务器宕机等故障。只要数据库重新启动,那么一定能够将其恢复到事务成功结束后的状态

事务的并发问题

脏读:读取到了没有提交的数据, 事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的 数据是脏数据。

不可重复读:同⼀条命令返回不同的结果集(更新)。事务 A 多次读取同一数据,事务 B 在事务A 多次读取的过程中,对数据做了更新并提交,导致事务A多次读取同一数据时,结果不一致。

幻读:重复查询的过程中,数据就发⽣了量的变化(insert, delete)。

事务的隔离级别

在这里插入图片描述

4种事务隔离级别从上往下,级别越高,并发性越差,安全性就越来越高。 ⼀般数据默认级别是读以提交或可重复读

查看当前会话中事务的隔离级别:

mysql> select @@tx_isolation; //mysql8之前
mysql> select @@transaction_isolation;//mysql8之后
+-----------------+ 
| @@tx_isolation | 
+-----------------+ 
| REPEATABLE-READ | 
+-----------------+ 
1 row in set, 1 warning (0.93 sec) 

设置当前会话中的事务隔离级别

mysql> set session transaction isolation level read uncommitted; 
Query OK, 0 rows affffected (0.00 sec) 
  1. 读未提交(READ_UNCOMMITTED)

读未提交,该隔离级别允许脏读取,其隔离级别是最低的。换句话说,如果一个事务正在处理理某一数据,并对其进⾏了更新,但同时尚未完成事务,因此还没有提交事务;而以此同时,允许另一个事务也能够访问该数据。

脏读示例

在事务A和事务B同时执行时可能会出现如下场景:

在这里插入图片描述

余额应该为1500元才对。请看T5时间点,事务A此时查询的余额为0,这个数据就是脏数据,他是事务B 造成的,很明显是事务没有进行隔离造成的。

  1. 读已提交(READ_COMMITTED)

读已提交是不同的事务执行的时候只能获取到已经提交的数据。 这样就不会出现上面的脏读的情况了。 但是在同一个事务中执行同一个读取,结果不一致 。

不可重复读示例

可是解决了脏读问题,但是还是解决不了可重复读问题。

在这里插入图片描述

事务A其实除了查询两次以外,其它什什么事情都没做,结果钱就从1000变成0了了,这就是不可重复读的问题。

  1. 可重复读(REPEATABLE_READ)

可重复读就是保证在事务处理理过程中,多次读取同一个数据时,该数据的值和事务开始时刻是一致的。因此该事务级别限制了不可重复读和脏读,但是有可能出现幻读的数据。

幻读

幻读就是指同样的事务操作,在前后两个时间段内执行对同一个数据项的读取,可能出现不一致的结果。

诡异的更新事件

在这里插入图片描述

  1. 顺序读(SERIALIZABLE)

顺序读是最严格的事务隔离级别。它要求所有的事务排队顺序执⾏行行,即事务只能一个接一个地处理,不能并发。

不同的隔离级别锁的情况(了解)

  1. 读未提交(RU): 有行级的锁,没有间隙锁。它与RC的区别是能够查询到未提交的数据。

  2. 读已提交(RC):有行级的锁,没有间隙锁,读不到没有提交的数据。

  3. 可重复读(RR):有行级的锁,也有间隙锁,每次读取的数据都是一样的,并且没有幻读的情况。

  4. 序列化(S):有行级锁,也有间隙锁,读表的时候,就已经上锁了

隐式提交(了解)

DQL:查询语句句

DML:写操作(添加,删除,修改)

DDL:定义语句句(建库,建表,修改表,索引操作,存储过程,视图)

DCL: 控制语⾔言(给⽤用户授权,或删除授权)

DDL(Data Defifine Language):都是隐式提交。

隐式提交:执⾏这种语句相当于执⾏commit; DDL

https://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html

JDBC

JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。

Java 具有坚固、安全、易于使用、易于理解和可从网络上自动下载等特性,是编写数据库应用程序的杰出语言。所需要的只是 Java应用程序与各种不同数据库之间进行对话的方法。JDBC可以在各种平台上使用Java,如Windows,Mac OS和各种版本的UNIX。

JDBC库包括通常与数据库使用相关的下面提到的每个任务的API。

- 连接数据库。

- 创建SQL或MySQL语句。

- 在数据库中执行SQL或MySQL查询。

- 查看和修改生成的记录。

JDBC体系结构

JDBC API支持用于数据库访问的两层和三层处理模型,但通常,JDBC体系结构由两层组成:

- JDBC API:这提供了应用程序到JDBC管理器连接。

- JDBC驱动程序API:这支持JDBC管理器到驱动程序连接。

JDBC API使用驱动程序管理器和特定于数据库的驱动程序来提供与异构数据库的透明连接。

在这里插入图片描述

JDBC核心组件

DriverManager:此类管理数据库驱动程序列表。使用通信子协议将来自java应用程序的连接请求与适当的数据库驱动程序匹配。

Driver:此接口处理与数据库服务器的通信,我们很少会直接与Driver对象进行交互。而是使用DriverManager对象来管理这种类型的对象。

Connection:该界面具有用于联系数据库的所有方法。连接对象表示通信上下文,即,与数据库的所有通信仅通过连接对象。

Statement:使用从此接口创建的对象将SQL语句提交到数据库。除了执行存储过程之外,一些派生接口还接受参数。

ResultSet:在使用Statement对象执行SQL查询后,这些对象保存从数据库检索的数据。它作为一个迭代器,允许我们移动其数据。

SQLException:此类处理数据库应用程序中发生的任何错误。

CRUD语法介绍

SQL 是一种标准化的语言,它允许你在数据库上执行操作,如创建项目,查询内容,更新内容,并删除条目等操作。Create, Read, Update, and Delete 通常称为CRUD操作。

CREATE DATABASE语句用于创建新的数据库:

SQL> CREATE DATABASE DATABASE_NAME;

DROP DATABASE语句用于删除现有数据库:

SQL> DROP DATABASE DATABASE_NAME;

CREATE TABLE语句用于创建新表。语法是 -

SQL> CREATE TABLE Employees ( 
    id INT NOT NULL, 
    age INT NOT NULL, 
    first VARCHAR(255), 
    last VARCHAR(255), 
    PRIMARY KEY ( id ) 
);

DROP TABLE语句用于删除现有表。

SQL> DROP TABLE table_name;

INSERT的语法类似于以下内容,其中column1,column2等表示要显示在相应列中的新数据

SQL> INSERT INTO table_name VALUES (column1, column2, ...);

SELECT语句用于从数据库中检索数据。SELECT的语法是 -

SQL> SELECT column_name, column_name, ... FROM table_name WHERE conditions;

WHERE子句可以使用比较运算符,例如=,!=,<,>,<=和> =,以及BETWEEN和LIKE运算符。

UPDATE语句用于更新数据。

SQL> UPDATE table_name 
	SET column_name = value, column_name = value, ... 
	WHERE conditions; 

WHERE子句可以使用比较运算符,例如=,!=,<,>,<=和> =,以及BETWEEN和LIKE运算符。

DELETE语句用于从表中删除数据。

SQL> DELETE FROM table_name WHERE conditions;

WHERE子句可以使用比较运算符,例如=,!=,<,>,<=和> =,以及BETWEEN和LIKE运算符。

使用步骤

构建JDBC应用程序涉及以下六个步骤:

- 导入包:需要导入包含数据库编程所需的JDBC类的包。大多数情况下,使用import java.sql.*就足够了。

- 注册JDBC驱动程序:要求您初始化驱动程序,以便您可以打开与数据库的通信通道。

- 打开连接:需要使用DriverManager.getConnection()方法创建一个Connection对象,该对象表示与数据库的物理连接。

- 执行查询:需要使用类型为Statement的对象来构建和提交SQL语句到数据库。

- 从结果集中提取数据:需要使用相应的ResultSet.getXXX()方法从结果集中检索数据。

- 释放资源:需要明确地关闭所有数据库资源,而不依赖于JVM的垃圾收集。

JDBC连接步骤

建立JDBC连接所涉及的编程相当简单。这是简单的四个步骤

- 导入JDBC包:将Java语言的import语句添加到Java代码中导入所需的类。

- 注册JDBC驱动程序:此步骤将使JVM将所需的驱动程序实现加载到内存中,以便它可以满足您的JDBC请求。

- 数据库URL配置:这是为了创建一个格式正确的地址,指向要连接到的数据库。

- 创建连接对象:最后,调用DriverManager对象的getConnection()方法来建立实际的数据库连接。

Class.forName()

注册驱动程序最常见的方法是使用Java的Class.forName()方法,将驱动程序的类文件动态加载到内存中,并将其自动注册。

try {
    
    
	Class.forName("com.mysql.cj.jdbc.Driver"); 
}catch(ClassNotFoundException ex) {
    
     
	System.out.println("Error: unable to load driver class!"); 
	System.exit(1); 
}

DriverManager.registerDriver()

第二种方法是使用静态DriverManager.registerDriver()方法。

try {
    
    
    Driver myDriver = new com.mysql.cj.jdbc.Driver(); 
    DriverManager.registerDriver( myDriver ); 
}catch(ClassNotFoundException ex) {
    
     
    System.out.println("Error: unable to load driver class!"); 
    System.exit(1); 
}

数据库URL配置

加载驱动程序后,可以使用DriverManager.getConnection()方法建立连接。为了方便参考,让我列出三个重载的DriverManager.getConnection()方法

getConnection(String url) 
getConnection(String url,Properties prop) 
getConnection(String url,String user,String password) 

在这里插入图片描述

创建数据库连接对象

String URL = "jdbc:mysql://localhost:3306/yhp2?serverTimezone=UTC"; 
String USER = "username"; 
String PASS = "password";
Connection conn = DriverManager.getConnection(URL, USER, PASS);

完整的连接地址:

版本1:

jdbc:mysql://localhost:3306/数据库名?useSSL=false&useUnicode=true&characterEncoding=UTF-8 

版本2:

jdbc:mysql://localhost:3306/yhp2?serverTimezone=UTC

使用数据库URL和属性对象

DriverManager.getConnection()方法的第三种形式需要一个数据库URL和一个Properties对象。

DriverManager.getConnection(String url, Properties info);
import java.util.*; 
String URL = "jdbc:mysql://localhost:3306/yhp2?serverTimezone=UTC"; 
Properties info = new Properties( ); 
info.put( "user", "username" ); 
info.put( "password", "password" ); 
Connection conn = DriverManager.getConnection(URL, info);

关闭数据库连接

为确保连接关闭,您可以在代码中提供一个“finally”块。一个finally块总是执行,不管是否发生异常。

要关闭上面打开的连接,你应该调用close()方法如下:

conn.close();

JDBC执行SQL语句

一旦获得了连接,我们可以与数据库进行交互。JDBC Statement和PreparedStatement接口定义了使您能够发送SQL命令并从数据库接收数据的方法和属性。

| 接口 | 推荐使用 |

| ------------------ | ---------------------------------------- |

| Statement | 用于对数据库进行通用访问。在运行时使用静态SQL语句时很有用。Statement接口不能接受参数。 |

| PreparedStatement| 当您计划多次使用SQL语句时使用。PreparedStatement接口在运行时接受输入参数。 |

Statement

创建语句对象

在使用Statement对象执行SQL语句之前,需要使用Connection对象的createStatement()方法创建一个,如下例所示:

Statement stmt = null;
try {
    
    
    stmt = conn.createStatement( ); 
    . . . 
}catch (SQLException e) {
    
    
    . . . 
}finally {
    
    
    . . . 
}

创建Statement对象后,您可以使用它来执行一个SQL语句,其中有三个执行方法之一。

- boolean execute(String SQL):如果可以检索到ResultSet对象,则返回一个布尔值true; 否则返回false。使用此方法执行SQL DDL语句或需要使用真正的动态SQL时。

- int executeUpdate(String SQL):返回受SQL语句执行影响的行数。使用此方法执行预期会影响多个行的SQL语句,例如INSERT,UPDATE或DELETE语句。

- ResultSet executeQuery(String SQL):返回一个ResultSet对象。当您希望获得结果集时,请使用此方法,就像使用SELECT语句一样。

关闭Statement对象

就像我们关闭一个Connection对象以保存数据库资源一样,由于同样的原因,还应该关闭Statement对象。

一个简单的调用close()方法将执行该作业。如果先关闭Connection对象,它也会关闭Statement对象。但是,应始终显式关闭Statement对象,以确保正确清理。

Statement stmt = null; 
try {
    
    
    stmt = conn.createStatement( );
    . . . 
}catch (SQLException e) {
    
     
    . . .
}finally {
    
    
    stmt.close();
}

SQL注入

就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串中,最终达到欺骗服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。比如先前的很多影视网站泄露VIP会员密码大多就是通过WEB表单递交查询字符暴出的,这类表单特别容易受到SQL注入式攻击。

String username ="admin"; 
String password=" 'abc' or 1=1 "; //1=1恒成立,永远是正确的
String sql="select * from users where username= '"+username+"' and password= "+password;

PreparedStatement(预状态通道)

该PreparedStatement的接口扩展了Statement接口,它为您提供了一个通用的Statement对象有两个优点附加功能。

此语句使您可以动态地提供参数。

PreparedStatement pstmt = null; 
try {
    
    
    String SQL = "Update Employees SET age = ? WHERE id = ?"; 
    pstmt = conn.prepareStatement(SQL);
    . . . 
}catch (SQLException e) {
    
     
    . . . 
}finally {
    
    
    . . . 
}

JDBC中的所有参数都由?符号,这被称为参数标记。在执行SQL语句之前,必须为每个参数提供值。所述的setXXX()方法将值绑定到所述参数,其中XXX代表要绑定到输入参数的值的Java数据类型。如果忘记提供值,将收到一个SQLException。每个参数标记由其顺序位置引用。第一个标记表示位置1,下一个位置2等等。该方法与Java数组索引从0开始不同。

关闭PreparedStatement对象

就像关闭Statement对象一样,由于同样的原因,还应该关闭PreparedStatement对象。

一个简单的调用close()方法将执行该作业。如果先关闭Connection对象,它也会关闭PreparedStatement对象。但是,应始终显式关闭PreparedStatement对象,以确保正确清理。

PreparedStatement pstmt = null; 
try {
    
    
    String SQL = "Update Employees SET age = ? WHERE id = ?"; 
    pstmt = conn.preparedStatement(SQL); 
    . . . 
}catch (SQLException e) {
    
     
    . . . 
}finally {
    
     
    pstmt.close(); 
}

对比statement和PreparedStatement;

(1)statement属于状态通道,PreparedStatement属于预状态通道

(2)预状态通道会先编译sql语句,再去执行sql语句,而statement相反,比statement执行效率高

(3)预状态通道支持占位符?,给占位符赋值的时候,位置从1开始

(4)预状态通道可以防止sql注入,原因:预状态通道在处理值的时候以字符串的方式处理

ResultSet

SELECT语句是从数据库中选择行并在结果集中查看行的标准方法。该java.sql.ResultSet中的接口表示结果集数据库查询。

ResultSet对象维护指向结果集中当前行的游标。术语“结果集”是指包含在ResultSet对象中的行和列数据。

如果没有指定任何ResultSet类型,您将自动获得一个TYPE_FORWARD_ONLY。

在这里插入图片描述

try {
    
    
    Statement stmt = conn.createStatement( ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); }catch(Exception ex) {
    
     
    .... 
}finally {
    
     
    ....
}

JAVA操作两表关系

四种:双向一对一,一对多,多对一,多对多

多表关系处理数据

(1) 数据库通过外键建立两表关系

(2) 实体类通过属性的方式建立两表关系

实体类要求:类名=表名,列名=属性名

一对多(老师->学生)

(1)创建数据表

CREATE TABLE `student` ( 
	`stuid` int(11) NOT NULL AUTO_INCREMENT, 
	`stuname` varchar(255) DEFAULT NULL, 
	`teacherid` int(11) DEFAULT NULL, 
	PRIMARY KEY (`stuid`) 
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; 
INSERT INTO `student` VALUES ('1', 'aaa', '3'); 
INSERT INTO `student` VALUES ('2', 'bb', '1'); 
INSERT INTO `student` VALUES ('3', 'cc', '3'); 
INSERT INTO `student` VALUES ('4', 'dd', '1'); 
INSERT INTO `student` VALUES ('5', 'ee', '1'); 
INSERT INTO `student` VALUES ('6', 'ff', '2'); 
DROP TABLE IF EXISTS `teacher`; 
CREATE TABLE `teacher` ( 
	`tid` int(11) NOT NULL AUTO_INCREMENT, 
	`tname` varchar(255) DEFAULT NULL, 
	PRIMARY KEY (`tid`) 
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; 
INSERT INTO `teacher` VALUES ('1', '张三老师'); 
INSERT INTO `teacher` VALUES ('2', '李四老师'); 
INSERT INTO `teacher` VALUES ('3', '王五');

(2)创建实体类

public class Teacher {
    
     
	private int tid; 
	private String tname; 
	private List<Student> list=new ArrayList<Student>(); //一对多,在一的存放多的数据
	public List<Student> getList() {
    
     
		return list; 
	}
	public void setList(List<Student> list) {
    
     
		this.list = list; 
	}
	public int getTid() {
    
     
		return tid; 
	}
	public void setTid(int tid) {
    
     
		this.tid = tid; 
	}
	public String getTname() {
    
     
		return tname; 
	}
	public void setTname(String tname) {
    
     
		this.tname = tname; 
	} 
}
public class Student {
    
     
	private int stuid; 
	private String stuname; 
	//外键列一般不生成属性 
	// private int teacherid; 
	public int getStuid() {
    
     
		return stuid; 
	}
	public void setStuid(int stuid) {
    
     
		this.stuid = stuid; 
	}
	public String getStuname() {
    
     
		return stuname; 
	}
	public void setStuname(String stuname) {
    
     
		this.stuname = stuname; 
	} 
}

(3)定义dao接口

public interface TeacherDao {
    
     
    //定义操作方法 
    //1.定义一个根据老师id查询老师信息(学生的信息) 
    public Teacher getById(int tid); 
}

(4)定义实现类

public class TeacherDaoImpl implements TeacherDao {
    
     
    @Override 
    public Teacher getById(int tid) {
    
     
        //操作数据库 
        Connection connection =null; 
        PreparedStatement pps =null; 
        ResultSet resultSet =null;
        try {
    
    
            //1.加载驱动 
            Class.forName("com.mysql.cj.jdbc.Driver"); 
            //2.获得链接 
            String userName="root";
            String passWord="123456";
            String url="jdbc:mysql://localhost:3306/yhp3?serverTimezone=UTC"; 
            connection = DriverManager.getConnection(url, userName, passWord);
            //3.定义sql,创建预状态通道(进行sql语句的发送)
            String sql="select * from student s,teacher t where s.teacherid=t.tid and t.tid=?"; 
            pps = connection.prepareStatement(sql); 
            //给占位符赋值 (下标,内容) 从1开始
            pps.setInt(1,tid); 
            //执行sql 
            resultSet = pps.executeQuery(); 
            Teacher teacher = new Teacher(); 
            List<Student> students=new ArrayList<Student>(); 
            while (resultSet.next()){
    
     
                //1.取出各自的信息 
                teacher.setTid(resultSet.getInt("tid"));
                teacher.setTname(resultSet.getString("tname")); 
                Student student = new Student(); 
                student.setStuId(resultSet.getInt("stuid")); 		
                student.setStuName(resultSet.getString("stuname")); 
                //2.建立学生和老师之间的关系 
                students.add(student); 
            }
            teacher.setStudentList(students); 
            return teacher; 
        } catch (ClassNotFoundException e) {
    
     
            e.printStackTrace();
        } catch (SQLException throwables) {
    
     
            throwables.printStackTrace(); 
        } finally {
    
     
            try {
    
    
                //5.关闭资源 
                if (resultSet != null) {
    
     
                    resultSet.close();
                }if (pps != null) {
    
    
                    pps.close();
                }if (connection != null) {
    
     
                    connection.close();
                } 
            } catch (SQLException throwables) {
    
     
                throwables.printStackTrace();
            } 
        }
        return null; 
    } 
}

(4)定义测试类

public class Demo1 {
    
     
    public static void main(String[] args) {
    
    
        TeacherDao dao= new TeacherDaoImpl(); 
        Teacher teacher = dao.getById(1);
        System.out.println("老师姓名:"+teacher.getTname()); 
        List<Student> studentList = teacher.getStudentList(); 
        for (Student student : studentList) {
    
     
            System.out.println("\t studentname="+student.getStuName());
        } 
    } 
}

多对一(学生->老师)

数据表同上

实体类:

public class Student {
    
     
    private int stuid; 
    private String stuname; 
    //外键列一般不生成属性
    // private int teacherid;
    private Teacher teacher; //多对一,在多的存放一的数据
    public int getStuid() {
    
    
        return stuid;
    }
    public void setStuid(int stuid) {
    
     
        this.stuid = stuid; 
    }
    public String getStuname() {
    
    
        return stuname; 
    }
    public void setStuname(String stuname) {
    
    
        this.stuname = stuname; 
    }
    public Teacher getTeacher() {
    
     
        return teacher; 
    }
    public void setTeacher(Teacher teacher) {
    
     
        this.teacher = teacher; 
    }
}
public class Teacher {
    
     
    private int tid; 
    private String tname; 
    public int getTid() {
    
     
        return tid; 
    }
    public void setTid(int tid) {
    
     
        this.tid = tid; 
    }
    public String getTname() {
    
     
        return tname; 
    }
    public void setTname(String tname) {
    
     
        this.tname = tname; 
    }
}

接口:

//查询所有的学生(包含老师的信息) 
public List<Student> getAll();

实现类:

public List<Student> getAll() {
    
     
    //操作数据库 
    Connection connection =null; 
    PreparedStatement pps =null; 
    ResultSet resultSet =null;
    try {
    
    
        //1.加载驱动 
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.获得链接
        String userName="root"; 
        String passWord="123456";
        String url="jdbc:mysql://localhost:3306/yhp3?serverTimezone=UTC";
        connection = DriverManager.getConnection(url, userName, passWord);
        //3.定义sql,创建预状态通道(进行sql语句的发送)
        String sql="select * from student s,teacher t where s.teacherid=t.tid";
        pps = connection.prepareStatement(sql); 
        //执行sql
        resultSet = pps.executeQuery(); 
        List<Student> students=new ArrayList<>(); 
        while (resultSet.next()){
    
     
            //1.取出各自的信息 
            Student student = new Student(); 
            student.setStuId(resultSet.getInt("stuid")); 
            student.setStuName(resultSet.getString("stuname")); 
            Teacher teacher = new Teacher();
            teacher.setTid(resultSet.getInt("tid"));
            teacher.setTname(resultSet.getString("tname")); 
            //2.建立学生和老师之间的关系 
            student.setTeacher(teacher); 
            students.add(student); 
        }
        return students; 
    } catch (ClassNotFoundException e) {
    
     
        e.printStackTrace(); 
    } catch (SQLException throwables) {
    
     
        throwables.printStackTrace();
    } finally {
    
    
        try {
    
    
            //5.关闭资源 
            if (resultSet != null) {
    
     
                resultSet.close(); 
            }if (pps != null) {
    
     
                pps.close();
            }if (connection != null) {
    
    
                connection.close(); 
            } 
        } catch (SQLException throwables) {
    
     
            throwables.printStackTrace(); 
        }
    }
    return null;
}

测试类:

public static void main(String[] args) {
    
     
    TeacherDao dao= new TeacherDaoImpl(); 
    List<Student> students = dao.getAll(); 
    for (Student student : students) {
    
     	
        System.out.println(student.getStuName()+","+student.getTeacher().getTname()); 
    }
}

一对一(妻子丈夫)

数据表:

CREATE TABLE `husband` ( 
	`husid` int(11) NOT NULL AUTO_INCREMENT, 
	`husname` varchar(255) DEFAULT NULL, 
	PRIMARY KEY (`husid`) 
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 
INSERT INTO `husband` VALUES ('1', '邓超'); 
DROP TABLE IF EXISTS `wife`; 
CREATE TABLE `wife` ( 
	`wifeid` int(11) NOT NULL AUTO_INCREMENT, 
	`wifename` varchar(255) DEFAULT NULL, 
	`hid` int(11) DEFAULT NULL, 
	PRIMARY KEY (`wifeid`), 
	UNIQUE KEY `uq_wife_hid` (`hid`) 
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 
INSERT INTO `wife` VALUES ('1', '孙俪', '1'); 

实体类:

public class Husband {
    
     
	private int husid; 
	private String husname; 
	private Wife wife; //一对一,互相存放一的信息
	public int getHusid() {
    
     
		return husid; 
	}
	public void setHusid(int husid) {
    
     
		this.husid = husid; 
	}
	public String getHusname() {
    
     
		return husname; 
	}
	public void setHusname(String husname) {
    
     
		this.husname = husname;
	}
    public Wife getWife() {
    
     
        return wife; 
    }
    public void setWife(Wife wife) {
    
     
        this.wife = wife; 
    }
}
public class Wife {
    
     
    private int wifeid; 
    private String wifeName;
    private Husband husband; //一对一,互相存放一的信息
    public int getWifeid() {
    
     
        return wifeid;
    }
    public void setWifeid(int wifeid) {
    
    
        this.wifeid = wifeid;
    }
    public String getWifeName() {
    
     
        return wifeName; 
    }
    public void setWifeName(String wifeName) {
    
     
        this.wifeName = wifeName; 
    }
    public Husband getHusband() {
    
     
        return husband; 
    }
    public void setHusband(Husband husband) {
    
    
        this.husband = husband; 
    }
}

接口:

public interface WifeDao {
    
     
	//查询妻子信息(包含丈夫信息) 
	public Wife getWife(int wid); 
	//查询丈夫信息(包含妻子信息) 
	public Husband getHus(int hid); 
}

实现类:

public class WifeDaoImpl implements WifeDao {
    
     
	@Override 
	public Wife getWife(int wid) {
    
    
		//操作数据库 
		Connection connection =null; 
		PreparedStatement pps =null; 
		ResultSet resultSet =null; 
		try {
    
    
			//1.加载驱动 
			Class.forName("com.mysql.cj.jdbc.Driver"); 
			//2.获得链接 
			String userName="root"; 
			String passWord="123456"; 
			String url="jdbc:mysql://localhost:3306/yhp3?serverTimezone=UTC"; 
			connection = DriverManager.getConnection(url, userName, passWord); 
			//3.定义sql,创建预状态通道(进行sql语句的发送) 
			String sql="select * from wife w,husband h where w.hid=h.husid and w.wifeid=?";
			pps = connection.prepareStatement(sql); 
			pps.setInt(1,wid); 
			//执行sql 
			resultSet = pps.executeQuery(); 
			Wife wife = new Wife(); 
			while (resultSet.next()){
    
     
				//1.取出各自的信息 
				wife.setWifeId(resultSet.getInt("wifeid")); 
				wife.setWifeName(resultSet.getString("wifename")); 
				Husband husband = new Husband(); 
				husband.setHusId(resultSet.getInt("husid")); 
				husband.setHusName(resultSet.getString("husname")); 
				//2.建立妻子和丈夫之间的关系 
				wife.setHusband(husband); 
			}
			return wife; 
		} catch (ClassNotFoundException e) {
    
     
			e.printStackTrace(); 
		} catch (SQLException throwables) {
    
     
			throwables.printStackTrace(); 
		} finally {
    
     
			try {
    
    
				//5.关闭资源 
				if (resultSet != null) {
    
     
					resultSet.close(); 
				}if (pps != null) {
    
     
					pps.close(); 
				}if (connection != null) {
    
     
					connection.close(); 
				} 
			} catch (SQLException throwables) {
    
     
				throwables.printStackTrace(); 
			} 
		}
		return null; 
	}
	@Override
	public Husband getHus(int hid) {
    
     
		//操作数据库 
		Connection connection =null; 
		PreparedStatement pps =null; 
		ResultSet resultSet =null; 
		try {
    
    
			//1.加载驱动 
			Class.forName("com.mysql.cj.jdbc.Driver"); 
			//2.获得链接 
			String userName="root"; 
			String passWord="123456"; 
			String url="jdbc:mysql://localhost:3306/yhp3?serverTimezone=UTC"; 
			connection = DriverManager.getConnection(url, userName, passWord); 
			//3.定义sql,创建预状态通道(进行sql语句的发送) 
			String sql="select * from wife w,husband h where w.hid=h.husid and h.husid=?"; 
			pps = connection.prepareStatement(sql); 
			pps.setInt(1,hid); 
			//执行sql 
			resultSet = pps.executeQuery(); 
			Husband husband = new Husband(); 
			while (resultSet.next()){
    
     
				//1.取出各自的信息 
				Wife wife = new Wife(); 
				wife.setWifeId(resultSet.getInt("wifeid")); 
				wife.setWifeName(resultSet.getString("wifename")); 
				husband.setHusId(resultSet.getInt("husid")); 
				husband.setHusName(resultSet.getString("husname")); 
				//2.建立妻子和丈夫之间的关系 
				husband.setWife(wife); 
			}
			return husband; 
		} catch (ClassNotFoundException e) {
    
     
			e.printStackTrace(); 
		} catch (SQLException throwables) {
    
     
			throwables.printStackTrace(); 
		} finally {
    
     
			try {
    
    
				//5.关闭资源 
				if (resultSet != null) {
    
     
					resultSet.close(); 
				}if (pps != null) {
    
     
					pps.close(); 
				}if (connection != null) {
    
     
					connection.close(); 
				} 
			} catch (SQLException throwables) {
    
     
				throwables.printStackTrace(); 
			} 
		}
		return null; 
	}
}

测试类:

public static void main(String[] args) {
    
     
	WifeDaoImpl wifeDao = new WifeDaoImpl(); 
	Wife wife = wifeDao.getWife(1); 
	System.out.println(wife.getWifeName()+","+wife.getHusband().getHusName()); 
	Husband hus = wifeDao.getHus(1); 
	System.out.println(hus.getHusName()+","+hus.getWife().getWifeName()); 
} 

多对多(科目-学生)

数据表:

CREATE TABLE `middle` ( 
	`middleid` int(11) NOT NULL AUTO_INCREMENT, 
	`stuid` int(11) DEFAULT NULL, 
	`subid` int(11) DEFAULT NULL, 
	PRIMARY KEY (`middleid`) 
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8; 
-- ---------------------------- 

-- Records of middle 

-- ---------------------------- 
INSERT INTO `middle` VALUES ('1', '1', '1'); 
INSERT INTO `middle` VALUES ('2', '1', '2'); 
INSERT INTO `middle` VALUES ('3', '1', '3'); 
INSERT INTO `middle` VALUES ('4', '1', '5'); 
INSERT INTO `middle` VALUES ('5', '2', '2'); 
INSERT INTO `middle` VALUES ('6', '3', '2'); 
INSERT INTO `middle` VALUES ('7', '4', '2'); 
INSERT INTO `middle` VALUES ('8', '5', '2'); 
INSERT INTO `middle` VALUES ('9', '6', '2'); 
-- ---------------------------- 

-- Table structure for `student` 

-- ---------------------------- 
DROP TABLE IF EXISTS `student`; 
CREATE TABLE `student` ( 
	`stuid` int(11) NOT NULL AUTO_INCREMENT, 
	`stuname` varchar(255) DEFAULT NULL, 
	`teacherid` int(11) DEFAULT NULL, 
	PRIMARY KEY (`stuid`) 
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; 
-- ---------------------------- 

-- Records of student 

-- ---------------------------- 
INSERT INTO `student` VALUES ('1', '张三', '3'); 
INSERT INTO `student` VALUES ('2', '李四', '1'); 
INSERT INTO `student` VALUES ('3', '王五', '3'); 
INSERT INTO `student` VALUES ('4', '赵六', '1'); 
INSERT INTO `student` VALUES ('5', '花花', '1');
INSERT INTO `student` VALUES ('6', '潇潇', '2'); 
-- ---------------------------- 

-- Table structure for `subject` 

-- ---------------------------- 
DROP TABLE IF EXISTS `subject`; 
CREATE TABLE `subject` ( 
	`subid` int(11) NOT NULL AUTO_INCREMENT, 
	`subname` varchar(255) DEFAULT NULL, 
	PRIMARY KEY (`subid`) 
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; 
-- ---------------------------- 

-- Records of subject 

-- ---------------------------- 
INSERT INTO `subject` VALUES ('1', 'java'); 
INSERT INTO `subject` VALUES ('2', 'ui'); 
INSERT INTO `subject` VALUES ('3', 'h5'); 
INSERT INTO `subject` VALUES ('4', 'c'); 
INSERT INTO `subject` VALUES ('5', 'c++'); 
INSERT INTO `subject` VALUES ('6', 'c#');

实体类:

public class Subject {
    
     
	private int subid; 
	private String subname; 
	private List stulist; //存放学生信息
	public int getSubid() {
    
     
		return subid; 
	}
	public void setSubid(int subid) {
    
     
		this.subid = subid; 
	}
	public String getSubname() {
    
     
		return subname; 
	}
	public void setSubname(String subname) {
    
     
		this.subname = subname; 
	}
	public List getStulist() {
    
     
		return stulist; 
	}
	public void setStulist(List stulist) {
    
     
		this.stulist = stulist; 
	} 
}
public class Student {
    
     
	private int stuid; 
	private String stuname; 
	//外键列一般不生成属性 
	// private int teacherid; 
	private Teacher teacher; 
	private List<Subject> subjects; //存放科目信息
	public List<Subject> getSubjects() {
    
     
		return subjects; 
	}
	public void setSubjects(List<Subject> subjects) {
    
     
		this.subjects = subjects; 
	}
	public int getStuid() {
    
     
		return stuid; 
	}
	public void setStuid(int stuid) {
    
     
		this.stuid = stuid; 
	}
	public String getStuname() {
    
     
		return stuname; 
	}
	public void setStuname(String stuname) {
    
     
		this.stuname = stuname; 
	}
	public Teacher getTeacher() {
    
     
		return teacher; 
	}
	public void setTeacher(Teacher teacher) {
    
     
		this.teacher = teacher; 
	} 
}

接口:

public interface SubjectDao {
    
     
	//查询某个学生信息(查询出所学科目) 
	public Student findById(int id); 
	//查询某个科目以及对应的学生姓名 
	public Subject findBySubId(int subId); 
}

实现类:

public class SubjectDaoImpl implements SubjectDao {
    
     
	@Override 
	public Student findById(int id) {
    
     
		//操作数据库 
		Connection connection =null; 
		PreparedStatement pps =null; 
		ResultSet resultSet =null; 
		try {
    
    
			//1.加载驱动 
			Class.forName("com.mysql.cj.jdbc.Driver"); 
			//2.获得链接 
			String userName="root"; 
			String passWord="123456"; 
			String url="jdbc:mysql://localhost:3306/yhp3?serverTimezone=UTC"; 
			connection = DriverManager.getConnection(url, userName, passWord); 
			//3.定义sql,创建预状态通道(进行sql语句的发送) 
			String sql="select * from student s,subject su,middle m where 
			s.stuid=m.stuid and su.subid=m.subid and s.stuid=?"; 
			pps = connection.prepareStatement(sql); 
			pps.setInt(1,id); 
			//执行sql 
			resultSet = pps.executeQuery(); 
			Student student = new Student(); 
			List<Subject> subjects=new ArrayList<>(); 
			while (resultSet.next()){
    
     
				//1.取出各自的信息 
				student.setStuId(resultSet.getInt("stuid")); 
				student.setStuName(resultSet.getString("stuname")); 
				Subject subject = new Subject(); 
				subject.setSubId(resultSet.getInt("subid")); 
				subject.setSubName(resultSet.getString("subname")); 
				subjects.add(subject); 
			}
			//2.建立学生和科目之间的关系 
			student.setSubjects(subjects); 
			return student; 
		} catch (ClassNotFoundException e) {
    
     
			e.printStackTrace(); 
		} catch (SQLException throwables) {
    
     
			throwables.printStackTrace(); 
		} finally {
    
     
			try {
    
    
				//5.关闭资源 
				if (resultSet != null) {
    
     
					resultSet.close(); 
				}if (pps != null) {
    
     
					pps.close(); 
				}if (connection != null) {
    
     
					connection.close(); 
				} 
			} catch (SQLException throwables) {
    
     
				throwables.printStackTrace();
			} 
		}
		return null; 
	}
	@Override 
	public Subject findBySubId(int subId) {
    
     
		//操作数据库 
		Connection connection =null; 
		PreparedStatement pps =null; 
		ResultSet resultSet =null; 
		try {
    
    
			//1.加载驱动 
			Class.forName("com.mysql.cj.jdbc.Driver"); 
			//2.获得链接 
			String userName="root"; 
			String passWord="123456"; 
			String url="jdbc:mysql://localhost:3306/yhp3?serverTimezone=UTC"; 
			connection = DriverManager.getConnection(url, userName, passWord); 
			//3.定义sql,创建预状态通道(进行sql语句的发送) 
			String sql="select * from student s,subject su,middle m where 
			s.stuid=m.stuid and su.subid=m.subid and su.subid=?"; 
			pps = connection.prepareStatement(sql); 
			pps.setInt(1,subId); 
			//执行sql 
			resultSet = pps.executeQuery(); 
            Subject subject = new Subject(); 
            List<Student> studentList=new ArrayList<>(); 
            while (resultSet.next()){
    
     
                //1.取出各自的信息 
                Student student = new Student(); 
                student.setStuId(resultSet.getInt("stuid")); 
                student.setStuName(resultSet.getString("stuname")); 
                studentList.add(student); 
                subject.setSubId(resultSet.getInt("subid")); 
                subject.setSubName(resultSet.getString("subname")); 
            }
            //2.建立学生和科目之间的关系 
            subject.setStudentList(studentList); 
            return subject; 
        } catch (ClassNotFoundException e) {
    
     
            e.printStackTrace(); 
        } catch (SQLException throwables) {
    
     
            throwables.printStackTrace(); 
        } finally {
    
     
            try {
    
    
                //5.关闭资源 
                if (resultSet != null) {
    
     
                    resultSet.close(); 
                }
                if (pps != null) {
    
     
                    pps.close(); 
                }
                if (connection != null) {
    
     
                    connection.close();
                } 
            } catch (SQLException throwables) {
    
     
                throwables.printStackTrace(); 
            } 
        }
        return null; 
    }
}

测试类:

public static void main(String[] args) {
    
     
    SubjectDaoImpl subjectDao = new SubjectDaoImpl(); 
    /* Student student = subjectDao.findById(1); 
	System.out.println(student.getStuName()); 
	List<Subject> subjects = student.getSubjects(); 
	for (Subject subject : subjects) { 
		System.out.println("\t"+subject.getSubName()); 
	}*/ 
    Subject subject = subjectDao.findBySubId(2); 
    System.out.println(subject.getSubName()); 
    List<Student> studentList = subject.getStudentList(); 
    for (Student student : studentList) {
    
     
        System.out.println("\t"+student.getStuName()); 
    }
}

数据库的事务

一组要么同时执行成功,要么同时执行失败的SQL语句。是数据库操作的一个执行单元。

事务概述

数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。 事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性。事务是数据库运行中的逻辑工作单位,由DBMS中的事务管理子系统负责事务的处理。

事务开始于

- 连接到数据库上,并执行一条DML语句insert、update或delete

- 前一个事务结束后,又输入了另一条DML语句

事务结束于

- 执行commit或rollback语句。

- 执行一条DDL语句,例如create table语句,在这种情况下,会自动执行commit语句。

- 执行一条DDL语句,例如grant语句,在这种情况下,会自动执行commit。

- 断开与数据库的连接

- 执行了一条DML语句,该语句却失败了,在这种情况中,会为这个无效的DML语句执行rollback语句。

事务的四大特点(ACID)

- actomicity(原子性)

表示一个事务内的所有操作是一个整体,要么全部成功,要么全部失败

- consistency(一致性)

表示一个事务内有一个操作失败时,所有的更改过的数据都必须回滚到修改前状态

- isolation(隔离性)

事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看中间状态的数据。

- durability(持久性)

持久性事务完成之后,它对于系统的影响是永久性的。

JDBC中事务应用

如果JDBC连接处于自动提交模式,默认情况下,则每个SQL语句在完成后都会提交到数据库。

事务使您能够控制是否和何时更改应用于数据库。它将单个SQL语句或一组SQL语句视为一个逻辑单元,如果任何语句失败,则整个事务将失败。

要启用手动事务支持,而不是JDBC驱动程序默认使用的自动提交模式,请使用Connection对象的setAutoCommit()方法。如果将boolean false传递给setAutoCommit(),则关闭自动提交。我们可以传递一个布尔值true来重新打开它。

事务的提交和回滚

完成更改后,我们要提交更改,然后在连接对象上调用commit()方法,如下所示:

conn.commit();

否则,要使用连接名为conn的数据库回滚更新,请使用以下代码:

conn.rollback();
try{
    
    
    //Assume a valid connection object conn 
    conn.setAutoCommit(false); 
    Statement stmt = conn.createStatement(); 
    String SQL = "INSERT INTO Employees values (106, 20, 'Rita', 'Tez')"; 
    stmt.executeUpdate(SQL); 
    //Submit a malformed SQL statement that breaks 
    String SQL = "INSERTED IN Employees VALUES (107, 22, 'Sita', 'Singh')"; 
    stmt.executeUpdate(SQL); 
    // If there is no error. 
    conn.commit();
}catch(SQLException se){
    
     
    // If there is any error. 
    conn.rollback(); 
}

SavePoints

新的JDBC 3.0 Savepoint接口为您提供了额外的事务控制。

设置保存点时,可以在事务中定义逻辑回滚点。如果通过保存点发生错误,则可以使用回滚方法来撤消所有更改或仅保存在保存点之后所做的更改。即保存点之前的会执行成功。

Connection对象有两种新的方法来帮助您管理保存点:

- setSavepoint(String savepointName):定义新的保存点。它还返回一个Savepoint对象。

- releaseSavepoint(Savepoint savepointName):删除保存点。请注意,它需要一个Savepoint对象作为参数。此对象通常是由setSavepoint()方法生成的保存点。

try{
    
    
    //Assume a valid connection object conn 
    conn.setAutoCommit(false); 
    Statement stmt = conn.createStatement(); 
    String SQL = "INSERT INTO Employees VALUES (106, 20, 'Rita', 'Tez')"; 
    stmt.executeUpdate(SQL); 
    Savepoint savepoint1 = conn.setSavepoint("Savepoint1"); 
    String SQL = "INSERTED IN Employees VALUES (107, 22, 'Sita', 'Tez')"; 
    stmt.executeUpdate(SQL); 
    conn.commit(); 
}catch(SQLException se){
    
     
    conn.rollback(savepoint1); 
}

1、要取消掉JDBC的自动提交:void setAutoCommit(boolean autoCommit)

2、执行各个SQL语句,加入到批处理之中

3、如果所有语句执行成功,则提交事务 commit();如果出现了错误,则回滚:rollback()

try {
    
    
    connection.setAutoCommit(false); 
    add(connection); 
    // int i = 1/0; 
    sub(connection); 
    System.out.println("==============="); 
    connection.commit(); 
} catch (Exception e) {
    
     
    // TODO Auto-generated catch block 
    System.out.println("---------------"); 
    try {
    
    
        connection.rollback(); 
    } catch (SQLException e1) {
    
    
        // TODO Auto-generated catch block 
        e1.printStackTrace(); 
    } 
} 

以上就是java代码利用jdbc操作数据库的最简单版本,数据库事务通常要借助补捉异常语句。

JDBC批处理

批量处理允许您将相关的SQL语句分组到批处理中,并通过对数据库的一次调用提交它们。

当您一次向数据库发送多个SQL语句时,可以减少连接数据库的开销,从而提高性能。

Statement批处理

以下是使用语句对象的批处理的典型步骤序列

- 使用createStatement()方法创建Statement对象。

- 使用setAutoCommit()将auto-commit设置为false 。

- 使用addBatch()方法在创建的语句对象上添加您喜欢的SQL语句到批处理中。

- 在创建的语句对象上使用executeBatch()方法执行所有SQL语句。

- 最后,使用commit()方法提交所有更改。

Statement stmt = conn.createStatement(); 
conn.setAutoCommit(false); 
//sql1 
String SQL = "INSERT INTO Employees (id, first, last, age) VALUES(200,'Zia', 'Ali', 30)"; 
stmt.addBatch(SQL); 
//sql2 
String SQL = "INSERT INTO Employees (id, first, last, age) VALUES(201,'Raj', 'Kumar', 35)"; 
stmt.addBatch(SQL); 
//sql3 
String SQL = "UPDATE Employees SET age = 35 WHERE id = 100"; 
stmt.addBatch(SQL); 
int[] count = stmt.executeBatch(); 
conn.commit();

PreparedStatement批处理

  1. 使用占位符创建SQL语句。

  2. 使用prepareStatement() 方法创建PrepareStatement对象。

  3. 使用setAutoCommit()将auto-commit设置为false 。

  4. 使用addBatch()方法在创建的语句对象上添加您喜欢的SQL语句到批处理中。

  5. 在创建的语句对象上使用executeBatch()方法执行所有SQL语句。

  6. 最后,使用commit()方法提交所有更改。

String SQL = "INSERT INTO Employees (id, first, last, age) VALUES(?, ?, ?, ?)"; 
PreparedStatement pstmt = conn.prepareStatement(SQL); 
conn.setAutoCommit(false); 
// Set the variables 
pstmt.setInt( 1, 400 ); 
pstmt.setString( 2, "Pappu" ); 
pstmt.setString( 3, "Singh" ); 
pstmt.setInt( 4, 33 ); 
// Add it to the batch 
pstmt.addBatch(); 
// Set the variables 
pstmt.setInt( 1, 401 ); 
pstmt.setString( 2, "Pawan" ); 
pstmt.setString( 3, "Singh" ); 
pstmt.setInt( 4, 31 ); 
// Add it to the batch 
pstmt.addBatch(); 
//add more batches 
//Create an int[] to hold returned values 
int[] count = stmt.executeBatch(); 
//Explicitly commit statements to apply changes 
conn.commit(); 

反射处理结果集

接口:

//查询所有的学生信息 
public List<Student> findallstudent(Class cla); 

实现类:

@Override 
public List<Student> findallstudent(Class cla) {
    
     
    Connection con = null; 
    PreparedStatement pps = null; 
    ResultSet rs =null; 
    try {
    
    
        Class.forName("com.mysql.jdbc.Driver"); 
        con = DriverManager.getConnection("jdbc:mysql://localhost:3306/yhp", "root", "123456"); 
        List list=new ArrayList(); 
        String sqla = "select * from student";
        pps = con.prepareStatement(sqla); 
        rs = pps.executeQuery(); 
        //得到数据库中的所有的列有哪些? 
        ResultSetMetaData metaData = rs.getMetaData();//返回数据库中的相关信息 
        int count=metaData.getColumnCount();//得到列数 
        String[] columnnames=new String[count]; 
        for (int i = 0; i < count; i++) {
    
     
            // System.out.println(metaData.getColumnName(i+1));//列的位置从1开始 
            columnnames[i]=metaData.getColumnName(i+1); 
        }
        //得到实体类中的所有的方法 
        Method[] methods =cla.getDeclaredMethods(); 
        while(rs.next()){
    
     
            Object s=cla.newInstance();//调取无参构造创建对象 
            for (String columnname : columnnames) {
    
     
                String name="set"+columnname;//setstuid 
                for (Method method : methods) {
    
     
                    if(method.getName().equalsIgnoreCase(name)){
    
     
                        method.invoke(s,rs.getObject(columnname));//执行了对应的set方法 
                        break; 
                    } 
                } 
            }
            list.add(s); 
        }
        System.out.println("执行成功"); 
        return list; 
    } catch (Exception e) {
    
     
        e.printStackTrace(); 
    } finally {
    
     
        try {
    
    
            if (rs != null) {
    
     
                rs.close(); 
            }
            if (pps != null) {
    
     
                pps.close(); 
            }
            if (con != null) {
    
     
                con.close(); 
            } 
        } catch (SQLException e) {
    
     
            e.printStackTrace(); 
        } 
    }
    return null; 
}

工具类的定义:

public class DbUtils {
    
     
    //1.定义需要的工具类对象 
    protected Connection connection=null; 
    protected PreparedStatement pps=null; 
    protected ResultSet rs=null; 
    protected int k=0;//受影响的行数 
    private String url="jdbc:mysql://localhost:3306/yhp"; 
    private String username="root"; 
    private String password="123456"; 
    //2.加载驱动 
    static{
    
     
        try {
    
    
            Class.forName("com.mysql.cj.jdbc.Driver"); 
        } catch (ClassNotFoundException e) {
    
     
            e.printStackTrace(); 
        } 
    }
    //3.获得连接 
    protected Connection getConnection(){
    
     
        try {
    
    
            connection=DriverManager.getConnection(url,username,password); 
        } catch (SQLException e) {
    
     
            e.printStackTrace(); 
        }
        return connection; 
    }
    //4.创建通道 
    protected PreparedStatement getPps(String sql){
    
     
        try {
    
    
            getConnection();//insert into users values(?,?,?,?,) 
            pps=connection.prepareStatement(sql); 
        } catch (SQLException e) {
    
     
            e.printStackTrace(); 
        }
        return pps; 
    }
    //5.给占位符赋值 list中保存的是给占位符所赋的值 
    private void setParams(List list){
    
     
        try {
    
    
            if(list!=null&&list.size()>0){
    
     
                for (int i=0;i<list.size();i++) {
    
     
                    pps.setObject(i+1,list.get(i)); 
                } 
            } 
        } catch (SQLException e) {
    
     
            e.printStackTrace(); 
        } 
    }
    //6.增删改调取的方法
    protected int update(String sql,List params){
    
     
        try {
    
    
            getPps(sql); 
            setParams(params); 
            k= pps.executeUpdate(); 
        } catch (SQLException e) {
    
     
            e.printStackTrace();
        }
        return k; 
    }
    //7.查询的时候调取一个方法 
    protected ResultSet query(String sql,List list){
    
     
        try {
    
    
            getPps(sql); 
            setParams(list); 
            rs=pps.executeQuery(); 
            return rs; 
        } catch (SQLException e) {
    
     
            e.printStackTrace(); 
        }
        return null; 
    }
    //8.关闭资源 
    protected void closeall(){
    
     
        try {
    
    
            if (rs != null) {
    
     
                rs.close(); 
            }
            if (pps != null) {
    
     
                pps.close(); 
            }
            if (connection != null) {
    
     
                connection.close(); 
            } 
        } catch (SQLException e) {
    
     
            e.printStackTrace(); 
        } 
    } 
}

properties文件保存数据库信息-特点:key-value存储方式

db.properties

driver=com.mysql.jdbc.Driver 
url=jdbc:mysql://localhost:3306/yhp 
user=root 
password=123456

工具类中读取属性文件

InputStream inputStream = 当前类名.class.getClassLoader().getResourceAsStream("db.properties"); 
Properties properties = new Properties(); 
properties.load(inputStream); 
dirverName = properties.getProperty("driver"); 
url = properties.getProperty("url"); 
username = properties.getProperty("user"); 
password = properties.getProperty("password");

方式2:

static{
    
     
    //参数只写属性文件名即可,不需要写后缀 
    ResourceBundle bundle = ResourceBundle.getBundle("db"); 
    driver = bundle.getString("driver"); 
    url = bundle.getString("url"); 
    username = bundle.getString("user"); 
    password = bundle.getString("password"); 
} 

说明:

使用ResourceBundle访问本地资源

在设计时,我们往往需要访问一些适合本地修改的配置信息,如果作为静态变量,那么每次修改都需要重新编译一个class,.confifig保存此类信息并不适合,这时我们需要ResourceBundle。

通过ResourceBundle,我们需要访问位于/WEB-INF/classes目录下的一个后缀名为properties的文本类型文件,从里面读取我们需要的值。

连接池

自定义连接池

数据连接池原理

连接池基本的思想是在系统初始化的时候,将数据库连接作为对象存储在内存中,当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。而连接的建立、断开都由连接池自身来管理。同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等等,也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。

自定义连接池

我们可以通过自定义的方式实现连接池!分析连接池类应该包含特定的属性和方法!

属性: 集合 放置Connection

方法: 获取连接方法

​ 回收连接方法

具体实现代码:

public class Pool{
    
    
    static LinkedList<Connection> list = new LinkedList<Connection>(); 
    static{
    
     
        for (int i = 0; i < 10; i++) {
    
     
            Connection connection = JDBCUtils.newInstance().getConnection(); 
            list.add(connection); 
        } 
    }
    /**
\* 从连接池子中获取连接的方式 
\* @return 
*/ 
    public static Connection getConnection(){
    
     
        if (list.isEmpty()) {
    
     
            //JDBCUtils类是自定义类,封装了连接数据库的信息代码 
            Connection connection = JDBCUtils.newInstance().getConnection(); 
            list.addLast(connection); //追加到列表末尾
        }
        Connection conn = list.removeFirst(); 
        return conn; 
    }
    /**
\* 返回到连接池子中 
*/ 
    public static void addBack(Connection conn){
    
     
        if (list.size() >= 10) {
    
     
            try {
    
    
                conn.close(); 
            } catch (SQLException e) {
    
     
                // TODO Auto-generated catch block 
                e.printStackTrace(); 
            } 
        }else{
    
    
            list.addLast(conn); //10 
        } 
    }
    /**
\* 获取连接池子中连接数量的方法 
*/ 
    public static int getSize(){
    
     
        return list.size(); 
    } 
}

java规范实现连接池

Java为连接池实现提供了一个规范(接口),规范的写法,我们需要实现DataSource接口!

但是实现DataSource接口有一个弊端,没有提供回收链接方法!这里我们将使用装饰者模式!

装饰Connection!具体实现代码如下:

  1. 创建装饰Connection
public class MyConnection implements Connection{
    
     
    //将被装饰者导入 
    private Connection conn; 
    private LinkedList<Connection> list; 
    public MyConnection(Connection conn, LinkedList<Connection> list) {
    
     
        super(); 
        this.conn = conn; 
        this.list = list; 
    }
    @Override 
    public <T> T unwrap(Class<T> iface) throws SQLException {
    
     
        return conn.unwrap(iface); 
    }
    @Override 
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
    
     
        return conn.isWrapperFor(iface); 
    }
    @Override 
    public Statement createStatement() throws SQLException {
    
     
        return conn.createStatement(); 
    }
    @Override 
    public PreparedStatement prepareStatement(String sql) throws SQLException {
    
     
        return conn.prepareStatement(sql); 
    }
    @Override 
    public CallableStatement prepareCall(String sql) throws SQLException {
    
     
        return null; 
    }
    @Override 
    public String nativeSQL(String sql) throws SQLException {
    
     
        return null; 
    }
    @Override 
    public void setAutoCommit(boolean autoCommit) throws SQLException {
    
     
    }
    @Override 
    public boolean getAutoCommit() throws SQLException {
    
     
        return false; 
    }
    @Override
    public void commit() throws SQLException {
    
     
        conn.commit(); 
    }
    @Override 
    public void rollback() throws SQLException {
    
     
        conn.rollback(); 
    }
    @Override 
    public void close() throws SQLException {
    
     
        list.addLast(conn); 
    }
    ... 
}

基于规范实现的连接池

/**
\* 创建一个规范的连接池子 
*/ 
public class DataSourcePool implements DataSource{
    
     
    static LinkedList<Connection> list = new LinkedList<Connection>(); 
    static{
    
     
        for (int i = 0; i < 10; i++) {
    
     
            Connection connection = JDBCUtils.newInstance().getConnection(); 
            list.add(connection); 
        } 
    }
    public static int getSize(){
    
     
        return list.size(); 
    }
    @Override 
    public Connection getConnection() throws SQLException {
    
     
        Connection conn = list.removeFirst(); 
        MyConnection conn1 = new MyConnection(conn, list); 
        return conn1; 
    }
    @Override 
    public PrintWriter getLogWriter() throws SQLException {
    
     
        return null; 
    }
    @Override 
    public void setLogWriter(PrintWriter out) throws SQLException {
    
     
    }
    @Override 
    public void setLoginTimeout(int seconds) throws SQLException {
    
     
    }
    @Override
    public int getLoginTimeout() throws SQLException {
    
     
        return 0; 
    }
    @Override 
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
    
     
        return null; 
    }
    @Override 
    public <T> T unwrap(Class<T> iface) throws SQLException {
    
     
        return null; 
    }
    @Override 
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
    
     
        return false; 
    }
    @Override 
    public Connection getConnection(String username, String password) throws 
        SQLException {
    
     
        return null; 
    } 
}

最小连接数:

是数据库一直保持的数据库连接数,所以如果应用程序对数据库连接的使用量不大,将有大量的数据库资源被浪费。

初始化连接数:

连接池启动时创建的初始化数据库连接数量。

最大连接数

是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求被加入到等待队列中。

最大等待时间:

当没有可用连接时,连接池等待连接被归还的最大时间,超过时间则抛出异常,可设置参数为0或者负数使得无限等待(根据不同连接池配置)。

在这里插入图片描述

注1:在DBCP连接池的配置中,还有一个maxIdle的属性,表示最大空闲连接数,超过的空闲连接将被释放,默认值为8。对应的该属性在Druid连接池已不再使用,配置了也没有效果,c3p0连接池则没有对应的属性。

注2:数据库连接池在初始化的时候会创建initialSize个连接,当有数据库操作时,会从池中取出一个连接。如果当前池中正在使用的连接数等于maxActive,则会等待一段时间,等待其他操作释放掉某一个连接,如果这个等待时间超过了maxWait,则会报错;如果当前正在使用的连接数没有达到maxActive,则判断当前是否空闲连接,如果有则直接使用空闲连接,如果没有则新建立一个连接。在连接使用完毕后,不是将其物理连接关闭,而是将其放入池中等待其他操作复用。

DBCP连接池

DBCP是一个依赖Jakarta commons-pool对象池机制的数据库连接池。DBCP可以直接的在应用程序中使用,Tomcat的数据源使用的就是DBCP。

DBCP连接池的使用

导入相应jar包

mysql-jdbc.jar

commons-dbcp.jar

commons-pool.jar

硬编码使用DBCP

所谓的硬编码方式就是在代码中添加配置

@Test 
public void testHard() throws SQLException{
    
     
    // 硬编码 使用DBCP连接池子 
    BasicDataSource source = new BasicDataSource(); 
    //设置连接的信息 
    source.setDriverClassName("com.mysql.jdbc.Driver"); 
    source.setUrl("jdbc:mysql://localhost:3306/day2"); 
    source.setUsername("root"); 
    source.setPassword("111"); 
    Connection connection = source.getConnection();
    String sql = "select * from student"; 
    Statement createStatement = connection.createStatement(); 
    ResultSet executeQuery = createStatement.executeQuery(sql); 
    while (executeQuery.next()) {
    
     
        System.out.println(executeQuery.getString(2));
    }
    connection.close(); //回收 
}
软编码使用DBCP

所谓的软编码,就是在项目中添加配置文件,这样就不需要每次代码中添加配合!

1、项目中添加配置

文件名称: info.properties

文件位置: src下

#连接设置 
driverClassName=com.mysql.jdbc.Driver 
url=jdbc:mysql://localhost:3306/day2 
username=root 
password=111 
\#<!-- 初始化连接 --> 
initialSize=10 
\#最大连接数量 
maxActive=50 
\#<!-- 最大空闲连接 --> 
maxIdle=20 
\#<!-- 最小空闲连接 --> 
minIdle=5 
\#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 --> 
maxWait=6000

2、DButils工具类 代码实现、

//1.创建dbcp的工具类对象 
static BasicDataSource datasource=new BasicDataSource(); 
//2.加载驱动 
static {
    
     
    try {
    
    
        //加载属性文件 
        //1.使用工具类 ,参数是属性文件的文件名(不要加后缀) 
        ResourceBundle bundle = ResourceBundle.getBundle("db"); 
        driverClass = bundle.getString("driverclass"); 
        url = bundle.getString("url"); 
        username = bundle.getString("uname"); 
        password = bundle.getString("upass"); 
        init=bundle.getString("initsize"); 
        //2.将驱动地址等信息传递给dbcp 
        datasource.setDriverClassName(driverClass); 
        datasource.setUrl(url);
        datasource.setUsername(username); 
        datasource.setPassword(password); 
        datasource.setInitialSize(Integer.parseInt(init)); 
    } catch (Exception e) {
    
    
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
    } 
}
//3.获得连接 
public static Connection getConn() {
    
     
    try {
    
    
        con= datasource.getConnection(); 
    } catch (SQLException e) {
    
     
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
    }
    return con; 
} 

C3P0连接池

c3p0是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。

c3p0与dbcp区别:

1.dbcp没有自动回收空闲连接的功能;c3p0有自动回收空闲连接功能

2.dbcp需要手动设置配置文件;c3p0不需要手动设置

实现方式

1 .手动设置 ComboPooledDataSource

2 加载配置文件方式

src/c3p0-config.xml(文件名固定)

ComboPooledDataSource cpds = new ComboPooledDataSource(); 

加载 文件中 <default-config>中的配置

ComboPooledDataSource cpds = new ComboPooledDataSource("aaa"); 

加载 <named-config name=“aaa”> 中的配置

实现步骤

1、导入jar包

c3p0-0.9.1.2.jar

mysql-connector-java-5.0.8.jar

2、添加配置文件

c3p0是在外部添加配置文件,工具直接进行应用,因为直接引用,所以要求固定的命名和文件位置

文件位置: src

文件命名:c3p0-confifig.xml/c3p0-confifig.properties

<?xml version="1.0" encoding="utf-8"?> 
<c3p0-config> 
    <!-- 默认配置,如果没有指定则使用这个配置 --> 
    <default-config> 
        <!-- 基本配置 --> 
        <property name="driverClass">com.mysql.jdbc.Driver</property> 
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day2</property> 
        <property name="user">root</property> 
        <property name="password">111</property> 
        <!--扩展配置--> 
        <!-- 连接超过30秒报错--> 
        <property name="checkoutTimeout">30000</property> 
        <!--30秒检查空闲连接 --> 
        <property name="idleConnectionTestPeriod">30</property> 
        <property name="initialPoolSize">10</property> 
        <!-- 30秒不适用丢弃--> 
        <property name="maxIdleTime">30</property> 
        <property name="maxPoolSize">100</property> 
        <property name="minPoolSize">10</property> 
        <property name="maxStatements">200</property> 
    </default-config> 
    <!-- 命名的配置 --> 
    <named-config name="abc"> 
        <property name="driverClass">com.mysql.jdbc.Driver</property> 
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day2</property> 
        <property name="user">root</property> 
        <property name="password">111</property> 
        <!-- 如果池中数据连接不够时一次增长多少个 --> 
        <property name="acquireIncrement">5</property> 
        <property name="initialPoolSize">20</property> 
        <property name="minPoolSize">10</property> 
        <property name="maxPoolSize">40</property> 
        <property name="maxStatements">20</property> 
        <property name="maxStatementsPerConnection">5</property> 
    </named-config> 
</c3p0-config>

注意: c3p0的配置文件内部可以包含命名配置文件和默认配置文件!默认是选择默认配置!如果需要切换命名配置可以在创建c3p0连接池的时候填入命名即可!

3、定义代码
Connection con=null; 
ComboPooledDataSource db=new ComboPooledDataSource("abc"); 
public Connection getCon(){
    
     
    try {
    
    
        con=db.getConnection(); 
        System.out.println("初始化的链接数量:"+db.getInitialPoolSize()); 
    } catch (SQLException e) {
    
     
        e.printStackTrace(); 
    }
    return con; 
}

Druid(德鲁伊)连接池

阿里出品,淘宝和支付宝专用数据库连接池,但它不仅仅是一个数据库连接池,它还包含一个ProxyDriver(代理驱动),一系列内置的JDBC组件库,一个SQL Parser(sql解析器)。支持所有JDBC兼容的数据库,包括Oracle、MySql、Derby、Postgresql、SQL Server、H2等等。

Druid针对Oracle和MySql做了特别优化,比如Oracle的PS Cache内存占用优化,MySql的ping检测优化。

Druid提供了MySql、Oracle、Postgresql、SQL-92的SQL的完整支持,这是一个手写的高性能SQLParser,支持Visitor模式,使得分析SQL的抽象语法树很方便。简单SQL语句用时10微秒以内,复杂SQL用时30微秒。

通过Druid提供的SQL Parser可以在JDBC层拦截SQL做相应处理,比如说分库分表、审计等。Druid防御SQL注入攻击的WallFilter就是通过Druid的SQL Parser分析语义实现的。

Druid 是目前比较流行的高性能的,分布式列存储的OLAP框架(具体来说是MOLAP)。它有如下几个特点:

一. 亚秒级查询

druid提供了快速的聚合能力以及亚秒级的OLAP查询能力,多租户的设计,是面向用户分析应用的理想方式。

二.实时数据注入

druid支持流数据的注入,并提供了数据的事件驱动,保证在实时和离线环境下事件的实效性和统一性

三.可扩展的PB级存储

druid集群可以很方便的扩容到PB的数据量,每秒百万级别的数据注入。即便在加大数据规模的情况下,也能保证时其效性

四.多环境部署

druid既可以运行在商业的硬件上,也可以运行在云上。它可以从多种数据系统中注入数据,包括hadoop,spark,kafka,storm和samza等

五.丰富的社区

druid拥有丰富的社区,供大家学习

使用步骤

1、导入jar包
2、编写工具列
/**
\* 阿里的数据库连接池 
\* 性能最好的 
\* Druid 
\* */ 
public class DruidUtils {
    
     
    //声明连接池对象 
    private static DruidDataSource ds; 
    static{
    
     
        ///实例化数据库连接池对象 
        ds=new DruidDataSource(); 
        //实例化配置对象 
        Properties properties=new Properties(); 
        try {
    
    
            //加载配置文件内容 
            properties.load(DruidUtils.class.getResourceAsStream("dbcpconfig.properties")); 
            //设置驱动类全称 
            ds.setDriverClassName(properties.getProperty("driverClassName")); 
            //设置连接的数据库 
            ds.setUrl(properties.getProperty("url")); 
            //设置用户名 
            ds.setUsername(properties.getProperty("username")); 
            //设置密码 
            ds.setPassword(properties.getProperty("password")); 
            //设置最大连接数量 
            ds.setMaxActive(Integer.parseInt(properties.getProperty("maxActive"))); 
        } catch (IOException e) {
    
     
            // TODO Auto-generated catch block 
            e.printStackTrace(); 
        } 
    }
    //获取连接对象 
    public static Connection getConnection() {
    
     
        try {
    
    
            return ds.getConnection(); 
        } catch (SQLException e) {
    
     
            // TODO Auto-generated catch block 
            e.printStackTrace(); 
        }
        return null; 
    } 
} 

注:在Druid连接池的配置中,driverClassName可配可不配,如果不配置会根据url自动识别dbType(数据库类型),然后选择相应的driverClassName。

猜你喜欢

转载自blog.csdn.net/yang642019851/article/details/114387493