文章目录
- 1:索引
- 1.1:概念:
- 1.2 作用:
- 1.3 使用场景:
- 1.4 使用:
- 补充:
- **2:了解一下B树:**
- **3:了解一下B+树**
- 4:为什么使用B树/B+树来实现索引
- 2:事务
- 2.1:为什么使用事务
- 2.2:事务的概念
- 2.3:事务的特性:
- 1:原子性:
- 2:一致性:
- 3:隔离性:
- 4:持续性:
- 2.4:隔离界别
- 1:脏读:
- 2:不可重复读:
- 注意脏读与不可重复读的区别是:
- 3:幻读
- 3:Java的JDBC编程
- 3.1:数据库编程的必备条件
- 3.2:Java的数据库编程:JDBC
- 3.3:JDBC工作原理
- 3.4:JDBC使用
1:索引
增加了查找的速度但也加大了增删改的开销(进行增删改的时候要调整已创建好的索引)也会提高空间的开销,构造索引需要额外的空间来存。
1.1:概念:
索引是一种特殊的文件,包含着对数据表里所有记录的引用指针。可以对表中的一列或多列创建索引,
并指定索引的类型,各类索引有各自的数据结构实现。
1.2 作用:
数据库中的表、数据、索引之间的关系,类似于书架上的图书、书籍内容和书籍目录的关系。索引所起的作用类似书籍目录,可用于快速定位、检索数据。索引对于提高数据库的性能有很大的帮助,索引保存了排序的字段以及数据的地址
1.3 使用场景:
要考虑对数据库表的某列或某几列创建索引,需要考虑以下几点:
- 数据量较大,且经常对这些列进行条件查询。
- 该数据库表的插入操作,及对这些列的修改操作频率较低。
- 索引会占用额外的磁盘空间。
满足以上条件时,考虑对表中的这些字段创建索引,以提高查询效率。
反之,如果非条件查询列,或经常做插入、修改操作,或磁盘空间不足时,不考虑创建索引
1.4 使用:
创建主键约束(PRIMARY KEY)、唯一约束(UNIQUE)、外键约束(FOREIGN KEY)时,会自动创建
对应列的索引。因为如果没有索引会频繁的遍历,时间开销大。
-
查看索引
show index from 表名
-
创建索引
对于非primary key,非 foreign key ,非unique类型的字段,可以创建普通的索引。
create index 索引名 on 表名(字段名); -- 例如: create index IND on score(student_id);
创建索引最后在建表的时候就创建好,如果针对一个表中已经有很多很多记录的表来创 建索引,也是一个非常危险的操作。这时候就会吃掉大量的磁盘IO,花很长的时间(十 几分钟,后几个小时看数据量),在这段时间内数据库是无法使用的。
-
删除索引
drop index 索引名 on 表名 -- 例如: drop index IND on score;
补充:
1:索引是为了加快查询的速度,也不是所有的情况下加上索引就一定快,如果姓名有重复,索引能不能加 上会不会报错,能加上,只要你的重名不是很多,这个时候还是能大大加快查询的速度的。但是形如根据性别查询,或者根据大学生年龄查询,这样很多重复的数据来索引查询无法提高速度。
2:了解一下B树:
d为大于1的一个正整数,称为B-Tree的度。
h为一个正整数,称为B-Tree的高度。
每个非叶子节点由n-1个key和n个指针组成,其中d<=n<=2d。
**每个叶子节点最少包含一个key和两个指针,最多包含2d-1个key和2d个指针,**叶节点的指针均为null 。
所有叶节点具有相同的深度,等于树高h。
key和指针互相间隔,节点两端是指针。
一个节点中的key从左到右非递减排列。
所有节点组成树结构。
每个指针要么为null,要么指向另外一个节点。
所有的叶子节点位于同一层
由于B-Tree的特性,在B-Tree中按key检索数据的算法非常直观:首先从根节点进行二分查找,如果找到则返回对应节点的data,否则对相应区间的指针指向的节点递归进行查找,直到找到节点或找到null指针,前者查找成功,后者查找失败,另外,由于插入删除新的数据记录会破坏B-Tree的性质,因此在插入删除时,需要对树进行一个分裂、合并、转移等操作以保持B-Tree性质。
3:了解一下B+树
B-Tree有许多变种,其中最常见的是B+Tree,例如MySQL就普遍使用B+Tree实现其索引结构。
与B-Tree相比,B+Tree有以下不同点:
每个节点的指针上限为2d而不是2d+1。
内节点不存储data,只存储key;叶子节点不存储指针。
由于并不是所有节点都具有相同的域,因此B+Tree中叶节点和内节点一般大小不同。这点与B-Tree不同,虽然B-Tree中不同节点存放的key和指针可能数量不一致,但是每个节点的域和上限是一致的,所以在实现中B-Tree往往对每个节点申请同等大小的空间。
一般来说,B+Tree比B-Tree更适合实现外存储索引结构,具体原因与外存储器原理及计算机存取原理有关
带有顺序访问指针的B+Tree
一般在数据库系统或文件系统中使用的B+Tree结构都在经典B+Tree的基础上进行了优化,增加了顺序访问指针。
在B+Tree的每个叶子节点增加一个指向相邻叶子节点的指针,就形成了带有顺序访问指针的B+Tree。做这个优化的目的是为了提高区间访问的性能,例如图4中如果要查询key为从18到49的所有数据记录,当找到18后,只需顺着节点和指针顺序遍历就可以一次性访问到所有数据节点,极大提到了区间查询效率。
4:为什么使用B树/B+树来实现索引
一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往是以索引文件的形式存储在磁盘上,这样的话,索引查找过程就会产生磁盘的IO消耗,相对于内存的存取,磁盘IO存取的消耗要高几个数量级,所以评价一个数据结构作为索引的优劣最重要的指标是在查找过程中磁盘IO的存取次数。下面先介绍内存和磁盘存取原理,然后再结合这些原理分析B-/+Tree作为索引的效率。
主存存取原理:
目前计算机所使用的主存基本上都是随机读取存储器,现代RAM的结构于存取原理比较复杂,俺也不会,
主存是一系列存储单元组成的矩阵,每个存储单元存贮固定大小的数据,每个存贮单元都有唯一的地址,通过一个行地址和一个列地址就可以唯一定位到一个存储单元。
当系统需要读数据的时候,则将地址信号放在地址总线上传给主存,主存读到地址信号后,解析信号并定位到指定的存储单元,然后将此存储单元的数据放在数据总线上,供其他部件读取。
写主存的过程类似,系统将要写入存储单元的地址与数据分别放在地址总线与数据总线上,主存读取两个主线的内容做相应的写操作。
这里可以看出,主存的存与取的时间与存取次数成线性关系,不存在机械操作,两次存取的数据的“距离”不会对时间有任何影响,例如,先取A0再取A1和先取A0再取D3的时间消耗是一样的。
磁盘存取原理:
索引一般以文件的形式存储在磁盘上,索引的检索需要磁盘IO操作。与主存不同,磁盘IO存在机械运动的耗费,因此相比主存IO,磁盘IO的时间消耗是巨大的。
一个磁盘由大小相同且同轴的圆形盘片组成,磁盘可以转动(各个磁盘必须同步转动)。在磁盘的一侧有磁头支架,磁头支架固定了一组磁头,每个磁头负责存取一个磁盘的内容。磁头不能转动,但是可以沿磁盘半径方向运动(实际是斜切向运动),每个磁头同一时刻也必须是同轴的,即从正上方向下看,所有磁头任何时候都是重叠的(不过目前已经有多磁头独立技术,可不受此限制)。
盘片被划分成一系列的同心环,圆心是盘片中心,每一个圆心环叫做一个磁道,所有半径相同的磁道做成一个柱面。磁道被沿半径线划分成一个个小的段,每个段叫做一个扇区,每个扇区是磁盘的最小存储单元。
读数据:
当需要从磁盘读取数据的时候,系统会将数据逻辑地址传给磁盘,磁盘的控制电路按照寻址逻辑将逻辑地址翻译成物理地址,即确定要读的数据在哪个磁道,哪个扇区,为了读取这个扇区的数据,需要将磁头放到这个扇区的上方,为了实现这一点,磁头需要移动对准相应的磁道,这个过程叫寻道,所耗费的时间叫寻道时间,然后磁盘寻转将目标扇区旋转到磁头下方,这个过程所耗费的时间叫旋转时间。
写数据:
系统将文件存储到磁盘上时,按柱面、磁头、扇区的方式进行,即最先是第1磁道的第一磁头下(也就是第1盘面的第一磁道)的所有扇区,然后,是同一柱面的下一磁头,……,一个柱面存储满后就推进到下一个柱面,直到把文件内容全部写入磁盘。
局部性原理与磁盘预读
由于存储介质的特性,磁盘本省的存取就比主存慢很多,再加上机械运动的耗费,磁盘的存取速度往往是主存的几百分之一,因此为了提高效率,要尽量减少磁盘IO。为了达到这个目的,磁盘往往不是严格按需存取,而是每次都会预读,即使只是需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存中。这就叫做局部性原理。即:当一个数据被用到时,其附近的数据也会通常马上被使用。程序运行期间所需要的数据通常比较集中。由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率。预读的长度一般为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页得大小通常为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,然后异常返回,程序继续运行。
B-/+Tree索引的性能分析
上文说过一般使用磁盘I/O次数评价索引结构的优劣。先从B-Tree分析,根据B-Tree的定义,可知检索一次最多需要访问h个节点。数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入。为了达到这个目的,在实际实现B-Tree还需要使用如下技巧:
每次新建节点时,直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,加之计算机存储分配都是按页对齐的,就实现了一个node只需一次I/O。
B-Tree中一次检索最多需要h-1次I/O(根节点常驻内存),渐进复杂度为(O(h)=O(log_dN))。一般实际应用中,出度d是非常大的数字,通常超过100,因此h非常小(通常不超过3)。
综上所述,用B-Tree作为索引结构效率是非常高的。
而红黑树这种结构,h明显要深的多。由于逻辑上很近的节点(父子)物理上可能很远,无法利用局部性,所以红黑树的I/O渐进复杂度也为O(h),效率明显比B-Tree差很多。
上文还说过,B+Tree更适合外存索引,原因和内节点出度d有关。从上面分析可以看到,d越大索引的性能越好,而出度的上限取决于节点内key和data的大小:
(d_{max}=floor(pagesize / (keysize + datasize + pointsize)))
floor表示向下取整。由于B+Tree内节点去掉了data域,因此可以拥有更大的出度,拥有更好的性能。
4:数据库的数据结构是什么,是如何实现的,我们会想到数据结构中的哈希表,时间复杂度是O(1)但是哈希表只能比较相等,无法进行范围的查询,因为数据库有大量的使用范围查询,所以其数据结构不是哈希表。我们又想到二叉搜索树,如果属是比较平衡的,那么时间复杂度是O(LOGN),最坏的情况下是一个链表形状的单支树,时间复杂度是O(n),二叉树是可以范围查询的,查起点查终点(树里的元素是有序的),但是当元素非常多的时候,树的高度会非常的高,二叔的高度又决定了比较的次数,元素的比较次数就会增多,元素的比较是要读硬盘的。所以时间开销就比较大。N叉树,每个节点上有多个值,同时又有多个分支,树的高度就降低了,其中一种典型的实现叫B树
比较的次数没咋减少(因为一个节点中要比较多次),但是读写硬盘的次数减少了(每个节点都在硬盘上)。我们对B树进行进一步的改进,引入B+树,B+树就是为了索引这个场景量身定制的数据结构。
上述B+树的特点就带来了一些好处,首先他是一个N叉树,数的高度降下来了,硬盘IO的次数就比较少了,其次B+树更适合范围查询,最后所有的查询 都落在叶子节点上,无论查询哪个元素,中间比较的次数差不多,查询操作比较均衡(对于B树来说有的树可能查的快,有的差的慢,可能不均衡,因为可能在根节点或者深度不深的位置,都查的快,而B+树大家都是一个速度) ,因为所有的key都会在叶子节点上体现,因此非叶子节点,比不存表的真实记录(数据行),只需把所有的数据行放在叶子节点上即可,非叶子节点只需要存储索引列的值(比如存一个ID)。
由于非叶子节点只存简单的ID没有存一整行,也就意味着非叶子节点所占用的空间时大大降低的,有可能在内存中可以放进去缓存,更进一步的降低了硬盘的IO。提高查询的速度本质上就是降低硬盘的IO次数。
还需要注意的是,数据库里看到的这张表不一定是按照连续的空间来组织的,有很大的概率是基于B+树来组织的。尤其是带有主键的表。
有的表不只是主键索引,还有别的非主键列,也有索引。
这个情况会构造另一个B+树,B+树非叶子节点里面存的都是这一列里面的key比如一堆学生姓名,到叶子节点这一层,不是存之前的数据行,而是存主键ID,
使用主键列来查询,只要查一次B+树就可以了,如果是使用非主键列的索引来查询,则需要先查一遍索引列的B+树,再查一遍主键列的B+树。
2:事务
2.1:为什么使用事务
(保证以多条SQL要么全部执行成功,要么全部执行失败)
你在转账的时候,A给B转账100元,A账户的金额应该减少100,B账户的金额应该增加100,
-- A账户减少100
update accout set money=money-100 where name = 'A';
-- B账户增加100
update accout set money=money+100 where name = 'B';
假如在执行以上第一句SQL时,出现网络错误,或是数据库挂掉了,A的账户会减少100,但是
B的账户上就没有了增加的金额。
解决方案:使用事务来控制,保证以上两句SQL要么全部执行成功,要么全部执行失败。
2.2:事务的概念
事务指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败。
执行中间出错了,就让一条都不执行,注意这里不是真的一条都没执行,而是自动恢复成执行之前的样子,看起来像一个都没执行的样子。这也叫做回滚,就是把执行过的操作逆向恢复回去,数据库会把每个操作记录记录下来,如果某个操作出错了,就会把事务中前面的操作进行回滚,根据之前进行的操作,进行逆向操作(前面是插入,现在就删除,前面是修改现在是改回去),不过这些操作有很大的开销,你可以保存,但不可能无限的保存,最多就是把正在执行的事务保存下来。比如一个数据库一共有十亿条数据,占有几百个G,此时你不可能花几T的空间来记录这十亿条数据是咋来的。
在不同的环境中,都可以有事务。对应在数据库中,就是数据库事务。
2.3:事务的特性:
1:原子性:
事务是数据库的逻辑工作单位,事务中包含的诸多操作要么全做,要么全不做。因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
2:一致性:
事务执行的结果必须是使数据库从一个一致性状态变成另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。一致性与原子性是密切相关的。
3:隔离性:
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。
如果隔离性越低,就意味着事务之间的并发程度越高,执行效率越高,但是数据的准确性越低。如果隔离性越高,就意味着事务之间的并发程度越低,执行效率越低,但是数据的准确性越高。因此mysql提供了不同的隔离等级。
4:持续性:
一个事务一旦提交,他对数据库中数据的改变就应该是永久的。即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
拓展:
1:如果突然断电,已经写入硬盘的那一部分的怎么回滚?
事务在执行的时候,会把执行的过程先记录在硬盘上,再执行要进行的操作,比如执行到一半突然断电了(数据已经处在不正常的状态了),等你下次上电的时候,MySQL 就会感知到上个事务这里执行到中间状态,就会根据之前的记录过程进行回滚了。
2:为什么删除数据貌似比添加数据快?
猜测:硬盘上添加数据,需要进行写硬盘,添加1M就王硬盘里写1M,删除数据,操作系统管理硬盘的时候,通常会采取逻辑删除,不是真的把数据都给擦掉,而是设置一个”无效“标志位。如果你标记成无效,不着急立即删除,下次如果需要新增数据,就可以重复利用这些空间,将其覆盖掉,有的时候,把文件给删了,但是可以通过一些特殊手段(至少还原一部分),同理,删库也不是无解的,但是解决的成本是比较大的。
2.4:隔离界别
1:脏读:
脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
当一个事务正在多次修改某个数据,而在这个事务中这多次的修改都还未提交,这时一个并发的事务来访问该数据,就会造成两个事务得到的数据不一致。比如说:A在记账B在旁边看,B看见A给C记了100块钱,然后B就走了,但是在B走之后A又将100改成了1000,这样出现了脏读的问题。在这个场景中两个事务时完全并发的,没有任何的限制,在这个前提下会出现脏读问题。解决脏读的办法就是降低并发性,提高隔离性,比如:A在写的时候,B不能看,直到A写完提交上去了,B再看。
2:不可重复读:
不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。
举个例子:A同学写完代码提交到数据库了,然后B同学开始读,B同学在读的过程中,A同学突然有了一个好点子,然后就修改了一下原来的代码,又提交了一次,这时候B同学读到一半,突然代码自己变了,这就太烦了。
如何解决这个问题,给操作加锁,同学在读的时候,我不能修改,这样这两个事务的并发程度进一步的降低了,隔离性又进一步的提高了,运行的速度有进一步的变慢了,数据的准确性有进一步的提高了。
在某些情况下,不可重复读并不是问题,比如我们多次查询某个数据当然以最后查询得到的结果为主。但在另一些情况下就有可能发生问题,例如对于同一个数据A和B依次查询就可能不同,A和B就可能打起来了……
注意脏读与不可重复读的区别是:
脏读是某一事物读取了另一个事务未提交的脏数据。而不可重复读则是读取了前一事务新提交的数据。
3:幻读
当前已经约定了写枷锁与读枷锁,A在写代码的时候,B不可以读,B在读的时候,A不可以写,但是这都是针对同一个代码文件来说的。A看到B在读了,A虽然不能修改B正在读的文件,但是A可以搞别的,他可以再搞一个新的文件,此时虽然B读的代码内容没有变,但是他发现文件的数量变了。
解决幻读的方法是:串行化,彻底的抛弃并发,只要B在读A只能摸鱼。
3:Java的JDBC编程
3.1:数据库编程的必备条件
-
编程语言,如Java,C、C++、Python等
-
数据库,如Oracle,MySQL,SQL Server等
-
数据库驱动包:不同的数据库,对应不同的编程语言提供了不同的数据库驱动包,如:MySQL提
供了Java的驱动包mysql-connector-java,需要基于Java操作MySQL即需要该驱动包。同样的,
要基于Java操作Oracle数据库则需要Oracle的数据库驱动包ojdbc
3.2:Java的数据库编程:JDBC
JDBC,即Java Database Connectivity,java数据库连接。是一种用于执行SQL语句的Java API,它是
Java中的数据库连接规范。这个API由 java.sql.,javax.sql. 包中的一些类和接口组成,它为Java
开发人员操作数据库提供了一个标准的API,可以为多种关系数据库提供统一访问
3.3:JDBC工作原理
JDBC 为多种关系数据库提供了统一访问方式,作为特定厂商数据库访问API的一种高级抽象,它主要包
含一些通用的接口类。
JDBC访问数据库层次结构:
JDBC优势:
Java语言访问数据库操作完全面向抽象接口编程
开发数据库应用不用限定在特定数据库厂商的API
程序的可移植性大大增强
3.4:JDBC使用
刚上来的操作是:准备数据库驱动包,并添加到项目的依赖中
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class JDBCInsertDemo {
public static void main(String[] args) throws SQLException {
/*
* 先去创建数据源,描述一下数据库在哪里*/
DataSource dataSource = new MysqlDataSource();
((MysqlDataSource)dataSource).setURL("jdbc:mysql://127.0.0.1:3306/java106?characterEncoding=utf8&&useSSL=false");
//注意你数据库的指明在这里
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("171612cgj");
/*
* 连接数据库*/
Connection connection = dataSource.getConnection();
/*
* 写sql语句*/
String sql1 = "create table person(name varchar(20), ID varchar(20))";
/*
* 准备一下*/
PreparedStatement statement = connection.prepareStatement(sql1);
int ret = statement.executeUpdate();
System.out.println(ret);
/*
* 断开连接*/
statement.close();
connection.close();
}
}
JDBC的使用案例:
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;
public class JDBCInsertDemo3 {
/**
* 考勤系统,包含员工表,考勤记录表
*/
public static void main(String[] args) throws SQLException {
// 创建数据源,指明数据库在哪里
DataSource dataSource = new MysqlDataSource();
((MysqlDataSource)dataSource).setURL("jdbc:mysql://127.0.0.1:3306/java106?characterEncoding=utf8&&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("171612cgj");
// 建立连接
Connection connection = dataSource.getConnection();
// 写sql语句
String sql1 = "create table person(name varchar(20), ID varchar(20))";
String sql2 = "create table kaoqin(cishu int,ID varchar(20))";
String sql3 = "insert into person values(?,?)";
String sql4 = "select * from person,kaoqin where person.ID = kaoqin.ID";
String sql5 = "insert into kaoqin values(?,?)";
// 做一下准备
PreparedStatement statement1 = connection.prepareStatement(sql1);
PreparedStatement statement2 = connection.prepareStatement(sql2);
PreparedStatement statement3 = connection.prepareStatement(sql3);
PreparedStatement statement4 = connection.prepareStatement(sql4);
PreparedStatement statement5 = connection.prepareStatement(sql5);
//注意执行顺序:先把两个表给创建出来
int ret1 = statement1.executeUpdate();
System.out.println(ret1);
int ret2 = statement2.executeUpdate();
System.out.println(ret2);
// 开始往表里输入数据
Scanner scanner = new Scanner(System.in);
int count = 0;
while(count < 4) {
String name = scanner.next();
String ID = scanner.next();
int cishu = scanner.nextInt();
statement3.setString(1,name); // 指明字段的列号,已经输入的变量名
statement3.setString(2,ID);
statement5.setInt(1,cishu);
statement5.setString(2,ID);
int ret3 = statement3.executeUpdate(); //每次输入完都要executeUpdate
System.out.println(ret3);
int ret5 = statement5.executeUpdate();
count++;
}
ResultSet resultSet = statement4.executeQuery();
while(resultSet.next()) {
String nam = resultSet.getString("name");
int cishu = resultSet.getInt("cishu");
String ID = resultSet.getString("ID");
System.out.println(nam+" "+ ID +" "+ cishu);
}
// 断开连接
statement5.close();
statement4.close();
statement3.close();
statement2.close();
statement1.close();
connection.close();
}
}
注意:
- 增,删,改的 写完数据以后要 statement.executeUpdate() 返回值的类型是int类型,意思是影响了几行的数据
- 查:写完数据以后要 statement . executeQuery() 返回值的类型是 ResultSet ,是一个集合,可以写一个循环来一条一条的输出里面的数据。
- ResultSet对象
ResultSet对象它被称为结果集,它代表符合SQL语句条件的所有行,并且它通过一套getXXX方法提供
了对这些行中数据的访问。
ResultSet里的数据一行一行排列,每行有多个字段,并且有一个记录指针,指针所指的数据行叫做当
前数据行,我们只能来操作当前的数据行。我们如果想要取得某一条记录,就要使用ResultSet的next()
方法 ,如果我们想要得到ResultSet里的所有记录,就应该使用while循环。