Logo

郎哥编程

使用JDBC连接MySQL数据库

2021-07-23 337

学习目标:了解JDBC驱动的安装和使用,以及使用JDBC连接MySQL数据库,执行SQL语句并获取查询结果。

认识JDBC

在实际开发中,大量的开发都是基于数据库的,使用数据库可以方便地实现数据的存储与查询。

当前主流的数据库基本都是关系数据库,也就是使用SQL语言对数据库进行查询和操作。程序主要使用的关系数据库有MySQL、SQL Server、Oracle等数据库管理系统。

开发Java语言的团队为了方便程序员开发数据库程序,减少数据库开发的工作量,尽量做到同一数据库程序代码能够操作不同的数据库管理系统。他们开发了JDBC数据库驱动程序,JDBC是一种标准的执行SQL语句的Java API接口,它可以方便的实现对多种关系数据库管理系统的统一操作。

JDBC接口的具体实现由生产数据库的厂商来实现,当前主流的关系数据库管理系统(如MySQL、SQL Server、Oracle等),都提供了对JDBC接口的支持。

按照JDBC的实现方式,JDBC驱动程序分为以下四类:

JDBC—ODBC桥驱动

在这种实现方式中,JDBC并不直接操作数据库,而是通过Windows系统提供的ODBC(开放数据库连接)来操作数据库,ODBC可以看作是JDBC和数据库的连接桥梁,因此称作JDBC—ODBC桥驱动。

12.png

由于JDBC利用ODBC作为操作数据库的桥梁,限制了Java程序的跨平台性,Java程序只能在Windows平台下运行,另外在性能上也比较低效。因此这种JDBC实现方式在数据库应用开发中用的比较少。

JDBC本地驱动

在这种实现方式中,JDBC驱动程序通过数据库管理系统的API接口来直接操作数据库,减少了通过ODBC操作数据库的环节,提高了数据库的访问效率。这种实现方式只能支持特定的数据库系统,如果要支持多个数据库系统,需要接入多个JDBC本地驱动。

13.png

JDBC本地驱动要求在运行Java数据库程序的每台机器上,同时要安装数据库、JDBC驱动以及数据库系统的API。

JDBC网络驱动

在这种实现方式中,本地客户端Java程序可以直接通过约定的网络通信协议访问数据库,JDBC驱动程序会把数据库的访问操作转换为约定的网络协议,发送到数据库应用服务器,数据库应用服务器再把网络协议转换为数据库的API。

14.png

本地协议JDBC驱动

在这种实现方式中,将JDBC调用直接转换为数据库特定的网络通信协议。它是最常见的驱动程序类型,Java数据库程序开发多采用这类JDBC驱动程序,网上下载的JDBC驱动jar包基本都属于这类驱动,通常是由数据库厂商直接提供,例:mysql-connector-java.jar。因为是使用网络通信,驱动程序可以完全用java编写,支持跨平台部署,性能也较好。

15.png

JDBC提供了JDBC API类库,让开发者能够连接数据库、执行SQL语句、数据库表记录的添加、插入、更新、删除等操作。在JDBC的基本操作中,最常用的类和接口有DriverManager、Connection、Statement、Result、Prepared Statement。开发人员可以利用这些类和接口来开发数据库应用程序。

在后面的数据库编程课程中,将采用MySQL8.0数据库作为课程案例的操作数据库,读者需要在本地安装MySQL8.0数据库。

在项目中添加JDBC驱动

开发数据库程序,需要在项目中添加JDBC驱动程序,MySQL8.0数据库的JDBC驱动程序文件是mysql-connector-java-8.0.18.jar。

jar文件也称为jar包,它是一种java归档文件,该文件把具有同类功能且已编译的类calss文件、相关的程序资源(如文本、图片等)放置到一个归档文件中,作为类库来使用,这个归档文件就是jar文件。

jar文件的基础格式就是zip文件,将jar文件的扩展名修改为zip后,可以使用zip程序解压jar文件,查看jar包里面的内容。

新建项目PUnit15,在PUnit15项目中添加mysql-connector-java-8.0.18.jar包。添加步骤如下:

(1)在项目目录下,建立lib目录,将mysql-connector-java-8.0.18.jar包复制到lib目录。

(2)单击【File】菜单下的“Project Structure…”命令,该命令的快捷键为(Ctrl + Alt + Shift + s),IDEA弹出Project Structure对话框,选择Dependencies标签,添加已复制到项目目录下的mysql-connector-java-8.0.18.jar包,单击【OK】按钮即可。

16.png

3、  连接MySQL数据库

连接MySQL数据库,需要在程序中加载JDBC驱动程序,然后使用DriverManager类的getConnection()方法来连接MySQL数据库。连接步骤如下图所示:

17.png

(1)加载JDBC驱动程序

所有的JDBC驱动程序都实现了java.sql.driver接口,加载JDBC驱动就是加载JDBC实现该接口的类,一般使用Class类的forName()方法加载该类,forName()方法要求传入包含加载类路径的文本串,也就是JDBC实现java.sql.driver接口的类所在jar包的路径。

基于MySQL8.0数据库的JDBC实现java.sql.driver接口的类路径为:

com.mysql.cj.jdbc.Driver

加载类文件的语句为:

Class.forName(“com.mysql.cj.jdbc.Driver”);

使用winrar软件直接打开mysql-connector-java-8.0.18.jar文件,即可发现JDBC实现java.sql.driver接口的类所在jar包的路径。

(2)连接数据库

加载JDBC驱动程序成功后,下一步就可以连接数据库。连接数据库要用到DriverManager类,DriverManager类在java.sql包内。它负责JDBC驱动程序的管理,作用于程序和JDBC驱动程序之间,跟踪可用的驱动程序,并在数据库的驱动程序之间建立连接。

它提供的主要方法就是得到一个数据库的连接,getConnection()方法用于连接数据库,并返回一个Connection对象。该方法说明如下:

●   static Connection     getConnection(String url)

该方法用于建立和数据库的连接,数据库的连接路径由参数url给出。它会从已加载的JDBC驱动程序集合中选择适当的驱动程序。

参数url为数据库的连接路径,连接MySQL数据库的路径格式为:

 jdbc:mysql://IP地址:端口号/数据库名称?key1=value1&……& keyn=valuen

其中“jdbc”为连接协议,“mysql”为连接的子协议,该子协议指定是连接mysql数据库,“IP地址:端口号”指定数据库所在主机的IP地址和数据库的监听端口,在实际连接中,使用主机的IP地址和数据库的监听端口号来代替。“数据库名称”指定要连接的数据库名称。在“数据库名称”后面的key和value键值对可以有多个,也可以没有,这些键值对为驱动程序提供附加的连接信息。

●   static Connection     getConnection(String url, String user, String password)

该方法用于建立和数据库的连接,数据库的连接路径由参数url给出,数据库的登录用户由参数user给出,数据库用户的登录密码由参数password给出。

如果待连接的数据库需要指定用户名和密码才能访问时,就需要使用该方法来连接数据库。

(3)操作数据库

数据库连接成功后,就可以进行数据库的查询、更新和写入操作。对数据库的所有操作都在Connection接口内,关于Connection接口的内容在后面的课程会陆续介绍。

(4)关闭数据库的连接

完成数据库的操作后,需要关闭数据库的连接,以释放数据库连接资源。Connection类的close()方法用于关闭与数据库的连接。

本课案例中涉及的数据库可以在课程资源下载,下载的数据库是SQL文件,可以使用MySQL命令导入下载的数据库。

案例1:建立ConnectionMySQLTest类,连接数据库。

在PUnit15项目新建demo包,在demo包下新建ConnectionMySQLTest类。代码如下:

package demo;
 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
 
public class ConnectionMySQLTest {
 
    // 定义JDBC加载路径
    static String  jdbc = "com.mysql.cj.jdbc.Driver";
    // 定义MySQL数据库的连接地址
    static String  mysqlurl = "jdbc:mysql://localhost:3306/shop?serverTimezone=UTC";
    // 定义MySQL数据库的用户名
    static String  username = "root";
    // 定义MySQL数据库的用户名登录密码
    static String  password = "~123456q";
   
    public static void main(String[] args) {
 
        Connection conn = null;
        try {
            // 加载JDBC驱动
            Class.forName(jdbc);
            // 连接数据库
            conn = DriverManager.getConnection(mysqlurl, username, password);
            if( null != conn )
            {
                System.out.println(conn);
                conn.close();
            }
            else
            {
                System.out.println("数据库连接失败");
            }
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
 
 
    }
}

代码中mysqlurl数据库路径说明如下:

(1)    localhost 是指本地IP地址;

(2)    MySQL数据库的默认端口号是3306;

(3)    shop是连接数据库的名称,shop数据库可以在课程资源区下载,下载后可导入到MySQL数据库;

(4)    serverTimezone键值对是用来设置时区,可以将serverTimezone设置为UTC,UTC表示的是全球标准时间。如果程序运行是在中国,一般将该值设置为GMT%2B8,即北京时间东八区。

代码中的username和password分别是登录数据库的用户名和登录密码。

程序执行结果如下图所示:

01.png

程序输出以上信息,表示数据库连接成功。

4、  执行SQL语句

MySQL数据库连接成功后,就可以执行SQL语句了,使用SQL语句可以对数据库记录进行查询、更新、添加及删除等操作。执行SQL语句的操作由Statement接口完成,Statement接口在java.sql包内,该接口可以使用Connection接口提供的createStatement()方法实例化。

MySQL数据库连接成功后,就可以执行SQL语句了,使用SQL语句可以对数据库记录进行查询、更新、添加及删除等操作。执行SQL语句的操作由Statement接口完成,Statement接口在java.sql包内,该接口可以使用Connection接口提供的createStatement()方法实例化。

Connection接口的createStatement()方法是重载方法,具体说明如下:

●   Statement   createStatement()

该方法用于创建向数据库发送SQL语句的Statement对象。

●   Statement createStatement(int resultSetType, int resultSetConcurrency)

该方法用于创建具有给定类型和并发性的ResultSet对象的Statement对象。此方法与上面的createStatement方法相同,但它允许重写默认的结果集类型和并发性。

参数resultSetType设置结果集类型,结果集类型有如下常量定义:

ResultSet.TYPE_FORWARD_ONLY;

说明:结果集的游标只能向下滚动。

ResultSet.TYPE_SCROLL_INSENSITIVE;

说明:结果集的游标可以上下移动,当数据库变化时,当前结果集不变。

ResultSet.TYPE_SCROLL_INSENSITIVE;

说明:返回可滚动的结果集,当数据库变化时,当前结果集同步改变。

参数resultSetConcurrency设置并发类型,并发类型有如下常量定义:

ResultSet.CONCUR_READ_ONLY;

说明:不能用结果集更新数据库中的表。

ResultSet.CONCUR_UPDATETABLE;

说明:能用结果集更新数据库中的表。

●   Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)

此方法与上面的方法相同。不同之处是多了resultSetHoldability参数,该参数设置ResultSet的保持类型。该保持类型有如下常量定义:

ResultSet.HOLD_CURSORS_OVER_COMMIT;

说明:表示提交当前事务时, 具有此可保存性的打开的 resultset 对象不被关闭。

ResultSet.CLOSE_CURSORS_AT_COMMIT;

说明:表示提交当前事务时,具有此可保存性的打开的 resultset 对象将被关闭

Statement接口的主要方法说明如下:

02.JPG

03.JPG

该方法立即释放此语句对象的数据库和JDBC资源。

案例2:查询shop数据库shoper表的记录数。

在demo包下新建SqlDemoTest1类。代码如下:

package demo;
 
import java.sql.*;
 
public class SqlDemoTest1 {
    // 定义JDBC加载路径
    static String jdbc = "com.mysql.cj.jdbc.Driver";
    // 定义MySQL数据库的连接地址
    static String mysqlurl = "jdbc:mysql://localhost:3306/shop?serverTimezone=GMT%2B8";
    // 定义MySQL数据库的用户名
    static String username = "root";
    // 定义MySQL数据库的用户名登录密码
    static String password = "~123456q";
 
    public static void main(String[] args) {
        Connection conn = null;
        Statement statement = null;
        try {
            // 加载JDBC驱动
            Class.forName(jdbc);
            // 连接数据库
            conn = DriverManager.getConnection(mysqlurl, username, password);
            if( null != conn )
            {
                System.out.println(conn);
                // 获取表记录数的SQL语句
                String sql = "select * from shoper";
                // 实例化Statement对象
                statement = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
                        ResultSet.CONCUR_UPDATABLE);
                // 执行SQL语句
                boolean bSucess = statement.execute(sql);
                if( bSucess )
                {
                    // 获取记录数
                    ResultSet resultset =statement.getResultSet();
                    // 将结果集的光标移动到最后一条记录
                    resultset.last();
                    System.out.println("shoper表记录数:" + resultset.getRow());
                    resultset.close();
                }
                else
                {
                    System.out.println("SQL语句执行失败");
                }
                statement.close();
                conn.close();
            }
            else
            {
                System.out.println("数据库连接失败");
            }
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
 
    }
 
}

SqlDemoTest1程序的sql语句用于获取shoper表的全部记录,statement对象的execute()方法执行指定的sql语句,如果sql语句执行成功,该方法会返回true。statement对象的getResultSet()方法会返回execute()方法执行后的结果集。程序获取结果集后,调用resultset对象的last()方法将结果集光标移动到结果集的最后一条记录,然后resultset对象的getRow()方法获取当前结果集光标所在记录行数,该记录行数即为结果集的全部记录数。

5、 ResultSet与SQL查询

Statement接口执行SQL查询语句后,会以结果集的方式返回,查询到的数据库所有记录都会存储在结果集中,操作该结果集的接口就是ResultSet接口。

ResultSet接口的常用方法说明如下:

●   boolean      first()

该方法将光标移动到此ResultSet结果集中的第1行。

●   boolean      next()

该方法将光标从当前记录位置向后移动一行。

●   boolean      last()

该方法将光标移动到此ResultSet结果集中的最后一行。

●   int  getRow()

该方法返回当前光标所在ResultSet结果集中的行号。

●   int       getInt(int columnIndex)

该方法获取指定ResultSet结果集当前光标所在行中列号的内容,内容以int类型返回。

●   int       getInt(String columnLabel)

该方法获取指定ResultSet结果集当前光标所在行中列名称的内容,内容以int类型返回。

●   float    getFloat(int columnIndex)

该方法获取指定ResultSet结果集当前光标所在行中列号的内容,内容以float类型返回。

●   float    getFloat(String columnLabel)

该方法获取指定ResultSet结果集当前光标所在行中列名称的内容,内容以float类型返回。

●   double getDouble(int columnIndex)

该方法获取指定ResultSet结果集当前光标所在行中列号的内容,内容以double类型返回。

●   double getDouble(String columnLabel)

该方法获取指定ResultSet结果集当前光标所在行中列名称的内容,内容以double类型返回。

●   String  getString(int columnIndex)

该方法获取指定ResultSet结果集当前光标所在行中列号的内容,内容以String类型返回。

●   String  getString(String columnLabel)

该方法获取指定ResultSet结果集当前光标所在行中列名称的内容,内容以String类型返回。

●   Date    getDate(int columnIndex)

该方法获取指定ResultSet结果集当前光标所在行中列号的内容,内容以Date类型返回。

●   Date    getDate(String columnLabel)

该方法获取指定ResultSet结果集当前光标所在行中列名称的内容,内容以Date类型返回。

●   void     close()

该方法立即释放此结果集ResultSet对象占用的资源

案例3:查询shoper表的所有记录,并将记录内容输出到控制台。

在demo包下新建SqlDemoTest2类。代码如下:

package demo;
 
import java.math.BigDecimal;
import java.sql.*;
 
public class SqlDemoTest2 {
    // 定义JDBC加载路径
    static String jdbc = "com.mysql.cj.jdbc.Driver";
    // 定义MySQL数据库的连接地址
    static String mysqlurl = "jdbc:mysql://localhost:3306/shop?serverTimezone=GMT%2B8";
    // 定义MySQL数据库的用户名
    static String username = "root";
    // 定义MySQL数据库的用户名登录密码
    static String password = "~123456q";
 
    public static void main(String[] args) {
        Connection conn = null;
        Statement statement = null;
        try {
            // 加载JDBC驱动
            Class.forName(jdbc);
            // 连接数据库
            conn = DriverManager.getConnection(mysqlurl, username, password);
            if (null != conn) {
                System.out.println(conn);
                // 获取表记录数的SQL语句
                String sql = "select * from shoper";
                // 实例化Statement对象
                statement = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
                        ResultSet.CONCUR_UPDATABLE);
                // 执行SQL查询语句
                ResultSet resultset = statement.executeQuery(sql);
                if (null != resultset) {
                    while( resultset.next() )
                    {
                        // 获取id字段内容
                        String id = resultset.getString("id");
                        // 获取userId字段内容
                        String userId = resultset.getString("userId");
                        // 获取name字段内容
                        String name = resultset.getString("name");
                        // 获取brief字段内容
                        String brief = resultset.getString("brief");
                        // 获取log字段内容
                        String log = resultset.getString("log");
                        // 获取shopMoney字段内容
                        BigDecimal shopMoney = resultset.getBigDecimal("shopMoney");
                        // 获取status字段内容
                        int status = resultset.getInt("status");
                        // 获取createDate字段内容
                        Date createDate = resultset.getDate("createDate");
                        System.out.printf("-----输出第%d条记录-----\n",resultset.getRow());
                        System.out.println("id:" + id + ";");
                        System.out.println("userId:" + userId + ";");
                        System.out.println("name:" + name + ";");
                        System.out.println("brief:" + brief + ";");
                        System.out.println("log:" + log + ";");
                        System.out.println("shopMoney:" + shopMoney.toString() + ";");
                        System.out.println("status:" + status + ";");
                        System.out.println("createDate:" + createDate.toString() + ";");
                        System.out.println("-------------------------------");
                    }
 
                    resultset.close();
                } else {
                    System.out.println("SQL语句执行失败");
                }
                statement.close();
                conn.close();
            } else {
                System.out.println("数据库连接失败");
            }
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
 
    }
}

statement对象执行SQL查询语句后,会返回ResultSet结果集,ResultSet结果集存储了已查询到的数据库记录。遍历ResultSet结果集可以循环调用ResultSet接口的next()方法,next()方法将当前结果集的光标移动到下一行。因为ResultSet结果集的初始光标是在第1行记录的前面,因此在循环中第一次调用next()方法时,会将光标移动到结果集的第1行,当结果集的记录遍历完毕,next()方法返回false。

结果集当前记录的列内容可以调用ResultSet接口getXxx()方法获取,如果获取的是String类型的内容,当内容含有中文并且出现中文乱码时,需要检eclipse的字符编码和数据库的字符编码是否一致。

程序执行结果如下图所示:

02.png

代码在线纠错(通义千问 qwen-max)

支持粘贴多个代码文件,提交后由阿里云通义千问自动分析代码漏洞、语法错误、逻辑问题并给出修改建议。
您已解锁 AI 代码纠错功能,可正常使用!

评论区

登录 后发表评论
暂无评论