Java的JDBC编程

Java的数据库编程:JDBC

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

JDBC工作原理

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

JDBC优势:

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

Java通过 JDBC 这样的技术来操作 MySQL
MySQL提供了一系列API让程序猿调用,从而通过代码来操作数据库

应用程序编程接口(Application Programming Interface), 简称API
API都是现成的,可以直接进行调用,实现一些效果
对于Java来说,Java提供了"标准库",只要安装了Java,此时就可以使用标准库中的类和方法(标准库的API), 也可以使用其他大佬写好的类和方法(第三方库的API),但这不是自带的,需要额外安装

同理C/C++也都有标准库自带的API,也有第三方库的API
有的库,提供的API特别多,形成了一系列体系,这时也可以称为"SDK" (software development kit :软件开发工具包),Java中的 JDK 就是一个SDK, 只不过给他起了个专属的名字: Java软件开发工具包

数据库驱动包:不同的数据库,对应不同的编程语言提供了不同的数据库驱动包,如:MySQL提
供了Java的驱动包mysql-connector-java,同样的,要基于Java操作Oracle数据库则需要Oracle的数据库驱动包ojdbc。

Oracle, SQL Server, SQLite…也都提供了这样的API让程序猿调用,这些数据库都是不同的厂商/不同的人开发的,这些不同数据库的API提供的功能大同小异,但是细节上存在很大的差异。一个程序猿如果在项目中用到不同的数据库,就需要学习掌握多份API的使用,非常麻烦。Java就说他那边出一套API接口标准,数据库厂商都照着他那一套适配过来,每个数据库厂商额外写一些代码,能够按照Java提供的这一套标准把原生的API重新封装。后续程序猿只需要学习掌握Java这一套API就可以无缝切换各种数据库了,而这一套API就是JDBC
在这里插入图片描述
这样的"接口转换"程序称为"数据库驱动",类似于电脑上的"转接头"

具体步骤

准备数据库驱动包

JDBC 是 Java标准库提供的,只要安装了JDK就自带 JDBC
但是使用JDBC 操作 MySQL 就需要下载并导入MySQL的驱动包
下载安装一个程序最好去官网下载,但是Oracle官网特别复杂,很难找。
而对Java程序猿来说,日常开发中会用到大量的第三方库,就有大佬把这些第三方库的安装包收集到一起,统一整理到一个网站上,称为"中央仓库"(类似于收集APP的应用商店)。我们直接去中央仓库下载就行了
中央仓库网址:https://mvnrepository.com/

在搜索框搜索mysql
在这里插入图片描述
在这里插入图片描述
这两个都是MySQL提供的JDBC的驱动包,我的数据库是 mysql 5.7 就搭配旧版本使用了,如果是 mysql 8, 使用新版本就行
在这里插入图片描述
有很多小版本,但区别不大。只要最前面第一位的大版本匹配,后续的小版本无所谓
我就以5.1.14示范:

这一框显示的是漏洞
在这里插入图片描述
自己的数据库非常安全,不怕漏洞,但未来实际开发中,公司里要选择稳健一点的版本

下载的地方比较隐蔽,下载到哪都无所谓,待会要拷贝到项目中
在这里插入图片描述
就是这个:
在这里插入图片描述
.jar其实是一个类似于.rar这样的压缩包文件,包含了很多的.class文件(.java 编译生成的字节码文件)

将编译出来的.class拷贝给别人,别人就能运行使用了
由于一个程序中,往往.class很多,会涉及到一些复杂的目录结构.直接拷贝一堆.class会乱(不知道拷多还是拷少了)
于是约定把这些要发布的.class按照特定格式,打包压缩,就得到了.jar文件.后续直接拷贝.jar文件即可,JVM可以直接识别.jar内部的.class文件并直接运行

.jar的方式是最常见的一种发布Java程序的方式

把驱动包添加到项目的依赖中

1.在项目中随便创建一个目录,命名为lib (library: 图书馆, 库 的缩写)
在这里插入图片描述

2.把刚才的依赖包mysql-connector-java-5.1.14.jar拷贝到lib中
在这里插入图片描述
3.右键lib这个目录,点击选项中的添加为库…(Add as Library…),弹出框点击确定.这是告诉IDEA,当前这个目录是存放第三方库的目录,此时IDEA就能识别到咱们拷贝进来的驱动包了
在这里插入图片描述
添加之后, 此处就能看到"可展开",IDEA已经识别到驱动包了
在这里插入图片描述
上述操作是JDBC程序的准备工作,之后就可以编写代码了

编写代码

先建一个类,我取名为JDBCDemo
Demo 是例子的意思,很多时候阅读一些别人写的文档,一般都是先看Demo/Example/Quick Start…

要想编写JDBC,还需要准备好数据库和数据表(虽然 JDBC 也能进行建表操作,但一般都是提前创建好的)
在这里插入图片描述
我就以 javamysql数据库里的 test表 为例进行演示

数据库连接Connection

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

  • 一种是通过DriverManager(驱动管理类)的静态方法获取:

  • 一种是通过DataSource(数据源)对象获取。实际应用中会使用DataSource对象。

  DataSource dataSource = new MysqlDataSource();//这个类就来自于驱动包
  ((MysqlDataSource) dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/javamysql?characterEncoding=utf8&useSSL=false");//向下转型
  //除了要设置URl,还要设置用户名和密码
  //用户名
  ((MysqlDataSource) dataSource).setUser("root");//向下转型
  //密码
  ((MysqlDataSource) dataSource).setPassword("123456");//向下转型

  //2.和数据库服务器建立连接
  Connection connection = dataSource.getConnection();

以上两种方式的区别是:

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

Url (唯一资源定位符)
MySQL是一个客户端-服务器程序
服务器是保存数据的本体
JDBC 代码就是在写一个 MySQL客户端(之前用的cmd黑框就是MySQL自带的客户端)
我们自己写的客户端就要通过网络访问到数据库服务器,进一步进行增删改查,而URL描述了服务器/服务器上的资源在网络中所在的位置

URL格式:协议的名称://IP地址:端口号/数据库名?参数=值&参数=值
因为我的客户端和服务器在同一主机上,URL就是jdbc:mysql://127.0.0.1:3306/javamysql?characterEncoding=utf8&useSSL=false

  • jdbc:mysql:描述的是给jdbc中的 mysql 使用的 url
  • 127.0.0.1:IP地址,描述的是一个主机在网络上的位置
  • 3306: 端口号,区分主机上的应用程序。3306 是mysql 默认的端口号,安装的时候可以设置,如果没修改,默认是 3306
  • javamysql:数据库的名字
  • characterEncoding=utf8:指定字符集UTF8
  • useSSL=false:关闭加密通信(不写的话默认开启。有些主机上对应的依赖程序可能有问题,导致连不上)

mysql中可以手动创建各种名字的用户,默认自带一个root 用户,而且这个 root 是管理员账户(权限最大)

DataSource要能对接到不同的数据库,而不同的数据库设置数据源的方式是不一样的,对于mysql是设置url, user, password ,对于其他数据库就不一定了。所以如果是向上转型创建的DataSource对象需要向下转型回去

进行 客户端 - 服务器之间通信的时候,常见有两种通信模式:
1)有连接(JDBC就属于"有连接"),类似于打电话,拨号之后,对方得接通了,才能说话,对方要是挂断了,没的通信
2)无连接,类似于发短信,无论对方是否接受,都能把数据发过去

在这里插入图片描述
选第一个,这个Connection就表示 客户端和服务器之间的 “连接对象”
如果一切顺利,连接建立成功,此时就能够得到 Connection 对象
但 getConnection 很可能失败(服务器没有接受连接),原因有很多,包括不限于:

  • 数据库服务器没有正确启动
  • url写错了
  • 用户名写错了
  • 密码写错了
  • 网络断开了(网线掉了)

虽然是通过Java来操作数据库,实际上核心还是SQL,只不过是把SQL嵌入到Java中
在这里插入图片描述
这是字符串结构的 sql, 往往还需构造一个"语句对象"

Statement对象

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

Statement用于执行不带参数的简单SQL语句
PreparedStatement1.用于执行带或者不带参数的SQL语句
2.SQL语句会预编译在数据库系统
3.执行速度快于Statement对象
CallableStatement用于执行数据库存储过程的调用

一个字符串 sql 语句发送到数据库服务器上,是要先对其进行解析和各种校验(判定是否符合语法要求)才能执行,这个解析操作也是需要花费一定的开销,虽然开销不是很大,但mysql服务器同时要给多个客户端提供服务。为了减轻数据库服务器的负担,就可以在客户端这边完成预编译,此时把解析后的结果发给服务器,服务器直接执行即可。所以实际开发中最常用的是PreparedStatement对象,以下对其的总结:

  • 参数化SQL查询
  • 占位符不能使用多值
  • 性能比Statement高
  • 占位符:? 下标从1开始
  • SQL预编译
  • 阻止常见SQL注入攻击

在这里插入图片描述
SQL语句不用加分号
这个就是把字符串形式的SQL 转为 语句对象(内部会对sql进行解析校验)

执行SQL的方法

主要掌握两种执行SQL的方法:

executeQuery()方法执行后返回单个结果集的,通常用于select语句
executeUpdate()方法返回值是一个整数,指示受影响的行数,通常用于update、insert、delete语句

在这里插入图片描述
执行这个方法,就会给数据库服务器发起请求,请求中就是包含了解析后的 SQL 语句,等待数据库执行 SQL。数据库执行完 SQL 后, 返回响应。这个方法再获取到响应,并且把数据库返回的结果通过 返回值 体现出来

释放资源

关于为什么释放资源,参考此文
在这里插入图片描述
这里的释放顺序是确保 先创建的后释放。释放顺序通常和创建的顺序相反

内存是有限的,用了之后不释放,继续再申请,迟早会把内存耗尽。Java有 垃圾回收机制(GC), 可以自动帮我们回收不用的内存,但是编程过程中涉及到的"有限资源" 不仅仅是内存,这些资源Java无能为力,需要手动释放了

完整的代码:

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

import javax.sql.DataSource;//JDBC 自带的 Interface
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Scanner;


public class JDBCDemo {
    public static void main(String[] args) throws SQLException {
        //接下来通过 JDBC 进行一个简单的 插入数据 的操作

        Scanner in = new Scanner(System.in);

        //1,创建"数据源"(DataSource) -- 要操作的数据库,数据是在哪里。
        // 这个过程就是所谓的数据库厂商写代码和JDBC进行对接,让数据库厂商实现JDBC提供的 interface
        //在 MySQL 中,就需要设定好 MySQL服务器的位置,要访问的数据库的名字,访问的数据库的用户名和密码

        DataSource dataSource = new MysqlDataSource();//这个类就来自于驱动包
        ((MysqlDataSource) dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/javamysql?characterEncoding=utf8&useSSL=false");//向下转型
        //除了要设置URl,还要设置用户名和密码
        //用户名
        ((MysqlDataSource) dataSource).setUser("root");//向下转型
        //密码
        ((MysqlDataSource) dataSource).setPassword("123456");//向下转型

        //2.和数据库服务器建立连接
        Connection connection = dataSource.getConnection();

        //3.构造一个操作数据库的SQL语句
        System.out.println("请输入学号: ");
        int id = in.nextInt();
        System.out.println("请输入姓名: ");
        String name = in.next();
        String sql = "insert into test values("+ id + ", '" + name + "')";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //4.执行SQL ,把刚才解析好的语句发给数据库服务器
        //返回值是一个int 类型, 表示这个操作影响了几行数据
        int n = preparedStatement.executeUpdate();
        System.out.println("成功插入" + n + "条记录");

        //5.执行完毕,进行收尾操作,释放前面创建的各种资源
        //主要是释放 语句对象 和 连接对象, DataSource不必释放
        preparedStatement.close();
        connection.close();
        
    }
}

运行一下:
在这里插入图片描述
切到控制台查看一下:
在这里插入图片描述

拼接字符串,就可以完成输入的预期效果,这种做法虽然可行。但不推荐,理由如下:

1)看起来非常奇怪,可读性不高
2)不太安全, 可能会引起 sql 注入攻击这样的漏洞

比较推荐通过PreparedStatement 提供的 API 来完成动态内容的设置

在这里插入图片描述
? 就是占位符,需要后续代码做出内容替换
在这里插入图片描述
在这里插入图片描述
当前这里需要啥类型的数据就使用哪个方法

完整代码:

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

import javax.sql.DataSource;//JDBC 自带的 Interface
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Scanner;


public class JDBCDemo {
    public static void main(String[] args) throws SQLException {
        //接下来通过 JDBC 进行一个简单的 插入数据 的操作

        Scanner in = new Scanner(System.in);

        //1,创建"数据源"(DataSource) -- 要操作的数据库,数据是在哪里。
        // 这个过程就是所谓的数据库厂商写代码和JDBC进行对接,让数据库厂商实现JDBC提供的 interface
        //在 MySQL 中,就需要设定好 MySQL服务器的位置,要访问的数据库的名字,访问的数据库的用户名和密码

        DataSource dataSource = new MysqlDataSource();//这个类就来自于驱动包
        ((MysqlDataSource) dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/javamysql?characterEncoding=utf8&useSSL=false");//向下转型
        //除了要设置URl,还要设置用户名和密码
        //用户名
        ((MysqlDataSource) dataSource).setUser("root");//向下转型
        //密码
        ((MysqlDataSource) dataSource).setPassword("123456");//向下转型

        //2.和数据库服务器建立连接
        Connection connection = dataSource.getConnection();

        //3.构造一个操作数据库的SQL语句
        System.out.println("请输入学号: ");
        int id = in.nextInt();
        System.out.println("请输入姓名: ");
        String name = in.next();
        String sql = "insert into test values(?, ?)";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setInt(1, id );
        preparedStatement.setString(2, name);
        
        //4.执行SQL ,把刚才解析好的语句发给数据库服务器
        //返回值是一个int 类型, 表示这个操作影响了几行数据
        int n = preparedStatement.executeUpdate();
        System.out.println("成功插入" + n + "条记录");

        //5.执行完毕,进行收尾操作,释放前面创建的各种资源
        //主要是释放 语句对象 和 连接对象, DataSource不必释放
        preparedStatement.close();
        connection.close();

    }
}

运行一下:
在这里插入图片描述

切到控制台查看一下:
在这里插入图片描述

查询:

ResultSet对象

ResultSet对象被称为结果集,它代表符合SQL语句条件的所有行,并且它通过一套getXXX方法提供了对这些行中数据的访问。
ResultSet里的数据一行一行排列,每行有多个字段,并且有一个记录指针,指针所指的数据行叫做当前数据行,我们只能来操作当前的数据行。我们如果想要取得某一条记录,就要使用ResultSet的next()方法 ,如果我们想要得到ResultSet里的所有记录,就应该使用while循环。

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

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

        //2.建立连接
        Connection connection = dataSource.getConnection();

        //3.构造sql
        String sql = "select * from test";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //4.执行sql
        ResultSet resultSet = preparedStatement.executeQuery();

        //5.遍历结果集
        while (resultSet.next()) {
            //针对这里的光标指向的行进行处理
            //初始情况下,光标在第一行数据的前一个位置
            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            System.out.println("id = " + id + ", name = " + name);
        }

        //6.释放资源
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }
}

在这里插入图片描述

  • 30
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值