Java数据结构与算法:数据库,Java程序与数据库的连接和基本操作

数据库

数据库是可能保存大量数据的仓库,为能快速地存储、查询数据的目的并以多种方式来组织。数据库管理系统(database management system)是一个软件,它能提供对数据库中所保存数据的快速访问或查询功能,对数据一般有4类基本操作:创建、读取、更新及删除(称为CRUD)。例如大学课程表管理系统或着航空公司票务预订系统,都是使用数据库来组织并管理系统中的大量数据。

数据库有许多不同类型,例如面向对象的数据库、基于平面文件的数据库、关系型数据库等。在此只关注一类现有的最常用的数据库类型——关系型数据库。关系型数据库(relational database)将基本信息组织为一张或多张(table)。更重要的是,不同数据元素之间的关系可能也保存在表中。

下面以一个简单的关系型数据库为例。它由两张表组成:Person和Location。Person表包含了一连串称为记录的行。表中每个记录包含了数据库中每个人的信息。每个人的记录有几个,包括名(firstName)、姓(lastName)和两个整数值(personID 和 locationID)。personID是唯一的整数值(即在表中具有唯一性),用来标识一条具体的记录。例如Peter的personID是1,John的personID是2。每条记录还包含一个locationID。这个值用来查找Location表中一条精确匹配的记录。

在这里插入图片描述

使用Person表中Peter记录的locationID可以找到Location表中的值,从而确定Peter居住在Portsmouth,RI。注意到Matthew也住在这里。这就是分成两个表的好处了,数据库可以很容易判定哪些人住在同一个城市,因为Person表中同一城市的人的locationID域是同一个整数值。但如果不使用Location的话,城市名和州名都成了Person表的一部分,数据库会反复复制同一个值从而占据更多的空间。使用Location表,可以避免数据重复而节省了空间。

建立与数据库的连接

在Java程序中能与数据库进行交互前,必须先建立到数据库的连接。为此,要使用Java Database Connectivity(JDBC)API。JDBC API 提供了在Java程序中对数据进行管理和类及方法。JDBC已经是Java Development Environment(JDK)的一部分,我们不需要下载其他软件来获得API的功能。但是,要连接数据库,必须要有数据库专用的驱动程序(driver),将我们的数据库请求传给数据库应用程序(也称服务器),来自数据库的反馈也通过驱动程序传给程序。

获得数据库驱动程序

有超过200个用于不同数据库的JDBC驱动程序。要找到你系统能用的程序,可以查阅Java的ADBC Data Access API页面。我所使用的是MySQL连接器驱动程序来连接另一台机器上安装的数据库。下载的文件是jar格式,把它放在“connector”目录中。

安装后,只需通过CLASSPATH环境变量指名它的位置(在编译和运行期间)。根据自己的配置,可以把这些值保存在一个shell配置中,或在命令行中提供。下面是一个简单的程序,用到了几个专用于JDBC的类。程序完成了三个动作:JDBC驱动程序的加载、尝试建立与数据库服务器的连接、确认已经打开连接后关闭连接。

import java.sql.*;

public class DatabaseConnector
{
	public static void main(String args[])
	{
		try
		{
			Connection conn = null;
			Class.forName(“com.mysql.jdbc.Driver”); //加载驱动程序

			conn = DriverManager.getConnection(“jdbc:mysql://contor.org/+
				”javafoundations?user=jf2e&password=hirsch”); //通过DriverManager与特定的数据库建立连接

			if (conn != null) //连接成功
			{
				System.out.println(“We have connected to our database!);
				conn.close();
			}
		}
		catch (SQLException ex)
		{
			System.out.println(“SQLException:+ ex.getMessage());
			ex.printStackTrace();
		}
		catch (Exception ex)
		{
			System.out.println(“Exception:+ ex.getMessage());
			ex.printStackTrace();
		}
	}
}

程序的开始,尝试从下载的jar文件中加载JDBC驱动程序(com.mysql.jdbc.Driver)。当驱动程序加载后,将建立自己的一个实例,并将其注册到DriverManager类(java.sql.DriverManager)。接下来试图通过DriverManager类来建立到数据库的连接。DriverManager将从已注册到DriverManager类的一组驱动程序中尝试选择一个合适的驱动程序。本例中我们只使用了一个驱动程序,所以它是唯一可选的驱动程序。调用DriverManager类的getConnection方法来接收定义了数据库实例的一个URL。它由我们必须提供的几个部分组成,包括主机名(数据库服务器所在的机器名)、数据库名,以及访问所选数据库时所用的用户名和口令。如果一切顺利,就会向conn返回Connection(java.sql.Connection)对象。该对象代表到数据库的一个专一连接,是用于查询数据库及从数据库获得反馈的通道。最后,程序检查是否接到一个非空的Connection对象,如果是,则打印一条成功信息给用户,并关闭到数据库的连接。

创建并修改数据库表

创建表

创建数据库的最开始的SQL语句是CREATE TABLE < tablename >。还需要提供具体的表名和创建时要描述的任意。例如我们创建一张学生表。目前表中含有一个ID值(作为关键字)、学生的名和姓。可以执行如下命令:

CREATE TABLE Student (student_ID INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY (student_ID), 
					firstName varchar(255), lastName varchar(255))

表的每个域列在表名(Student)后面的括号中并由逗号分开。该表初始时包括了如下的域:

  1. student_ID——无符号整数,不能为空,每次在表中添加一个新学生时自动加一。
  2. firstName——可变长度字符串,最长为255个字符。
  3. lastName——可变长度字符串,最长为255个字符。
    上面没有提及到一个域:PRIMARY KEY 域,这不是一个真正的域,它只是表的一个设置,用来指定哪个(些)域能让记录具有区别于其他记录的唯一性。因为每个学生都有一个唯一的标识符,所以student_ID域可以充当表的关键字。

我们在上一节的代码中实现创建表,可以紧接在连接成功信息之后添加如下的代码:

Statement stmt = conn.createStatement();
boolean result = stmt.execute(“CREATE TABLE Student ” +(student_ID INT UNSIGNED NOT NULL AUTO_INCREMENT,+ “PRIMARY KEY (student_ID),+ “firstName varchar(255),+ “lastName varchar(255)));
System.out.print(“\tTable creation result:+ result + ”\t”);

Statement类(java.sql.Statement)是一个接口类,我们用它来准备并运行SQL语句。可以让Connection对象创建Statement对象。一旦有了Statement对象,就可以调用它的execute方法,并把SQL语句字符串传给它,由数据库来执行。如果调用后能返回一个ResultSet对象,则execute方法返回TRUE,否则返回FALSE。

当我们执行上面的代码时,实际上会返回一个FALSE值,并创建了表。而如果尝试再次运行这个程序,将会抛出一个异常,声明那个表已经存在。

修改表

表的结构常常在创建时就确定下来并且不再改变,但有时也会需要改变已有的表。可以选择丢弃(删除)已存在的表并从零开始创建一个新表。但如果这样做,将会丢失目前保存在表中的数据。所以最好在已存在的表中增加新的域或删除已有的域(及它们的数据)。

在学生表中增加 age 和 gpa 域。对于 age 域可以使用最小的无符号整数域以节省空间。在MySQL中是无符号的tinyint——范围从0到255,足以做年龄域。添加 gpa 域可以使用有三位数字的无符号浮点型,其中小数部分为2位。修改操作同样需要创建一个Statement对象,并使用ALTER TABLE< tablename > ADD COLUMN 语句。调用Statement对象的execute方法执行这个语句。这次同样没有结果集返回。

Statement stmt2 = conn.createStatement();
result = stmt2.execute(“ALTER TABLE Student ADD COLUMN ” +(age tinyint UNSIGNED, gpa FLOAT (3,2) unsigned));
System.out.print(“\tTable modification result:+ result + “\t”);

当然,表的修改还可能是丢弃一列而非添加一列或多列。为此,可以使用 ALTER TABLE 语句,在表名后使用 DROP COLUMN 命令,后面接一个或多个(用逗号分隔)想要丢弃的列名,例如firstName等。

查询数据库

我们可以通过查询数据库来了解表的结构。为此建立另一个Statement对象,并传给数据库服务器来处理。这次与前面例子不同的是,我们期望返回一个ResultSet对象,它管理含有结果的记录集。ResultSet是必须管理的重要内容,得到它是为了获取数据库的信息。它的功能与Scanner对象类似,提供一个访问和遍历数据集的方法。默认的ResultSet对象允许在数据集中从头到尾从第一个对象访问到最后一个对象,但不能修改。

举一个最简单的例子:查询表的结构信息。

Statement stmt3 = conn.createStatement();
ResultSet rSet = stmt.executeQuery(“SHOW COLUMNS FROM Student”);

一旦得到从查询返回的结果,就能够从 ResultSet 的 ResultSetMetaData 对象得到某些信息。例如下面两行代码使用ResultSetMetaData 对象中的信息,产生用户需要的输出(包括表名和结果中的列数)。

ResultSetMetaData rsmd = rSet.getMetaData();
int numColumns = rsmd.getColumnCount();

插入、查看及更新数据

因为前面定义了studentID域是auto_increnebt域(每次向表中放入一个数据时它自动加1),所以实际上只需要插入学生的lastName、age 和 gpa 数据。

插入数据,还是创建新的Statement,并使用 INSERT < tablename > ( column name, …) VALUES ( expression, …)语句。表名后面时一个或多个列,然后列出要放入指定域的值。

INSERT Student (lastName, age, gpa) VALUES (“Eden”, 21,3.99)

可以使用下面两行源代码完成插入操作:

Statement stmt4 = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
int rowCount = stmt4.executeUpdate(“INSERT Student ” +(lastName, age, gpa)+ 
				“VALUES (\”Eden\”,21,3.99));

为了能更新表的数据值,必须调用与之前不同的两个方法。首先,当构造 Statement 对象时,我们指定得到的 ResultSet 指针只能前移,而且对 ResultSet的修改继续传给数据库。其次,我们调用了executeUpdate方法,而不是之前用过的execute或executeQuery方法。executeUpdate方法返回update查询影响到的行数。上例中仅修改了1行。

对数据库最常进行的动作之一就是查询,为的是查看、获取数据。SELECT···FROM 语句的基本语法如下:

SELECT <columns,> FROM <tablename> WHERE <conditions,>

SELECT 语句有多个部分,我们在此暂且讨论它的入门级内容。例如,如果只想要知道 Student 表中年龄在21岁及以上并且gpa不大于3.5的学生的 lastName 和gpa 值,可以构造如下的查询语句:

SELECT lastName, gpa FROM Student WHERE age >= 21 && gpa <= 3.5

WHERE 条件子句是可选的,如果不写,就指定表中的所有行都被选中。条件是一个必须计算为TRUE值的表达式,可以包含函数和像&&、|| 这样的运算符及其他许多运算符。返回的选中项(上例中的 lastName 和 gpa )要列出来并用逗号分隔。如果想从表中返回所有的列,可以用星号(*)来替换列名,类似于其他命令语句里通配符的用法。

更新数据库中的数据是另一项常用操作。更新是一个相对简单的过程,可以分为3步。首先,获得一个 ResultSet ,并浏览到要更新的行。然后,更新 ResultSet 中要修改的值。最后,使用 ResultSet 的记录来更新数据库。

一般情况下,最好让 ResultSet 只获取你想修改的数据行,这样如果你只想要修改一行数据,那么 ResultSet 中也就仅包含那一行,就可以调用集合的 first 方法跳到第一行(也是唯一一行)。接下来使用 updateXXX 方法(updateString、updateFloat 等)来修改集合中的数据。调用 updateRow 方法对数据库做永久修改。例如:

ResultSet rset = stmt2.executeQuery(“SELECT * FROM Student WHERE ” + “lastName=\”John\””);
rset.first();
rset.updateFloat(“gpa”, 3.41f);
rset.updateRow();

删除数据和数据库表

从表中删除数据的SQL语句是 DELETE FROM 语句。该语句的语法中,WHERE条件子句同样也是可选的。和前一节对INSERT 语句的做法一样,需要使用一个 Statement 来得到一个只能向前移动的 RecordSet 对象(ResultSet.TYPE_FORWARD_ONLY),然后它再更新数据库(ResultSet.CONCUR_UPDATABLE)。

从数据库中删除表也很简单。使用Statement 对象的 executeUpdate 方法,并传给它 DROP TABLE < tablename >语句即可。

  • 0
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

成名在望xy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值