数据库是存储在计算机上的数据的集合。数据库中的数据通常被组织成记录,如员工记录、医疗记录、销售记录等。
数据库五个最基本要求:
使数据库持久化的一种常见方法是将其记录存储在文件中。最简单和最直接的方法是让数据库系统将记录存储在文本文件中,每个记录类型有一个文件;每条记录都可以是一行文本,其值用分隔符分隔。
这种方法的优点是,用户可以使用文本编辑器来检查和修改文件。但是这种方法效率太低以至于不建议采用。缺点在于:大型文本文件花费的读写时间太长。
因此,数据库系统需要更加聪明地了解如何存储记录(例如建立索引[index]),因此对文件的更新只需要很小的本地重写。
当许多用户共享一个数据库时,他们很有可能同时访问数据库的一些数据文件。并发是一件好事,因为每个用户都可以快速得到服务,而无需等待其他用户完成服务。但是并发过度会导致数据库变得不准确。解决这个问题的一个方法是限制并发度。用户还可能需要撤销他们所做的数据库更新。
数据库系统必须为用户提供指定更改何时永久的能力;用户提交更改。一旦用户提交,更改就会变得可见,无法撤消。
实现数据库系统从崩溃中正常恢复,从而撤销在崩溃发生时正在运行的所有程序的更新。
数据库系统面临着以下难题:它必须管理比主内存系统更多的数据,使用较慢的设备,多个人争夺访问数据,并使其完全恢复,同时保持合理的响应时间。
解决这个难题的很大一部分是使用缓存。每当数据库系统需要处理一条记录时,它就会将其加载到RAM中,并尽可能长时间地保存在那里。因此,主内存将包含当前正在使用的数据库的那部分。所有的读写都是用RAM进行的。这种策略的优点是使用快速主存而不是慢的持久内存,但缺点是数据库的持久版本可能会过时。数据库系统需要实现技术来保持数据库的持久版本与RAM版本同步,即使在系统崩溃(RAM内容被销毁时)也是如此。
如果数据库的用户不能轻松地提取他们想要的数据,那么该数据库就不是很有用。SQL语句比Java程序要短得多、更清晰,这主要是因为它指定了要从文件中提取的值,而不必指定如何检索它们。
Apache Derby 是一个Apache DB 子项目,是一个完全用 Java 实现的开源关系数据库,可在Apache 许可证 2.0 版下使用。一些关键功能包括:
Derby内嵌开发需要引入derby.jar
,以下为其中一个例子:
//创建数据库
public static void creat() {
String url = "jdbc:derby:d:/test/studentdb;create=true";//其中d:/test/studentdb指定数据库studentdb的存储位置为d:/test/,create=true表明此连接创建数据库
Driver d = new EmbeddedDriver();
try (Connection conn = d.connect(url, null);
Statement stmt = conn.createStatement()) {
String s = "create table STUDENT(SId int, SName varchar(10), MajorId int, GradYear int)";//创建STUDENT表
stmt.executeUpdate(s);
System.out.println("Table STUDENT created.");
s = "insert into STUDENT(SId, SName, MajorId, GradYear) values ";
String[] studvals = {"(1, 'joe', 10, 2021)",
"(2, 'amy', 20, 2020)",
"(3, 'max', 10, 2022)",
"(4, 'sue', 20, 2022)",
"(5, 'bob', 30, 2020)",
"(6, 'kim', 20, 2020)",
"(7, 'art', 30, 2021)",
"(8, 'pat', 20, 2019)",
"(9, 'lee', 10, 2021)"};
for (int i = 0; i < studvals.length; i++)
stmt.executeUpdate(s + studvals[i]);
System.out.println("STUDENT records inserted.");
s = "create table DEPT(DId int, DName varchar(8))";
stmt.executeUpdate(s);
System.out.println("Table DEPT created.");
s = "insert into DEPT(DId, DName) values ";
String[] deptvals = {"(10, 'compsci')",
"(20, 'math')",
"(30, 'drama')"};
for (int i = 0; i < deptvals.length; i++)
stmt.executeUpdate(s + deptvals[i]);
System.out.println("DEPT records inserted.");
s = "create table COURSE(CId int, Title varchar(20), DeptId int)";
stmt.executeUpdate(s);
System.out.println("Table COURSE created.");
s = "insert into COURSE(CId, Title, DeptId) values ";
String[] coursevals = {"(12, 'db systems', 10)",
"(22, 'compilers', 10)",
"(32, 'calculus', 20)",
"(42, 'algebra', 20)",
"(52, 'acting', 30)",
"(62, 'elocution', 30)"};
for (int i = 0; i < coursevals.length; i++)
stmt.executeUpdate(s + coursevals[i]);
System.out.println("COURSE records inserted.");
s = "create table SECTION(SectId int, CourseId int, Prof varchar(8), YearOffered int)";
stmt.executeUpdate(s);
System.out.println("Table SECTION created.");
s = "insert into SECTION(SectId, CourseId, Prof, YearOffered) values ";
String[] sectvals = {"(13, 12, 'turing', 2018)",
"(23, 12, 'turing', 2019)",
"(33, 32, 'newton', 2019)",
"(43, 32, 'einstein', 2017)",
"(53, 62, 'brando', 2018)"};
for (int i = 0; i < sectvals.length; i++)
stmt.executeUpdate(s + sectvals[i]);
System.out.println("SECTION records inserted.");
s = "create table ENROLL(EId int, StudentId int, SectionId int, Grade varchar(2))";
stmt.executeUpdate(s);
System.out.println("Table ENROLL created.");
s = "insert into ENROLL(EId, StudentId, SectionId, Grade) values ";
String[] enrollvals = {"(14, 1, 13, 'A')",
"(24, 1, 43, 'C' )",
"(34, 2, 43, 'B+')",
"(44, 4, 33, 'B' )",
"(54, 4, 53, 'A' )",
"(64, 6, 53, 'A' )"};
for (int i = 0; i < enrollvals.length; i++)
stmt.executeUpdate(s + enrollvals[i]);
System.out.println("ENROLL records inserted.");
} catch (SQLException e) {
e.printStackTrace();
}
}
查询例子如下:
public static void query() {
String url = "jdbc:derby:d:/test/studentdb";
String qry = "select SName, DName "
+ "from DEPT, STUDENT "
+ "where MajorId = DId";
Driver d = new EmbeddedDriver();
try ( Connection conn = d.connect(url, null);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(qry)) {
System.out.println("Name\tMajor");
while (rs.next()) {
String sname = rs.getString("SName");
String dname = rs.getString("DName");
System.out.println(sname + "\t" + dname);
}
}
catch(SQLException e) {
e.printStackTrace();
}
}
数据库应用程序一般由两个独立的部分组成:用户界面(或UI)和访问数据库的代码。后一种代码被称为数据库引擎。将UI与数据库引擎分离是一个很好的系统设计,因为它简化了应用程序的开发。这种分离还增加了系统的灵活性:应用程序设计器可以对不同的数据库引擎使用相同的用户界面,或者为同一数据库引擎构建不同的用户界面。
UI通过连接到所需的引擎,然后从引擎的API中调用方法来访问数据库。数据库引擎通常支持多个标准api。当一个Java程序连接到一个引擎时,所选择的API被称为JDBC
从UI到数据库引擎的连接可以是嵌入式的或基于服务器的。在嵌入式连接中,数据库引擎的代码与UI的代码运行相同的过程,这允许UI对引擎的独占访问。只有当数据库“属于”该应用程序并且与该应用程序存储在同一台机器上时,该应用程序才应该使用嵌入式连接。其他应用程序也需要使用基于服务器的连接。
在一个基于服务器的连接中,数据库引擎的代码在一个专用的服务器程序中执行。此服务器程序始终在运行,等待客户端连接,并且不需要与其客户端在同一台机器上。在客户端与服务器建立连接后,客户机向服务器发送JDBC请求并接收响应。一个服务器可以同时连接到多个客户端。当服务器正在处理一个客户端的请求时,其他客户端可以发送他们自己的请求。服务器包含一个调度程序,它将为等待服务的请求排队,并确定它们何时被执行。每个客户端都不知道其他客户端,而且(除了由于调度而造成的延迟)都有一种令人愉快的错觉,即服务器是专门处理它的。基于服务器的连接的连接字符串必须指定服务器计算机的网络或IP地址。
Derby是一个复杂的、功能齐全的数据库系统。然而,这种复杂性意味着它的源代码不容易理解或修改。作者编写SimpleDB数据库系统是为了实现与Derby相反的方法——它的代码很小、易于阅读、易于修改。它省略了所有不必要的功能,只实现了SQL的一小部分,并且只使用了最简单的(通常是非常不切实际的)算法。它的目的是让读者清楚地了解数据库引擎的每个组件以及这些组件是如何交互的。
最新版本的SimpleDB可以从其网站cs.bc.edu/~scioreb下载。
Derby几乎实现了几乎所有的标准SQL。而SimpleDB只实现了标准SQL的一个很小的子集,并施加了SQL标准中不存在的限制。SimpleDB中的查询只包含选择-where子句,这些子句中的选择子句包含一个字段名列表(不包含AS关键字),而from子句包含一个表名列表(不包含范围变量)。可选的where子句中的术语只能通过布尔运算符和进行连接。术语只能比较常量和字段名是否相等。与标准SQL不同,没有其他比较运算符,没有其他布尔运算符,没有算术运算符或内置函数,也没有圆括号。因此,不支持嵌套的查询、聚合和计算值。
因为没有范围变量和重命名,所以查询中的所有字段名都必须不相交。因为没有按子句分组或顺序排序,所以不支持分组和排序。其他限制包括: