【MySQL】JDBC 编程详解

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. 创建 sqlString sql = "select * from student"; // 不用加上 ;PreparedStatement statement = connection.prepareStatement(sql);// 4. 执行查询的 sqlResultSet 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.hqwc.cn/news/101097.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Cmake入门(一文读懂)

目录 1、Cmake简介2、安装CMake3、单目录简单实例3.1、CMakeLists.txt3.2、构建bulid内部构建外部构建 3.3、运行C语言程序 4、多目录文件简单实例4.1、根目录CMakeLists.txt4.2、源文件目录4.3、utils.h4.4、创建build 5、链接外部库文件6、注意 1、Cmake简介 CMake是一个强大…

基于Hata模型的BPSK调制信号小区覆盖模拟matlab完整程序分享

基于Hata信道模型的BPSK调制信号小区覆盖模拟matlab仿真,对比VoIP, Live Video,FTP/Email 完整程序: clc; clear; close all; warning off; addpath(genpath(pwd)); % Random bits are generated here. bits randi([0, 1], [50,1]); M 2; t 1:1:50; …

sqlserver 查询数据显示行号

查询的数据需要增加一个行号 SELECT ROW_NUMBER() OVER(ORDER BY witd_wages_area ,witd_wages_type ,witd_department_id ,witd_give_out_time) 行号,ISNULL(witd_wages_area, 0) witd_wages_area ,witd_wages_type ,witd_department_id ,ISNULL(CONVERT(VARCHAR(7), witd_gi…

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

当当网是知名的综合性网上购物商城,由国内著名出版机构科文公司、美国老虎基金、美国IDG集团、卢森堡剑桥集团、亚洲创业投资基金(原名软银中国创业基金)共同投资成立1。 当当网从1999年11月正式开通,已从早期的网上卖书拓展到网…

函数式接口:Java 中的函数式编程利器

文章目录 1. 函数式接口概念2. 注解3. 自定义函数式接口4. 函数式编程4.1 Lambda的延迟执行效果4.2 使用Lambda作为参数和返回值作为参数使用作为返回值使用 5. 常用的函数接口5.1 Supplier:生产者5.2 Consumer:消费者5.3 Predicate:判断5.4 …

薅羊毛零撸小游戏是这样赚米的!

薅羊毛小游戏作为一种特殊类型的游戏,吸引了一大批用户的关注。本文将探讨薅羊毛小游戏的盈利模式、用户体验以及对游戏产业的影响,旨在为读者提供专业而有深度的思考和启示。 一、薅羊毛小游戏的盈利模式: 1.广告变现:薅羊毛小游…

PageHelper分页原理解析

大家好,我是Leo! 今天给大家带来的是关于PageHelper原理的解析,最近遇到一个SQL优化的问题,顺便研究了一下PageHelper的原理,毕竟也是比较常用,源码也比较好看的懂,如果感兴趣的小伙伴可以跟着过程去DEBUG源…

直播倒计时 1 天|SOFAChannel#35《SOFABoot 4.0 — 迈向 JDK 17 新时代》

🙌 SOFAChannel#35 直播倒计时 1 天! 直播预约 1. 视频号 SOFAGirl 直播 ⬇️点击一键预约⬇️ 2. 添加 SOFAGirl 微信 加入 SOFAChannel 技术交流群 3. 钉钉搜索:44858463 钉钉群同步直播,讲师在线答疑 4. 扫描👇下方…

一款内网信息收集利用工具

FuckDomainMini 简介 这是一款基于java开发Windows的内网信息收集、利用工具 可以节省您的信息收集所花费的,又或者是做免杀所花费的时间 现在这个版本是先行版本,目前先行版只有一个功能,更多的功能还在调试与开发中。 尽情期待&#x…

JVM学习(一)--程序计数器

作用:记住下一个jvm指令的执行地址 每一行java源代码,会被编译为多行jvm指令,上文所说的执行地址就是这里的0,3,4等 ,由于执行访问特别频繁,程序计数器的底层是有寄存器来实现的 特点: 线程私有&#xff…

层次分析法(matlab实现)

1.层次分析法(AHP) 在决策理论中,层次分析法是一种以数学和心理学为基础,组织和分析复杂决策的结构化技术,它代表了一种量化决策标准权重的准确方法,通过成对比较,利用个别专家的经验来估计因素…

带纽扣电池产品出口澳洲安全标准,纽扣电池IEC 60086认证

澳大利亚政府公布了《消费品(纽扣/硬币电池)安全标准》和《消费品(纽扣/硬币电池)信息标准》。届时出口纽扣/硬币电池以及含有纽扣/硬币电池产品到澳大利亚的供应商,必须遵守这些标准中的要求。 一、 安全标准及信息标…