目录
1. Java的JDBC编程
JDBC(Java Database connect)就是用 Java代码操作数据库
真正在公司中操作数据库,99.9999%的情况都是通过代码来操作的,很少会手动在客户端里输入sql语句,所以我们之前用cmd,而不用navicat等之类的数据库客户端的原因。系统中执行了10w次sql,也不一定手动敲一次sql。
1.1 Java的数据库编程:JDBC
- 数据库编程,是需要数据库服务器提供一些API,供程序猿调使用的。
- API,即Application Programming Interface,应用程序编程接口。是一组类/函数提供给程序猿,让我们去调用,完成一些功能。
- 各种数据库,MySQL, Oracle, SQL Server在开发的时候,都提供了一组各自的编程接口(API)。
- 即给你个软件,你能对他干啥(代码层次上的),基于它提供的这些功能,就可以写一些其他代码了。
- 如MySQL, Oracle, SQL Server等其他关系型数据库,在开发时设置的API接口是不完全相同的,没有一个标准,大大增加了程序猿的学习成本。
- 于是Java设计出一套api的规范,其他数据库提供的api要与Java设计api的规范对接上,其他数据库就要在自己的api基础上加以修改,转换为java的api形状(实现兼容)。程序猿只要了解一套api,就可以操作各种数据库了。java设计的标准API 就是JDBC
- (隔壁C++同学又已经馋哭了),C++的官方不给力,至今没有统一的api来操作数据库,各种开源组织陷入混战。
JDBC(Java Database Connectivity,Java数据库连接)是 Java语言用于连接和操作数据库的标准API,它提供了一套统一的接口,允许Java程序与多种关系型数据库(如MySQL、Oracle、SQL Server等)进行交互,而无需关心底层数据库的具体实现细节。它是一种用于执行SQL语句的Java API,是Java中的数据库连接规范。
JDBC由 java.sql.*,javax.sql.*包中的一些类和接口组成,为Java开发人员操作数据库提供了一个标准的API,可以为多种关系数据库提供统一访问。
数据库的api是怎么对接上JDBC的:这些数据库有自己的原生api,数据库厂商专门提供了一个驱动程序(软件),通过这个驱动程序把原生api转换成符合JDBC要求的api。
- 驱动程序起到的作用,类似于转接头,扩展坞。平时经常见到硬件的驱动,例如鼠标驱动。
- 硬件的驱动是让操作系统认识新的硬件设备,数据库的驱动是让JDBC能认识数据库的api
- Java程序猿要想进行数据库开发,就需要在项目中导入对应数据库的驱动包,才能编写代码。
1.2 JDBC工作原理
JDBC 为多种关系数据库提供了统一访问方式,作为特定厂商数据库访问API的一种高级抽象,它主要包含一些通用的接口类。
JDBC访问数据库层次结构:
JDBC优势:
- Java语言访问数据库操作完全面向抽象接口编程。
- 开发数据库应用不用限定在特定数据库厂商的API。
- 程序的可移植性大大增强。
要想在程序中操作mysql就需要先安装mysql的驱动包,并且要把驱动包引入到项目里。
1.3 如何在项目中导入数据库驱动包
驱动包从哪里来?数据库厂商提供的:
- mysql的官方网站获取(下策,mysql被oracle 收购之后官网也合并,麻烦)
- github
- maven 中央仓库
maven相当于应用商店。通过应用商店,就可以访问到软件程序包下载下来。中央仓库是服务器,托管了各种软件程序包。
例如:手机app,金铲铲之战,可以去腾讯官网下载,也可以去应用商店(华为商店,小米商店...)
如何下载数据库驱动包
Maven Repository: mysqlhttps://mvnrepository.com/search?q=mysql
注意此处的版本,大版本要和数据库服务器匹配,数据库服务器用的是5系列,此处的驱动包也得用5系列,不能用8系列。小版本无所谓大版本不能错。
- .jar 是 java 中常见的后缀类型。
- java写的程序如何发布给别人:jar 是一个 最常见的方式。
- JDBC是通过jar包发布的。jar包是口语化的表述,读作jar包,不是架包。
java发布程序的典型方式:
- java通过 .java源文件编译成 .class文件,jvm来解释执行 .class
- 每个.java 都 一 一对应一个 .class,如果代码里 .java非常多呢(类非常多)?
- 把一大堆的 .class 给打成压缩包(类似于.rar .zip),是以.jar为扩展名的jar包(jar),把jar包拷贝给对方,对方就可以直接使用jvm来运行了。
- 此处mysql 驱动包(jar包/jar)不是单独运行的jar,可以把它导入到项目中,然后就可以调用其中的方法和类来进行编程了。
jar 如何引入项目中
数据库驱动包,每次创建项目就要导入一次。但在实际开发中,创建项目是非常低频的操作。一两年都没机会创建一次项目。
1)创建一个Directory(目录)
创建后,选中,将mysql驱动包复制粘贴到里面;这里不需要解压,jvm会自动识别
2)把这个目录标记成项目的库(library)
library(图书馆),在计算机中(idea)里面是库的意思
- 此时idea就识别出这里面有哪些目录,有哪些的类,以及.class文件,识别成功就导入成功了。
- idea能识别这个目录里的jar包,从而就可以调用里面的类来写代码了。
Demo,Example,Tutor =》 例子。看开源项目,可以从这几个角度切入。
2. 编写JDBC代码
JDBC 需要通过以下步骤来完成开发:
- 创建并初始化一个数据源
- 和数据库服务器建立连接
- 构造 sql 语句
- 发送 sql 给服务器,执行 sql 语句
- 释放资源,关闭连接
这里的代码是固定套路。正因为如此,实际开发中,就会使用一些框架来简化数据库操作代码,MyBatis这样的框架就是如此。框架是一直在变的,但是JDBC是不变的。
1. 创建并初始化一个数据源(DataSource)
DataSource 意思:数据源
数据源:数据从哪里来,到哪里去(数据的源头在哪里);通过DataSource类(接口)来描述,数据库服务器所在的位置。
其中DataSource接口就是JDBC给我们提供的API,来自java.sql这个包中的接口(创建时注意区分);
MysqlDataSource类(实体类)来自我们上面导入的mysql驱动包中(如果MysqlDataSource类名没有提示,说明上面导入过程不成功)。发生向上转型。
这里发生了向上转型(父类引用指向子类对象),然后再向下转型,为什么要多此一举呢?
- 这种转型的写法,初心是为了让MysqIDataSource这个类名不要扩散到代码的其他地方。
- 后续如果要修改数据库为别的数据库,代码改动比较小。(mysql和我们程序(java程序)之间的耦合比较低)能够进一步的降低耦合。
- 同时setUrl方法只有MysqIDataSource类中有,DataSource接口中没有;父类引用不能调用子类中独有的方法,所以我要强转回来,要使用父类中没有但是子类拥有的方法。
上述方式写法仅仅是因为在Java圈中流行那样写的,也可以这样直来直去写,不使用向上转型,向下转型。C++就是这样写的,因为C++中没有统一的api
优势:简单直观。缺陷:换别的数据库时,需要对代码进行大规模修改。
两种写法各自有各自的作用和意义,可以根据不同场景,选择不同的方式。
mysql是一个客户端服务器结构的程序,通过网络进行交互的。既然是通过网络交互的,需要知道服务器在网络的那个地方。
URL 计算机中非常重要的概念, 唯一资源定位符(网址),描述网络上的某个资源所在的位置。
setUrl 与 setURL一样没区别,之所以这样设定,就是为了减少程序猿的心智负担。
- "jdbc:mysql://127.0.0.1:3306/java107?characterEncoding=utf8&useSSL=false"
url 中有一些特殊符号,是有特定含义的 : / ? & #......
? 后面,表示访问资源时,需要哪些参数。
- 这里指定的utf8是完全体的,不能写作utf8mb4,utf8mb4是mysql数据库特有的。JDBC是通用的接口,在JDBC是识别不了utd8mb4的。
- useSSL表示通信过程中是否要加密。这里建议统一设置为false(不加密)。一方面因为当前通信并没有设计到安全问题;另一方面设置为true,有的同学可能连接不上。为什么可能会连接不上:一可能存在环境差异;二想要安全加密连接需要安装对应的证书且要通过证书的验证匹配。关于证书后续http/https章节会详细介绍。
- 环回ip(loopback):
- setUrl:设置Url,找到服务器的位置。
- setUser:设置用户名,当前以什么样的身份去访问。这里的root是管理员,mysql默认自带的用户。也可以创建其他用户,此处不涉及,用root就可以了。
- setPassword:设置密码,当前访问服务器用户的密码。
这几个东西都设置了,才能正确的访问数据库服务器。上述这些工作只是准备工作,还没有真正和mysql服务器进行通信。
2. 和数据库服务器建立连接
打电话 拨号 -》 对方接听,这个过程就是“建立连接”(抽象的概念)
Connection 意思:连接。 链接(Link)快捷方式
这里使用的Connection类 一定是JDBC提供的;建立成功就连接成功了,没成功就抛出异常(这里已经声明异常了)
3. 构造 sql 语句
即使是使用代码来操作数据库,仍然是依赖sql语句完成,只不过是换成用代码来构造sql,前面学过的sql各种语法在这里同样适用。
这里插入的前提是在数据库中已经创建过了这个表。
先构造字符串sql,再构造出PreparedStatement对象。
PreparedStatement 语句是对sql语句进行预编译一下,为什么要进行提前预编译一下:
- 如果请求是字符串格式的sql,服务器是可以处理的。但是这么做不好,字符串sql有可能是存在语法错误之类的。
- mysql服务器就需要对sql进行解析和校验,mysql服务器是服务于多个客户端,此时总的开销,总的负担就很大。
- 通过PreparedStatement 语句预处理,让客户端先解析检查 sql,看sql是否有问题。解析完毕之后,也会得到结构化数据(语法树),直接把解析好的结构化数据发给数据库服务器,服务器就省下了这部分解析的工作。好比学校食堂,要求同学吃完饭,自己把盘子送到回收处。
4. 发送 sql 给服务器,执行 sql 语句
把 sql语句(预编译过的)发送给数据库服务器,由服务器做出响应。这个过程就涉及到一系列的网络通信、数据库底层实现,这里不做过多介绍,记住这个过程就可以了。
这里使用insert、delete、update ,sql语句操作,都是用executUpdate代码方法。返回值为int类型,表示影响到的行数,这属于服务器给客户端返回的响应数据。
如果使用select ,sql语句操作,则用 executeQuery代码方法,更复杂一点,具体代码操作看最后。
5. 释放资源,关闭连接
- 释放资源,谁是后创建的,谁就先释放 —— “先进后出”,虚拟机栈。
- 程序通过代码和服务器进行通信,是需要消耗一定的软/硬件资源的(系统资源),包括不限于,CPU,内存,硬盘,带宽...
- 在程序结束时,需要告知服务器,释放这些资源。客户端也需要释放资源。俗话说,有借有还再借不难。
JDBC编写的流程以及改进
多次运行每次都插入一条数据:
修改和删除操作,代码写法和插入是非常类似的。只要调整sql内容即可。查询操作代码下面有详细解释。
删除操作代码:
修改操作代码:
上述代码存在一些问题:
我们构造SQL语句的时候,要插入的数据是写死的(硬编码),更合适的做法是把数据通过其他方式让用户输入,比如通过控制台输入。动态的拼接sql
改进后:
- 通过这种方式确实可以完成用户动态输入数据的效果,但是这种写法代码,非常容易出错,不优雅,且不安全,可能会导致 sql 注入攻击。
- 如果用户不好好输入,没有按照对应的数据输入,而是在控制台中输入了name时写作: 王五'); drop database xxxx; 或者 select * from xxx;
- 因此这种写法容易被sql注入,也就是网络安全中的典型攻击方式(输入过程中夹带私货)
更好的写法是,借助这个PreparedStatement的拼装功能来实现,拼接 sql
PreparedStatement:适合动态参数查询,同时可防止 SQL 注入。
- 使用 ? 作为占位符。
- 替换的值是int类型,使用setInt。替换的值是String类型,使用 setString。即替换的值是xxx类型,使用setXxx
- setXxx里面第一个参数表示替换第几个 ? ,从1开始算,不是0
- 除了插入操作,修改、删除、查询同样也支持使用 ?作为占位符。
在拼接好sql后,打印一下statement,就在控制台中看到拼接后的语句了
如果执行的sql 出错了,就可以把Statement打印出来,检查哪里有语法错误,或者也可以直接把这个sql拷贝到控制台中执行。
小结
JDBC编程的基本工作:
准备工作:
- 下载mysql 驱动包 maven中央仓库
- 导入到项目中 复制到项目目录下,标志为库(library)
编写代码:
- 创建数据源(描述数据库服务器所在的位置)
- 和数据库服务器建立连接
- 构造 sql 语句
- 发送 sql 给服务器,执行 sql 语句
- 释放资源,关闭连接
代码操作数据库实现查询操作
执行sql语句的时候分情况:
- 如果构造sql语句,是使用insert、delete、update 操作,都是用executeUpdate代码方法执行sql,返回值为int类型,表示影响到的行数。
- 如果构造sql语句,是使用select 操作,则用 executeQuery 代码方法执行sql,返回ResultSet(结果集合)
- 对查询来说,返回结果不是单纯的int 了,而是ResultSet对象;ResultSet 表示查询的结果集(临时表),使用类似迭代器方式遍历这张表,然后再控制台中显示出来。
- 迭代器的
hasNext()
是用于判断集合中是否还存在可访问的下一个元素的方法。- 迭代器的
next()
是用于获取集合中下一个元素并移动游标的方法。
3. JDBC常用接口和类
3.1 JDBC API
在Java JDBC编程中对数据库的操作均使用JDK自带的API统一处理,通常与特定数据库的驱动类是 完全解耦的。所以掌握Java JDBC API (位于 java.sql 包下) 即可掌握Java数据库编程。
3.2 数据库连接Connection
Connection接口实现类由数据库提供,获取Connection对象通常有两种方式:
- 一种是通过DriverManager(驱动管理类)的静态方法获取:
- 一种是通过DataSource(数据源)对象获取。实际应用中会使用DataSource对象。
![]()
以上两种方式的区别是:
- DriverManager类来获取的Connection连接,是无法重复利用的,每次使用完以后释放资源时,通过connection.close()都是关闭物理连接。
- DataSource提供连接池的支持。连接池在初始化时将创建一定数量的数据库连接,这些连接是可以复用的,每次使用完数据库连接,释放资源调用connection.close()都是将Conncetion连接对象回收。
3.3 Statement对象
Statement对象主要是将SQL语句发送到数据库中。JDBC API中主要提供了三种Statement对象:
Statement:适用于静态 SQL 查询。
PreparedStatement:适合动态参数查询,可防止 SQL 注入。
实际开发中最常用的是PreparedStatement对象,以下对其的总结:
- SQL预编译
- 占位符:?下标从1开始
- 占位符不能使用多值
- 阻止常见SQL注入攻击
- 参数化SQL查询
- 性能比Statement高
主要掌握两种执行SQL的方法:
- executeQuery() 方法执行后返回单个结果集的,通常用于select语句
- executeUpdate()方法返回值是一个整数,指示受影响的行数,通常用于update、insert、delete 语句
3.4 ResultSet对象
ResultSet对象它被称为结果集,它代表符合SQL语句条件的所有行,并且它通过一套getXXX方法提供了对这些行中数据的访问。
ResultSet里的数据一行一行排列,每行有多个字段,并且有一个记录指针,指针所指的数据行叫做当前数据行,我们只能来操作当前的数据行。我们如果想要取得某一条记录,就要使用ResultSet的next()方法 ,如果我们想要得到ResultSet里的所有记录,就应该使用while循环。
面试问答:
- 数据库连接有哪些方式?分别有什么区别
- 数据库Statement和PreparedStatement有什么区别?
耦合、内聚
耦合:是两个模块之间的关联关系是否非常紧密,这边的变化会不会影响到另外一边。
- 高耦合,两个模块关联非常紧密,一边变化影响另一边程度比较大。
- 低耦合,两个模块关系不紧密,一边变化影响另一边程度比较小。
写代码要追求低耦合。如果耦合高了,随便改某个代码都会引起其他模块出现bug。
内聚:把相同的/相关联的功能,放到一起,内聚就高。零零散散哪里都有,内聚就低。
一个模块最好只实现一个功能。
写代码追求高内聚,低耦合。即相同功能代码放到一起,同时模块与模块之间不要有太大影响。
好啦Y(^o^)Y,本节内容到此就结束了。下一篇内容一定会火速更新!!!
后续还会持续更新MySQL方面的内容,还请大家多多关注本博主,第一时间获取新鲜的知识。
如果觉得文章不错,别忘了一键三连哟!