0基础入门学习数据库,实战JDBC-MySql。内附*牛客题库经典例题与讲解

995 阅读26分钟

数据库

观察现实生活,大大小小的软件应用最基本都会有用户数据的存储。再说的大一点,不都说当今社会是一个数据流量为王的时代。

通过上学期对java面向对象语言的学习,我们来思考两个问题?

1.不使用数据库,我们要如何展开对数据的操作呢?(增删改查)

相信大家肯定马上就想到Java中的序列化

ObjectOutputStream -> Java对象 -> 序列化到文件当中。 (一顿操作猛虎如,结果是真虎。)

1.1序列化的步骤(写入)

第一步:创建FileOutputStream,FileOutputStream把字节写入文件
FileOutputStream fs = new FileOutputStream("foo.ser”);\\如果文件footsore不存在,会自动创建该文件
第二步:创建ObjectOutputStream,ObjectOutputStream把对象转换成可以写入串流的数据
ObjectOutputStream os = new ObjectOutputStream(fs);\\fs是一个FileOutputStream对象
第三步:写入对象,ObjectOutputStream调用writeObject方法把对象打成串流送到FileOutputStream来写入文件
os.writeObject(myBox);\\myBox必须是一个实现了Serializable接口的类,是可以序列化的
第四步:关闭ObjectOutputStream
os.close();\\关闭所关联的输出串流

1.2反序列化的步骤(读出:还原对象)

第一步:创建FileInputStream,读取文件字节,如果文件不存在会抛出异常
FileInputStream fileStream = new FileInputStream("foo.ser”);\\如果文件不存在,会抛出异常
第二步:创建ObjectInputStream,将自己转换成对象的stream
ObjectInputStream os=new ObjectInputStream(fileStream);
第三步:读取对象,会按照os.writeObject(xx)存储的顺序读取,与写入顺序相同
Object one=os.readObject();Object two=os.readObject();
第四步:转换对象类型。默认返回的是Object类型,需要强转换为存储时的类型
GamecCharacter test1=(GameCharacter) one;GamecCharacter test2=(GameCharacter) two;
第五步:关闭ObjectInputStream
os.close();

1.3小结

上述操作对数据的操作当然是可行的,但是回看我们最原始的需求,我们仅仅只是想读取数据,还没涉及到对数据“真正的操作”(需要动脑设计表等等...),对此,如何进行高效的数据管理,提出了数据库技术。

可得好处:

  1. 方便、高效(最直观的感受)
  2. 数据应用效率高

这里所提到的数据库技术 ≠ 数据库。

1.4 数据库技术

**数据:1.**描述事物的符号记录_(Eg: 网页上看到卖家秀:图片。)_

          2.是数据库存储的基本对象。(这个概念结合上面那波 new java对象 read对象 write对象)

数据库:是长期储存在计算机内、有组织的、可共享的大量数据集合(可以理解为对象的集合)

数据库和数据:数据库中的数据是按一定的数据模型组织、描述和储存的,具有较少的冗余度、较高的数据独立性和易拓展性,可为各种用户共享。数据库实际上在硬盘上以文件的形式存在。

数据库管理系统:是用户和操作系统之间的一层数据管理软件

目的:为了科学地组织和存储数据、高效地获取和维护数据。

主要功能:数据定义、数据组织存储和管理、数据操纵、数据库的事务管理和运行管理、数据库的建立和维护功能...(具体点,它负责去执行sql语句,通过执行sql来操作数据库中的数据)

具体流程:数据库管理系统 -(执行)-> SQL语句 -(操作)-> 数据库

常见的DBMS有:MySQL Oracle DB2 Sybase SqlServer...

**数据库系统:**包含了数据库、数据库管理系统、应用程序和数据库管理者组成的存储、管理、处理和维护数据的系统。一般都简称数据库系统为数据库。

SQL: 结构化查询语言,是一门标准通用的语言。``标准的sql适合于所有的数据库产品。SQL属于高级语言。只要能看懂英语单词的,写出来的sql语句,很简单,可以读懂什么意思。 SQL语句在执行的时候,实际上内部也会先进行编译,然后再执行sql。(sql语句的编译由数据库管理系统完成。)

2.对比数据库系统,文件系统的缺点是什么?

**文件系统:**操作系统自带的数据管理软件,同样以文件存储在硬件设备中。

(window的同学,在编辑文档的时候,另存为的时候是不是要让你选择保存的路径,这就是文件系统的其中一套流程。“由文件名访问,按记录进行存取”)

结合我们实际的案例想一想这套体系下存在的缺点:

(1)数据和数据之间共享性差,冗余度也大。

这里的共享性应该好理解,想象这里有两个wordword中存在有相同的文字,之间无法实现共享性,冗余度大。(在数据库下:文档A有   文档B可以没有 ,冗余度可以理解多余 无意义的东西)

(2)数据独立性差。(难以对文件内的数据进行二次或多次操作)

大家可以借助下面的表格深化理解,文件系统≈大型数据库,不同的地方即上面的缺点的合理解释。

第二节课

回顾数据库概念

1.关系数据库内由什么构成?

表:table = 关系(关系数据库),是数据库的基本组成单元,所有的数据都以表格的形式组织。

表中的每行(即数据库中的每条记录)就是一个元组,每列就是一个属性。

思考:每一列内的数据应该包括哪些?      数据、数据类型、约束条件

2. 开始建表操作

# 用户:root  密码:111  端口:3306 //注意这是我本机的,需要根据你们自己的配置进行修改。

登录:mysql -uroot -p111 //111是我的密码,修改成你们设定的密码。

# 以下是对数据库的操作笔记: 

查看数据库:show databases;     //默认数据库,四条记录 

创建: create database kexie;  //新建一个数据库命名为kexie
*删除: drop database kexie;  //删除之前新建的kexie库

使用:use kexie;      //要对kexie这个数据库展开操作 = use

查看数据库路径:show global variables like "%datadir%";    // 考虑到有些同学会忘记mysql存放的路径,这是关于查找路径的指令,右边是我本机的mysql的路径:D:\mysql80\data\

# 复制上面路径,进入到kexie文件,把kexie.sql复制到kexie文件中去。

# 以下是对数据库中表的操作笔记: 

查看表:show tables;    //这时候因为没有结果初始化(可以理解成"提醒"数据库系统),因此还是空表。 

初始化数据:mysql> source D:\mysql80\data\kexie\kexie.sql  //提示数据库系统你"有"了

# 退出重进。再回顾上面的操作:登录+use kexie 数据库切换要记得

查看表结构:show tables;  //好使了,可以看到数据库中共有三张表,分别为dept,emp,salgrade
*新建表:create table de; //表示新建了一张表,名为de,注意新建名与其他表名不可相同。
*合并操作:查看创建表的语句:show create table emp;

kexie.sql百度网盘链接如下:_链接:https://pan.baidu.com/s/1E-i5mVIelSJPLD506-2i-Q 提取码:igsi_

第三节课

在上节课我们执行的语句就称为sql语句。那么Sql语句包括增删改查。集合sql语句的文件称为sql脚本

注意:直接使用source命令可以执行sql脚本,直接拖拽进入也可以。但当sql脚本中的数据量太大的时候,无法打开,需要使用source 文件路径。_(记事本打不开的情况"卡") _

*1.那么SQL语句那么多,如何进行分类呢?

DQL(数据查询语言): 查询语句,凡是select语句都是DQL。 
DML(数据操作语言):insert delete update,对表当中的数据进行增删改。
DDL(数据定义语言):create drop alter,对表结构的增删改。
TCL(事务控制语言):commit提交事务,rollback回滚事务。(TCL中的T是Transaction)
DCL(数据控制语言): grant授权、revoke撤销权限等。

数据库系统的三级模式

外模式:又称子模式或用户模式。一个数据库可以有多个外模式(设限 模式的子集)
模式:又称逻辑模式。一个数据库只有一个模式。(DBMS)
内模式:又称存储模式。一个数据库只有一个内模式。(database)

以图书管理系统为例:外模式:借图书的人和采购图书的人的“需求” 模式:DBMS 数据库管理系统内模式:database数据库

2. 对表进行操作

2.1查看表结构

查看数据库中表dept结构:desc dept;   #部门表

查看数据库中表emp结构:desc emp;     #员工表

查看数据库中表emp结构:desc salgrade;   #公司等级表

属性名解释:dept部门表中:DEPTNO 部门编号 DNAME 部门名称 LOC 部门位置 emp员工表中:EMPNO 员工编号 ENAME 员工姓名 JOB 工作岗位 MGR 上级领导编号 HIREDATE 入职日期 SAL 月薪 COMM 补助/津贴 DEPTNO 部门编号salgrade公司等级表中:GRADE 等级 LOSAL 最低薪资 HISAL 最高薪资

2.2 查看表中数据

查看数据库中表dept结构: select * from dept; //注意:实际开发给用户反馈时肯定不能一查就查全表。

查看数据库中表emp结构: select * from emp;

查看数据库中表salgrade结构: select * from salgrade;

# 以emp为例,这里用navicat展示表中数据

2.3 常用命令

查看当前使用的是哪个数据库:select database(); 

查看mysql的版本号:select version();

结束语句:\c

退出mysql:exit

2.4 数据查询语句(DQL)

单表查询:select 属性1,属性2,属性3,.... from 表名;  (若属性为数,可进行数学运算)

问题:查询员工的月薪?

1.员工表是哪个?emp表

2.月薪的属性名是? sal属性

select sal from emp; #为了方便理解,是不是该把员工姓名也加上。员工姓名属性名:ename。
select ename,sal from emp;

3.要是我想查询的是员工的年薪?

select ename,sal*12 from emp;

4.想将月薪列改成年薪列?

select ename,sal * 12 as yearsal from emp;

5.不想要英文命名属性名,就想要年薪?

select ename,sal * 12 as 年薪 from emp; // 错误

select ename,sal * 12 as '年薪' from emp;

6.如果这里我想查找月薪大于等于3000?

这时候就有了条件。不是简单的全返回。也简单。

条件查询: select 属性1,属性2... from 表名 where 条件; (注意是条件,不能是函数)

select ename,sal from emp where sal >= 3000;

7. 找出工资在1100和3000之间的员工,包括1100和3000?

select ename,sal from emp where sal >= 1100 and sal <= 3000;

select ename,sal from emp where sal between 1100 and 3000; // between...and...是闭区间 [1100 ~ 3000]

拓展:between and除了可以使用在数字方面之外,还可以使用在字符串方面。

select ename from emp where ename between 'A' and 'C';  //没有C!注意

8.找出哪些人没有津贴 /实习生/ ?

0和NULL不同,津贴属性名为:comm

select comm from emp where comm is null;

select ename,comm from emp where comm is null;

9.找出名字当中含有O的?

**模糊查询LIKE:**掌握两个特殊的符号

中文模糊查询(注意:’ ‘)

  1. %(刘% 可以表示为为刘一 刘二 刘一二 刘1234856789 表中所有姓刘的人)
  2. _ (刘_,这里只能刘一 刘二 刘三 共两个字;刘__ 两个下划线,只能表示为刘一一 刘二二 刘一二 共三个字)

问题:查询表中姓名含有“少"的人?

select ename from emp where ename like '%少%';

问题:查询表中姓名姓“申"的人?

select ename from emp where ename like '申%';

英文模糊查询_(思考为啥这里也要加’  ‘ ) _

select ename from emp where ename like '%O%'; //表示

10.找出名字中第二个字母是A的?

select ename from emp where ename like '_A%';

11.找出名字中最后一个字母是T的?

select ename from emp where ename like '%T';

12.想对员工工资进行排序(升序、降序)?(默认是升序)

 select 属性1,属性2... from 表名 order by 属性2 属性3; 

select ename , sal from emp order by sal; // 升序 从小到大

select ename , sal from emp order by sal asc; // asc升序 从小到大

select ename , sal from emp order by sal desc; // desc降序 从大到小 一般都是这个 好看嘛

13.若工资相同,按照名字的从小到大排列。

select ename,sal from emp order by sal desc , ename asc;

区别:关键字 函数 方法

关键字 是内置的 具有特殊意义的标识符关键字后面不需要使用括号 函数封装了独立功能,可以直接调用函数名(参数)函数需要死记硬背 方法和函数类似 同样是封装了独立功能方法需要通过对象来调用,表示针对这个对象要做的操作对象.方法名(参数)在变量后面输入. 然后选择针对这个变量要执行的操作,记忆起来比函数要简单很多。

牛客题库

1.给定学生和成绩的数据库表,写一个sql语句求出每门课程的平均分。

A.SELECT SUM(score) FROM course GROUP BY student_id;

B.SELECT score FROM course GROUP BY course_id;

C.SELECT AVG(score) FROM course GROUP BY course_id;

D.SELECT AVG(score) FROM course ;

2.下列关于关系数据库语言 SQL 语言的说法不正确的是()。

A.SQL 支持数据库的三级模式结构

B.SQL 的功能包括查询、操作、定义和控制等四个方面

C.SQL 是作为独立语言由联机终端用户在交互环境下使用的命令语言,它不能嵌入高级语言内

D.SQL 除应用在数据库领域外,还在软件工程、人工智能领域有广泛的应用

3.子模式 DDL 用来描述 ( )。

A.数据库的总体逻辑结构

B.数据库的局部逻辑结构

C.数据库的物理存储结构

D.数据库的概念结构

4.有两个关系 S,P ,已经存有如下数据。S (供应商) P (零件)

其中 S 表的主码为供应商号, P 表的主码为零件号,外码为供应商号,参照 S 表的供应商号,请问下面哪一条语句能插入到 P 表中? ( )

A.('S100',‘红’,‘T20’)

B.(‘TQOT’,‘绿’,NULL)

C.(‘A001’,‘黄’,‘T11’)

D.(NULL,‘红’,‘S20’)

答案1.C  从course选出学生信息,然后通过课程代码进行分组操作,最后使用AVG函数对每个组内的学生成绩求平均值。

2.C sql一种是独立的交互使用数据查询、数据更新等操作,称为自含语言。另一种是嵌入到某种高级语言中,作为高级语言的一种扩充,是程序员编程时既可使用数据库语言又可使用常规的程序设计语言,这时高级语言叫宿主语言。

3.B 外模式也称子模式(Subschema)或用户模式,它是数据库用户(包括应用程序员和最终用户)能看见和使用的局部数据的逻辑结构和特征描述,是数据库用户的数据视图,是与某一应用有关的数据逻辑表示。对应于用户级。它是某个或某几个用户所看到的数据库的数据视图,是与某一应用有关的数据的逻辑表示。DDL(data definition language)是数据定义语言:DDL比DML要多,主要的命令有CREATE、ALTER、DROP等,DDL主要是用在定义或改变表(TABLE)的结构,数据类型,表之间的链接和约束等初始化工作上,他们大多在建立表时使用。

4.B 选项A: P中的主键是零件号,所以零件号不能重复,故A错误。选项C: S中没有 T11 这个主键,故不能成为P中的外键。选项D: 主键不能为空。

练习题

1.查看dept表的结构 

DESC dept; 

 2.查看dept表的所有数据 

SELECT * FROM dept; 

3.查看dept表中所有部门的MGR 

SELECT ename, MGR FROM emp; 

4.找出工资在1100和3000之间的员工,包括1100和3000? 

SELECT ENAME,SAL FROM emp WHERE SAL >= 1100 and SAL <=3000; 

SELECT ENAME,SAL FROM emp WHERE SAL between 1100 and 3000; 

5.找出哪些人没有津贴或津贴为0的? 

SELECT ENAME,COMM FROM emp WHERE COMM is NULL OR COMM = 0; 

6.查询出所有名字中带有A 

SELECT ENAME FROM emp where ename like '%A%'; 

7.查询出所有员工名字中最后一个字母是K的 

select ename from emp where ename like '%K'; 

8.对员工工资进行降序(从大到小),若工资相同,对员工**EMPNO**进行升序(从小到大)。 

select ename,sal from emp order by sal desc, empno asc;

9.查询所有员工的平均工资 

select avg(sal) from emp; 

10.查询员工中JOBMANAGER,并且对他们的薪资进行排序 

select ename,sal from emp WHERE job='MANAGER' order by sal desc; 

第四节课

先复习一下上节课对简单sql语句的学习与应用,自查(敲)回顾一下上周的课后题。

这节课我们来学习Java语言连接数据库 JDBC(Java DataBase Connectivity)。

1.回顾思考

下图大家应该比较熟悉,这套是我们目前操作逻辑体系。想一下是不是~

“我们” ->mysql下敲sql语句(eg:查询表的所有数据) ->运行编译 ->本地数据库进行查询操作 -> 返回“目的”数据 

这时候如果用户想要查数据/改数据,他“可以”直接到数据库端用数据库产品来对数据进行操作。但这个使用场景的前提是:

  1. 得拥有数据库端的用户权限(对应到具体问题:普通用户登录数据库后select * 某表是可以返回所有数据的)
  2. 数据操作(增删改查)的工作量必须是人类体力/脑力能够承担的。(对应到具体问题:工作量)

这里有个例子很好理解:店掌柜把账本交给一个可靠的人(帐房先生),帐房先生根据不同人汇总上来的账目,对账本的数据进行各种更改/查询。

2.那么如何利用程序获得数据库中的数据?

答案就是JDBC。观察下列体系的流程图。

试想JDBC体系下是如何解决上面的两个问题。

  1. 权限:数据库端的权限不开放给普通用户,决定权在root手里。(具体操作:在JDBC和数据库之间存在“授权操作”,使得root用户可以给普通用户开发权限,使得他们能够获得公司一定的数据)

    Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/practice?characterEncoding=GBK", "root", "123456");
    
  2. 工作量:程序解决(具体展开后续说)

2.1 JDBC本质?

*本质是一套接口

  • 面向调用者和实现者。 
  • 面向接口调用、面向接口写实现类,这都属于面向接口编程。 

*为什么要面向接口编程?

 解耦合:降低程序的耦合度,提高程序的扩展力。 

多态机制就是非常典型的:面向抽象编程。(不要面向具体编程) 

建议:

Animal a = new Cat();  Animal a = new Dog(); 

  public void feed(Animal a){  }  //面向父类型编程

不建议

 Dog d = new Dog();   Cat c = new Cat();

  public void feed(Dog d){  } 

  public void feed(Cat c){  } 

这里一个个实现类称作驱动: SqlServer.class -> SqlServer驱动

我们的mysql-版本号.jar 解压可以看到很多.class ,这些都是对JDBC接口进行的实现。

思考?为什么要制定一套JDBC接口呢?

  1. 适用性更广、跨平台。
  2. 对应用程序开发者而言更加灵活,JDBC对数据库厂商而言则更加标准化。

具体:因为每一个数据库的底层实现原理都不一样。 Oracle数据库有自己的原理。 MySQL数据库也有自己的原理。SqlServer数据库也有自己的原理。 .... 每一个数据库产品都有自己独特的实现原理。

3.配置JDBC

1.下载JDBC-MYSQL数据库驱动

这里要注意与不同版本的mysql安装版本是不同的。

eg: 我的mysql8.0 对应的jdbc 版本是mysql-connector-java-8.0.21

_新版本:dev.mysql.com/downloads/c…](dev.mysql.com/downloads/c…)

_历史版本:downloads.mysql.com/archives/c-…](downloads.mysql.com/archives/c-…)

2.手动配置

  1. 将下载的压缩包解压,将目录下的mysql-connector-java-8.0.21.jar文件(驱动)复制到JDK的拓展目录中(即JAVA_HOME环境变量指定的JDK中) 
  2. 再将mysql-connector-java-8.0.21.jar文件(驱动)复制到  C:\Program Files\Java\jre1.8.0_241\lib\ext中。保证启动该环境运行程序时,会有驱动的包存在。

JDK是Java的开发工具,它不仅提供了Java程序运行所需的JRE,还提供了一系列的编译,运行等工具,如javac,java,javaw等。JRE只是Java程序的运行环境,它最核心的内容就是JVM(Java虚拟机)及核心类库。

3.验证是否成功,加载驱动,尝试连接

1.在本地新建一个test.java (txt改后缀名暴力转)

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * 使用JDBC连接MySQL数据库
 *
 * @author lloyd
 */
public class test {
	public static void main(String[] args) {
		try {
			// 1.加载数据库驱动类
			Class.forName("com.mysql.cj.jdbc.Driver");
			System.out.println("数据库驱动加载成功");

			// 2.创建连接
			//格式为jdbc:mysql:
			//注意改成自己的账号和密码。 127.0.0.1:3306/数据库名称?useSSL=true&characterEncoding=utf-8&user=账号名&password=密码
			Connection connection = DriverManager.getConnection
					("jdbc:mysql://127.0.0.1:3306/mysql?useSSL=true&characterEncoding=utf-8&serverTimezone=GMT&user=root&password=111");
			System.out.println("创建连接成功");

		}
		catch (ClassNotFoundException cnfe) {
			cnfe.printStackTrace();
		} catch (SQLException sqle) {
			sqle.printStackTrace();
		}
	}
}

2.win+r cmd进到dos界面,输入

编译:javac test.java   //目的将java源文件编译为class字节码文件

运行:java test      //java运行class字节码文件,注意java命令后面不要再加.class。不然会使得java去寻找test.class.class。java指令默认找class文件,找其地址通过CLASSPATH环境变量中指定的目录中寻找。

*4.JDBC编程六步走

  1. 注册驱动(作用:告诉Java程序,即将要连接的是哪个品牌的数据库 Mysql) 
  2. 获取连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完之后一定要关闭通道。) 
  3. 获取数据库操作对象(专门执行sql语句的对象) 
  4. 执行SQL语句DQL数据查询 DML数据操纵....) 
  5. 处理查询结果集(只有当第四步执行的是select语句的时候,才有这第五步处理查询结果集。) 
  6. 释放资源(使用完资源之后一定要关闭资源。Java数据库属于进程间的通信,开启之后一定要关闭。)

牛客题库

1.某学院包含多个专业如计算机科学、信息管理、软件工程、网络工程。每个专业每年都招收一个班级的学生。在招生过程中就已明确规定,一个学生只能就读于该学院的一个班级,但是一个班级可以招收不超过60个学生。那么,学生和班级之间是________的关系。

A.一对多    B.多对多    C.一对一     D.多对一

2.若要“查询选修了3门以上课程的学生的学号”,则正确的SQL语句是( )

A.SELECT S# FROM SC GROUP BY S# WHERE COUNT(*)> 3

B.SELECT S# FROM SC GROUP BY S# HAVING COUNT(*)> 3

C.SELECT S# FROM SC ORDER BY S# WHERE COUNT(*)> 3

D.SELECT S# FROM SC ORDER BY S# HAVING COUNT(*)> 3

3.SQL中,下面对于数据定义语言DDL描述正确的是()

A.DDL关心的是数据库中的数据

B.联盟链

C.控制对数据库的访问

D.定义数据库的结构

4.SQL语言共分为三大类(亦有说法分为四大类),那么不属于数据操纵语言的有()

A.update   B.grant    C.delete    D.insert

5.数据库管理系统(DBMS)是( )

A.数学软件     B.应用软件    C.计算机辅助设计    D.系统软件

6.关系型数据库创建表都有主键。以下对主键描述正确的是:()

A.主键是唯一索引,唯一索引也是主键

B.主键是一种特殊的唯一性索引,只可以是聚集索引

C.主键是唯一、不为空值 的列

D.对于聚集索引来说,创建主键时,不会自动创建主键的聚集索引

7.数据库、数据库系统和数据库管理系统三者之间的关系是()

A.数据库系统包括数据库和数据库管理系统

B.数据库管理系统包括数据库和数据库系统

C.数据库包括数据库系统和数据库管理系统

D.数据库系统就是数据库,也就是数据库管理系统

8. 在关系数据库设计中,设计关系模式是数据库设计中( )阶段的任务

A.逻辑设计    B.物理设计    C.需求分析     D.概念设计

9.在MYSQL数据库中,以下哪条语句用于统计BOOK表中的记录总数?

A.select sum(*) from book;

B.select max(*) from book;

C.select avg(*) from book;

D.select count(*) from book;

答案 1.D 一个学生只能找到一个班级,一个班级有多名学生。学生vs班级是多对一关系。

2.B order by 从英文里理解就是行的排序方式,默认的为升序(从小到大)。order by 后面必须列出要排序的属性,可以是多个属性。group by 从英文里理解就是分组。必须有“聚合函数”来配合才能使用,使用时至少需要一个分组标志字段(即按什么进行分组)。group by的条件语句用having关键字来写。注意:聚合函数对一组值执行计算并返回单一的值。常见的聚合函数有五种,分别是sum()、count()、avg()、max()、min()。

3.D SQL主要分成四部分。

DQL(数据查询语言): 查询语句,凡是select语句都是DQL。 
DML(数据操作语言):insert delete update,对表当中的数据进行增删改。
DDL(数据定义语言):create drop alter,对表结构的增删改。
TCL(事务控制语言):commit提交事务,
rollback回滚事务。(TCL中的T是Transaction)
DCL(数据控制语言): grant授权、revoke撤销权限等。

4.B  

数据查询语言(DQL):是由SELECT子句,FROM子句,WHERE子句组成的查询块 
数据操纵语言(DML): SELECT(查询) INSERT(插入) UPDATE(更新) DELETE(删除) 
数据定义语言(DDL):CREATE(创建数据库或表或索引)ALTER(修改表或者数据库)DROP(删除表或索引) 
事务控制语言(TCL):SAVEPOINT (设置保存点)ROLLBACK (回滚)  COMMIT(提交) 
数据控制语言(DCL):GRANT(赋予用户权限) REVOKE(收回权限) DENY(禁止权限) 

5.D  DBMS数据库管理系统是位于用户与操作系统之间的一层数据管理软件。因此常常也被称为系统软件或软件系统。

6.C  7.A  8.A  9.D 秒选!这里要注意,主键是一种约束(潜在的限制条件),要区分索引!

第五节课

上节课我们学习了Java语言连接数据库JDBC的逻辑体系也自行配置和动手操作了连接,这节课我们将实现JDBC编程的六步走,实现sql注入

1.JDBC编程六步走

   注册驱动   获取连接   获取数据库操作对象   执行SQL语句   处理查询结果集   释放资源

//新建test.java

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

/*
	使用JDBC连接MySQL数据库
 */
public class test {
	public static void main(String[] args) {
		Connection connection = null;
		Statement stmt = null;
		try {
			// 1.加载数据库驱动类
			Class.forName("com.mysql.cj.jdbc.Driver");
			System.out.println("数据库驱动Driver.class加载成功");
			/* 2.驱动创建连接
				格式为jdbc:mysql:
				127.0.0.1:3306/数据库名称?useSSL=true&characterEncoding=utf-8&user=账号名&password=密码
				useSSL=true 可不加,这块在与服务器建立连接时需使用,为服务器证书验证提供信任存储。
				characterEncoding 需要根据自己本地的编码方式是否会有中文?需要进行设置。(utf-8)-> 数据转换为字节码 ->(eg:mysql默认GBK编码)
				serverTimezone=GMT jdbc连接服务器的时区设置
			*/
			connection = DriverManager.getConnection
					("jdbc:mysql://127.0.0.1:3306/kexie?useSSL=true&characterEncoding=utf-8&serverTimezone=GMT&user=root&password=111");
			System.out.println("创建连接成功");

			// 3、获取数据库操作对象
			// Statement createStatement() 创建一个 Statement 对象封装Sql语句发送到数据库。

			stmt = connection.createStatement();

			// 4、执行sql语句
			// int executeUpdate(String sql) 专门执行DML语句 insert delete update。
			int count = stmt.executeUpdate("update dept set dname = '销售部',loc = '合肥' where deptno = 20;");
			System.out.println(count == 1 ? "保存成功":"保存失败"); // 返回值是影响数据库中的记录条数

			// 5、处理查询结果集 这里对原表进行的直接操作,因此没有返回结果,不需要处理。eg: 查询
		}
		catch (ClassNotFoundException cnfe) {
			cnfe.printStackTrace();
		} catch(SQLException e) {
			e.printStackTrace();
		} finally {
			// 6、释放资源
			// 从小到大依次关闭 先开启的connection"小" 之后开启的stmt"大"。
			// 注意:不可以一起try
			if(stmt != null) {
				try	{
					stmt.close();
				}
				catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if(connection != null) {
				try	{
					connection.close();
				}
				catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

总结

1.注册驱动   Class.forName(驱动位置)

2.获取连接  connection = DriverManager.getConnection(url)

3. 获取数据库操作对象 stmt = connection.createStatement();

4. 执行SQL语句 

stmt.executeUpdate("sql")

5. 处理查询结果集 (针对更新,删除和插入这种只改变原表,而不返回表内容的,不需要执行处理。select要)

6.释放资源(这里最要注意的就是资源释放的顺序问题,connection最先开启,stmt后开启,因此需要先关闭stmt之后再关闭connection

catch抛出错误,stmt对应一个catchconnection一个catch。观察下面错误示例,思考如果程序中connection没有连接成功为null, 那么作为程序员的我们,要怎么判断错误源自哪里呢?

错误示例:

try{
    if(stmt != null){        stmt.close();
    }
    if(connection != null) {
        connection.close();
    }
}catch (SQLException e) {    e.printStackTrace();
}

2.sql注入

Navicat中新建一张t_user的表,设计表结构时,属性名分别为userNameuserPassword,类型默认varChar即可。之后打开表,填充数据。这步是模拟网页后端数据库的建立过程。

//新建test.java

import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

/*
    模拟实现用户登录功能
 */
public class test {
	public static void main(String[] args) {
		// 初始化界面
		Map<String,String> userLoginInfo = initUI();
		// 验证用户名和密码
		boolean loginSuccess = login(userLoginInfo);
		// 输出最后结果
		System.out.println(loginSuccess ? "登录成功" : "登录失败");
	}

	/**
	 * 用户登录
	 * @param userLoginInfo 用户登录信息
	 * @return true表示登录成功,false表示登录失败
	 */
	private static boolean login(Map<String, String> userLoginInfo) {
		boolean loginSuccess = false;
		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null;
		try {
			// 1、注册驱动
			Class.forName("com.mysql.cj.jdbc.Driver");
			// 2、获取连接
			conn = DriverManager.getConnection
					("jdbc:mysql://127.0.0.1:3306/kexie?useSSL=true&characterEncoding=utf-8&serverTimezone=GMT&user=root&password=111");
			// 3、获取数据库操作对象
			stmt = conn.createStatement();
			// 4、执行sql语句
			String sql = "select * from t_user where userName = '"+ userLoginInfo.get("userName")+ "' and userPassword = '" + userLoginInfo.get("userPassword")+ "'";
			rs = stmt.executeQuery(sql);
			// 5、处理结果集
			if(rs.next()) {
				loginSuccess = true;
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException throwables) {
			throwables.printStackTrace();
		} finally {
			// 6、释放资源
			if (rs != null) {
				try {
					rs.close();
				} catch (SQLException throwables) {
					throwables.printStackTrace();
				}
			}
			if (stmt != null) {
				try {
					stmt.close();
				} catch (SQLException throwables) {
					throwables.printStackTrace();
				}
			}
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException throwables) {
					throwables.printStackTrace();
				}
			}
		}
		return loginSuccess;
	}

	/**
	 * 初试化界面
	 * @return 用户输入的用户名和密码等登录信息
	 */
	private static Map<String, String> initUI() {
		Scanner s = new Scanner(System.in);

		System.out.print("请输入用户:");
		String userName = s.nextLine();
		System.out.print("请输入密码:");
		String userPassword = s.nextLine();

		Map<String,String> userLoginInfo = new HashMap<>();
		userLoginInfo.put("userName",userName);
		userLoginInfo.put("userPassword",userPassword);

		return userLoginInfo;
	}
}

输入:任意用户,任意密码'or' 1'='1可得

运行结果如下图所示:

在封装Sql语句的stmt对象后设置断点,Debug分析其中原因。

可得传入sql语句中的sql语句被封装为一个恒成立条件,因此不需要数据库验证都可以登录成功。

问题就在于userName变量+password变量需要通过拼接组装成字符串(sql语句),而变量通过接受得到的拼接使用的是' '单引号拼接。

黑客在输入时就利用了这个bug,通过自己输入任意密码'or' 1'='1,原代码前后再封装'任意密码'or' 1'='1'形成恒成立语句。

select * from t_user where userName = '任意用户' and userPassword = '任意密码'or' 1'='1';

牛客题库

1.下面哪些字符最可能会导致sql注入?

A. ‘(单引号)      B. /          C.  "(双引号)         D. $

2.某IT公司人事管理采用专门的人事管理系统来实现。后台数据库名为LF。新来的人事部张经理新官上任,第一件事是要对公司的员工做全面的了解。可是他在访问员工信息表EMPL里的工资和奖金字段的时被拒绝,只能查看该表其他字段。作为LF的开发者你将如何解决这一问题:( )

A.废除张经理的数据库用户帐户对表EMPL里的工资列和奖金列的SELECT权限

B.添加张经理到r角色

C.添加张经理到db_accessadmin角色

D.授予张经理的数据库用户帐户对表EMPL里的工资列和奖金列的SELECT权限。

(*多选)3.mysql数据库中一张user表中,其中包含字段A,B,C,字段类型如下:A:int,B:int,C:int根据字段A,B,C按照ABC顺序建立复合索引idx_A_B_C,以下查询语句中使用到索引idx_A_B_C的语句有哪些?

A.select *from user where A=1 and B=1

B.select *from user where 1=1 and A=1 and B=1

C.select *from user where B=1 and C=1

D.select *from user where A=1 and C=1

(*多选)4.表结构如下:

以下查询语句结果一定相等的是() 

A.SELECT sum(score) / count(*) FROM score WHERE cno = 2;

B.SELECT sum(score) / count(id) FROM score WHERE cno = 2;

C.SELECT sum(score) / count(sno) FROM score WHERE cno = 2;

D.SELECT sum(score) / count(score) FROM score WHERE cno = 2;

E.SELECT sum(score) / count(1) FROM score WHERE cno = 2;

F.SELECT avg(score) FROM score WHERE cno = 2; 

答案1.A 控制台输出拼接后的sql为:select * from user_table where username='xxx' and password='xxx' or '1'='1'(达到攻击效果,查询出所有用户信息)所以是单引号导致逻辑发生变化。

public static void main(String[] args) throws Exception {

        String name="xxx";

        String password="xxx' or '1'='1";

        String sql="select * from user_table where username='"+name+"' and 

password='"+password+"'";

        System.out.println(sql);

}

2. D 具体操作: 

1.Grant <权限> on 表名[(列名)] to 用户 With grant option   或 GRANT <权限> ON <数据对象> FROM <数据库用户>

2.权限:select,update,insert,delete

3.回收权限 revoke  REVOKE <权限> ON <数据对象> FROM <数据库用户名>

3\. ABD 复合索引可以只使用复合索引中的一部分,但必须是由最左部分开始,且可以存在常量。因复合索引为idx_A_B_C,所以查询条件只能是在a,ab,abc,ac才算,使用到索引idx_A_B_C。

  A 复合索引最左前缀规则对于条件A=1 and B=1 显然会用到索引。

  B 对于条件 A = 1 and B = 1满足最左前缀 1 = 1 常量表达式这部分不通过索引。

  C 不满足最左前缀条件,不能使用索引。

  D 通过条件A = 1来使用索引进行查询。因为是最左前缀中一种,对于C=1,就无法使用索引而是全表扫描,这是用于多个and条件连接的条件或单条件应用最左前缀若是or则不行。

4\. 所有的统计函数都会忽略空值(null)。 

A :统计所有学生的平均分,就算成绩为空的学生,最后计算count(*)时也作为分母基数,计算得到所有学生的平均分。B :与A一样,因为id主键非空,count(id)所得分母基数是所有学生。 C : 与B一样,非空属性sno。 E: count(1)与count(*)一样。 F:avg(score)会忽略空值,故计算结果为有成绩的学生的平均分。