简介

通过使用JDBC,Java程序可以很方便地操作各种主流数据库,这就是Java语言的魅力所在。

程序使用JDBC API统一的方式来连接不同的数据库,然后通过Statement对象来执行标准的SQL语句,并可以获得SQL语句访问数据库的结果,因此掌握标准的SQL语句是学习JDBC编程的基础。

JDBC:Java Database Connectivity ,即Java数据库连接,它是一种可以执行SQL语句的Java API。

关系数据库基本概念和MySQL基本命令

数据库管理系统:Database Management System,简称DBMS。

它是所有数据的知识库,它负责管理数据的存储、安全、一致性、并发、恢复和访问等操作。DBMS有一个数据字典(有时也被称为系统表),用于存储它拥有的每个事务的相关信息,例如名字、结构、位置和类型,这种关于数据的数据也被称为元数据(metadata)。

查看当前实例下包含多少个数据库

show databases;

如果用户需要创建新的数据库

create database [IF NOT EXISTS] 数据库名;

如果用户需要删除指定数据库

drop database 数据库名;

建立了数据库之后,想要操作该数据库则需要进入该数据库

use 数据库名;

进入该数据库之后,需要查询该数据库下包含多少个数据表

show tables;

如果想查看指定数据表的表结构(查看该表有多少列,每列的数据类型等信息)

desc 表名;

该命令用于启动MySQL命令行客户端。

mysql -u 用户名 -p 密码 -h 主机名 --default-character-set=utf8

SQL语句基础

SQL的全称:Structured Query Language也就是结构化查询语言

标准的SQL语句通常可分为如下几种类型:

  • 查询语句:主要由select关键字完成,查询语句是SQL语句中最复杂、功能最丰富的语句。
  • DML(Data Manipulation Language,数据操作语言)语句:主要由insert、update和delete三个关键字完成。
  • DDL(Data Definition Language,数据定义语言)语句:主要由create、alter、drop和truncate四个关键字完成。
  • DCL(Data Control Language,数据控制语言)语句:主要由grant和revoke两个关键字完成。
  • 事务控制语句:主要由commit、rollback和savepoint三个关键字完成。

DDL语句

1.创建表的语法

create table [模式名.]表名
(
	#可以有多个列定义
	columnName1 datatype[default expr],
);

建立数据表只是建立表结构,就是指定该数据表有多少列,每列的数据类型,所以建表语句的重点就是圆括号里的列定义,列定义由列名、列类型和可选的默认值组成。

例如下面的建表语句

create table test
(
	#整型通常用int
	test_id int,
	#小数点
	test_price decimal,
	#普通长度文本,使用default指定默认值
	test_name varchar(255) default 'xxx',
	#大文本类型
	test_desc text,
	#图片
	test_img blob,
	test_date datetime
);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-45KDkVhX-1639387409699)(/Users/wushiwei/Library/Application Support/typora-user-images/image-20211212093534897.png)]

2.修改表结构的语法

修改表结构使用alter table,修改表结构包括增加列定义、修改列定义、删除列、重命名列等操作。

增加列定义的语法:

alter table 表名
add
(
	#可以有多个列定义
	column_name1 datatype [default expr],
	...
);

为数据表增加字段的SQL语句:

# 为hehe数据表增加一个hehe_id字段,该字段的类型为int
alter table hehe add hehe_id int;
# 为hehe数据表增加aaa、bbb字段,两个字段的类型都为varchar(255)
alter table hehe
add
(
	aaa varchar(255) default 'xxx',
	bbb varchar(255)
);

值得指出的是,SQL语句中的字符串值不是由双引号引起,而是用单引号引起的。

增加字段时需要注意:如果数据表中已有数据记录,除非给新增的列指定了默认值,否则新增的数据列不可以指定非空约束,因为那些已有的记录在新增列上肯定是空(实际上,修改表结构很容易失败,只要新增的约束与已有数据冲突,修改就会失败)

修改列定义的语法:

alter table 表名
modify column_name datatype [default expr] [first | after col_name];

上面语法中first或者after col_name 指定需要将目标修改到指定位置。

例如:

# 将hehe表中的hehe_id列修改成varchar(255)类型
alter table hehe modify hehe_id varchar(255);
# 将hehe表的bbb列修改成int类型
alter table hehe modify bbb int;

add新增的列名必须是原表中不存在的,而modify修改的列名必须是原表中已存在的。

从数据表中删除列:

alter table 表名 drop column_name;

删除列只要在drop后紧跟需要删除的列名即可。

# 删除hehe表中的aaa字段
alter table hehe drop aaa;

从数据表中删除列定义通常总是可以成功,删除列定义时将从每行中删除该列的数据,并释放该列在数据块中占有的空间。所以删除大表中的字段时需要比较长的时间,因为还需要回收空间。

重命名数据表和完全改变列定义

alter table 表名 rename to 新表名;
#将hehe数据表重命名为wawa
alter table hehe rename to wawa;

MySQL为alter table 提供了change选项,该选项可以改变列名。

alter table 表名
change old_column_name new_column_name type [default expr] [first | after col_name];

对比change和modify两个选项,不难发现:change选项比modify选项多了一个列名,因为change选项可以改变列名,所以它需要两个列名。一般而言,如果不需要改变列名,使用alter table 的modify 选项即可,只有当需要修改列名时才会使用change选项

3.删除表的语法

drop table 表名;

删除数据表的效果如下

  • 表结构被删除,表对象不再存在
  • 表里的所有数据也被删除
  • 该表所有相关的索引、约束也被删除。
truncate表

truncate被称为"截断"某个表—它的作用是删除该表里的全部数据,但保留表结构。相对于DML里的delete命令而言,truncate的速度要快得多,而且truncate不像delete可以删除指定的记录,truncate只能一次性删除整个表的全部记录。

truncate 表名

数据库约束

数据库的5种完整性约束

  • NOT NULL – 非空约束,指定某列不能为空
  • UNIQUE – 唯一约束,指定某列或者几列组合不能重复
  • PRIMARY KEY – 主键,指定该列的值可以唯一地标识该条记录
  • FOREIGN KEY – 外键,指定该行记录从属于主表中的一行记录,主要用于保证参照完整性
  • CHECK – 检查,指定一个布尔表达式,用于指定对应列的值必须满足该表达式
FOREIGN KEY

外键约束主要用于保证一个或两个数据表之间的参照完整性,外键是构建于一个表的两个字段或者两个表的两个字段之间的参照关系。外键确保了相关的两个字段的参照关系:子(从)表外键列的值必须在主表被参照列的值范围之内,或者为空(也可以通过非空约束来约束外键列不允许为空)。

当主表的记录被从表记录参照时,主表记录不允许被删除,必须先把从表里参照该记录的所有记录全部删除之后,才能删除主表的该记录。还有一种方式,删除主表记录时级联删除从表中所有参照该记录的从表记录。

DML语句语法

与DDL操作数据库对象不同,DML主要操作数据表里的数据

  • 插入新数据
  • 修改已有的数据
  • 删除不需要的数据

DML语句由insert into 、update和delete from 三个命令组成。

insert into 语句

用于向指定数据表中插入记录。对于标准的SQL语句而言,每次只能插入一条记录。

insert into table_name [(column[,column...])]
values (value [,value...]);

执行插入操作时,表名后可以用括号列出所有需要插入值的列名,而values后用括号列出对应需要插入的值

insert into teacher_table2(teacher_name) values ('xyz');

如果省略了表名后面的括号及括号里的列名列表,默认将为所有列都插入值,则需要为每一列都指定一个值。

insert into teacher_table2 
# 使用null代替主键列的值
values (null,'acb');

MySQL提供了一种扩展的语法,通过这种扩展的语法也可以一次插入多条记录。MySQL允许在values后使用多个括号包含多条记录,表示多条记录的多个括号之间以英文逗号(,)隔开。

insert into teacher_tables2
#同时插入多个值
values(null,"Yeeku"),(null,"Sharfly");

update语句

update语句用于修改数据表的记录,每次可以修改多条记录,通过使用where子句限定修改哪些记录。where子句是一个条件表达式,该条件表达式类似于Java语言的if,只有符合该条件的记录才会被修改。没有where子句则意味着where表达式的值总是true,即该表的所有记录都会被修改。

update table_name set column1 =  value1[,value2]..
[where condition];

使用update语句不仅可以一次修改多条记录,也可以一次修改多列。修改多列通过在set关键字后使用column1=value1,column2=value2…来实现,修改多列的值之间用英文逗号隔开。

delete from语句

delete from 语句用于删除指定数据表的记录。使用delete from语句删除时不需要指定列名,因为总是整行地删除。使用delete from语句可以一次删除多行,删除哪些行采用where子句限定,只删除满足where条件的记录。没有where子句限定将会把表里的全部记录删除。

delete from table_name
[where condition];

多表连接查询

  • 交叉连接
  • 自然连接
  • 使用using子句的连接
  • 使用on子句的连接
  • 全外连接或者左、右外连接

交叉连接

交叉连接效果就是广义笛卡尔积,所以交叉连接无须任何连接条件

select s.* , teacher_name
from student_table s
cross join teacher_table t;

自然连接

自然连接表面上看起来也无须指定连接条件,但自然连接是有连接条件的,自然连接会以两个表中的同名列作为连接条件;如果两个表中没有同名列,则自然连接与交叉连接的效果完全一样—因为没有连接条件

select s.* , teacher_name
from student_table s
natural join teacher_table t;

using子句连接

可以指定一列或多列,用于显示指定两个表中的同名列作为连接条件。假设两个表中有超过一列的同名列,如果使用natural join,则会把所有的同名列当成连接条件;使用using子句,就可显示指定使用哪些同名列作为连接条件。

select s.* , teacher_name
from student_table s
cross join teacher_table t
using (teacher_id);

on子句连接

这是最常用的连接方式,每个on子句只指定一个连接条件。

select s.* , teacher_name
from student_table s
join teacher_table t
on s.java_teacher = t.teacher_id

左、右、全外连接

这三种外连接分别使用left [outer] join、right [outer] join和full [outer] join,这三种外连接的连接条件一样通过on子句来指定,既可以是等值连接条件,也可以是非等值连接条件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VJCOXyKY-1639387409700)(/Users/wushiwei/Library/Application Support/typora-user-images/image-20211213110424460.png)]

左外连接将会把左边表中所有不满足连接条件的记录全部列出,右外连接将会把右边表中所有不满足连接条件的记录全部列出。

子查询

子查询就是指在查询语句中嵌套另一个查询,子查询可以支持多层嵌套。对于一个普通的查询语句而言,子查询可以出现在两个位置。

  • 出现在from语句后当成数据表
  • 出现在where条件后作为过滤条件的值

使用子查询时需要注意以下几点:

  1. 子查询要用括号括起来
  2. 把子查询当成数据表时(出现在from之后),可以为该子查询起别名,尤其是作为前缀来限定数据列时,必须给子查询起别名
  3. 把子查询当成过滤条件时,将子查询放在比较运算符的右边,这样可以增强查询的可读性。
  4. 把子查询当成过滤条件时,单行子查询使用单行运算符,多行子查询使用多行运算符。

JDBC的典型用法

JDBC常用接口和类简介

  • DriverManager:用于管理JDBC驱动的服务类。程序中使用该类的主要功能是获取Connection对象,该类包含如下方法。
public static synchronized Connection getConnection(String url,String user,String pass)
throws SQLException   #该方法获得url对应数据库的连接。
  • Connection:代表数据库连接对象,每个Connection代表一个物理连接会话,要想访问数据库,必须先获取数据库连接。
Statement createStatement() throws SQLException:该方法返回一个Statement对象。

PreparedStatement prepareStatement(String sql) throws SQLException: 该方法返回预编译的Statement对象,即将SQL语句提交到数据库进行预编译。

CallableStatement prepareCall(String sql) throws SQLException:该方法返回CallableStatement对象,该对象用于调用存储过程。

这三个方法都返回用于执行SQL语句的Statement对象,只有获得了Statement之后才可执行SQL语句。

  • Statement:用于执行SQL语句的工具接口。该对象既可用于执行DDL、DCL语句,也可用于执行DML语句,还可用于执行SQL查询,当执行SQL查询时,返回查询到的结果集。
  • PreparedStatement:预编译的Statement对象。它允许数据库预编译SQL语句(这些SQL语句通常带有参数),以后每次只改变SQL命令的参数,避免数据库每次都需要编译SQL语句,因此性能更好,只要为预编译的SQL语句传入参数值即可。
  • ResultSet:结果集对象。该对象包含访问查询结果的方法,ResultSet可以通过列索引或列名获得列数据。

JDBC编程步骤

加载数据库驱动。通常使用Class类的forName()静态方法来加载驱动

Class.forName(driverClass);
// 加载MySQL驱动
Class.forName("com.mysql.jdbc.Driver")
// 加载Oracle驱动
Class.forName("oracle.jdbc.driver.OracleDriver")

通过DriverManager获取数据库连接。

// 获取数据库连接
DriverManager.getConnection(String url,String user,String pass);

通过Connection对象创建Statement对象。

  • createStatement():创建基本的Statement对象
  • prepareStatement(String sql): 根据传入的SQL语句创建预编译的Statement对象。
  • prepareCall(String sql): 根据传入的SQL语句创建CallableStatement对象。

使用Statement执行SQL语句。

  • execute():可以执行任何SQL语句。
  • executeUpdate():主要用于执行DML和DDL语句,执行DML语句返回受SQL语句影响的行数,执行DDL语句返回0
  • executeQuery():只能执行查询语句,执行后返回代表查询结果的ResultSet对象。

操作结果集

如果执行的SQL语句是查询语句,则执行结果将返回一个ResultSet对象,该对象里保存了SQL语句查询的结果。

回收数据库资源

执行SQL语句的方式

使用executeUpdate方法执行DDL和DML语句

区别是executeUpdate()执行DDL语句后返回0,而执行DML语句后返回受影响的记录条数。

使用execute方法执行SQL语句

使用execute()方法执行SQL语句的返回值只是boolean值,它表明执行该SQL语句是否返回了ResultSet对象。

那么如何获取执行SQL语句后得到的ResultSet对象呢?Statement提供了如下两个方法来获取执行结果。

  • getResultSet():获取该Statement执行查询语句所返回的ResultSet对象。
  • getUpdateCount():获取该Statement执行DML语句所影响的记录行数。

使用ResultSetMetaData分析结果集

当执行SQL查询后可以通过移动记录指针来遍历ResultSet的每条记录,但程序可能不清楚该ResultSet里包含哪些数据列,以及每个数据列的数据类型,那么可以通过ResultSetMetaData来获取关于ResultSet的描述信息。

MetaData的意思是元数据,即描述其他数据的数据,因此ResultSetMetaData封装了描述ResultSet对象的数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TMSxHmbh-1639387409700)(/Users/wushiwei/Library/Application Support/typora-user-images/image-20211213160041704.png)]

JDBC的事务支持

JDBC连接的事务支持由Connection提供,Connection默认打开自动提交,即关闭事务,在这种情况下,每条SQL语句一旦执行,便会立即提交到数据库,永久生效,无法对其进行回滚操作。

可以调用Connection的setAutoCommit()方法来关闭自动提交,开启事务。

// 关闭自动提交,开启事务
conn.setAutoCommit(false);
// 提交事务
conn.commit();
// 回滚事务
conn.rollback();

使用连接池管理连接

DBCP数据源