写点什么

Java 高手速成│Java 程序怎样和数据库对话

作者:TiAmo
  • 2022-12-29
    江苏
  • 本文字数:6111 字

    阅读完需:约 20 分钟

Java高手速成│Java程序怎样和数据库对话

从上一篇 https://xie.infoq.cn/article/88baa0d950c335f4b8f7f1187 的例子中可以看出,Java 和数据库的连接和对话离不开 JDK 库类,如 java.sql 包中支持数据库编程的各种 API 类、数据库软件 DBMS、JDBC 驱动软件或 Java Connector 以及你编写的数据库编程代码。

并且,在访问数据库的程序中,除调用 Class 类的 forName()来装载 JDBC 驱动软件外,所有数据库操作的 API 类由库包 java.sql 提供,各种操作包括:数据库连接、SQL 指令的传送、选择记录的回传、提取和相关操作,以及数据表信息 metadada 的获取。如下内容通过实例详细讨论这些具体的数据库编程技术。

01、连接数据库——高手都会这样做

首先调用 Class 类的静态方法 forName()装载指定的 JDBC 驱动软件,再调用 java.sql 包提供的 DriverManager 的静态方法 getConnection(),对指定的数据库进行连接操作。其一般代码格式如下:

try {  Class.forName(driverName);  Connection con = DriverManager.getConnection(dbUrl, username, password);  ...}catch (ClassNotFoundException e) {  System.err.println(e);}catch (SQLException ex) {  System.err.println(e);}
复制代码

其中:

driverName——字符串参数。由数据库指定的 JDBC 驱动软件名。如 MySQL 的驱动软件名为:

drivername = "com.mysql.cj.jdbc.Driver";
复制代码

注意,不同的数据库使用各自规定的驱动软件名。使用时必须参考该数据库对驱动软件的命名。

dbUrl——字符串参数。指定的数据库连接方式和地址,可以包括已存在的数据库名。如以本机方式连接 MySQL 服务器的 dbUrl 为:

dbUrl = "jdbc:mysql://localhost:3306"; //连接到本机数据库服务器//或:"jdbc:mysql://localhost:3306/ProductDB";//连接到本机服务器中的数据库
复制代码

以远程方式通过网络连接 MySQL 服务器的 dbUrl 为:

dbUrl = "jdbc:mysql://hostServerIP:3306/ProductDB";
复制代码

其中 hostServerIP 为 MySQL 服务器的 IP 地址或服务器名;3306 为 MySQL 预设的网络端口;ProductDB 为已创建的数据库名。

Username——字符串参数。指定的用户名。如"root"。

Password——字符串参数。指定的密码。如果没有密码,则为""。

forName()方法将抛出检查性异常 ClassNotFoundException,getConection()将抛出检查性异常 SQLException,代码中必须提供处理这两个异常的机制。

同样要注意不同的数据库使用各自规定的 dbUrl 和路径进行远程数据库连接。使用时必须参考该数据库对通过网络对数据库连接的规定。

02、向数据库发送 SQL 指令

发送 SQL 指令的一般代码格式为:

try {  Connection con = DriverManager.getConnection(dbUrl, username, password);  Statement stmt = con.createStatement(); //返回Statement对象  stmt.executeUpdate(sqlString); //调用发送SQL指令方法;  ... //更多调用发送SQL指令的方法  stmt.close(); //关闭发送}catch (ClassNotFoundException e) {  System.err.println(e);}catch (SQLException ex) {  System.err.println(e);}
复制代码

其中 createStatement()将返回一个 Statement 的对象,然后调用其 executeUpdate()方法,将指定的 SQL 指令发送到数据库,并加以执行。

sqlString 为字符串,必须是合法 SQL 指令,否则将产生检查性异常 SQLException。sqlString 可以是除 SELECT 之外的任何 SQL 指令。

最后调用 close()方法,关闭发送 SQL 指令的操作。

如下是常用发送 SQL 指令的例子。


...String createTable = "CREATE TABLE Books (ISBN CHAR(13),Title VARCHAR  (50),Price DECIMAL("+ "6, 2),Inventory INT, Publisher VARCHAR(30))";String insertRecord1 = "INSERT INTO Books (ISBN, Title, Price, Inventory, Publisher)VALUES ("+ "'9781890774555', 'Java Lover', 66, 10, 'ABC Press') ";String updateRecord1 = "UPDATE Books SET Price = 69.15 WHERE Price= 66";//String deleteRecord1 = " DELETE FROM Books WHERE ISBN = '9781890774555'";stmt.executeUpdate(createTable); //调用发送SQL指令方法;stmt.executeUpdate(insertRecord1);stmt.executeUpdate(updateRecord1);//stmt.executeUpdate(deleteRecord1); //可在理解这个编程实例后再执行删除指令... //其他操作指令Stmt.close(); //关闭... //异常处理
复制代码

这个例子向数据库发送了 4 个常用指令:

CREATEINSERTUPDATEDELETE
复制代码


建议你在理解了从 java 程序怎样发送指令到数据库后,再执行删除指令。另外建议你打开数据库服务器:

MySQL 8.0 Command Line Client

直接发送 SQL 指令,如 use(调用数据库)、desc(描述数据表)、select (显示数据表)等帮助你查询和了解在执行了 Java 语句后数据库内容的变化。

03、接收从数据库传回的记录

调用 Statement 的方法 executeQuery()将返回一个 ResultSet 对象。executeQuery()将执行指定的获取数据表数据的 SQL 指令,如 SELECT,并将执行结果封装在这个 ResultSet 对象中。ResultSet 提供了一系列方法和静态字段,用来提取回传的结果。

以 Products 数据表为例,得到回传结果的一般代码格式为:

try {  Connection con = DriverManager.getConnection(dbUrl, username, password);  Statement stmt = con.createStatement(); //返回Statement对象  String sqlString = "SELECT * FROM Products"; //定义SQL指令  ResultSet rs = stmt.executeQuery(sqlString); //执行SQL指令并得到回传结果  while(rs.next()) { //如果还有记录则继续循环    String code = rs.getString();//得到当前记录中的第一个 字段的值    String Title = rs.getString(); //得到当前记录中的第二个字段的值    Double price = rs.getDouble(); //得到当前记录中的第三个字段的值    ... //执行利用得到数据的各种操作  }  rs.close(); //关闭  catch (ClassNotFoundException e) {    System.err.println(e);  }  catch (SQLException ex) {    System.err.println(e);  }
复制代码

可以看到,利用 executeUpdate()方法向数据库发送 SQL 指令;而利用 executeQuery()得到数据库记录的回传结果。值得一提的是,ResultSet 本身是一个接口,在执行 executeQuery()时,产生一个完善了 ResultSet 的对象,并作为引用返回这个对象。

在提取封装在 ResultSet 中的记录时,涉及三类操作:

设置提取方式以及对 ResultSet 中记录指示器(也称光标)的操作,如移动或证实当前记录器位置。

得到记录中的数据或者删除、更新记录的操作。

得到有关数据表信息 metadata,既元数据的操作。

java.sql 包中的 ResultSet 提供静态字段来设置对记录的提取方式。表 2 列出了 ResultSet 常用字段和移动/证实记录指示器的常用方法。

表 2 ResultSet 的常用字段和移动/证实记录指示器常用方法


注意  ResultSet 预设的纪录指示器位置为 0。首次调用 next()时,记录指示器位置为第一个记录的开始。如果记录指示器指向一个不存在的记录时,ResultSet 对象则为 null。

例子之一:利用 ResultSet 的静态字段设置按记录中的相反次序提取数据,并允许变更。提取方式的设定通过调用 Statement 重载的方法 createStatement()实现,如:

Statement stmt = con.createStatement(ResultSet.FETCH_REVERSE, CONCUR_UPDATABLE);
复制代码

例子之二:调用其他移动记录指示器的方法。

...rs.first(); //指示器移到第一个记录的开始rs.last(); //指示器移到最后一个记录的开始rs.absolute(10); //指示器移到第10个记录的开始rs.absolute(1); //等同于rs.first()rs.absolute(-1); //等同于rs.last()rs.absolute(-2); //倒数第二个记录的开始rs.relative(-3); //指示器从当前位置返回3个记录rs.relative(5); //指示器从当前位置往下移动5个记录。如果移至的位置无记录,ResultSet为nullif (isFirst())  rs.next();//记录指示器在第二个记录的开始...
复制代码


04、提取和更新传回的记录

表 3 列出了 ResultSet 用来提取记录数据以及变更和删除记录的常用方法。

 表 3 esultSet 提取记录数据和更新记录的常用方法

值得一提的是,数据库操作中所有记录位置指示器,包括字段位置(列),都从 1 算起。

例子之一:提取 ResultSet 中的结果。假设执行了如下 SQL 指令:

ResultSet rs = stmt.executeQuery("SELECT * FROM Books");
复制代码


例子之二:调用 next()方法并利用循环提取 ResultSet 中的所有记录数据。


...while (rs.next()) { //如果还有记录,则继续循环  //执行封装在rs中的各记录数据的操作  System.out.println("ISNB: " + rs.getString(1)); //或rs.getString("ISNB"));  System.out.println("Book Title: " + rs.getString(2)); //或rs.getString("Title"));  System.out.println("Price: " + rs.getDouble(3)); //或rs.getString("Price"));  System.out.println("Inventory: " + rs.getInt(4)); //或rs.getString("Inventory"));  System.out.println("Publisher: " + rs.getString(5)); //或rs.getString("Publisher"));}...
复制代码

例子之三:更新当前记录的内容。

rs.updateString(1, "1109123466666"); //修改当前记录指定字段(ISBN)的字符 //串值为新值rs.updateDouble("Price", 125.89); //修改当前记录指定字段(Price)的值为 125.89
复制代码

以上对当前记录的修改也可利用 SQL 指令 UPDATE 完成,如:

stmt.executeUpdate("UPDATE Books SET Code = '1109123466666', Price = 125.89"+ "WHERE Code = '9781890774555'");
复制代码

这种操作指涉及数据库,而不影响当前在 ResultSet 中的该记录。

例子之四:继续上例,删除指定的记录。

rs.delelteRow(); //删除在ResultSet中以及数据库Books表中当前记录
复制代码

同上,也可利用 SQL 指令 DELETE 删除数据库中的指定记录,如:

stmt.executeUpdate("DELETE FROM Books WHERE Code = '1109123466666'");
复制代码

你可利用 ResultSetTest.java 这个程序实例,加入这里讨论的 4 个指令,运行并分析结果,以便加深理解如何应用这些操作。

05、预备指令是怎么回事

如果一个 SQL 指令需要以不同的数值或参数执行多次,预备指令,又称预备语句,则为首选。预备指令 prepared statement,也称问号指令。指在 SQL 指令中将字段的值以问号?形式,设为变量,在执行中将被具体数据所代替。这种指令也称为参数化指令。

前面讨论的由 Statement 的 executeUpdate()以及 executeQuery()发送的 SQL 指令,都必须经过数据库编译后,方可执行。而预备指令,正如其名,则产生预先编译好的 SQL 指令,再由其 setXxx()方法将具体参数值提供给 SQL 指令。预备指令实现了抽象指令模式和具体执行指令的分离,减少代码重复,提高编程效率。

预备指令功能包括在由 java.sql 包提供的 PraparedStatement 中,通过调用 Connection 的 prepareStatement()方法,由其返回一个 PreparedStatement 对象而得到。调用其各种 setXxx()方法得到参数值,再调用其 executeUpdate()或 executeQuery()完成指令的执行。如:

例子之一:一个典型预备指令。

//完整程序见本文压缩附件中名为PreparedStatementTest1.java源代码...try {  Connection con = DriverManager.getConnection(url, username, password);  String selectSql = "UPDATE Products SET Price = ? WHERE Code = ?";  PreparedStatement ps = con.prepareStatement(selectSql); //编译预备指令  ps.setDouble(1, 1209.88); //1代表第一个问号  ps.setString(2, "2200"); //2代表第二个问号  ps.executeUpdate(); //执行预备指令  ps.close(); //关闭}catch(ClassNotFoundException e){  System.out.println("Database driver not found.");}catch (SQLException e) {e.printStackTrace();}
复制代码

以上代码中,两个问号代表指令参数。在调用 setXxx()方法指定其值时,首先提供代表问号的序号(从 1 开始),再提供代表问号的值。如上例中,1 代表第一个问号,表示 Price 的参数;2 代表第二个问号,代表 Code 的参数。

一个预备指令中可以有多个问号。其序号按出现次序确定。PreparedStatement 提供了设置所有数据类型值的方法 setXx(),调用时必须注意数据类型的匹配。预备指令将抛出检查性异常 SQLException,代码中必须提供处理这个异常的机制。

例子之二:利用预备指令在数据表中加入记录。


...try {  Connection con = DriverManager.getConnection(url, username, password);  String insertSql = "INSERT INTO Products (Code, Title, Price) VALUES (?, ?, ?)";  PreparedStatement ps = con.prepareStatement(insertSql); //编译预备指令  ps.setString(1, "1110"); //1代表第一个问号  ps.setString(2, "Java EE Programming" ); //2代表第二个问号  ps.setDouble(3, 77.02); //3代表第三个问号  ps.executeUpdate() //执行预备指令;  ps.close(); //关闭}catch(ClassNotFoundException e){    System.out.println("Database driver not found.");}catch (SQLException e) {e.printStackTrace();}
复制代码

例子之三:利用预备指令选择指定数据表中的记录。


...String choice = "y";ResultSet rs = null;Connection con = DriverManager.getConnection(dbURL, username, password);String deleteSql = "SELECT * FROM Products WHERE Code = ?";PreparedStatement ps = con.prepareStatement(deleteSql);  while (true) {    code = JOptionPane.showInputDialog("Enter the product code: ");    ps.setString(1, code); //指定的记录    rs = ps.executeQuery(); //执行预备指令    rs.next(); //指向这个记录    String record = rs.getString(1) + " " + rs.getString(2) + " " + rs.getDouble(3); //产生记录格式    JOptionPane.showMessageDialog(null, record); //显示记录    choice = JOptionPane.showInputDialog("是否继续?(y/n): "); ;    if (choice.equalsIgnoreCase("n"))      break;  }  ps.close();...
复制代码

例子之四:利用预备指令在数据表中删除记录。


...try {  double price = 0;  boolean quit = false;  Scanner sc = new Scanner(System.in);  Connection con = DriverManager.getConnection(url, username, password);  String deleteSql = "DELETE FROM Products WHERE Price) = ?";  PreparedStatement ps = con.prepareStatement(deleteSql);    while (true) {      System.out.println("Please enter the price you want that record to be deleted: ");      price = sc.nextDouble();      ps.setDouble(1, price); //删除由price指定的记录      ps.executeUpdate(); //执行预备指令      System.out.println("Do you want to continue? (y/n): ");           choice = sc.next();        if (choice.equalsIgnoreCase("n"))          break;        else      sc.nextLine();  }  ps.close();}catch(ClassNotFoundException e){    System.out.println("Database driver not found.");}catch (SQLException e) {e.printStackTrace();}
复制代码


发布于: 2022-12-29阅读数: 33
用户头像

TiAmo

关注

有能力爱自己,有余力爱别人! 2022-06-16 加入

CSDN全栈领域优质创作者;阿里云创作者社区专家博主、技术博主、星级博主;华为云享专家;

评论

发布
暂无评论
Java高手速成│Java程序怎样和数据库对话_JDBC_TiAmo_InfoQ写作社区