MySQL的索引和事务、Java的JDBC编程

一、索引

1、什么是索引

索引(index):
一种特殊的文件,包含着对数据表里所有记录的引用指针。可以对表中的一列或多列创建索引,并指定索引的类型,各类索引有各自的数据结构实现。

相当于一本书的目录(index),根据目录中每个章节对应的页码,就能快速的找到对应的章节,MySQL的索引也是一样的,当从数据库中进行查找的时候,例如按照一定的条件来查找。

查找可以遍历表,但是遍历表操作比较低效,就需要想办法尽量的避免遍历,可以通过一些特殊的数据结构,来表示一些记录的特征,通过这些特征来减少比较次数,加快比较的效率。

索引主要的意义就是进行查找,要提高查找的效率,查找效率是提高了,但是同时也会付出一些代价:

书的目录也是费纸的,数据库的索引,也是需要消耗一定的额外存储空间的,数据量越大,索引消耗的额外空间就越多

书的目录如果确定了,后续每次对书的内容调整,都可能会影响到目录的准确性就需要重新调整目录,
数据库的索引也是一样,当进行增删改的时候,往往也需要同步地调整索引的结构

作用:
索引带来的好处,提高了查找的速度
索引带来的坏处,占用了更多的空间,拖慢了增删改的速度.

虽然看起来索引的坏处好像更多点,但是索引仍然是一个很常用的东西
实际需求场景中,查找操作往往是最高频的操作
例如像布置作业,每节课都需要布置作业,每天都要进行查询操作
如果有同学反馈,某个作业有问题,或者某个作业不太合适了,就需要进行增删改查,一个月才改动一次

使用场景:
要考虑对数据库表的某列或某几列创建索引,需要考虑以下几点:
1、数据量较大,且经常对这些列进行条件查询。
2、该数据库表的插入操作,及对这些列的修改操作频率较低。
3、索引会占用额外的磁盘空间。
满足以上条件时,考虑对表中的这些字段创建索引,以提高查询效率。
反之,如果非条件查询列,或经常做插入、修改操作,或磁盘空间不足时,不考虑创建索引。


2、使用

创建主键约束(PRIMARY KEY)、唯一约束(UNIQUE)、外键约束(FOREIGN KEY)时,会自动创建
对应列的索引

1、查看索引

show index from 表名;

案例:查看学生表已有的索引

在这里插入图片描述
直接查看student表,发现里面其实已经自带了一个索引,这个自带的索引,就是 primary key 这个主键约束带来的
查询的时候,如果查询条件指定了根据主键查询,这个时候查询速度就会非常快
unique 也是要带索引

给一个表中的某个列来创建索引create index 索引名字on表名(列名);

在这里插入图片描述
创建索引这件事情是一个非常低效的事情,尤其是当前表里面已经有很多数据的时候.后面你针对线上的数据库,如果这个表没有索引,你不要贸然去创建索引

删除索引drop index索引名字on表名;

删除索引操作和创建同理,都是非常低效的事情,也容易把数据库搞挂,创建表的时候,就应该把索引规划好


3、索引背后的数据结构

3.1、多叉搜索树

索引,数据结构需要能够加快查找的速度

常见的数据结构,哪个适合索引?

顺度表 要想查找,你必须得遍历
链表
二交树(二叉搜索树)AVL树红黑树
哈希表

1、顺序表
此处咱们说的查找,是**"按照值"查找**,而不是按照下标"查找”,按照下标来访问元素,不叫查找

为啥顺序表按照下标访问的速度就快呢?
=>顺序表是在连续内存空间上=>内存支持随机访问操作(访问任意地址上的数据,速度都是极快的/差不多)

2、二叉树:O(N)
最坏的情况·单枝树(就是链表)
要做的是,不要让二叉搜索树变成单枝树
AVL:要求任意节点左右子树高度差不超过1
红黑树:要求更宽松的平衡二叉树
也不太适合

二叉树最大的问题,就是当元素多了的时候,高度就高了.(高度就对应着比较次数),对于数据库来说,每次比较都意味着磁盘IO

3、哈希表
也不太适合
虽然哈希表查找速度很快O(1)
哈希表只能针对 “相等" 进行判定,不能对“大于小于”,以及范围查找进行判定

堆更不适合作为索引,堆只能找最大/最小


最适合做索引的,还得是树形结构,只不过就不再是二叉树了
如果我们使用 “多叉搜索树” ,高度自然也就下降了

在数据库中使用的这个多叉搜索树,又不太一样,是一个很特殊的树称为B+树 (这个是数据库索引中最常见的数据结构)

数据库有很多种,每个数据库底层又支持多种存储引擎(实现了数据具体按照啥结构来存储的程序),每个存储引擎存储数据的结构可能都不一样,背后的索引数据结构可能也不同

要想理解B+树,需要先理解它的前身 B树 (有的资料上也写作B-树 ),这个是 B树 的另外一个写法,而不是B减树!


3.2、B树

在这里插入图片描述
B树的每个节点上,都会存储N个key值,N个key值就划分出了N+1个区间,每个区间都对应到一个子树
在B树中查找元素,过程就和二叉搜索树非常相似,先从根节点出发,根据待比较的元素,确定一个区间
在确定区间的时候,不也是多次比较嘛,这里比较多次和二叉搜索树相比,优势体现在哪里呢?
二叉搜索树,每个节点比一次,比较的次数是和高度相关
但是B树,高度是少了,但是每个节点比较多次了
相比于比较次数来说,,IO次数是更关键的!是以节点为单位进行磁盘IO的


3.3、B+树

B树只是B+树的前身,B+树相比于B树又做出了一些改进

B+树也是一个N叉搜索树,每个节点上都包含多个key值
每个节点如果又N个key,就分成了N个区间
父节点的值,都会在子节点中体现
非叶子节点中的每个值,最终都会在叶子节点中体现出来
父节点中的值,会作为子节点中的最大值(最小值),咱们这个图画的是最大值的情况
最下面的叶子节点,就使用链表进行按顺序连接

在这里插入图片描述
在这里插入图片描述

B+树就是为了数据库索引量身打造的

1.使用B+树进行查找的时候,整体的IO次数也是比较少
⒉所有的查询最终都会落到叶子节点上,每次查询的IO次数都是差不多的,查询速度稳定
3.叶子结点用链表连接之后,非常适合进行范围查找,例如,要找到>= 5<=11的值
4.所有的数据存储(载荷)都是放到叶子节点上的,非叶子节点中只保存key值即可,因此非叶子节点整体占用的空间较小,甚至可以缓存到内存
一旦能够全放内存里,这个时候,磁盘IO几乎就没了


二、事务

1、示例

准备测试表:

drop table if exists accout;
create table accout(
	id int primary key auto_increment,
	name varchar(20) comment '账户名称',
	money decimal(11,2) comment '金额'
);
insert into accout(name, money) values
	('阿里巴巴', 5000),
	('四十大盗', 1000);

比如说,四十大盗把从阿里巴巴的账户上偷盗了2000元

-- 阿里巴巴账户减少2000
update accout set money=money-2000 where name = '阿里巴巴';
-- 四十大盗账户增加2000
update accout set money=money+2000 where name = '四十大盗';

假如在执行以上第一句SQL时,出现网络错误,或是数据库挂掉了,阿里巴巴的账户会减少2000,但是四十大盗的账户上就没有了增加的金额。

解决方案:使用事务来控制,保证以上两句SQL要么全部执行成功,要么全部执行失败

事务:

指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败。
在不同的环境中,都可以有事务。对应在数据库中,就是数据库事务。

事务诞生的目的,就是为了把若干个独立的操作给打包成一个整体,在SQL中,有的复杂的任务需要多个SQL来执行
有的时候,也同样需要打包在一起,前一个SQL是为了给后一个SQL
提供支持,如果后一个SQL不执行了或者执行出问题了,前一个SQL 也就失去意义了

原子性:要么全都执行完,要么一个都不执行,任务不可以被再细分了

事务的这个原子性到底是咋保证的呢?
要么就全都执行成功,要么就一个都不执行

但是该执行还是得执行,因为我们无法预知失败

当出现执行失败之后,由数据库自动执行一些“还原"性的工作,来消除前面的SQL带来的影响,看起来就好像一个都没执行一样

数据库是如何知道该还原成哪个值的呢?
数据库会拿个小本子,把执行过的每个操作,都给记录下来
既然都能还原,是不是我就可以放心大胆的删除数据库了呢?
数据库要想记录上面的详细操,也是要消耗大量的时间和空间的,因此这个记录势必不会保存那么久,
你的数据库是经过了一年的时间沉淀出来的数据,但是你的这些记录可能只记录了几天的。


2、事务的基本特性

  1. 原子性
  2. 一致性
    在事务执行之前,和执行之后,数据库中的数据都得是合理合法的
    例如,你转账完了之后,不能够出现这种账户为负数的情况
  3. 持久性
    事务一旦提交了之后,数据就持久化存储起来了,数据就写入到硬盘了
  4. 隔离性 (特别不好办)
    隔离性描述的是,事务并发执行时候,产生的情况

并发编程是当下最主要的一个编程的方式写出来的代码,是并发式的执行的,就是一心两用

当并发执行多个事务,尤其是这多个事务在尝试修改 / 读取同一份数据,这个时候就容易出现一些问题
事务的隔离性,就是在解决上述问题


3、脏读与幻读

并发执行事务可能带来的问题:

有一天,我在写代码,在写一个即将课堂上要讲的代码
比如,我在代码中写了一个Student类
在我写这个代码的过程中,有一个同学,在后面偷偷的瞄了我的屏幕(他就看到了我写的代码里面有一个Student类,有一些属性…),看完之后,他就走了
然后我把代码给改了
在这里偷偷瞄的一眼,瞄到的不是一个最终版本的数据
而是一个中间过程的数据,这里可能最终会被改成别的值

脏读问题:
事务A在对某个数据进行修改,修改的同时,事务B去读取了这个数据
此时事务B读到的很可能是一个 “脏数据” (这个数据是一个临时的结果,而不是最终的结果)

出现脏读的问题,原因就是事务和事务之间,没有进行任何的隔离,加上了一些约束限制,就可以有效的避免脏读问题

处理脏读:

1)给写操作加锁

在修改的过程中,别人不能读了 (加锁的状态)
等修改完了之后,别人才能读 (接触加锁)
我和同学们约定好,你们不要着急窥屏
我写完的代码会提交到码云上,你们来看我码云上的代码
码云上就是我修改完毕的代码,这样,大家读到的数据就不再是中间的数据了
一旦加了这个写锁之后,意味着事务之间的隔离性就高了,并发性就降低了

在这里插入图片描述

2)不可重复读

在一个事务中,包含了多次的读操作,这多次的读操作读出来的结果不一致
刚才约定的是,写的过程中不能读,没说读的过程中不能写
导致不可重复读

刚才这个问题就好比说,有个同学在通过码云读代码,随便刷新一下,发现代码变了…(又得重新理解了呀…)

再做一个约定,之前咱们约定的是,你修改的时候我不能读,现在再约定一下,我读的时候,你也别修改,给读操作也加锁了
意味着我必须得等同学把代码读完了,才能进行修改
通过给读操作也加锁,就解决了不可重复读的问题

在这里插入图片描述

事务之间的并发性又降低了,隔离性又提高,并发性和隔离性是二者不可得兼的,
到底是要跑的快,还是跑的对? 这件事情需要进行取舍了


给读操作也加锁的情况下,在同学读代码时,我去写另一个代码了:
在这里插入图片描述

事务虽然在提交隔离性的时候要进行一系列加锁,但是这个锁也不是把整个数据库都给锁定了,还可以改其他的表,甚至说改这个表的其它的行

什么是锁的粒度:封楼的范围
如果一个小区里有一个确诊,就封这栋楼
影响到这一个楼的居民 (锁的粒度小)
如果一个小区里有两个确诊,就封整个小区
影响到整个小区的居民 (锁的粒度大)

那么在刚才这个情况下,我就没闲着
但是这个事情也带来了一个问题:

当同学读代码的过程中,发现,代码的数量变了,本来是只有一个A .java,现在又多了个B.java

幻读问题:
一个事务执行过程中进行多次查询,多次查询的结果集不一样,(多了一条或者少了一条),这个操作算是一种特殊的不可重复读

解决幻读问题:彻底串行化执行
同学们和我说好,老湿,我们读代码的时候,你就赶紧摸鱼,不要写任何代码了

隔离性最高,并发程度最低,数据最可靠,速度最慢


4、隔离级别

以上就是所介绍的,关于隔离性相关问题了解了,
并发(快) 和 隔离(准) 是不能兼得的

就可以根据实际需要来调整数据库的隔离级别,通过不同的隔离级别,也就控制了事务之间的隔离性,也就控制了并发程度

MySQL中事务的隔离级别,提供了这么几种:

  1. read uncommitted : 允许读取未提交的数据,并发程度最高,隔离程度最低。会引入脏读+不可重复读+幻读问题
  2. read committed : 只允许读取提交之后的数据,相当于写加锁。并发程度降低了一些,隔离程度提高了一些。解决了脏读,会引入不可重复读+幻读
  3. repeatable read : 相当于给读和写都加锁,并发程度又降低了,隔离程度又提高了。解决了脏读和不可重复度,会引入幻读。
  4. serializable : 串行化,并发程度最低(串行执行),隔离程度最高。解决了脏读、不可重复度、幻读问题,但是执行速度最慢。

就可以通过修改my.ini这个配置文件,来设置当前的隔离级别根据实际需求场景,来决定使用那种隔离级别


三、Java的JDBC编程

1、JDBC

首先MySQL是一个客户端-服务器结构的程序

黑框框只是一个官方提供的客户端,官方也允许程序猿自己实现 mysql 的客户端,并且mysql提供了一组API来支持程序猿实现这样的客户端,自己实现的客户端就可以根据需要来完成一些更具体的增删改查功能

API是计算机行业中经常见到的数据,Application Programming Interface(应用编程接口),
MySQL提供了一些函数/方法/类,供程序猿直接使用

但数据库又不是只有MySQL~~
还有Oracle, SQL Server, SQLite…MySQL提供了APl
oracle也提供了API
显然,这些不同的数据库是出自不同厂商之手.
而且关于数据库API的约定,并没有一个业界统一的标准

Java约定了一组API,称为JDBC,这组API里面就包含了一些类和一些方法,通过这些类和方法来实现数据库的基本操作,再由各个厂商,提供各自的"数据库驱动包",来和JDBC的API对接,程序猿只需要掌握这一套JDBC API就可以操作各种数据库了

数据库编程的必备条件

  • 编程语言,如Java,C、C++、Python等
  • 数据库,如Oracle,MySQL,SQL Server等
  • 数据库驱动包:不同的数据库,对应不同的编程语言提供了不同的数据库驱动包,如:MySQL提供了Java的驱动包mysql-connector-java,需要基于Java操作MySQL即需要该驱动包。同样的,
    要基于Java操作Oracle数据库则需要Oracle的数据库驱动包ojdbc

Java的数据库编程:JDBC
JDBC,即Java Database Connectivity,java数据库连接。是一种用于执行SQL语句的Java API,它是
Java中的数据库连接规范。这个API由 java.sql.,javax.sql. 包中的一些类和接口组成,它为Java
开发人员操作数据库提供了一个标准的API,可以为多种关系数据库提供统一访问

JDBC工作原理
DBC 为多种关系数据库提供了统一访问方式,作为特定厂商数据库访问API的一种高级抽象,它主要包
含一些通用的接口类

JDBC优势:
Java语言访问数据库操作完全面向抽象接口编程
开发数据库应用不用限定在特定数据库厂商的API
程序的可移植性大大增强


2、JDBC使用步骤

1、创建项目

2、引入依赖,JDBC编程需要用到 mysq l的驱动包。(驱动包就是把 MySQL自身的 API 给转换成 JDBC 风格的)
驱动包是 mysql 官方提供的

1). 下载驱动包
官网oraclemaven中央仓库

得到一个jar 这样的文件 (就相当于.zip这样的压缩包一样)
jar 里面就是一些其他人写好的 .class文件

在这里插入图片描述

2). 导入到项目中

a) 创建个目录,随便起个名字,例如叫做lib

在这里插入图片描述

b) 把刚才下载的 jar 文件拷贝到刚才的目录中

在这里插入图片描述

c) 右键刚才的目录,有一个选项,叫做 add as library,点击这个选项,才能把这个 jar 引入到项目中,此时项目才会从jar 里面读取内部的 .class,否则,代码就找不到jar中的一些类了

在这里插入图片描述

3、编写代码


3、JDBC 的基本编程流程

3.1、建立数据源

1、创建 DataSource 对象,这个对象就描述了数据库服务器在哪

javax.sql :JDBC提供的类和方法都在这个包里
其中 DataSource 就是其中的一个重要的类 (接口)

DataSource :JDBC 带的接口
MysqlDataSource :这个是来自于刚才下载好的那个 mysql 的 jar 包,这里提供了一个实现了 DataSource 接口的类

2、还需要描述数据库服务器在哪里

在这里插入图片描述


URL :就是平时说的网址,在WWW上,每一信息资源都有统一的且在网上唯一的地址,该地址就叫 URL(Uniform Resource Locator,统一资源定位器),它是WWW的统一资源定位标志,就是指网络地址。就是在描述互联网上的一个资源所在的位置的。

mysql 作为一个服务器,也是以 url 这样的风格来提供资源,外面要想访问 mysql 上的数据库,也同样通过 url 来进行描述

  • 设置数据库所在的地址:
jdbc:mysql://127.0.0.1:3306/java102?characterEncoding=utf8&useSSL=false

jdbc:mysql :类型 (协议名称),表示这个url是用于jdbc mysql 的 url

127.0.0.1IP地址,mysql服务器所在的主机的IP地址,IP地址就描述了,网络上一个主机的位置,环回IP,表示你的本机IP,localhost 等价于127.0.0.1

3306端口号,端口号.对应到主机上的一个具体应用程序,一个主机上的应用程序有很多,收到的数据报到底给谁,得区分清楚,每个服务器程序启动的时候都关联一个端口号(一个整数),3306表示了mysql服务器,(安装 mysql的时候,有个环节就是设置了mysql服务器的端口号,默认就是3306)

characterEncoding=utf8 :字符集,注意,此处如果你的服务器字符集是 utf8mb4,此处仍然写 utf8

useSSL=false :是否要传输过程中加密,此处一般不用加密

  • root :这是mysql默认自带的管理员用户

3.2、让代码和数据库服务器建立连接

一定要认准,使用的是 JDBC 的 Connection ,不是MySQL驱动包里的Connection,更不是其他的Connection

在这里插入图片描述
直接 getConnection 此处有一个错误提示:

在这里插入图片描述

建立连接失败,就会抛这个异常

声明异常:throws SQLException


3.3、操作数据库

以插入数据为例:构造一个 SQL 语句

在 JDBC 中构造 SQL,不需带上分号,它只是在命令行中用来区分不同语句的

String sql = "insert into student values (1, '张三')";

只有 String 类型的 sql还不行,需要把这个 String 包装成一个 “语句对象”

PreparedStatement statement = connection.prepareStatement(sql);

PreparedStatement 准备的语句
通过connection 里的 prepareStatement 方法构造语句对象

这个操作就是把 字符串 风格的 sql 转成一个 JDBC 里的对象


3.4、执行 SQL

SQL 里如果是insert,update,delete, 都是用 executeUpdate(执行更新) 方法
SQL 里如果是select, 就使用 executeQuery(执行查询) 方法

返回的数字同于:5 rows in set (0.00 sec)

int ret = statement.executeUpdate(); // 执行更新

3.5、释放资源

此时 SQL 已经执行完毕,需要释放资源。
创建好相关的连接之后JVM就会从系统这里申请到一些硬件资源,这些资源不用了,就要得记得释放
释放资源的时候,要先释放 statement ,后释放 Connection先创建的,后释放

statement.close();
connection.close();

3.6、代替输入

刚才在插入的时候,插入的数据都是直接写死的,写死并不好,需要灵活的处理要插入的数据,例如,想让用户通过控制台,输入要插入的学号和姓名,如何来处理这个让用户输入数据的插入?

int id = scanner.nextInt();
String name = scanner.nextLine();
String sql = "insert into student values (" + id + ", '张三')";

此处要进行的操作,就是要把 id 和 name 给替换到 insert 语句的对应位置上

通过字符串拼接这个操作来构造 sql ,也是可行的,但是并不科学!
1.这么写非常麻烦,容易写错
2.这么写也容易引起 "sql注入攻击”,黑客攻击服务器的一种手段,这么直接拼字符串,属于上个时代 (200x的时候)。

在这里使用一个代替的写法,避免直接拼接字符串:

String sql = "insert into student values (?, ?)";

相当于告诉 java 程序,这两个字段的值,还不确定呢,此时就使用?先给占个位置,再使用 PreparedStatement 的 setXXX 系列方法进行替换

statement.setInt(1, id); // 这个就是把第一个?给替换成id这样的值
statement.setString(2, name); // 这个就是把第二个?替换成name的值

这里的下标从 1 开始
setXXX 方法有很多,需要让这里的方法和数据库的列的类型匹配

PreparedStatement 这个类,除了能用于描述一个SQL之外,还可以辅助程序猿对SQL进行动态的拼接

使用问号代替后,重新运行,通过这个打印操作,就能看到拼装好之后的 sql 是怎样的

System.out.println("statement" + statement);

很多时候如果 jdbc 中执行的 sql 报错了
如何看这个错误?就往往需要先把这个拼好的 sql 先打印出来,执行出错,大概率是这里的拼接出问题

请输入学号:
3
请输入姓名:
李四
statement: com.mysql.jdbc.JDBC42PreparedStatement@4bf558aa: insert into student values (3, ‘李四’)
1

TestJDBC.java
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Scanner;

public class TestJDBC{
    public static void main(String[] args) throws SQLException {
        Scanner scanner = new Scanner(System.in);

        // 1、创建好数据源
        DataSource dataSource = new MysqlDataSource(); // 向下转型
        // 设置数据库所在的地址
        ((MysqlDataSource) dataSource).setURL("jdbc:mysql://127.0.0.1:3306/work?characterEncoding=utf8&useSSL=false");
        // 设置数据库的用户名
        ((MysqlDataSource) dataSource).setUser("root"); // 向上转型
        // 设置登录数据库的密码
        ((MysqlDataSource) dataSource).setPassword("11111");

        // 2、让代码和数据库服务器建立连接 - 相当于快递到达了菜鸟驿站
        Connection connection = dataSource.getConnection();

        // 2.5、让用户通过控制台 输入待插入的数据
        System.out.println("请输入学号:");
        int id = scanner.nextInt();
        System.out.println("请输入姓名:");
        String name = scanner.next();

        // 3、操作数据库
        // 以插入数据为例:构造一个 SQL 语句
        // 在 JDBC 中构造 SQL,不需带上分号,它只是在命令行中用来区分不同语句的

        // String sql = "insert into student values (1, '张三')";
        // String sql = "insert into student values (" + id + ", '张三')";
        //String sql = "insert into student values (?, ?)";
        String sql = "insert into student values (?, ?)";
        // String sql = "insert into student values(?, ?), (?, ?),  (?, ?)";

        // 只有 String 类型的 sql还不行,需要把这个 String 包装成一个 “语句对象”
        PreparedStatement statement = connection.prepareStatement(sql);
        // PreparedStatement 准备的语句
        // 通过 connection 里的 prepareStatement 方法构造语句对象

        // 进行替换操作
        statement.setInt(1, id);
        statement.setString(2, name);
        System.out.println("statement: " + statement);

        // 4、执行 SQL - 相当于扫码取件
        // SQL 里如果是 insert,update,delete,都是用executeUpdate(执行更新) 方法
        // SQL 里如果是 select,就使用 execute
        // Query(执行查询) 方法
        int ret = statement.executeUpdate(); // 执行更新
        System.out.println(ret);
        // 返回的数字同于:5 rows in set (0.00 sec)

        // 5、此时 SQL 已经执行完毕,需要释放资源
        statement.close();
        connection.close();
    }
}

4、删除 TestJDBCDelete.java

删除数据库记录:
让用户输入一个 id, 根据 id 来删除.

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Scanner;

public class TestJDBCDelete {
    public static void main(String[] args) throws SQLException {
        // 让用户输入一个 id, 根据 id 来删除.
        // 1、创建数据源
        DataSource dataSource = new MysqlDataSource();
        ((MysqlDataSource) dataSource).setURL("jdbc:mysql://127.0.0.1:3306/work?characterEncoding=utf8&useSSL=false");
        ((MysqlDataSource) dataSource).setUser("root");
        ((MysqlDataSource) dataSource).setPassword("11111");

        // 2、建立连接
        Connection connection = dataSource.getConnection();

        // 3、用户输入 id
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一个要删除的 id: ");
        int id = scanner.nextInt();

        // 4、拼装 sql 语句
        String sql = "delete from student where id = ?";
        PreparedStatement statement = connection.prepareStatement(sql);
        statement.setInt(1, id);

        // 5、执行 sql
        int ret = statement.executeUpdate();
        System.out.println(ret);

        // 6、回收释放资源
        statement.close();
        connection.close();
    }
}

5、修改 TestJDBCUpdate.java

根据 id 来修改学生姓名,让用户输入要修改的 id,以及对应的修改后的名字

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Scanner;

public class TestJDBCUpdate {
    public static void main(String[] args) throws SQLException {
        // 1、创建数据源
        DataSource dataSource = new MysqlDataSource();
        ((MysqlDataSource) dataSource).setURL("jdbc:mysql://127.0.0.1:3306/work?characterEncoding=utf8&useSSL=false");
        ((MysqlDataSource) dataSource).setUser("root");
        ((MysqlDataSource) dataSource).setPassword("11111");

        // 2、和数据库建立连接
        Connection connection = dataSource.getConnection();

        // 3、输入信息
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要修改的学生id:");
        int id =  scanner.nextInt();
        System.out.println("请输入要修改的学生姓名:");
        String name = scanner.next();

        // 4、拼装 sql
        String sql = "update student set name = ? where id = ?";
        PreparedStatement statement = connection.prepareStatement(sql);
        statement.setString(1, name);
        statement.setInt(2, id);
        System.out.println("statement: " + statement);

        // 5、执行 sql
        int ret = statement.executeUpdate();
        System.out.println(ret);

        // 6、回收资源
        statement.close();
        connection.close();
    }
}

6、查询 TestJDBCSelect.java

查询多了一个步骤,遍历结果集 (把结果中的数据给获取到)

执行 sql,对于查询操作,需要使用 executeQuery
查询操作返回的不是 int,而是一个**“临时表”**
使用ResultSet 表示这个表
ResultSet resultSet = statement.executeQuery();

遍历结果集合(返回的临时表),先获取到这一行,再获取到这一行的每一列
next 方法表示获取到行的记录,同时把光标向后移动一行
如果遍历到表的结束位置,此处的next直接返回 false

在这里插入图片描述
1.传的参数是第几列(从1开始数)
2.传的是列的名字(主要是用这个)

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;

public class TestJDBCSelect {
    public static void main(String[] args) throws SQLException {
        // 1、创建数据源
        DataSource dataSource = new MysqlDataSource();
        ((MysqlDataSource) dataSource).setURL("jdbc:mysql://127.0.0.1:3306/work?characterEncoding=utf8&useSSL=false");
        ((MysqlDataSource) dataSource).setUser("root");
        ((MysqlDataSource) dataSource).setPassword("11111");

        // 2、建立连接
        Connection connection = dataSource.getConnection();

        // 3、拼装 sql 语句
        String sql = "select * from student";
        PreparedStatement statement = connection.prepareStatement(sql);

        // 4、执行 sql,对于查询操作,需要使用 executeQuery
        //    查询操作返回的不是int 而是一个“临时表”
        //    使用 ResultSet 表示这个表
        ResultSet resultSet = statement.executeQuery();

        // 5、遍历结果集合(返回的临时表),先获取到这一行,再获取到这一行的每一列
        //    next 方法表示获取到行的记录,同时把光标向后移动一行
        //    如果遍历到表的结束位置,此处的 next 直接返回 false
        // +------+--------+
        // | id   | name   |
        // +------+--------+
        // |   1  | 张三   |
        // |   2  | 李四   |
        // +------+--------+
        while (resultSet.next()) {
            // 针对当前这一行,获取其中的列
            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            System.out.println("id: " + id + ", name: " + name);
        }

        // 6、回收释放资源
        resultSet.close();
        statement.close();
        connection.close();
    }
}

当前表中有两条记录每个记录都是有两列
通过 while 循环,搭配 resultSet.next()
就能依次获取到表中的每一行
第一次执行 resultSet.next,就表示获取到了第一行 (id=2)
第二次执行 resultSet.next,就表示获取到了第二行 (id=3)
第三次执行 resultSet.next,由于表已经到达了末尾,next方法返回false循环就结束了

  • 27
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 47
    评论
评论 47
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三春去后诸芳尽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值