【MySQL】JDBC 编程详解

news2024/9/30 1:32:22

JDBC 编程详解

  • 一. 概念
  • 二. JDBC 工作原理
  • 三. JDBC 使用
    • 1. 创建项目
    • 2. 引入依赖
    • 3. 编写代码
      • (1). 创建数据源
      • (2). 建立数据库连接
      • (3). 创建 SQL
      • (4). 执行 SQL
      • (5). 遍历结果集
      • (6). 释放连接
    • 4. 完整的代码
    • 5. 如何不把 sql 写死 ?
    • 6. 获取连接失败的情况
  • 四. JDBC常用接口和类
    • 1. 数据库连接 Connection
    • 2. Statement 对象
    • 3. PreparedStatement 的预编译

一. 概念

JDBC: 即Java Database Connectivity,java数据库连接。是一种用于执行SQL语句的Java API,
它是Java中的数据库连接规范。这个API由 java.sql.*,javax.sql.* 包中的一些类和接口组成,
它为Java开发人员操作数据库提供了一个标准的API,可以为多种关系数据库提供统一访问。

产生的原因:
MySQL、Oracle、SQL server 这些数据库都提供自己的 API 来支持程序员实现自己的客户端来完成一些增删改查功能, 没有业界统一的标准,Java 当然不乐意了, 因为 Java 诞生就是为了跨平台的。
所以 Java 就搞出了 JDBC,约定一组 API 为 JDBC,里面包含一些类和方法, 通过这些类和方法实现数据库的基本操作,由各厂商提供各自的 “数据库驱动包” 来和 JDBC 的 API 对接。
所以,只要掌握这一套 JDBC API 就可以操作各种数据库了。

二. JDBC 工作原理

JDBC 为多种关系数据库提供了统一访问方式,作为特定厂商数据库访问API的一种高级抽象,它主要包含一些通用的接口类。

JDBC 访问数据库层次结构:
在这里插入图片描述

JDBC 优势:

  • Java 语言访问数据库操作完全面向抽象接口编程
  • 开发数据库应用不用限定在特定数据库厂商的 API
  • 程序的可移植性大大增强

三. JDBC 使用

注意:
JDBC 只支持关系型数据库, 不支持非关系型数据库。

1. 创建项目

创建一个项目即可。

2. 引入依赖

  • 准备数据库驱动包,并添加到项目的依赖中:
    在项目中创建文件夹 lib,并将依赖包(MySQL 使用的是哪个系列就用哪个系列的驱动)如 mysql-connector-java-5.1.47.jar (下载地址:Maven 中央仓库) 复制到 lib 中。
    右键上面的目录,选择 add as library 选项,这样才能把 jar 包导入到项目中,项目才能读取到里面的 .class 文件。

3. 编写代码

(1). 创建数据源

// 创建数据源
DataSource dataSource = new MysqlDataSource();
((MysqlDataSource)dataSource).setURL("jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("你的用户名");
((MysqlDataSource)dataSource).setPassword("你的数据库密码");

注意:

  1. DataSource 对象描述了数据库对象在哪。

  2. MySQL 数据连接的 URL 参数格式如下:
    jdbc:mysql://服务器地址:端口/数据库名?参数名=参数值&参数名=参数值&…
    服务器地址:我们写 的 127.0.0.1 是因为我们连接的是 本机的数据库, 所以用的是环回 IP 127.0.0.1,如果连接的是服务器上的数据库就得写对应的 IP 地址。
    端口:MySQL 的默认端口号为 3306
    数据库名:代码中的 URL 中的 test 要换为自己的数据库的名字。

  3. setUser、setPassword 都是自己的用户名(MySQL 默认用户为 root )和密码。

  4. 为什么 使用向上转型,DataSource 引用 MysqlDataSource 对象,然后设置参数时 先强转(向下转型)回来,为什么不直接使用 MysqlDataSource 对象,这样不就不用来回转了嘛 ?
    为了使代码低耦合:
    后面代码中若需要用到 DataSource 类型,使用的相关参数也是 dataSource , 未来如果 不适用 MySQL 做数据库了, 使用其他数据库了 如 PostgreSQL 了,代码几乎不用怎么改动。
    但是如果直接写成 :

MysqlDataSource mysqlDataSource = new MysqlDataSource();

这样不需要来回转换, 但是代码中充斥着 MysqlDataSource,到时候一旦更换数据库,代码要改动的地方就非常多了, 基本凉凉。

(2). 建立数据库连接

// 建立连接
Connection connection = dataSource.getConnection(); // 注意处理异常

(3). 创建 SQL

JDBC 中构造的 sql 不需要带上 ; 符号

// 创建 sql

// 更新的 sql 
String sql = "insert into student values ('wangwu', 90)";
PreparedStatement statement = connection.prepareStatement(sql);

// 查询的 sql
// String sql2 = "select * from student"; // 不用加上 ;
// PreparedStatement statement2 = connection.prepareStatement(sql2);

只一个 String 类型的 sql 还不行,需要把这个 String 包装成一个语句对象 PreparedStatement 。

(4). 执行 SQL

// 执行 sql
int ret = statement.executeUpdate();

// 执行查询的 sql 
// ResultSet resultSet = statement2.executeQuery();

执行 增删改 的 sql 使用 executeUpdate 方法, 返回值是受影响的行数。
执行 查询 的 sql 使用 executeQuery 方法,并使用 ResultSet 接收结果。

(5). 遍历结果集

      // 执行 查询的 sql 时需要遍历结果集
      while (resultSet.next()) {
          int age = resultSet.getInt("age");
          String name = resultSet.getString("name");

          System.out.println(" 姓名: " + name + " 年龄: " + age);
      }

可以根据 列名 获取对应的值, 也可以根据列的下标(从 1 开始)获取,但是不推荐。

(6). 释放连接

        // resultSet.close(); // 如果查询的话需要释放
        statement.close(); 
        connection.close();
        // 也要注意处理异常

先释放 statement 再 释放 connection
类似于关冰箱,先把抽屉关了, 再把冰箱门关了。

4. 完整的代码

以查询为例:

    public static void main(String[] args) throws SQLException {
        // 1. 创建数据源
        DataSource dataSource = new MysqlDataSource();
        ((MysqlDataSource)dataSource).setURL("jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&useSSL=false");
        ((MysqlDataSource)dataSource).setUser("root");
        ((MysqlDataSource)dataSource).setPassword("123456");

        // 2. 建立数据库连接
        Connection connection = dataSource.getConnection(); // 注意处理异常

         // 3. 创建 sql
         String sql = "select * from student"; // 不用加上 ;
         PreparedStatement statement = connection.prepareStatement(sql);

         // 4. 执行查询的 sql
         ResultSet resultSet = statement.executeQuery();

        // 5. 遍历结果集 (查询的话需要)
        while (resultSet.next()) {
            int age = resultSet.getInt("age");
            String name = resultSet.getString("name");

            System.out.println(" 姓名: " + name + " 年龄: " + age);
        }

        // 6. 释放连接
        resultSet.close(); // 如果查询的话需要释放
        statement.close();
        connection.close();
    }

在这里插入图片描述

使用步骤总结:

六个步骤:

  1. 创建数据源: DataSourse
  2. 建立数据库连接: Connection
  3. 创建 sql: PreparedStatement
  4. 执行 sql: executeUpdate/executeQuery
  5. 遍历结果集(如果是 查询的话): ResultSet
  6. 释放连接 ResultSet / PreparedStatement / Connection close()

注意:

整个写代码的时候注意处理异常,要么抛给上层调用者,要么自己处理掉。

5. 如何不把 sql 写死 ?

使用 PreparedStatement 并使用占位符

String name = "zhouba";
int age = 88;
String sql = "insert into student values(?,?)"; // 不用加上 ;

PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1, name);
statement.setInt(2, age);
statement.executeUpdate();

使用 ? 先占一个位置,再使用 PreparedStatement 的 setXXX 方法进行替换,注意类型要一致。
注意该方法的第一个参数 是 ? 的位置, 下标从 1 开始,第二个参数是要填上去的值。

如果想一次插入多条数据, 就使用多个 ?, 对应替换上去就行了。

String sql = "insert into student values(?,?),(?,?),(?,?),(?,?)"; // 不用加上 ;

不要使用字符串拼接实现动态 sql, 因为会引入 sql 注入问题
比如: 可能我们如果使用 字符串拼接的话, 会像下面这么写

Scanner scanner = new Scanner(System.in);
String name = scanner.nextLine();
String password = scanner.nextLine();
String sql = "select * from user where name=" + name + " and password=" + password;
PreparedStatement statement = connection.prepareStatement(sql);

正常情况下没有什么问题, 但是, 有一个巨大的漏洞,
当用户输入:

'zhangsan'
'' or 1=1

(或者输入一些其他的恶意代码)
拼接成的 sql 为

select * from user where name='zhangsan' and password='' or 1=1

最后出现了一个 or 1=1
这在什么条件下都会成立的,所以说这个 sql 完全能执行成功,
在恶意用户不用知道 正确的用户名和密码的情况下,能查询数据库中的所有内容, 这是相当可怕的,尤其在一些银行等数据库内容非常机密的情况下。
所以, 我们不能使用 字符串拼接来实现动态的 sql ,
而 使用 PreparedStatement 的占位符则不会出现这种情况。

6. 获取连接失败的情况

  • 数据库地址不对
  • 端口号不对
  • 数据库名不对
  • 用户名不对
  • 密码不对
  • 其他情况

四. JDBC常用接口和类

1. 数据库连接 Connection

Connection 接口实现类由数据库提供,获取 Connection 对象通常有两种方式:

  • 一种是通过 DriverManager(驱动管理类)的静态方法获取:
// 加载JDBC驱动程序:反射,这样调用初始化com.mysql.jdbc.Driver类,即将该类加载到JVM方法区,并执行该类的静态方法块、静态属性。
Class.forName("com.mysql.jdbc.Driver");

// 创建数据库连接
Connection connection = DriverManager.getConnection(url,user,password);
  • 一种是通过 DataSource(数据源)对象获取。实际应用中会使用 DataSource 对象。
DataSource ds = new MysqlDataSource();
((MysqlDataSource) ds).setUrl("jdbc:mysql://localhost:3306/test");
((MysqlDataSource) ds).setUser("root");
((MysqlDataSource) ds).setPassword("123456");
Connection connection = ds.getConnection();

以上两种方式的区别是:

  1. DriverManager 类来获取的 Connection 连接,是无法重复利用的,每次使用完以后释放资源时,通过 connection.close() 都是关闭物理连接。
  2. DataSource 提供连接池的支持。连接池在初始化时将创建一定数量的数据库连接,这些连接是可以复用的,每次使用完数据库连接,释放资源调用 connection.close() 都是将 Conncetion 连接对象重新初始化然后回收,不用关闭物理连接。

非常不建议使用 DriverManager :

  1. 使用了反射,非常影响代码的可读性,也不利于 IDEA 对代码解析校验。
  2. DataSourse 内置连接池,在频繁创建/断开连接时,DataSourse 比 DriverManager 的方式更高效。

2. Statement 对象

Statement 对象主要是将SQL语句发送到数据库中。JDBC API 中主要提供了三种 Statement 对象。

在这里插入图片描述

实际开发中最常用的是 PreparedStatement 对象:

  1. 可以参数化 SQL 查询。
  2. 占位符为 ?,下标从 1 开始,占位符不能使用多值。
  3. 使用 SQL 预编译。
  4. 可以阻止常见的 SQL 注入攻击。
  5. 性能比 Statement 高。

PreparedStatement 与 Statement 的区别:

  1. Statement 是 PreparedStatement 的父接口。
  2. 语法不同:PreparedStatement 使用预编译,使用动态 sql 时,相同的 sql 语句,除了参数不同,只需发送一次 sql,后面的只发送了参数,共用一个 sql 语句。(同构)
    而 Statement 只能使用静态 sql。(异构)
  3. 效率不同:PreparedStatement 使用了缓冲区,效率比 Statement 高。
  4. 安全性不同: PreparedStatement 可有效防止 sql 注入,Statement 不能。

3. PreparedStatement 的预编译

  1. 只有数据库服务器支持预编译,JDBC 驱动才能使用数据库预编译功能,预编译在比较新的 JDBC 驱动中默认是关闭的,需要配置才能打开。
  2. PreparedStatement 预编译是数据库进行的,编译后 函数的 key 缓存在 PreparedStatement 中,函数本身 缓存在 数据库服务器中。
  3. 预编译前检查 sql 语法是否正确。
  4. PreparedStatement 需要使用带 占位符的 sql, 如果使用静态 sql, 也还是会编译多次。Statement 本身就只支持静态的 sql。
  5. 数据库服务器对 sql 模板进行编译,且 PreparedStatement 存储了函数 的 key, 所以 PreparedStatement 做的就是把参数转义后直接传参数到数据库服务器中,然后让函数执行,所以 PreparedStatement 能防止 sql 注入。
  6. PreparedStatement 存储的 key 和 数据库存储的函数都建立在数据库连接的基础上,连接断开,key 和函数都清空。
  7. 各个连接之间的预编译都是相互独立的。
  8. Statement 不缓存函数的key, 数据库也不缓存函数, 所以多次执行相同一条 sql 时,还是会先检查 sql 的语法,再编译执行。

好啦,以上就是对 JDBC 编程的讲解,希望能帮到你 !
评论区欢迎指正 !

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/984207.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关于假冒我司关联公司进行欺诈活动的严正声明!

近日,有客户致电我司,反馈其在力软的“关联公司”进行了产品咨询时,对方声称可以以更低的价格出售力软次级品牌低代码产品。 对此,我司经过调查,确认所谓“关联公司”系冒充,我司对此类冒用我司名义进行的…

山洪、地质灾害监测利器-泥石流、山体滑坡AI视觉仪

1、设备介绍 AI视觉仪通过AI算法智能化摄像机,能够及时、全面的把握边坡潜在安全风险,有效防范自然灾害。支持全天候运行,在恶劣环境及气候条件下仍能正常进行监测数据采集。自动识别监控区域内是否有泥石流、山体滑坡等,一旦检测…

java中使用 Integer 和 int 的 含义、使用方法 及之间的区别

学习目标: 学习目标如下: 明确 Integer 和 int 的 含义、使用方法 及之间的区别 学习内容: 一、区别: 1.Integer是int的包装类,int则是java的一种基本的数据类型; 2.Integer变量必须实例化之后才能使用&a…

python爬取网站图片案例

下载指定页的图片Get方式 # _*_ coding : utf-8 _*_ # Time : 2023/9/6 9:50 # Author : xiaoyu # File : 爬取表情包 # Project : basicimport urllib.request from lxml import etreedef get_request(page):if (page 1):url "https://sc.chinaz.com/tupian/keaitupia…

【C++ Core Guidelines解析】C++学习之路的一盏明灯

前言:C语言的功能非常丰富,表达能力非常强。因为一种成功的通用编程语言拥有的功能必须比任何开发人员所需要的更多,任何一种有生命力且不断发展的语言都会不断积累用于表达程序员思想的替代用法。这会导致选择过载。那么,开发人员…

本地电脑搭建web服务器、个人博客网站并发布公网访问 【无公网IP】(1)

文章目录 前言1. 安装套件软件2. 创建网页运行环境 指定网页输出的端口号3. 让WordPress在所需环境中安装并运行 生成网页4. “装修”个人网站5. 将位于本地电脑上的网页发布到公共互联网上 前言 在现代社会,网络已经成为我们生活离不开的必需品,而纷繁…

【react】Hooks原理和实战

前言 在最初学习react的时候,我们大部分会选择去扒一扒React的官方文档,看看他是什么,怎么使用的。而我却很好奇在文档里学习的第一个完整的组件是 类(Class)组件,但是在实际工作中我们看到项目中所声明的…

无涯教程-JavaScript - ERFC.PRECISE函数

描述 ERFC.PRECISE函数返回x和无穷大之间集成的互补ERF函数。 互补误差函数等于1-ERF(即1-误差函数),由等式给出- $$Erfc(x) \frac {2} {\sqrt {\pi}} \int_ {x} ^ {\infty} e ^ {-t ^ 2} dt $$ 语法 ERFC.PRECISE(x)争论 Argument描述Required/OptionalxThe lower bound…

K8s 多集群实践思考和探索

作者:vivo 互联网容器团队 - Zhang Rong 本文主要讲述了一些对于K8s多集群管理的思考,包括为什么需要多集群、多集群的优势以及现有的一些基于Kubernetes衍生出的多集群管理架构实践。 一、为什么需要多集群 随着K8s和云原生技术的快速发展&#xff0c…

如何优雅的实现一个Mybatis插件

定位:此篇尝试用另一种角度描述如何完成一个mybatis插件,全程可以按段落跳跃阅读,有任何不适欢迎指出Thanks♪(・ω・)ノ 为什么这么设计 如果我想实现一个orm增强插件,首先就应该避免硬编码&…

C#写一个UDP程序判断延迟并运行在Centos上

服务端 using System.Net.Sockets; using System.Net;int serverPort 50001; Socket server; EndPoint client new IPEndPoint(IPAddress.Any, 0);//用来保存发送方的ip和端口号CreateSocket();void CreateSocket() {server new Socket(AddressFamily.InterNetwork, SocketT…

低代码是程序员“玩”出来的

一、前言 所谓“低代码”,最开始的雏形是程序员写一些重复的东西写腻了,产品今天想加个请假表,明天加个物资申请表,后天又想统计一下记录等等要求。对程序员来说开发这些就是一个个没意思的重复开发工作,所以就想着搞个…

LVGL Animations(动画)的简单使用

一、前言 哈喽,大家好。在进行界面设计的时候,动画的使用是必不可少的,今天这篇文章就跟大家分享一下 LVGL Animations(动画)的简单使用。笔者将在模拟器上运行演示,LVGL 版本号为 8.3.0。 二、Animation…

【HTML专栏3】!DOCTYPE、lang、字符集的作用

本文属于HTML/CSS专栏文章,适合WEB前端开发入门学习,详细介绍HTML/CSS如果使用,如果对你有所帮助请一键三连支持,对博主系列文章感兴趣点击下方专栏了解详细。 博客主页:Duck Bro 博客主页系列专栏:HTML/CS…

当AI遇到IoT:开启智能生活的无限可能

文章目录 1. AI和IoT的融合1.1 什么是人工智能(AI)?1.2 什么是物联网(IoT)?1.3 AI和IoT的融合 2. 智能家居2.1 智能家居安全2.2 智能家居自动化 3. 医疗保健3.1 远程监护3.2 个性化医疗 4. 智能交通4.1 交通…

Json“牵手”易贝商品详情数据方法,易贝商品详情API接口,易贝API申请指南

易贝是一个可让全球民众在网上买卖物品的线上拍卖及购物网站,易贝(EBAY)于1995年9月4日由Pierre Omidyar以Auctionweb的名称创立于加利福尼亚州圣荷塞。人们可以在易贝上通过网络出售商品。2014年2月20日,易贝宣布收购3D虚拟试衣公…

Ros noetic 机器人坐标记录运动路径和发布 实战教程(C)

前言: 承接上一篇博文本文将编写并记录上文中详细的工程项目,用于保存小车的运动路径,生成对应的csv,和加载所保存的路径到实际的Rviz中,本文将开源完整的工程项目,工程结构如下: 工程原码位于文章末尾: 路径存储: waypoint_saver 用于存储 waypoint 的节点 waypo…

控制三盏灯

驱动代码 #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/io.h> #include "head.h"unsigned int major;//保存主设备号 char kbuf[128] {0}; unsigned int …

C# Emgu.CV+Tesseract实现识别图像验证码

效果图&#xff0c;简单的还行&#xff0c;复杂的。。。拉跨 懒得写讲解了&#xff0c;全部源码直接上吧 /// <summary>/// 验证码识别/// </summary>public partial class FrmCodeIdentify : FrmBase{private string _filePath;// 原图像Image<Bgr, byte> …

基于Python开发的DIY字符画程序(源码+可执行程序exe文件+程序配置说明书+程序使用说明书)

一、项目简介 本项目是一套基于Python开发的DIY字符画程序&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Python学习者。 包含&#xff1a;项目源码、项目文档、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&…