Java学习笔记21——使用JDBC访问MySQL数据库

JDBC(Java Database Connectivity,Java数据库连接)是应用程序编程借口(API),描述了一套访问关系数据库的标准Java类库。可以在程序中使用这些API,连接到关系数据库,执行SQL语句,对数据进行处理。JDBC不但提供了访问关系数据库的标准API,还为数据库厂商提供了一个标准的体系结构,让厂商可以为自己的数据库提供JDBC驱动程序,这些驱动程序可以让Java应用程序直接访问厂商的数据库产品,从而提高了Java程序访问数据库的效率。

1、JDBC驱动程序的类型:

JDBC驱动程序可以分为4类,分别是:

  • 1)JDBC-ODBC桥:通过ODBC访问数据库。
  • 2)部分本地API,部分Java驱动程序:大都C语言编写,依赖具体的平台。
  • 3)JDBC网络纯Java驱动程序:利用中间件的应用服务器来访问数据库,应用服务器作为数据库的网关,客户端通过它可以连接到不同的数据库服务器,如WebLogic服务器。
  • 4)本地协议纯Java驱动程序:目前,绝大多数数据库厂商支持客户程序通过网络直接与数据库通信的网络协议来直接访问数据库。这种类型的JDBC驱动程序完全用Java编写,通过与数据库建立直接的套接字连接,采用具体厂商的网络协议把JDBC API调用转换为直接的网络调用。这种类型的驱动是访问数据库效率最高的,不过,由于每个数据库厂商有自己的协议,因此访问不同厂商的数据库,需要不同的JDBC驱动程序。

2、下载MySQL JDBC 驱动

MySQL的JDBC 驱动程序没有包含在数据库的安装包中,需要单独下载,下载的网址为:MySQL :: Download Connector/Jicon-default.png?t=N7T8https://dev.mysql.com/downloads/connector/j/在浏览器中输入上面的网址,进入MySQL的JDBC 驱动程序的下载页面,如下图:

在上图下拉框中选择“Platfrom Independent”,然后出现如下图,Window平台下载zip文件下载。

将zip文件解压缩,将出现一个类似mysql-connector-j-8.3.0.jar的文件,它就是MySQL数据库的JDBC驱动。

3、JDBC API

JDBC API包含在JDK中,被分为两个包:java.sql和javax.sql。java.sql包中定义了访问数据库的接口和类,其中一些接口由驱动程序提供商来实现。

在开发数据库访问程序时,经常会编写如下的代码。

String url = "jdbc:mysql://127.0.0.1:3306/mysql";     
String username = "root"; 
String password = "2005Liuzx0302";
	    	
// 加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");		
conn = DriverManager.getConnection(url,username,password);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select * from user_info");

从上面代码可以看出利用JDBC访问数据库有统一的步骤。

1)首先调用Class类的forName()方法加载要访问的数据库的JDBC驱动类,

2)接下来调用Java的JDBC API(java.sql包)中的DriverManager类中的getConnection方法建立与数据库的连接,

3)然后调用Connection对象的createStatement方法,

4)最后调用Statement对象的executeQuery方法执行SQL语句得到ResultSet对象,就可以利用该对象来获取数据库中的数据了。

当然,最后要关闭数据库连接对象,释放数据库连接资源。

4、加载并注册数据库驱动

使用JDBC方法数据库,首先就是要调用Class类的forName方法加载要访问数据库厂商的JDBC驱动。了解这行代码背后发生的事情让我感觉有些豁然开朗。

1)Driver接口

java.sql.Driver是所有JDBC驱动程序需要实现的接口。这个接口是提供给数据库厂商使用的,不同厂商实现该接口的类名是不同的。常见的有:

  • com.microsoft.jdbc.sqlserver.SQLServerDriver:微软SQL Server2000版本驱动
  • com.microsoft.sqlserver.jdbc.SQLServerDriver:微软SQL Server2005及以后版本驱动
  • oracle.jdbc.driver.OracleDriver:是Oracle的JDBC驱动的类名,Oracle安装目录下jdbc目录中
  • com.mysql.cj.jdbc.Driver:是MySQL 8.0.x版本的驱动的类名。

Driver接口提供了一个connection方法,用来建立到数据库的连接,该方法的签名如下:

Connection connect(String url,Properties info) throws SQLException

开发人员在程序中不需要直接去访问这些实现了Driver接口的类,而是由Java的JDK 中的JDBC API(java.sql)中的驱动程序管理器类提供的方法来建立数据库连接,通过驱动程序管理器类的getConnection()方法来获取数据库连接。

2)深入理解JDBC的加载与注册驱动背后发生的事情

加载JDBC驱动是调用Class类的静态方法forName,向其传递要加载的JDBC驱动的类名。在运行时,类加载从CLASSPATH路径中定位和加载JDBC驱动类。在加载驱动程序后,需要注册驱动程序的一个实例。

DriverManager类是属于JDBC API(java.sql包)中提供的,用来负责管理加载的驱动程序,这个类中的所有方法都是静态的,在DriverManager类中提供了registerDriver方法来注册驱动程序类的实例,该方法的签名如下:

public statcic void registerDriver(Driver driver) throw SQLException

需要清楚的是,通常不需要开发人员在程序中直接调用registerDriver方法来注册驱动程序的实例,因为实现Driver接口的驱动程序类都包含了一个静态代码块,在这个静态代码块中,会调用DriverManager.registerDriver方法来注册自身的的一个实例。当使用Class类的forName方法在数据库驱动程序类的时候,会自动执行该静态代码块,从而注册驱动程序的一个实例,这样使用DriverManager.getConnection方法,就返回相应的驱动程序的数据库连接(Connection)对象。

下面是MySQL 驱动程序类的代码片段:

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    //
    // Register ourselves with the DriverManager
    //
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException e) {
            throw new RuntimeException("Can't register driver!");
        }
    
    }
    ...

}

从上面代码看,加载数据库厂商的驱动类,会自动执行该类的静态代码块,调用JDBC的驱动管理类DriverManager.registerDriver方法,并传入参数(new Driver())注册该厂商自身的驱动实例。

5、建立数据库连接

调用JDBC的DriverManager类的getConnection方法建立数据库连接,上面提到DriverManager类在java.sql包中,其中方法都是静态的,所以可以直接通过类名DriverManager.getConnection()来调用。在DriverManager类中重载了3个getConnection方法:

1)public static Connection getConnection(String url) throws SQLException

该方法通过给出的数据库URL建立到数据库的连接。

2)public static Connection getConnection(String url,String user,String password) throws SQLException

该方法除了需要数据库URL,还需要用户名和密码来建立到数据库的连接。

3)public static Connection getConnection(String url,Properties info) throws SQLException

该方法需要数据库URL和java.util.Properties对象。Properties对象包含了用于特定数据库所需要的参数,以键值对的方式指定连接参数,来建立到数据库的连接。

JDBC URL用于标识一个被注册的驱动程序,驱动程序管理器通过这个URL来选择正确的驱动程序,从而建立到数据库的连接。JDBC URL的语法如下:

jdbc:subprotocol:subname

整个URL用冒号(:)被分成了3个部分。

协议:在上面语法中,jdbc为协议,在JDBC中,它是唯一允许的协议,即总是它,只能是它。

子协议(subprotocl):子协议用于标识一个数据库驱动程序。

子名称(subname):子名称的语法与具体的驱动程序相关,不同驱动语法不尽相同。

常用数据可的JDBC URL:

SQL Server2005及以后:jdbc:sqlserver://localhost:1433;databasename=pubs

Oracle9i、10g、11g:jdbc:oracel:thin@localhost:1521:ORCL

MySQL8.0.x:jdbc:mysql://localhost:3306/databasename?userSSL=false&serverTimezone=UTC

  • userSSL=false: 这个参数指示JDBC驱动程序不要使用SSL来加密与MySQL服务器的连接。在某些情况下,为了简化配置或出于性能考虑,你可能想要禁用SSL。但请注意,禁用SSL可能会降低连接的安全性。
  • serverTimezone=UTC: 这个参数设置MySQL服务器使用的时区为UTC(协调世界时)。这有助于避免在Java应用程序和MySQL服务器之间由于时区差异而可能产生的时间问题。

serverTimezone=UTC 并不是JDBC连接MySQL时必须的参数。这个参数的主要用途是告诉JDBC驱动程序期望从MySQL服务器接收的时间戳使用哪个时区。如果不指定这个参数,JDBC驱动程序通常会使用系统的默认时区来解析时间戳,这可能导致在Java应用程序和MySQL服务器之间出现时区相关的问题。

如果你确定你的Java应用程序和MySQL服务器使用的时区是一致的,或者你愿意让Java应用程序使用系统的默认时区来解析时间戳,那么你可以省略这个参数。

然而,在实际应用中,为了避免潜在的时区问题,很多开发者会选择明确指定 serverTimezone 参数。特别是当你的应用程序需要处理跨时区的数据,或者当你的应用程序和数据库服务器部署在不同的地理位置时,指定时区参数会是一个好习惯。

另外,需要注意的是,除了 serverTimezone 参数,MySQL JDBC驱动程序还提供了其他与时区相关的参数,如 useLegacyDatetimeCode=false 和 useSSL=false 等。这些参数可以根据你的具体需求进行配置。

总的来说,是否指定 serverTimezone=UTC 取决于你的具体应用场景和需求。如果你不确定是否需要它,或者你的应用程序可能会受到时区问题的影响,那么建议添加这个参数以确保时间戳的正确处理。

6、访问数据库

数据库连接被用于向数据库服务器发送命令和SQL语句,在连接建立后,需要对数据库进行访问,执行SQL语句。在java.sql包中提供了3个接口,分别定义了对数据库调用的不同方式,分别是:Statement、PreparedStatement、CallableStatement。

1)Statement

调用Connnect对象的createStatement方法创建一个Statement对象。Statement对象用于执行静态的SQL语句,返回执行的结果。在Connection接口中定义了createStatement()方法,如下:

Statement createStatement() throws SQLException,常用来执行没有参数的SQL语句。

Statement接口中定义了如下的方法执行SQL语句:

  • ResultSet executeQuery(String sql) throws SQLException,该方法执行参数sql指定的SQL语句,返回一个ResultSet对象。ResultSet对象用于查看执行的结果。
  • int exceuteUpdate(String sql) throws SQLException,该方法执行参数sql指定的insert 、update或delete语句。该方法也可以用来执行SQL DDL 语句,必须创建表。
  • boolean execute(String sql) throws SQLException,该方法执行返回多个结果集的SQL语句。在某些情况下,一条SQL语句可以返回多个结果集或者更新行数。在通常情况下,不使用这个方法。除非要执行返回多个结果集的存储过程或者动态执行一个未知的SQL串。如果使用该方法,就必须使用getResultSet()或者getUpdateCount()方法获取结果,并且调用getMoreResults()方法来访问下一个结果集。如果返回的第一个结果是一个ResultSet对象,那么这个方法返回的是true;如果返回的是一个更新的行数或者没有结果,那么返回的是false
  • int[] executeBatch() throws SQLException,该方法允许向数据库提交一批命令,然后一起执行。如果所有的命令都成功执行,那么返回值是一个更新行数的数组。数组中的每一个int元素都是按照加入命令的先后顺序来存储的,表示了相应命令的更新行数。可以使用addBatch()方法将SQL命令加入到命令列表中。

2)PreparedStatement

在程序中传递的SQL语句在执行前需要被预先编译,包括语句分析、代码优化等,然后才能被数据库引擎执行。如果重复执行只有参数不同的SQL语句,则程序效率会比较低。这就是PreparedStatement使用比Statement多的原因。

如果要用不同的参数来多次执行同一个SQL语句,推荐使用PreparedStatement对象。PreparedStatement接口是继承Statement接口,它的对象表示一条预编译过的SQL语句。PreparedStatement对象所代表的SQL语句中的参数使用问号(?)来表示,调用PreparedStatement对象的setXxx方法来设置这些参数。setXxx方法有两个参数,第一个参数是要设置SQL语句中的参数索引(从1开始),第二个参数是要设置的SQL语句中的参数的值。

conn = DriverManager.getConnection(url,user,password);
Statement stmt = conn.createStatement();
stmt.executeUpdate("create table emp(id,INT,name VARCHAR(10),hiredate DATE");

PreparedStatement pstmt = conn.prepareStatement("insert emp(?,?,?)");

pstmt.setInt(1,1);
pstmt.setString(2,"liuzx");
pstmt.setDate(3,java.sql.Date.valueOf("2005-3-3"));
pstmt.executeUpdate();

pstmt.setInt(1,2);
pstmt.setString(2,"renrui");
pstmt.setDate(3,java.sql.Date.valueOf("1976-9-19"));
pstmt.executeUpdate();

针对不同类型的参数,需要使用对应的setXxx方法,尤其对应数据库日期类型的Java类型是java.sql.Date而不是java.util.Date(虽然java.sql.Date是java.util.Date的子类),MySQL数据库中的数据类型与Java中的数据类型有一定的对应关系,需要注意。

3)ResultSet

ResultSet对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet对象维护了一个指向当前数据行的游标,在初始的时候,游标在第一行之前,调用ResultSet对象的next方法移动到游标的第一行,next方法移动到游标的下一行,如果新的数据行有效(有数据),则返回true,否则返回false。通过判断这个方法的返回值,可以循环读取结果集中的数据库,直到最后。

该方法如下:

boolean next() throws SQLException

在ResultSet接口中定义了很多方法来获取当前行中列的数据,根据表中字段类型的不同,采用不同的方法来获取数据。如:getBoolean()、getInt()、getLong()、getString()、getDate()等。

在上面这些方法中,提供了两种形式的调用:一种是以列的索引作为参数(索引值从1开始),另一种是以列的名字作为参数。例如对于getString()方法,有下面两种形式:

  • String getString(int columnIndex) throws SQLException
  • String getString(String columnName) throws SQLException

如果不知道要获取的列数据的类型,则可以一律采用getString()方法来得到一个String类型的数据。

3.1)对于要遍历结果集ResultSet,利用ResultSet对象的next方法,循环取出每行的数据。需要注意的是,在ResultSet对象初始化的时候,ResultSet对象的游标指向第一行之前,所以需要先调用一次next方法(通常在while循环的条件判断中调用),将游标移动到第一行,读取最后一行后,再次执行next方法则返回false,while循环也就结束,完成了ResultSet结果集的遍历(从第一行到最后一行)。

try(rs) {
    while(rs.next()) {
        System.out.print(rs.getString("title") + "\t");
        System.out.print(rs.getString("author") + "\t");
        System.out.print(rs.getFloat("price") + "\t");
        System.out.println();
    }
}

在Java中,当你看到try后面直接跟着一对圆括号并包含一个或多个对象时,这通常是Java 7引入的“try-with-resources”语句的用法。这个特性主要用于自动管理实现了AutoCloseableCloseable接口的资源,如数据库连接、文件流等,确保在try块执行完毕后,这些资源能够被正确地关闭,即使发生异常也是如此。

AutoCloseable接口是java.lang包中的一个标记接口,它定义了一个close()方法,用于释放资源。任何实现了这个接口的对象都可以用在try-with-resources语句中。

在上面的代码中,当try块执行完毕后,rs对象的close()方法会自动被调用,无论是否发生了异常。这使得资源的管理更加简洁和安全,避免了因为忘记关闭资源而导致的潜在问题,如内存泄漏或文件句柄泄漏等。

这种结构特别适用于那些需要在finally块中关闭的资源,因为它自动处理了资源的关闭逻辑,使得代码更加清晰和易于维护。

3.3)多个条件的SQL语句,在应用中动态构造查询语句,往往可能需要多个不同条件,因此需要进行判断,并在程序中拼接and条件,可是while子句后的第一个条件是没有and关键字的,所以往往做法是判断是否有一个条件了,后面才好拼接“and”字符串。为了避免复杂的if判断,通常做法是在where子句后附加一个恒等条件:1=1,这样就简化了条件的字符串拼接,只要某个条件存在,就统一拼接:and  field=value,例如:

StringBuffer sb = new StringBuffer("select * from bookinfo where 1=1");
// and前有一个空格
sb.append(" and title=" + title);
sb.append(" and author=" + authoor);

ResultSet rs = stmt.executeQuery(sb.toString());

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

由浅到深认识C语言(7):预处理二进制

该文章Github地址:https://github.com/AntonyCheng/c-notes 在此介绍一下作者开源的SpringBoot项目初始化模板(Github仓库地址:https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址:https://blog.csdn…

Oracle19C静默安装教程

一、安装前的准备 1、安装Linux操作系统 红帽Linux安装教程 2、配置网络源或者本地源 网络源:网络源配置方法 本地源:本地源配置方法 3、hosts文件配置 配置hostname: hostnamectl set-hostname p19c配置hosts文件: cat &…

从零开始写 Docker(七)---实现 mydocker commit 打包容器成镜像

本文为从零开始写 Docker 系列第七篇,实现类似 docker commit 的功能,把运行状态的容器存储成镜像保存下来。 完整代码见:https://github.com/lixd/mydocker 欢迎 Star 推荐阅读以下文章对 docker 基本实现有一个大致认识: 核心原…

多个线程交替打印ABC

多个线程交替打印ABC package 多个线程交替打印ABC;import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier;/*** Created with IntelliJ IDEA.** Author: AlenXu* Date: 2024/03/20/10:10* Description:*/ public class ThreadLoopP…

数字功放VS模拟功放,选择适合你的音频解决方案

数字功放和模拟功放是音频系统中常用的两种功放技术,适用于不同的音频应用,都具有各自的优势和特点。本文将为您详细介绍数字功放和模拟功放的差异,并帮助您找到适合自己的音频解决方案。 1、数字功放是一种利用数字信号处理技术的功放。它将…

0基础 三个月掌握C语言(13)

数据在内存中的存储 整数在内存中的存储 在讲解操作符时 我们就已经学习了该部分的内容 这里我们回顾一下 整数的二进制表示方法有三种:原码 反码 补码 有符号的整数(unsigned) 三种表达方式均有符号位和数值位两部分 最高位的一位被当…

【C语言步行梯】C语言实现扫雷游戏(含详细分析)

🎯每日努力一点点,技术进步看得见 🏠专栏介绍:【C语言步行梯】专栏用于介绍C语言相关内容,每篇文章将通过图片代码片段网络相关题目的方式编写,欢迎订阅~~ 文章目录 需求分析具体实现主函数体菜单实现游戏实…

AI预测福彩3D第14弹【2024年3月20日预测--新算法重新开始计算第11次测试】

今天继续对第一套算法进行测试。废话不多说了,直接上分析出的图表,再上结果。 最终,经过研判分析,2024年3月20日福彩3D的七码预测结果如下: 百位:7 8 4 3 5 6 9(1换9) 十位:5 4 3 6 1 2 0(07分一…

day14-SpringBoot 原理篇

一、配置优先级 SpringBoot 中支持三种格式的配置文件: 注意事项 虽然 springboot 支持多种格式配置文件,但是在项目开发时,推荐统一使用一种格式的配置 (yml 是主流)。 配置文件优先级排名(从高到低&…

事务同步管理器TransactionSynchronizationManager

事务同步管理器的使用场景: 同步涉及的资源包括:SqlSession & Connection。同步资源核心目的是线程共享,意味着必须跟线程绑定。同步资源伴随着线程生存或者消亡,意味着线程结束之前必须手动清除其绑定的资源。事务同步管理器…

基于SpringBoot+Redis实现接口限流

前言 业务中需要对一些接口进行限流处理&#xff0c;防止机器人调用或者保证服务质量&#xff1b; 实现方式 基于redis的lua脚本 引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis&…

Linux Deepin系统安装x11vnc+cpolar实现Windows系统电脑远程其桌面

文章目录 1. 安装x11vnc2. 本地远程连接测试3. Deepin安装Cpolar4. 配置公网远程地址5. 公网远程连接Deepin桌面6. 固定连接公网地址7. 固定公网地址连接测试 x11vnc是一种在Linux系统中实现远程桌面控制的工具&#xff0c;它的原理是通过X Window系统的协议来实现远程桌面的展…

linux用git拉取我云端以及git处理冲突

拉取后切换一个跟云端分支(dev)一样的 git branch --set-upstream-toorigin/dev dev 之后就同步了 A在dev分支写了iii,提交 B在dev分支写了hhh,提交,冲突 怎么修改,B把云端的拉下来,随便改改就行

YOLOv7 | 添加GSConv,VoVGSCSP等多种卷积,有效提升目标检测效果,代码改进(超详细)

⭐欢迎大家订阅我的专栏一起学习⭐ &#x1f680;&#x1f680;&#x1f680;订阅专栏&#xff0c;更新及时查看不迷路&#x1f680;&#x1f680;&#x1f680; YOLOv5涨点专栏&#xff1a;http://t.csdnimg.cn/QdCj6 YOLOv7专栏&#xff1a; http://t.csdnimg.cn/dy…

Qt 多元素控件

Qt开发 多元素控件 Qt 中提供的多元素控件有: QListWidgetQListViewQTableWidgetQTableViewQTreeWidgetQTreeView xxWidget 和 xxView 之间的区别 以 QTableWidget 和 QTableView 为例. QTableView 是基于 MVC 设计的控件. QTableView 自身不持有数据. 使用QTableView 的 …

latex如何让标题section取消数字标号

解决方法——加一个*号 在LaTeX中&#xff0c;如果你想让section标题取消数字标号&#xff0c;可以使用section*代替section。section*将生成一个不带数字标号的节标题。 例如&#xff0c;你可以这样写&#xff1a; \section*{这是不带数字标号的节标题}这将生成一个标题&am…

给老婆整了个短剧搜索机器人APP

最近短剧挺火&#xff0c;很多群友们都在做一些资源分享&#xff0c;老胡于是基于这些资源做了个短剧搜索引擎&#xff0c;挺多朋友喜欢看的&#xff0c;我老婆也在看哈哈&#xff0c;真上头&#xff0c;废话不多说&#xff0c;上短剧机器人。 短剧机器人 直接在微信群输入&…

解决由于历史原因解析tflite失败的问题

文章目录 0. 背景1. tflite 历史遗留问题2. schema3. flatbuffers 编译器3.1 安装 FlatBuffers 编译器3.2. 编译 FlatBuffers schema 文件3.3 使用生成的 Python 文件 4 问题未解决终极解决方案 写在最前面&#xff1a;解决方法是升级tensorflow版本&#xff0c;重新生成tflite…

【go从入门到精通】if else 条件控制

Go 语言条件语句&#xff1a; 在 Go 语言中&#xff0c;条件语句用于根据不同的条件执行不同的代码。Go 语言提供了两种条件语句&#xff1a;if 语句和switch 语句。 if语句 if由一个布尔表达式后紧跟一个或多个语句组成。 语法 Go 编程语言中 if 语句的语法如下&#xff…

反向海淘系统中的数据安全挑战与解决方案探讨

**反向海淘系统中的数据安全挑战与解决方案探讨** **一、背景** 随着反向海淘市场的不断扩大&#xff0c;涉及的数据安全挑战也日益增多。本文旨在探讨反向海淘系统中面临的数据安全挑战&#xff0c;以及相应的解决方案。 **二、数据安全挑战** 1. **数据传输安全**&#x…