事务: ★
概述: 逻辑上的一组操作,组成这组操作的各个单元要么同时成功,要么同时失败.
事务是一个最小的执行单元.
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时遇到的相关问题及解决方案?
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);
需求:
将转账的业务,写入存储过程,调用存储过程实现转账的业务
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 ;
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中有一个存储引擎的概念, 针对不同的存储需求可以选择最优的存储引擎。
存储引擎就是存储数据,建立索引,更新查询数据等等技术的实现方式 。存储引擎是基于表的,而不是基于库的。所以存储引擎也可被称为表类型。
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%';
下面重点介绍几种常用的存储引擎, 并对比各个存储引擎之间的区别, 如下表所示 :
特点 | InnoDB | MyISAM | MEMORY | MERGE | NDB |
---|---|---|---|---|---|
存储限制 | 64TB | 有 | 有 | 没有 | 有 |
事务安全 | 支持 | ||||
锁机制 | 行锁(适合高并发) | 表锁 | 表锁 | 表锁 | 行锁 |
B树索引 | 支持 | 支持 | 支持 | 支持 | 支持 |
哈希索引 | 支持 | ||||
全文索引 | 支持(5.6版本后) | 支持 | |||
集群索引 | 支持 | ||||
数据索引 | 支持 | 支持 | 支持 | ||
索引缓存 | 支持 | 支持 | 支持 | 支持 | 支持 |
数据可压缩 | 支持 | ||||
空间使用 | 高 | 低 | N/A | 低 | 低 |
内存使用 | 高 | 低 | 中等 | 低 | 高 |
批量插入速度 | 低 | 高 | 高 | 高 | 高 |
支持外键 | 支持 |
下面我们将重点介绍最长使用的两种存储引擎: InnoDB、MyISAM , 另外两种 MEMORY、MERGE , 了解即可。
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文件中
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个文件,其文件名都和表名相同,但拓展名分别是 :
在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合。以下是几种常用的存储引擎的使用环境。
锁是计算机协调多个进程或线程并发访问某一资源的机制(避免争抢)。
在数据库中,除传统的计算资源(如 CPU、RAM、I/O 等)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。
从对数据操作的粒度分 :
从对数据操作的类型分:
相对其他数据库而言,MySQL的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制。
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,此值高说明存在着较为严重的表级锁争用情况。
查询操作执行成功;
行锁特点 :偏向InnoDB 存储引擎,开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
InnoDB 与 MyISAM 的最大不同有两点:一是支持事务;二是 采用了行级锁。
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锁) 。
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: 系统启动后到现在总共等待的次数
当等待的次数很高,而且每次等待的时长也不小的时候,我们就需要分析系统中为什么会有如此多的等待,然后根据分析结果着手制定优化计划。
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中增删改数据
Java DataBase Connectivity:Java的数据库连接技术,它是一组Java语言编写的接口API,给多种数据库提供统一的访问接口,是Java访问数据库的标准规范
JDBC访问数据库需要驱动程序,利用驱动程序和数据库进行通信。驱动程序是Java语言编写的实现了JDBC接口的类,==由第三方数据库厂商提供==
接口: 规范
接口具有强制性,强制实现类必须拥有指定的方法,方法名称,返回值,参数列表都不可修改
父接口 对象名称 = new 子实现类(); // 多态
JDBC本质上就是一组接口,制定了java连接数据库的规范,实现类由各个数据库厂商提供,这些实现类我们称之为驱动.
驱动: 实现了接口的实现类.
java代码想连接哪个数据库,就将那个数据库的驱动导入即可.
父接口 对象名 = new 子实现类();
高内聚,低耦合:
高内聚: 一个方法只做一件事情
耦合: 程序代码之间的依赖关系
JDBC的核心接口和类
接口或类 | 作用 |
---|---|
DriverManager类 | 创建数据库连接 |
Connection接口 | 表示一个数据库连接对象 |
Statement接口 | 表示一个发送给数据库执行的SQL语句 |
ResultSet接口 | 数据库返回的查询结果集 |
JDBC访问数据库的步骤
新建模块的另一种方式,在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
作用
//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中完成了自动注册
* */
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();
}
作用
表示一个连接对象,获取的是它的子类对象
方法
Connection接口中的方法 | 描述 |
---|---|
Statement createStatement() | 创建一个Statement对象,用来发送SQL语句给数据库服务器执行 |
作用
表示一个数据库执行的SQL语句,获取的是它的子类对象
方法
Statement接口中的方法 | 描述 |
---|---|
boolean execute(String sql) | 作用:可以执行任意的SQL语句,常用来执行DDL 返回值: true:返回的第一个结果是ResultSet对象 false:如果是更新计数或没有结果 |
int executeUpdate(String sql) | 作用:执行增删改操作 返回值:返回影响的记录行数 |
ResultSet executeQuery(String sql) | 作用:用于执行查询操作 返回值:数据库查询的结果集 |
原理图
方法
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); |
释放资源
每次访问数据库结束,都需要释放资源
需要释放的对象:Connection、Statement、ResultSet
释放顺序:和创建的顺序相反,
创建是时候:connnection->statement->resultSet
释放的时候:resultSet->statement->connection
写在哪里:finally的代码块
是否可以放在try()代码块中:需要实现AutoCloseable接口,均可以放
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
没有程序入口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();
}
}
修改
@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();
}
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();
}
}
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: 数据类型
参数:
字段名
字段所在的列数(第几列)
框架就是把java代码,把重复的逻辑给封装了
封装原则:
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