day30 | 黑马程序员Java全程笔记 | 第二阶段MySQL高级 JDBC

01.反馈回顾

事务: ★

概述: 逻辑上的一组操作,组成这组操作的各个单元要么同时成功,要么同时失败.

事务是一个最小的执行单元.

mysql中的事务控制:

手动事务: 需要手动开启,提交,回滚

  开启事务: begin;  start transaction;

  编写sql语句...

  提交: commit;

  回滚: rollback;

自动事务: mysql中的事务是自动的

  查看事务提交状态: select @@autocommit;

    0: 关闭自动事务提交

    1: 开启自动事务提交(默认)

  修改事务自动提交:

    set @@autocommit = 0;

  注意: mysql的事务提交是自动的,一条sql就是一个独立的事务,

    执行完毕后,会立即提交事务.

四大特性:

A: 原子性,一个事务不可在分割,是一个独立的整体

C: 一致性,事务中的各个单元(sql)要么都成功,要么都失败

I: 隔离性,事务和事务之间是相互隔离的

D: 持久性,事务一旦提交,对数据库的影响是持久的

并发访问mysql相关问题和解决方案:

问题:

  脏读: 一个事务读取到另一个事务未提交的数据

  不可重复度: 在一个事务中执行同一个sql,执行结果不一致(其他事务修改了数据)

  幻读/虚度: 在一个事务中执行同一个sql,执行结果不一致(其他事务增删了数据)

解决方案: 设置事务的隔离级别

  Read uncommitted (读未提交):级别最低,会同时出现三种问题

  Read committed (读已提交):可以解决脏读的问题

  Repeatable read (可重复读):可以解决脏读和不可重复读的问题

  Serializable (串行化):所有的事务都是以串行的方式执行,没有并发执行的事务

索引:

概念: 一种用于快速查询数据的数据结构.

作用: 提高查询效率

优点: 提供高效的查询

缺点:

索引也需要占用硬盘空间

增删改时,不进需要增删改原数据,还要对索引进行维护

mysql中常见的数据结构:

BTree

B+Tree: 默认

创建索引原则:

1.给搜索频次高,但不经常改变的数据添加索引

2.索引字段应当选择较短的字段

3.索引并非是越多越好,因为数据改变时需要维护索引

视图:

视图好比一张临时表,存储一条sql的查询结果.使用方式与表没有太大的区别.

mysql中权限的最小单位是表,如果想具体到字段,可以采用视图.

将指定的查询结果,存放到视图中,然后讲视图的权限分发给指定的用户

触发器:

在指定的情况下被触发,触发后可以做一些日志记录性的相关工作

分类:

创建型触发器,在添加数据时触发

修改型触发器,在修改数据时触发

删除型触发器,在删除数据时触发

存储过程和存储函数:

概念: sql语句集,当调用存储过程或函数时,过程或函数中的sql都会执行

存储过程: 没有返回值

存储函数: 有返回值

目标:

1.什么是事务?

2.mysql中如何进行事务操作?

3.事务的相关特性?

4.并发访问mysql时遇到的相关问题及解决方案?

02.存储函数

定义函数

create function 函数名称(参数名 参数类型) -- 定义函数

returns 数据类型 -- 声明返回值类型

begin

sql语句集

return 变量名称; -- 返回值

end;

调用函数

select 函数名称(参数列表);

需求: 定义一个存储函数,根据传入的条件,计算满足条件的总记录数

设置开启binlog日志

SET GLOBAL log_bin_trust_function_creators=TRUE;

需求:定义一个存储函数,根据传入的条件,计算满足条件的总记录数

DELIMITER $

CREATE FUNCTION city_total(cid INT)

RETURNS INT

BEGIN

DECLARE total INT(11);

SELECT COUNT(*) INTO total FROM city WHERE country_id = cid;

RETURN total;

END$

DELIMITER ;

SELECT city_total(3);

03.存储过程练习

需求:

将转账的业务,写入存储过程,调用存储过程实现转账的业务
ROW_COUNT() : 获取执行的sql对数据库的影响行数

sql

--  创建表的sql
CREATE TABLE account(
  id INT(11) PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(20),
    money FLOAT
);
INSERT INTO account VALUES(NULL,"tom",1000),(NULL,"rose",1000);
-- 存储过程sql

DELIMITER $
CREATE PROCEDURE pro_transfer(
  IN fromSub VARCHAR(50),
  IN toSub VARCHAR(50),
  IN money_ FLOAT
  )
BEGIN  
  DECLARE result INT(2) DEFAULT 0;
  START TRANSACTION;
  UPDATE account SET money=money-money_ WHERE `name`=fromSub;
  IF ROW_COUNT() > 0 THEN
    UPDATE account SET money=money+money_ WHERE `name`=toSub; 
  END IF;
  IF ROW_COUNT() > 0 THEN
    SET result = 1;
    COMMIT;
  ELSE
    ROLLBACK;
  END IF;
  SELECT result;
END $
DELIMITER ;

04.mysql体系结构

整个 MySQL Server由以下组成

  • Connections : 连接组件
  • Connection Pool : 连接池组件
  • Management Services & Utilities : 管理服务和工具组件
  • SQL Interface : SQL 接口组件
  • Parser : 查询分析器组件
  • Optimizer : 优化器组件
  • Caches & Buffers : 缓冲池组件
  • Pluggable Storage Engines : 存储引擎
  • File System : 文件系统
Connections :  连接组件
     Connectors组件,是MySQL向外提供的交互组件,如java,.net,php等语言可以通过该组件来操作SQL语句,实现与SQL的交互。
Management Services & Utilities :  管理服务和工具组件
     提供对MySQL的集成管理,如备份(Backup),恢复(Recovery),安全管理(Security)等 
Connection Pool :  连接池组件
     负责监听对客户端向MySQL Server端的各种请求,接收请求,转发请求到目标模块。每个成功连接MySQL Server的客户请求都会被创建或分配一个线程,该线程负责客户端与MySQL Server端的通信,接收客户端发送的命令,传递服务端的结果信息等。
SQL Interface : SQL 接口组件
     接收用户SQL命令,如DML,DDL和存储过程等,并将最终结果返回给用户。
Parser :  查询分析器组件
     首先分析SQL命令语法的合法性,并尝试将SQL命令分解成数据结构,若分解失败,则提示SQL语句不合理。
Optimizer :  优化器组件
     对SQL命令按照标准流程进行优化分析。
Caches & Buffers :  缓冲池组件
     缓存和缓冲组件
Pluggable Storage Engines :  存储引擎
File System :  文件系统

连接层

最上层是一些客户端和链接服务,包含本地socket 通信和大多数基于客户端/服务端工具实现的类似于 TCP/IP的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。

服务层

第二层架构主要完成大多数的核心服务功能,如 SQL接口,并完成缓存的查询,SQL的分析和优化,部分内置函数的执行。所有跨存储引擎的功能也在这一层实现,如 过程、函数等。在该层,服务器会解析查询并创建相应的内部解析树,并对其完成相应的优化如确定表的查询的顺序,是否利用索引等, 最后生成相应的执行操作。如果是select语句,服务器还会查询内部的缓存,如果缓存空间足够大,这样在解决大量读操作的环境中能够很好的提升系统的性能。

引擎层

存储引擎层, 存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需要,来选取合适的存储引擎。

存储层

数据存储层, 主要是将数据存储在文件系统之上,并完成与存储引擎的交互。

mysql的不同

和其他数据库相比,MySQL有点与众不同,它的架构可以在多种不同场景中应用并发挥良好作用。主要体现在存储引擎上,插件式的存储引擎架构,将查询处理和其他的系统任务以及数据的存储提取分离。这种架构可以根据业务的需求和实际需要选择合适的存储引擎。

05.存储引擎概述和常见存储引擎

1 存储引擎概述

和大多数的数据库不同, MySQL中有一个存储引擎的概念, 针对不同的存储需求可以选择最优的存储引擎。

存储引擎就是存储数据,建立索引,更新查询数据等等技术的实现方式 。存储引擎是基于表的,而不是基于库的。所以存储引擎也可被称为表类型。

Oracle,SqlServer等数据库只有一种存储引擎。MySQL提供了插件式的存储引擎架构。所以MySQL存在多种存储引擎,可以根据需要使用相应引擎,或者编写存储引擎。

MySQL5.0支持的存储引擎包含 : InnoDB 、MyISAM 、BDB、MEMORY、MERGE、EXAMPLE、NDB Cluster、ARCHIVE、CSV、BLACKHOLE、FEDERATED等,其中InnoDB和BDB提供事务安全表,其他存储引擎是非事务安全表。

可以通过指定 show engines , 来查询当前数据库支持的存储引擎 :

创建新表时如果不指定存储引擎,那么系统就会使用默认的存储引擎, MySQL5.5之前的默认存储引擎是

MyISAM,5.5之后就改为了InnoDB。

查看Mysql数据库默认的存储引擎 , 指令

show variables like '%storage_engine%';

2 存储引擎特性

下面重点介绍几种常用的存储引擎, 并对比各个存储引擎之间的区别, 如下表所示 :

特点 InnoDB MyISAM MEMORY MERGE NDB
存储限制 64TB 没有
事务安全 支持
锁机制 行锁(适合高并发) 表锁 表锁 表锁 行锁
B树索引 支持 支持 支持 支持 支持
哈希索引 支持
全文索引 支持(5.6版本后) 支持
集群索引 支持
数据索引 支持 支持 支持
索引缓存 支持 支持 支持 支持 支持
数据可压缩 支持
空间使用 N/A
内存使用 中等
批量插入速度
支持外键 支持

下面我们将重点介绍最长使用的两种存储引擎: InnoDB、MyISAM , 另外两种 MEMORY、MERGE , 了解即可。

06.InnoDB

InnoDB存储引擎是Mysql的默认存储引擎。InnoDB存储引擎提供了具有提交、回滚、崩溃恢复能力的事务安全。但是对比MyISAM的存储引擎,InnoDB写的处理效率差一些,并且会占用更多的磁盘空间以保留数据和索引。

InnoDB存储引擎不同于其他存储引擎的特点 :

事务控制

InnoDB是支持事务控制,示例如下:

sql脚本:

create table goods_innodb(
    id int NOT NULL AUTO_INCREMENT,
    name varchar(20) NOT NULL,
     primary key(id)
)ENGINE=innodb DEFAULT CHARSET=utf8;

执行sql:

start transaction;
insert into goods_innodb(id,name)values(null,'华为p40');
commit;

外键约束

MySQL支持外键的存储引擎只有InnoDB , 在创建外键的时候, 要求主表必须有对应的索引 ,从表在创建外键的时候,关联对应的索引字段。

下面两张表中 , country_innodb是主表 , country_id为主键索引,city_innodb表是从表,country_id字段为外键,对应于country_innodb表的主键country_id 。

create table country_innodb(
    country_id int NOT NULL AUTO_INCREMENT,
     country_name varchar(100) NOT NULL,
     primary key(country_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

create table city_innodb(
   city_id int NOT NULL AUTO_INCREMENT,
     city_name varchar(50) NOT NULL,
     country_id int NOT NULL,
     primary key(city_id),
     key idx_fk_country_id(country_id),
     CONSTRAINT `fk_city_country` FOREIGN KEY(country_id) REFERENCES
    country_innodb(country_id) ON DELETE RESTRICT ON UPDATE CASCADE
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into country_innodb values(null,'China'),(null,'America'),(null,'Japan');
insert into city_innodb values(null,'Xian',1),(null,'NewYork',2),
(null,'BeiJing',1);

删除country_id为1 的country数据:

delete from country_innodb where country_id = 1;

更新主表 country表的字段 country_id :

update country_innodb set country_id = 100 where country_id = 1; 

知识小贴士

在创建索引时, 可以指定在删除、更新主表时,对从表进行的相应操作,包括 RESTRICT、CASCADE、SET NULL和 NO ACTION。

RESTRICT和NO ACTION相同, 是指限制在从表有关联记录的情况下, 主表不能更新;

CASCADE表示主表在更新或者删除时,更新或者删除从表对应的记录;

SET NULL 则表示主表在更新或者删除的时候,从表的对应字段被SET NULL 。

针对上面创建的两个表, 从表的外键指定是ON DELETE RESTRICT ON UPDATE CASCADE 方式的, 那么在主表删除记录的时候, 如果从表有对应记录, 则不允许删除, 主表在更新记录的时候, 如果从表有对应记录, 则从表对应更新 。

存储位置

表结构存放在.frm文件中

数据和索引保存在.idb文件中

07.MyISAM

MyISAM

MyISAM 不支持事务、也不支持外键,其优势是访问的速度快,对事务的完整性没有要求或者以SELECT、INSERT为主的应用基本上都可以使用这个引擎来创建表 。有以下两个比较重要的特点:

不支持事务

create table goods_myisam(
    id int NOT NULL AUTO_INCREMENT,
    name varchar(20) NOT NULL,
     primary key(id)
)ENGINE=myisam DEFAULT CHARSET=utf8;

执行sql

insert into goods_myisam values(null,'笔记本');

通过测试,我们发现,在 MyISAM存储引擎中,是没有事务控制的

文件存储方式

每个MyISAM在磁盘上存储成3个文件,其文件名都和表名相同,但拓展名分别是 :

  • .frm (存储表定义);
  • .MYD(MYData , 存储数据);
  • .MYI(MYIndex , 存储索引);

08.存储引擎选择

在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合。以下是几种常用的存储引擎的使用环境。

  • InnoDB : 是Mysql的默认存储引擎,用于事务处理应用程序,支持外键。如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询意外,还包含很多的更新、删除操作,那么InnoDB存储引擎是比较合适的选择。InnoDB存储引擎除了有效的降低由于删除和更新导致的锁定, 还可以确保事务的完整提交和回滚,对于类似于计费系统或者财务系统等对数据准确性要求比较高的系统,InnoDB是最合适的选择。
  • MyISAM : 如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不是很高,那么选择这个存储引擎是非常合适的。
  • MEMORY :将所有数据保存在内存中,在需要快速定位记录和其他类似数据环境下,可以提供几块的访问。MEMORY的缺陷就是对表的大小有限制,太大的表无法缓存在内存中,其次是要确保表的数据可以恢复,数据库异常终止后表中的数据是可以恢复的。MEMORY表通常用于更新不太频繁的小表,用以快速得到访问结果。
  • MERGE :用于将一系列等同的MyISAM表以逻辑方式组合在一起,并作为一个对象引用他们。MERGE表的优点在于可以突破对单个MyISAM表的大小限制,并且通过将不同的表分布在多个磁盘上,可以有效的改善MERGE表的访问效率。这对于存储诸如数据仓储等VLDB环境十分合适。

09.锁-概述和分类

1 锁概述

锁是计算机协调多个进程或线程并发访问某一资源的机制(避免争抢)。

在数据库中,除传统的计算资源(如 CPU、RAM、I/O 等)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。

2 锁分类

从对数据操作的粒度分 :

  • 表锁:操作时,会锁定整个表。
  • 行锁:操作时,会锁定当前操作行。

从对数据操作的类型分:

  • 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响。
  • 写锁(排它锁):当前操作没有完成之前,它会阻断其他写锁和读锁。

3 mysql锁

相对其他数据库而言,MySQL的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制。

10.myisam-表的读写锁

MyISAM 存储引擎只支持表锁,这也是MySQL开始几个版本中唯一支持的锁类型。

如何加表锁

MyISAM 在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATE、DELETE、INSERT 等)前,会自动给涉及的表加写锁,这个过程并不需要用户干预,因此,用户一般不需要直接用 LOCKTABLE 命令给 MyISAM 表显式加锁。

加表锁语法

加读锁 : lock table table_name read;
加写锁 : lock table table_name write;

读锁案例

准备环境

create database demo03 default charset=utf8mb4;
use demo03;

CREATE TABLE `tb_book` (
    `id` INT(11) auto_increment,
    `name` VARCHAR(50) DEFAULT NULL,
    `publish_time` DATE DEFAULT NULL,
    `status` CHAR(1) DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE=myisam DEFAULT CHARSET=utf8 ;

INSERT INTO tb_book (id, name, publish_time, status) VALUES(NULL,'java编程思
想','2088-08-01','1');
INSERT INTO tb_book (id, name, publish_time, status) VALUES(NULL,'solr编程思
想','2088-08-08','0');

CREATE TABLE `tb_user` (
    `id` INT(11) auto_increment,
    `name` VARCHAR(50) DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE=myisam DEFAULT CHARSET=utf8 ;

INSERT INTO tb_user (id, name) VALUES(NULL,'令狐冲');
INSERT INTO tb_user (id, name) VALUES(NULL,'田伯光');

示例

客户端 一:

1 获得tb_book 表的读锁:

lock table tb_book read;

2 执行查询操作

select * from tb_book;

可以正常执行 , 查询出数据。

客户端 二 :

3 执行查询操作

select * from tb_book; 

客户端 一 :

4 查询未锁定的表

select name from tb_user; 

客户端 二 :

5 查询未锁定的表

select name from tb_user; 

可以正常查询出未锁定的表;

客户端 一 :

6 执行插入操作

insert into tb_book values(null,'Mysql 高级','2088-01-01','1');

执行插入, 直接报错 , 由于当前tb_book 获得的是 读锁, 不能执行更新操作。

客户端 二 :

7 执行插入操作

insert into tb_book values(null,'Mysql 高级','2088-01-01','1'); 

当在客户端一中释放锁指令 ==unlock tables== 后 , 客户端二中的 inesrt 语句 , 立即执行 ;

写锁案例

客户端 一 :

1 获得tb_book 表的写锁

lock table tb_book write ; 

2 执行查询操作

select * from tb_book ; 

3 执行更新操作

update tb_book set name = 'java 编程思想(第二版)' where id = 1; 

客户端 二 :

4 执行查询操作

select * from tb_book ; 

当在客户端一中释放锁指令 unlock tables 后 , 客户端二中的 select 语句 , 立即执行 ;

结论

锁模式的相互兼容性如表中所示:

由上表可见:

1) 对MyISAM 表的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求;

2) 对MyISAM 表的写操作,则会阻塞其他用户对同一表的读和写操作;

简而言之,就是读锁会阻塞写,但是不会阻塞读。而写锁,则既会阻塞读,又会阻塞写。

此外,MyISAM 的读写锁调度是写优先,这也是MyISAM不适合做写为主的表的存储引擎的原因。因为写锁后,其他线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永远阻塞。

查看锁的争用情况

show open tables ;

In_user : 表当前被查询使用的次数。如果该数为零,则表是打开的,但是当前没有被使用。

Name_locked:表名称是否被锁定。名称锁定用于取消表或对表进行重命名等操作。

show status like 'Table_locks%'; 

Table_locks_immediate : 指的是能够立即获得表级锁的次数,每立即获取锁,值加1。

Table_locks_waited : 指的是不能立即获取表级锁而需要等待的次数,每等待一次,该值加1,此值高说明存在着较为严重的表级锁争用情况。

查询操作执行成功;

11.innodb-行的读写锁

行锁介绍

行锁特点 :偏向InnoDB 存储引擎,开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

InnoDB 与 MyISAM 的最大不同有两点:一是支持事务;二是 采用了行级锁。

InnoDB的行锁模式

  • InnoDB 实现了以下两种类型的行锁。

    共享锁( S):又称为读锁,简称S锁,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。

  • 排他锁( X):又称为写锁,简称X锁,排他锁就是不能与其他锁并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。

对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);

对于普通SELECT语句,InnoDB不会加任何锁;

可以通过以下语句显示给记录集加共享锁或排他锁 。

共享锁( S): SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE
排他锁(X) : SELECT * FROM table_name WHERE ... FOR UPDATE

案例准备工作

create table test_innodb_lock(
    id int(11),
    name varchar(16),
    sex varchar(1)
)engine = innodb default charset=utf8;

insert into test_innodb_lock values(1,'100','1');
insert into test_innodb_lock values(3,'3','1');
insert into test_innodb_lock values(4,'400','0');
insert into test_innodb_lock values(5,'500','1');
insert into test_innodb_lock values(6,'600','0');
insert into test_innodb_lock values(7,'700','0');
insert into test_innodb_lock values(8,'800','1');
insert into test_innodb_lock values(9,'900','1');
insert into test_innodb_lock values(1,'200','0');

create index idx_test_innodb_lock_id on test_innodb_lock(id);
create index idx_test_innodb_lock_name on test_innodb_lock(name);

行锁基本演示

以上, 操作的都是同一行的数据,接下来,演示不同行的数据 :

无索引行锁升级为表锁

如果不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,实际效果跟表锁一样。

查看当前表的索引 :

show index from test_innodb_lock ;

由于 执行更新时 , name字段本来为varchar类型, 我们是作为数组类型使用,存在类型转换,索引失效,最终行锁变为表锁 ;

间隙锁危害

当我们用范围条件,而不是使用相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据进行加锁; 对于键值在条件范围内但并不存在的记录,叫做 "间隙(GAP)" , InnoDB也会对这个 "间隙" 加锁,这种锁机制就是所谓的 间隙锁(Next-Key锁) 。

示例:

InnoDB行锁争用情况

show status  like 'innodb_row_lock%';

Innodb_row_lock_current_waits:  当前正在等待锁定的数量

Innodb_row_lock_time: 从系统启动到现在锁定总时间长度

Innodb_row_lock_time_avg:每次等待所花平均时长

Innodb_row_lock_time_max:从系统启动到现在等待最长的一次所花的时间

Innodb_row_lock_waits: 系统启动后到现在总共等待的次数

当等待的次数很高,而且每次等待的时长也不小的时候,我们就需要分析系统中为什么会有如此多的等待,然后根据分析结果着手制定优化计划。

12.上午总结

InnoDB 存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面带来了性能损耗可能比表锁会更高一些,但是在整体并发处理能力方面要远远由于MyISAM的表锁的。当系统并发量较高的时候,InnoDB的整体性能和MyISAM相比就会有比较明显的优势。

但是,InnoDB的行级锁同样也有其脆弱的一面,当我们使用不当的时候,可能会让InnoDB的整体性能表现不仅不能比MyISAM高,甚至可能会更差。

优化建议:

  • 尽可能让所有数据检索都能通过索引来完成,避免无索引行锁升级为表锁。
  • 合理设计索引,尽量缩小锁的范围
  • 尽可能减少索引条件,及索引范围,避免间隙锁
  • 尽量控制事务大小,减少锁定资源量和时间长度
  • 尽可使用低级别事务隔离(但是需要业务层面满足需求)

总结

存储函数:
    注意: 需要在数据库中开启binlog支持
    配置方式:
        命令方式: SET GLOBAL log_bin_trust_function_creators=TRUE;
        配置方式: 
            修改my.cnf文件在文件中添加: 编码/etc/my.cnf
            [mysqld]
            log_bin_trust_function_creators=1
            注意: 配置完毕后,重启mysql
            systemctl restart mysql
体系结构: 了解
    连接层: dos窗口 sqlyog java代码
    服务层: 提供服务,建立连接,接收sql,解析sql,优化sql,缓存数据(缓存sql)
    引擎层: 存储引擎所在的层,用于读写数据
    存储层: 硬盘,数据最终存放的位置
存储引擎:
    作用: mysql之所以可以将数据存放到硬盘,就是通过存储引擎实现的
    常见的存储引擎:
        InnoDB: 默认,支持事务,支持外键,行级锁
        MyISAM: 不支持事务,不支持外键,表级锁
锁:
    作用: 解决并发访问mysql的相关问题
    根据粒度:
        表级锁: 直接给表加锁
        行级锁: 给一行数据加锁
    根据操作行为:
        读锁(共享锁): 从mysql中查询数据
        写锁(排它锁): 往mysql中增删改数据

13.jdbc概述和特点

什么是JDBC

Java DataBase Connectivity:Java的数据库连接技术,它是一组Java语言编写的接口API,给多种数据库提供统一的访问接口,是Java访问数据库的标准规范

JDBC访问数据库需要驱动程序,利用驱动程序和数据库进行通信。驱动程序是Java语言编写的实现了JDBC接口的类,==由第三方数据库厂商提供==

接口: 规范
    接口具有强制性,强制实现类必须拥有指定的方法,方法名称,返回值,参数列表都不可修改
    父接口 对象名称 = new 子实现类(); // 多态
JDBC本质上就是一组接口,制定了java连接数据库的规范,实现类由各个数据库厂商提供,这些实现类我们称之为驱动.
驱动: 实现了接口的实现类.
    java代码想连接哪个数据库,就将那个数据库的驱动导入即可.

JDBC的特点

  1. 它是一组接口,我们的程序是面向接口开发
  2. 不需要关注具体的实现细节,驱动程序由数据库厂商提供

面向接口开发的优势

父接口 对象名 = new 子实现类();
  1. 程序不产生耦合
  2. 易于程序扩展
  3. 方便程序维护
高内聚,低耦合:
  高内聚: 一个方法只做一件事情
    耦合: 程序代码之间的依赖关系

14.jdbcapi-连接数据库流程

JDBC的核心接口和类


接口或类 作用
DriverManager类 创建数据库连接
Connection接口 表示一个数据库连接对象
Statement接口 表示一个发送给数据库执行的SQL语句
ResultSet接口 数据库返回的查询结果集

JDBC访问数据库的步骤

  1. 利用DriverManager创建数据库连接
  2. 获取一个连接对象,客户端利用Statement对象发送SQL到数据库
  3. 数据库执行SQL语句
  4. 数据库返回查询的结果集ResultSet给客户端

15.入门案例

新建模块的另一种方式,在file里面的项目结构

点加号,添加模块

导包,在模块文件夹右击new-directory

CTRL+c复制文件包,点lib 再CTRL+v粘贴

导完后,右击lib点add as library

然后点ok

框架底层代码*

package com.itheima.jdbc;

import java.sql.*;

public class Demo01 {
    /**
     * 编写Java代码连接数据库
     */
    public static void main(String[] args) throws Exception {
        //导入MySQL的驱动包(jdbc接口的实现类包)
        //1.获取连接对象
        String url ="jdbc:mysql://127.0.0.1:3306/db1";
        String user = "root";
        String password = "root";
        Connection conn = DriverManager.getConnection(url, user, password);
        //2.获取语句执行者对象--小货车(将sql运送给mysql)
        Statement st = conn.createStatement();
        //3.编写sql语句(sql语句在Java代码中就是一个字符串,句末分号可加可不加)
        String sql="select * from dept";
        //4.执行sql并接收结果集
        ResultSet rs = st.executeQuery(sql);
        //5.处理结果集(获取查询到的数据信息)
        while(rs.next()){
            int id = rs.getInt("id");
            String dname = rs.getString("dname");
            String loc = rs.getString("loc");
            System.out.println(id+" : "+dname+" : "+loc);
        }
        //6.释放资源
        rs.close();
        st.close();
        conn.close();


    }
}

URL用于标识数据库的位置,程序员通过url地址告诉jdbc连接哪个数据库

URL后面参数的作用


如果数据库出现乱码,可以指定参数 ?characterEncoding=utf8,表示让数据库以UTF-8编码来处理数据

jdbc:mysql://localhost:3306/itcast?characterEncoding=utf8

如果服务器是本地,端口号是默认的3306,可以简写为

jdbc:mysql:///itcast?characterEncoding=utf8

16.jdbc-api-DriverManager类

DriverManager类

作用


  1. 注册驱动,JDK1.5之后自动注册
  2. 创建数据库连接
//TODO:0.注册驱动(将Mysql提供的驱动类加载到内存中)
        //DriverManager.registerDriver(new com.mysql.jdbc.Driver());
        //Class.forName("com.mysql.jdbc.Driver");
        /*
        * 在Mysql中有一个Driver类,在该类中实现了java代码与mysql服务器的连接
        * 我们要想连接mysql就需要使用此类对象.该类对象不需要我们自己调用.
        * 我们只需要将类对象创建出来,交给DriverManager管理即可.
        * 在JDK1.5之前需要手动创建Driver对象,并注册给DriverManager
        *       DriverManager.registerDriver(new Driver());
        *       Class.forName("com.mysql.jdbc.Driver");
        *           类加载到内存,执行静态代码块,在静态代码块中完成了自我注册
        * 在JDK1.5只后,jdk中完成了自动注册
        * */

框架底层代码*(用drivermanager类中的静态方法再写一遍)

package com.itheima.jdbc;

import java.sql.*;
import java.util.Properties;

public class Demo01 {

    public static void main(String[] args) throws Exception {
        //TODO:1.获取连接对象
        String url = "jdbc:mysql://192.168.190.149:3306/db1";
        String user = "root";
        String password = "root";
        Properties prop = new Properties();
        prop.setProperty("user",user);
        prop.setProperty("password",password);
        Connection conn = DriverManager.getConnection(url, prop);
        //2.获取语句执行者对象 -- 小货车(将sql运送给mysql)
        Statement st = conn.createStatement();
        //3.编写sql (sql语句在java代码中就是一个字符串)
        String sql = "select * from account";
        //4.执行sql并接收结果集
        ResultSet rs = st.executeQuery(sql);
        //5.处理结果集 (获取查询到的数据信息)
        while(rs.next()){ // 判断是否有下一条数据
            int id = rs.getInt("id");
            String name = rs.getString("name");
            int balance = rs.getInt("balance");
            System.out.println(id+" : "+name+" : "+balance);
        }
        //6.释放资源
        rs.close();
        st.close();
        conn.close();
    }

17.jdbc-api-其他接口

Connection接口

作用


表示一个连接对象,获取的是它的子类对象

方法


Connection接口中的方法 描述
Statement createStatement() 创建一个Statement对象,用来发送SQL语句给数据库服务器执行

Statement接口

作用


表示一个数据库执行的SQL语句,获取的是它的子类对象

方法


Statement接口中的方法 描述
boolean execute(String sql) 作用:可以执行任意的SQL语句,常用来执行DDL 返回值: true:返回的第一个结果是ResultSet对象 false:如果是更新计数或没有结果
int executeUpdate(String sql) 作用:执行增删改操作 返回值:返回影响的记录行数
ResultSet executeQuery(String sql) 作用:用于执行查询操作 返回值:数据库查询的结果集

ResultSet接口

原理图

方法


ResultSet接口中的方法 描述
boolean next() 将指针向下移动一行,如果当前行由数据,就返回true;否则,返回false
数据类型 get数据类型(参数) 1. 根据列名 2. 根据列号,从1开始编号

ResultSet接口中的getXXX()方法

方法签名
boolean getBoolean(String columnLabel);
byte getByte(String columnLabel);
short getShort(String columnLabel);
int getInt(String columnLabel);
long getLong(String columnLabel);
float getFloat(String columnLabel);
double getDouble(String columnLabel);
String getString(String columnLabel);

释放资源


每次访问数据库结束,都需要释放资源

  1. 需要释放的对象:Connection、Statement、ResultSet

  2. 释放顺序:和创建的顺序相反,

    创建是时候:connnection->statement->resultSet

    释放的时候:resultSet->statement->connection

  3. 写在哪里:finally的代码块

  4. 是否可以放在try()代码块中:需要实现AutoCloseable接口,均可以放

18.jdbc-执行ddl语句-创建数据库表

package com.itheima.jdbc;

import org.junit.Test;

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

public class Demo02 {
/**
     * JDBC执行DDL语句:
     * 需求: 创建一张student表
     *
     */
    @Test
   public void test01() throws Exception{
        //1.获取连接
        String url="jdbc:mysql://localhost:3306/day06";
        String user ="root";
        String password="root";
        Connection conn = DriverManager.getConnection(url, user, password);
        //2.获取语句执行者对象   小货车
        Statement st = conn.createStatement();
        //3.编写sql语句
        String sql ="create table stdent(\n" +
                "id int primary key auto_increment,\n" +
                "name varchar(20) not null," +
                "sex boolean,\n" +
                "birthday date\n" +
                ");";
        //4.执行sql并返回结果集
        //注意:execute,执行查询语句返回true,执行非查询语句返回false
        boolean flag = st.execute(sql);
        //5.处理结果
        if(flag){
            System.out.println("查询");
        }else{
            System.out.println("非查询");
        }
        //6.释放资源
        st.close();
        conn.close();

    }
}

  //注意:execute,执行查询语句返回true,执行非查询语句返回false

19.jdbc-执行DML语句-添加

没有程序入口main的话加@Test 也能执行代码

package com.itheima.jdbc;

import org.junit.Test;

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

public class Demo03 {
    @Test
    public void insert() throws Exception {
        //1.获取连接
        String url="jdbc:mysql://localhost:3306/day06";
        Connection conn = DriverManager.getConnection(url, "root", "root");
        //自动补全 抛出异常
        //2.获取语句执行者对象  小货车(将sql语句运送给mysql)
        Statement st = conn.createStatement();
        //3.编写sql语句
        String sql ="insert into student values(null,'大幂幂',0,'2001-01-01')";
        //4.执行sql并接收结果集
        //注意:executeUpdate,执行增删改操作,返回对数据库影响的行数
        int i=st.executeUpdate(sql);
        //5.判断执行结果
        if (i > 0) {
            System.out.println("添加成功");
        }
        //6.释放资源
        st.close();
        conn.close();

    }
}

20.jdbc-执行DML语句-修改和删除

修改

 @Test
    public void update() throws Exception {
        //1.获取连接
        String url="jdbc:mysql://localhost:3306/day06";
        Connection conn = DriverManager.getConnection(url, "root", "root");
        //自动补全 抛出异常
        //2.获取语句执行者对象  小货车(将sql语句运送给mysql)
        Statement st = conn.createStatement();
        //3.编写sql语句
        String sql ="update student set name ='迪丽热巴' where id=1;";
        //4.执行sql并接收结果集
        //注意:executeUpdate,执行增删改操作,返回对数据库影响的行数
        int i=st.executeUpdate(sql);
        //5.判断执行结果
        if (i > 0) {
            System.out.println("修改成功");
        }
        //6.释放资源
        st.close();
        conn.close();

    }

删除

 @Test
    public void delete() throws Exception {
        //1.获取连接
        String url="jdbc:mysql://localhost:3306/day06";
        Connection conn = DriverManager.getConnection(url, "root", "root");
        //自动补全 抛出异常
        //2.获取语句执行者对象  小货车(将sql语句运送给mysql)
        Statement st = conn.createStatement();
        //3.编写sql语句
        String sql ="delete from student where id=1";
        //4.执行sql并接收结果集
        //注意:executeUpdate,执行增删改操作,返回对数据库影响的行数
        int i=st.executeUpdate(sql);
        //5.判断执行结果
        if (i > 0) {
            System.out.println("删除成功");
        }
        //6.释放资源
        st.close();
        conn.close();

    }

21.jdbc-执行DQL语句-查询

package com.itheima.jdbc;

import java.sql.*;

public class Demo04 {
    public static void main(String[] args) throws SQLException {
        //1.获取连接对象
        String url ="jdbc:mysql://localhost:3306/day06";
        Connection conn = DriverManager.getConnection(url,"root","root");
        //2.获取语句执行者对象
        Statement st = conn.createStatement();
        //3.编写sql语句
        String sql="select * from student";
        //4.执行sql并接收结果集(从小货车里面拿出结果集来接收)
        ResultSet rs=st.executeQuery(sql);
        //5处理结果集  遍历
        while(rs.next()){
            int id = rs.getInt("id");
            String name = rs.getString("name");
            Boolean sex = rs.getBoolean("sex");
            Date birthday = rs.getDate("birthday");
            System.out.println(id+" : "+name+" : "+sex+" : "+birthday);
        }
        //6.释放资源
        rs.close();
        st.close();
        conn.close();

    }
}

或者不写字段名,写第几列

package com.itheima.jdbc;

import java.sql.*;

public class Demo04 {
    public static void main(String[] args) throws SQLException {
        //1.获取连接对象
        String url ="jdbc:mysql://localhost:3306/day06";
        Connection conn = DriverManager.getConnection(url,"root","root");
        //2.获取语句执行者对象
        Statement st = conn.createStatement();
        //3.编写sql语句
        String sql="select * from student";
        //4.执行sql并接收结果集(从小货车里面拿出结果集来接收)
        ResultSet rs=st.executeQuery(sql);
        //5处理结果集  遍历
        while(rs.next()){
            int id = rs.getInt(1);
            String name = rs.getString(2);
            Boolean sex = rs.getBoolean(3);
            Date birthday = rs.getDate(4);
            System.out.println(id+" : "+name+" : "+sex+" : "+birthday);
        }
        //6.释放资源
        rs.close();
        st.close();
        conn.close();

    }
}

22.jdbc小结

DriverManager类: 驱动管理类
    Connection getConnection(url,user,password); // 获取连接数据库的对象
Connection接口: 连接对象接口
    Statement createStatement();// 获取语句执行者对象(发送sql给mysql执行-小货车)
    void close(); // 关闭连接
Statement接口: 语句执行者对象,小货车
    boolean execute(sql); // 可以执行所有的sql语句
        执行查询,返回true
        执行非查询,返回false
    int executeUpdate(sql); // 执行增删改,返回影响数据库的行数
    ResultSet executeQuery(sql); // 执行查询,返回查询结果
ResultSet接口: 封装查询结果集
    boolean next();//判断集合中是否有下一条数据
        true: 有下一条
        false: 没有下一条
    getXxx(参数);
        Xxx: 数据类型
        参数:
            字段名
            字段所在的列数(第几列)

23.封装jdbc工具类

框架就是把java代码,把重复的逻辑给封装了

封装原则:

  1. 封装冗余的代码(封装重复的代码)
  2. 封装相同的逻辑

JdbcUtils代码,里面封装了释放资源

package com.itheima.utils;

import java.sql.*;
import java.util.ResourceBundle;

public class JdbcUtils {


    private static String url;
    private static String user;
    private static String password;


//配置文件解析
    static{
        ResourceBundle bundle= ResourceBundle.getBundle("jdbc");
        url = bundle.getString("jdbc.url");
        user = bundle.getString("jdbc.user");
        password = bundle.getString("jdbc.password");
        System.out.println(url+" : "+user+" : "+password);
    }




    //获取连接的方法
    public static Connection getConnection() throws Exception {
        Connection conn = DriverManager.getConnection(url, user, password);
        return conn;
    }


    //释放资源的方法  如果是查询的话释放三个 多一个rs,如果是增删释放两个st,conn
    public static void close(Connection conn, Statement st, ResultSet rs){
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(st!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }
    public static void close(Connection conn, Statement st){
        close(conn,st,null);
    }

}

TestJdbcUtils代码,测试封装的释放资源类

package com.itheima.utils;

import java.sql.*;

public class TestJdbcUtils {
    public static void main(String[] args) throws Exception {
        Connection conn =JdbcUtils.getConnection();
        Statement st = conn.createStatement();
        String sql ="select * from student";
        ResultSet rs=st.executeQuery(sql);
        while(rs.next()){
            int id = rs.getInt("id");
            String name = rs.getString("name");
            boolean sex = rs.getBoolean("sex");
            Date birthday = rs.getDate("birthday");
            System.out.println(id+" : "+name+" : "+"sex"+" : "+birthday);
        }
        JdbcUtils.close(conn,st,rs);



    }
}

配置文件

jdbc.url=jdbc:mysql://localhost:3306/day06
jdbc.user=root
jdbc.password=root

耦合度高时,可以配置文件

把可变的内容写到配置文件

1.创建配置文件,在src中new一个file

例如:jdbc.properties

2.把变量赋的值拿到文件中

例如:

jdbc.url=jdbc:mysql://localhost:3306/day06
jdbc.user=root
jdbc.password=root

3.配置文件解析

  static{
        ResourceBundle bundle= ResourceBundle.getBundle("jdbc");
        url = bundle.getString("jdbc.url");
        user = bundle.getString("jdbc.user");
        password = bundle.getString("jdbc.password");
        System.out.println(url+" : "+user+" : "+password);
    }

解析出来如下

但是一遍配置文件不在src中,而是再模块下新建一个文件夹new→directory,命名为resource

此时还不行,还需要标记一下,右击点Mark Directory as里面的resource root

你可能感兴趣的:(黑马程序员Java全程笔记,java,mysql)