JDBC(Java Database Connectivity)
一、JDBC简介
+++
1. JDBC是什么?
-
JDBC是JAVA语言连接数据库的一套工具API【应用程序编程接口】. -
JDBC主要由JDK的java.sql包和javax.sql.包提供核心API接口。
2. 三方的责任划分?
-
JAVA官方 JAVA官方制定如何连接数据库的标准
-
数据库厂商 数据库厂商来实现标准,即提供“驱动”;真正让JAVA语言真正连接数据库的代码
-
开发人员(我们) 开发人员学习标准使用实现
-
代码
public interface Connection{}
public class MySqlConnection implements Connection{}
-
标准、实现和使用:
-
因为有实现,所以我们不用自己实现JAVA语言连接每一个我们要用到的数据库;因为有标准,所以我们不用查看每一个具体实现的差异;我们只是:【学标准,用实现】 -
但凡是需要JAVA语言连接数据库的地方,都需要“驱动”,也就是jar包 mysql.jar
二、JDBC的核心API?
+++
JDBC核心的API主要来自java.sql 包和javax.sql 包。
1. java.sql.Driver 接口
-
作用
驱动,真正创建连接的API。 每一个驱动包都提供了一个获得连接的真正实现类。 -
核心方法
2. java.sql.DriverManager 类
3. java.sql.Connection 接口
-
作用
创建执行SQL的Statement执行器 管理事务 其他操作 -
核心方法
4. java.sql.Statement 接口
-
作用
执行SQL语句 获得或者设置当前执行器对象的特性 -
核心方法
5. java.sql.ResultSet 接口
-
作用
存放数据库查询的返回值 提供了可滚动和可更新的方法 -
核心方法
三、JDBC的基本流程?
+++
1. 基本步骤
1. 加载驱动【注册造桥的人】
2. 获得与数据库的链接【造桥】
3. 创建执行SQL语句的Statement对象【造车】
4. 执行SQL语句,需要返回结果的话返回结果【运送资源】
5. 处理结果【返回值】
6. 关闭资源【桶销毁、车报废、桥拆除】
2. 代码流程
import java.sql.*;
public class HelloJDBC{
public static void main(String args[])throws Exception{
Class.forName("com.mysql.cj.jdbc.Driver");
String url="jdbc:mysql://127.0.0.1:3309/et2110?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true";
String user="root";
String pwd="etoak";
Connection con = DriverManager.getConnection(url,user,pwd);
Statement sta = con.createStatement();
ResultSet rs = sta.executeQuery("select * from student");
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
String phone = rs.getString("phone");
String email = rs.getString("email");
System.out.println(id+"\t"+name+"\t"+age+"\t"+
phone+"\t"+email);
}
rs.close();sta.close();con.close();
}
}
四、JDBC基本流程中的每一个步骤的细节?
+++
1. 加载驱动的底层?
-
Class.forName(“com.mysql.cj.jdbc.Driver”) Class是一个用来描述类的类,其中forName() 方法用来加载制定名字的类的。所以以上Class.forName(“”)加载mysql的驱动类。 -
在com.mysql.cj.jdbc.Driver 类中有一个静态代码块。当类加载时静态代码块执行。 -
在DriverManager 中的registerDriver 方法中,把该驱动对象存放到内部(DriverManager )的集合中
image-20220618172400638
2. DriverManager 获得连接的方式?
-
三个参数获的连接 getConnection(url,user,pwd) -
两个参数的获得连接:getConnection(url,Properties info)
3. Statement执行器中执行SQL语句的方法介绍?
Statement 代表执行SQL语句的执行器,我们习惯叫运送SQL的车,其中有几个主要的执行方法需要重点掌握.
-
execute方法 结论: 1.execute是万能方法,可以执行任意的SQL语句。 2.execute方法返回值是以是否返回ResultSet为判断标准的。 select --》ResultSet —>true show table—>ResultSet—>true insert update delete—>没有ResultSet–>false -
executeUpdate方法 结论:
- 专门负责执行insert/update/delete语句,返回对数据库影响的行数
- 不能执行select
-
executeQuery方法 结论: ? 1.专门执行返回ResultSet的语句 -
executeBatch方法
- 批处理
JDBC中每一条SQL都是靠着以上方法执行的,选择合适的方法执行合适的SQL至关重要
4. ResultSet介绍?
-
ResultSet表示执行SQL查询语句返回的结果。有一个指向当前行的光标,最开始,光标在第一行之前,next方法将光标向下移动一行,因为该方法在没有下一行时返回false,所以可以在while循环中迭代输出。 -
ResultSet中的数据,即可以通过列名字,也可以通过列的索引下标,从1开始访问。
5. 预编译的执行器PreparedStatement使用?
-
预编译的Statement,即PreparedStatement是支持?占位符。 -
预编译的Statement,当创建Statement对象时,需要立即传入SQL语句。立刻把SQL发送到数据库编译存储。等到执行之前只需要替换?参数。 -
PreparedStatement 在执行时不需要传递SQL语句。
6. PreparedStatement和普通的Statement的比较?
-
Statement一般用来执行静态SQL(没有参数,不会改变)。不支持?占位符,如果有参数拼接。 -
PreparedStatement预编译的Statement,支持?占位符,而且效率和安全性要比普通的Statement高。[可以避免SQL注入] -
我们推荐使用PreparedStatement ,但是有些情况必须使用参数拼接:
- select * from ? :表名字不能使用?
- select * from xx where name like ‘?’:引号中不能使用?
- select * from xxxs group by xx order by ?:order by 后边不能使用?
MyBatis中#和$的区别就是这里的PreparedStatement和Statement的区别,还会回来。
7. 可滚动的结果集
-
可滚动?
- 默认ResultSet只能从第一行到最后一行,遍历一次【MySQL8驱动默认不可滚动】
-
可滚动: 可以通过设置可滚动属性,多次遍历结果集
2. 滚动方法
1.next +getxxx
2.滚动方法:
absolute(int row) :绝对定位到指定行
beforeFirst():把光标移动到第一行之前
afterLast():把光标移动到最后一行之后
first:把光标移动的第一行
last():
previous():向上 向前
3. 代码演示
public static void query(String name)throws Exception{
Connection con = getConnection();
String sql="select id,name as sname,age sage,birth,email from student ";
Statement pst = con.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = pst.executeQuery(sql);
while(rs.next()){
System.out.println(rs.getInt("id")+"\t"
+rs.getString("sname")+"\t"+rs.getInt(3)+"\t"+rs.getDate(4)+"\t"+rs.getString(5));
}
System.out.println("=====================");
while(rs.previous()){
System.out.println(rs.getInt("id")+"\t"
+rs.getString("sname")+"\t"+rs.getInt(3)+"\t"+rs.getDate(4)+"\t"+rs.getString(5));
}
rs.absolute(2);
System.out.println("=====================");
while(rs.previous()){
System.out.println(rs.getInt("id")+"\t"
+rs.getString("sname")+"\t"+rs.getInt(3)+"\t"+rs.getDate(4)+"\t"+rs.getString(5));
}
rs.close();
pst.close();
con.close();
}
4. 应用
8. 加载资源?
-
Student.class.getResourceAsStream("") :从包内开始寻找资源 -
Student.class.getClassLoader().getResourceAsStream("") :从包外开始寻找。 作业: 1.使用JDBC程序在ET2203数据库中新建一张表 school school( id int primary key , name:学校名字,phone联系电话 varchar(11), proid int ,cityid int,areaid int,info varchar(100)详细地址 ) 2.JDBC往表中添加10条测试数据
五、使用JDBC添加数据时如何获得新添加到数据库中的主键?
+++
0. 为什么要返回主键?
- 数据库中带有存放大对象(电影、图片、音频)字段类型的。lob类型(largeObject),其中在mysql中,二进制的大对象叫 blob, 文本类型的大对象 text.
- 大对象的类型数据是无法通过 命令行客户端工具(小黑板)添加的,如果遇到大对象类型的字段,一般都是通过程序来添加或者查询(IO)
- 在实际开发中,一般不会把文件、图片直接存放到数据库中,而是把文件直接保存到服务器上指定目录中。然后把图片的保存地址(/pics/xxx.jpg)存放到数据库中。
- 案例
1. 数字类型的可以自动增长的主键?
-
采用JDBC自带的开关 pst.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS) Connection con = getConnection();
String insert="insert into student(name,age,birth,email)values(?,?,?,?)";
PreparedStatement pst = con.prepareStatement(insert,Statement.RETURN_GENERATED_KEYS);
pst.setString(1,"zhaoliu");
pst.setInt(2,456);
java.util.Date d = new java.util.Date();
pst.setDate(3,new java.sql.Date(d.getTime()));
pst.setString(4,"zhaoliu@qq.com");
int count = pst.executeUpdate();
if(count>0){
ResultSet keys = pst.getGeneratedKeys();
while(keys.next()){
System.out.println(keys.getInt(1));
}
}
useGeneratedKey=true -
同一个连接的前提下:采用查询的方式 public static void testNumberKey1()throws Exception{
Connection con = getConnection();
String insert="insert into student(name,age,birth,email)values(?,?,?,?)";
PreparedStatement pst = con.prepareStatement(insert);
pst.setString(1,"zhaoliu");
pst.setInt(2,456);
java.util.Date d = new java.util.Date();
pst.setDate(3,new java.sql.Date(d.getTime()));
pst.setString(4,"zhaoliu@qq.com");
int count = pst.executeUpdate();
if(count>0){
pst = con.prepareStatement("select last_insert_id()");
ResultSet keys = pst.executeQuery();
while(keys.next()){
System.out.println(keys.getInt(1));
}
}
}
? select last_insert_id()
mybatis的自动增长的主键返回方式采用JDBC的方法。
2. 字符串类不可自动增长的主键?
-
先查询字符串生成主键,然后再添加。 public static void testStringKey()throws Exception{
Connection con = getConnection();
PreparedStatement pst = con.
prepareStatement("select replace(uuid(),'-','') as id");
ResultSet rs = pst.executeQuery();
rs.next();
String key = rs.getString("id");
String key1 = UUID.randomUUID().toString().replaceAll('-','');
String insert="insert into t1(id,name)values(?,?)";
pst = con.prepareStatement(insert);
pst.setString(1,key);
pst.setString(2,"etoak");
int count = pst.executeUpdate();
System.out.println(key);
}
七、元数据接口(Metadata)
+++
0. 元数据?
? 元数据就是关于“数据”的数据。如:数据库是存放数据的,关于数据库本身的数据就叫元数据,结果集是用来存放从数据库中查询的数据的,关于结果集中列的信息的数据就叫元数据
1. DatabaseMetaData:数据库的元数据
2. ResultSetMetaData:结果集的元数据 rs
exec:把et2203数据库中所有表和内容 都打印出来。
步骤:
1. 查询表名字
2. 组装select * from xxx
3. 把内容写出到表格中。
JAVA处理Excel:apache-poi组件
POI中提供了核心的API
Workbook ==================> 工作簿===》整个excel文档
HSSFWorkbook: 处理xx.xls文档
XSSFWorkbook:处理*.xlsx的文档 数据量少 <1W
SXSSFWorkbook: 处理*.xlsx文档 数量可以达到104万条
Sheet =====================>页
Row========================>行
Cell=======================>单元格
写:
1.内存中创建 Workbook对象
2.再workbook对象中创建sheet
3.给sheet添加row
4.给row添加cell
5.写出到文件
八、JDBC中的事务控制
+++
1. 事务的基本概念?
- 事务的概念?多条SQL 一个单元
- 事务的特征?A:原子性 C:一致性 I:隔离性 D:持久性
- 事务相互影响可能出现的问题?脏读、不可重复读、幻读
- 为了解决问题设置的隔离级别?读未提交、读已提交、可重复读、串行化
2. 事务的应用场景?
-
添加学生携带图片 -
清空购物车 购物车—》结账—》订单 1.insert into orders 2.insert into orderitem 3.update 库存 4.生成日志 5.清空购物车 6.通知卖家 7.物流。。 -
入职—》insert into Emp系统 、delete from emp系统 emp : flag 0 1 emp insert delete emp1 insert 2 查看今年有哪些人再公司离职? -
转账 1.update -100 2. update +100 -
其他各种需要多条对数据库修改的语句同时执行的场景。
3. JDBC事务控制的方式?
MyBatis和Spring都用JDBC的方式控制事务!!!
九、数据源(DataSource)?
+++
1. 数据源接口介绍?
2. 数据源的实现方式:数据库连接池?
1. 标准实现:参考标准版 DriverManager.getConnection()
2. 连接池实现:参考练接池版
3. 连接池版进阶:使用动态代理,处理close方法
3. 数据库连接池的实现原理? 能过够表达出来
-
当服务器启动时初始化一些 连接 放在连接池中,等待客户端请求连接。 -
当客户端请求连接时,首先判断连接池中是否有可用连接,如果有则返回;如果没有,则判断当前连接数,是否超过了最大可用连接数,如果没有超过则创建新的连接返回;如果超过了,则等待或者抛出无可用连接的异常。 -
当客户端使用完连接时,再次放回到连接池中,从而实现连接的重用。 -
连接池方式实现数据源最大特点是实现了连接的重用。
4. 数据库连接池的模仿
5. 常用连接池组件?
? commons-dbcp/c3p0/druid等 都是第三方提供的采用了连接池方式的数据源接口的具体实现类库。
6. 三者的关系?
- ?
javax.sql.DataSource :JDK官方提供的获得连接标准 - 数据库连接池:使用官方提供的数据源的方案。
- dbcp/c3p0/druid/我们自己写的类库 是采用连接池方案的标准的实现产品。
7. 通过手动实现数据源来模仿原理?
-
基本版本DriverManager.getConn… -
连接池的版本 -
使用JDK动态代理设计模式实现:拦截close方法,把连接放回到池中。 -
动态代理涉及的核心API?
-
java.lang.reflect.Proxy类:代理类 * 通过Proxy类中的方法来生成代理类和对象
* 其中newProxyInstance方法主要用来构造代理类的对象
static newProxyInstance(类加载器,代理的接口,处理器)
- 类加载器:用来加载该方法在内存中生成的代理类的字节码序列。
- 代理类的接口:代理类可以看做是实现类,实现哪个接口必须要传入的。【JDK的代理方式必须提供接口】
- 处理器:每一个代理中的方法 都会调用InvocationHandler的invoke方法。所以我们必须提供一个InvocationHandler接口类型的对象。
-
java.lang.reflect.InvocationHandler:处理器类 通过以上方法创建的任何代理对象中方法的调用都会执行InvocationHandler接口中的invoke方法
public Object invoke(Object proxy,Method method,Object[] args){
proxy:代表代理对象
method:当前执行的方法
args:参数
}
10.回顾1:MySQL数据库和Oracle数据库的区别?
-
Oracle中的表是属于某一个用户的,要想查看表首先需要登录到某个用户下;MySQL中的表都是属于某一个database的,要想查看表首先打开某一个database; -
MySQL中数字类型的主键支持auto_increment自动增长子句的。oracle中不支持的 -
MySQL中分页的关键字 limit,Oracle中分页的关键字 rownum -
MySQL中支持 if [not] exists ,oracle中不支持 drop table if exists student; -
MySQL中常用的数据类型: int varchar() date oracle …:number varchar2 date
11.回顾2:MySQL中的批量添加数据?
-
insert into (字段列表) values (),(),()… insert into student(name,age,birth,email,phone)
values
('lisi',234,now(),'lisi@qq.com','13512344321'),
('wangwu',345,now(),'ww@qq.com','1111');
Query OK, 2 rows affected, 2 warnings (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 2
-
insert into (字段列表) (select… ) union (select …) union (select) [mybatis.] insert into student(name,age,birth,email,phone)
-> (select 'zhaoliu',age,birth,email,'333' from student where id=2)union all
-> (select 'etoak',234,birth,email,phone from student where id=3);
12.回顾3:数据库中表和表之间的关系?
-
一对一 :建表原则 :通过主键关联或者通过唯一外键关联 person id name
1 zhangsan
idcard id address personid[外键] unique
1 xxx 1
2 xxx
-
一对多 :建表原则:通过外键关联 dept: deptno dname loc 一方
10
emp: empno ename job sal hiredate comm deptno【外键】 多方
10
10
-
多对多 :建表原则:通过第三张表关联 student
id name
course
id name
桥表 关联表 sc: sid cid
1 1
1 2
2 1
13.补充回顾4:SQL连表查询?
1. 查询学生信息和所在的学校信息?
mysql> select s.id as sid ,s.name as sname ,s.age,s.birth,sch.id,sch.name,sch.phone,concat_ws('-',pro.name,city.name,area.name ,sch.info) addr from student s left join school sch on s.schid = sch.id left join locations pro on sch.proid = pro.id
left join locations city on sch.cityid = city.id
left join locations area on sch.areaid = area.id where s.name like '%a%' limit 0,2;
+----+----------+------+---------------------+------+----------+-------------+-------+-------------------------------------------------------+
| id | name | age | birth | id | name | phone | proid | concat_ws('-',pro.name,city.name,area.name ,sch.info) |
+----+----------+------+---------------------+------+----------+-------------+-------+-------------------------------------------------------+
| 1 | zhangsan | 123 | 2021-03-29 11:17:46 | 1 | 山东大学 | 13012344323 | 1 | 山东省-济南市-历下区-山大路XX号 |
| 2 | lisi | 123 | 2021-03-29 11:17:51 | 1 | 山东大学 | 13012344323 | 1 | 山东省-济南市-历下区-山大路XX号 |
| 3 | wangwu | 23 | 2021-03-29 11:20:36 | 1 | 山东大学 | 13012344323 | 1 | 山东省-济南市-历下区-山大路XX号 |
| 4 | zhaoliu | 26 | 2021-03-29 11:20:36 | 2 | 青岛大学 | 13012344300 | 1 | 山东省-青岛市-市南区-青大路XX号 |
+----+----------+------+---------------------+------+----------+-------------+-------+-------------------------------------------------------+
4 rows in set (0.00 sec)
不使用like:
select s.id as sid ,s.name as sname ,s.age,s.birth,sch.id,sch.name,sch.phone,sch.proid,concat_ws('-',pro.name,city.name,area.name ,sch.info) info from student s left join school sch on s.schid = sch.id left join locations pro on sch.proid = pro.id
left join locations city on sch.cityid = city.id
left join locations area on sch.areaid = area.id where instr(s.name,'a')>0 limit 0,2
2. 查询每个学校的学生人数
mysql> select sch.id ,sch.name,count(s.id) as sl from school sch left join student s on s.schid = sch.id group by s.schid;
+----+----------+-------------+
| id | name | count(s.id) |
+----+----------+-------------+
| 3 | 济南大学 | 0 |
| 1 | 山东大学 | 3 |
| 2 | 青岛大学 | 1 |
+----+----------+-------------+
3 rows in set (0.00 sec)
3. 查询学生的选课信息和分数?
查询学校地址是济南的学生的以上信息 每页显示2条,显示第二页数据
mysql> select
s.id as sid,s.name as sname ,s.age,s.birth,
sch.id as schoolid,sch.name as schname,
concat_ws('-',pro.name,city.name,area.name,sch.info) addr,c.id as cid,c.name as cname ,c.code,
sc.score from student s
left join sc on s.id = sc.sid
left join course c on c.id = sc.cid
left join school sch on s.schid = sch.id
left join locations pro on sch.proid = pro.id
left join locations city on sch.cityid =city.id
left join locations area on sch.areaid = area.id
where concat_ws('-',pro.name,city.name,area.name,sch.info) like '%济南%'
order by sc.score desc limit 2,2;
+-----+----------+------+---------------------+----------+----------+---------------------------------+------+---------+------+-------+
| sid | sname | age | birth | schoolid | schname | addr | cid | cname | code | score |
+-----+----------+------+---------------------+----------+----------+---------------------------------+------+---------+------+-------+
| 3 | wangwu | 23 | 2021-03-29 11:20:36 | 1 | 山东大学 | 山东省-济南 市-历下区-山大路XX号 | 4 | DB | 1004 | 95 |
| 1 | zhangsan | 123 | 2021-03-29 11:17:46 | 1 | 山东大学 | 山东省-济南 市-历下区-山大路XX号 | 3 | JAVAWEB | 1003 | 92 |
+-----+----------+------+---------------------+----------+----------+---------------------------------+------+---------+------+-------+
2 rows in set (0.00 sec)
4. 查询每门课程的选课人数?
mysql> select name, count(sid) from sc left join course c on c.id = sc.cid group by cid;
+----------+------------+
| name | count(sid) |
+----------+------------+
| COREJAVA | 3 |
| ORACLE | 1 |
| JAVAWEB | 2 |
| DB | 2 |
| VUE | 2 |
| 事务 | 1 |
+----------+------------+
6 rows in set (0.00 sec)
5. 查询选修所有课程的学生信息?
mysql> select * from student where id in(select sid from sc group by sid having count(cid)=(select count(*) from course));
+----+----------+------+---------------------+-------+
| id | name | age | birth | schid |
+----+----------+------+---------------------+-------+
| 1 | zhangsan | 123 | 2021-03-29 11:17:46 | 1 |
+----+----------+------+---------------------+-------+
1 row in set (0.00 sec)
6. 查询选修了课程的学生信息?
7. 查询选修了比李四多的课程的学生信息?
mysql> select * from student where id in(select sid from sc group by sid having count(cid)>(select count(cid) from sc where sid=(select id from student where name='lisi')))
+----+----------+------+---------------------+-------+
| id | name | age | birth | schid |
+----+----------+------+---------------------+-------+
| 1 | zhangsan | 123 | 2021-03-29 11:17:46 | 1 |
| 3 | wangwu | 23 | 2021-03-29 11:20:36 | 1 |
+----+----------+------+---------------------+-------+
2 rows in set (0.00 sec)
8. 查询"COREJAVA"的最高分?
select s.id,s.name ,s.age,s.birth,sc.score from student s left join sc on s.id = sc.sid left join course c on c.id = sc.cid where s.id in (select sid from sc where score=(select max(score) from sc where cid=(select id from course where name='COREJAVA')) and cid=(select id from course where name='COREJAVA')) and c.name='COREJAVA';
14. 补充回顾5:SQL的常用命令?
1.建库 create database 库名字; etoak; 2.打开库 use etoak 3.建表 mysql> create table student(id int primary key auto_increment , -> name varchar(32),birth datetime,age int, email varchar(30)); Query OK, 0 rows affected (0.05 sec)
4.添加数据: insert into student(id,name,age,birth,email)values(null,‘zhangsan’,123,now(),‘etoak@qq.com’);
15.数据类型补充?
1. MySQL中我们常见的数据类型:int date varchar ,还有一种不常见的数据类型 lob(Large Object) 大对象类型 。如: 数据库中存放一部电影、一张图片
2. MySQL中有两种大对象类型:
1. blob : (binary lob )二进制大对象 存放字节数据
2. text:存放字符大对象 一篇论文
3. 大对象 类型数据不可以通过客户端添加的(小黑板不能添加),一般通过JDBC程序添加。
4. 我们一般不会把 图片、电影等直接存放到数据库,而是把这些数据存放到服务器的某个文件夹中,然后把地址放到数据库中。
##
16.图片上传:图片表
1.图片表主要用来存放上传到服务器的图片信息
2.字段 id , savepath[保存路径:一般改名之后再保存,避免命名重复之后覆盖] varchar(200) realname[原名字,改名之前的名字] varchar(200) uploadtime[上传时间] datetime flag[这张图片是否作为头像 0:不启用 1:启用] tinyint stuid[该图片属于哪一个学生] int
17.JAVA读写Excel?
1. JAVA读写Excel 比较著名的组件 apache-POI
2. 写出Excel
1. Workbook==>工作簿======>一个Excel文档
2. Sheet-------->页 -----》一个Excel页面
3. Row---------->行
4. Cell----------->列
3. Wokbook:
1. HSSFWorkbook:处理 .xls文档
2. XSSFWorkbook:处理.xlsx文档 一般万条数据以内
3. SXSSFWorkbook:最多可以处理104万条数据
18. JDBC的封装?
1. 为什么封装?
JDBC流程代码重复太多。
2. 如何封装?
变化的内容作为参数,不变的流程固定下
3. 封装案例?
apache DBUtils / Spring JdbcTemplate
4. 通过案例掌握DBUtils工具包?
代码实践
5. DBUtils的底层原理?
反射之后,再来详细研究,目前首先达到会用模式
|