JavaBeans和干系数据库的镜像
副标题#e#
摘要
JDK1.1包罗了新的数据库存 取(JDBC)及组件(JavaBeans)的应用措施接口(APIs)。这两个API团结在一起,可用来开拓通用 数据库代码。通过用独一的一个类去存取任何一种JDBC数据库(封装于差异组件中的各个应用 措施有着其详细的编码),用户就不必因为数据库布局一点点的细小变革去修改数据库编码。
一个干系数据库根基上包罗一系 列彼此关系的表,在每一个表中存有一类与应用系统相关的数据。譬喻一个地点簿数据库中,可 能有关于人员、住址、电话号码等方面的表。在数据库中,每一个这样的实体将被作为一系列的 字符串,整数及其它原始数据范例存贮起来。数据库中,表的界说将描写每一种与实体相关的信 息如安在一个表的字段中存储。譬喻,你可以在一个名为“人”的表中,有两个字段别暗示所存字 符串为“姓”和“名”。每一张表该当有一个或几个字段值作为标识,确保每笔记录的独一性。这些 标识或“键”可以用来毗连存在于差异表中的信息。譬喻你可以在“人员”表中,为每小我私家指定唯 一的“人员号码”的键值,并在“地点”表中的相应字段中利用同一个键值。这样,你可以通过对两 个表中的“人员号码”字段值的匹配,使每一小我私家和他的地点关联起来。
干系数据库系统呈现于七十年月, 时至今天,它仍然是存储巨量数据的主要方法。因而,Java软件东西有须要具备处理惩罚干系数据库 的本领。
干系数据库要想被某个Java应用 措施操作,首先需要办理两个问题。第一:需要某些基本的中间件来成立与数据库的毗连,向数 据库发出SQL查询等等;第二:哄骗数据库的处理惩罚功效要与哄骗任何一种Java信息一样利便—— 作为一个工具。前一个问题已被SUN及几个数据库产商办理;后一个问题则有待我们进一步去探 究。
在为普通的措施开拓业务界说大 量的APIs这项事情上,SUN一直保持着与很多软件公司的相助干系。在JDK1.1APIs中,JDBC 的API是最早成立起来的。并且,它已获得了为数浩瀚的应用。这些应用中,有的是100%的 纯Java,有的则是Java和其它措施的殽杂体,如:用现有的ODBC数据源举办毗连(参看 图1)。JavaSoft已将一个关于现有的JDBC驱动措施的先容放在它的Web站点 上(http://splash.javasoft.com/jdbc/jdbc.drivers.html)。
图1一个典范的JDBC或JDBC/ODBC设置
留意:此图已被简化。别的的组件 已包罗个中(如ODBD驱动措施)
很是明明,这些应用的优缺点取决 于你的情况和配置,在此我禁绝备对它们的各类环境举办逐一阐述。在下面的内容中,我们假 定,在你的呆板中已拥有某种Java开拓情况,而且你已正确地安装并测试过某个JDBC驱动措施, 可能运用过某种JDBC驱动措施及SUN的JDBC/ODBC桥。
JDBCAPI
JDBCAPI作为一个单独的Java 包(或类库,即java.sql)呈现,包罗有一系列的类。这些类提供了处理惩罚某个干系数据库的中间 件。本质上讲,它们使得你可以关联某个数据库,并向其发出查询。你可以对这些查询功效举办 处理惩罚,检索你数据库的meta-信息(meta-information),而且处理惩罚在此间大概产生的各类异常环境。
让我们来看一个简 单的JDBC例子,看一看应用了JavaJDBC之后,查询会获得奈何的简化。表1是一个极其简朴的数 据库。在清单1中的编码是一段最简朴的对干系数据库举办SQL查询所需的Java语句。
人员 # | 名 | 姓 |
43674 | Sandor | Spruit |
90329 | John | Doe |
65435 | Donald | Duck |
String ur1="jdbc:odbc:sample";
String query="SELECT * FROM PERSON";
boolean more;
try
{
Class.forName("sun.jdbc.odbc.jdbcOdbcDriver");
Connection con = DriverManager.getConnection(ur1,"sandor","guest");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(query);
While (more = rs,next())
{
int number = rs.getInt("PERSON#");
String firstName = rs.getString("FIRST_NAME");
String lastName = rs.getString("LAST_NAME");
System.out.printIn(number + " " + firstName + " " + lastName);
}
rs.close();
stmt.close();
con.close();
}
catch(SQLException ex)
{
ex.printStackTrace();
}
#p#副标题#e#
清单1:一个应用了JDBC的SQL查询
#p#分页标题#e#
这段编码的寄义是:先装入SUN 的JDBC/ODBC驱动措施,然后与被jdbc:odbc:sample指定的数据库成立起一个关联,最后对该数 据库举办一个简朴的SELECT查询。假如没有碰着查询异常(SQLException),措施将轮回地从结 果集(Result Set)中每次抽出一条数据库记录,并将其显示在屏幕上。
好了,此刻我们来看一看这段措施 尚有哪些不敷?在清单1的这类措施中,存在着两个根天性的错误:
1.这种编码用到了数量浩瀚的 数据库meta-信息,而这些信息都只妙手工编码到措施中。当你想要取一个数值时,你必需提前 知道你将取到的数值是一个整数、浮点数、照旧一个双精度数。这将会使得编写一个可以处理惩罚任 何一个数据库的类变得十分坚苦;而且每一个数据库的细小调解城市逼你去仔细地查抄和修改 措施。
2.数据库中的信息老是作为一 个单个的RecordSet(记录集)实例来通报,而这种实例并不是一个真正的工具。RecordSet类( 与其它的数据库类封装没什么不同)更象一个指针或游标,借助要领,它可以或许提供存取数据信息 的途径。RecordSet中的实例实际上并不包罗信息,它们仅仅暗示得到信息的方法。这正说明白 当你要挪用别的的Record Set要领去获取某些真实的数据信息的时候,你必需通过Record Set去 做更多的事情(操作RecordSet.NEXT()去移动指针)。实际上,JDBC类简直仅仅通报这类接洽松 散的字段。纵然你完全相识数据库的所有内部细节,这也没有任何代价,因为在Java提供了存储 和处理惩罚信息的要领。
所以,抱负的状态是,有一种好的 要领,可以或许逐一地从数据库中抽取记录或字段(通过Record Set),而且将取到的信息“填”入到新 生成的工具之中。
这一办理方法的要害在于干系数 据库(RDB)和面向工具的数据模子(ODB)之间的相似性。RDB中的表和ODB中的类的浸染很相似, 而记录和工具也有着某些沟通的属性。你可以将记录看作是用来初始化某个工具的数据元素的 数据组。假如你已将记录从数据库中抽取出来,就必需挪用类结构函数来生成上面所说的工具。 若可以或许将每一笔记录自动地传到适当的结构函数中,就可以垂手可得地由记录来结构工具。
在开拓一个小的应用措施时,有可 能将每一个记录通报给某个结构函数,以此来生成新的工具。你可以常常操作工具参照来哄骗 从数据库中抽取的任何数据。因为你通过Record Set所获得的每一个工具最终都 是java.lang.Object的扩充。你可以界说一个BibClass,使其具有种种的配合属性,Big Class类 操纵将操作Javainstanceof算子来及时抉择运行中所碰着的数据库信息,而且通过一个大 的switch选择,跳到相应的措施段中。
你也可以界说一个相似的带有多 个结构函数的BigClass,每个结构函数有不同不太大的挪用参数。你可以 用BigClass(int,int),BigClass(int,float)等等,取决于你从RecordSet中轮回取出的数据的 范例(这一要领虽然将会包罗很多冗余代码)。
然而,以上两种要领都不能真正解 决问题。因为记录和结构函数在措施中的干系仍将是僵硬的。若想获得通用的数据库编码,必需 自动地成立数据库和结构函数二者的关联。
Java的机能对付此时的我们就如 雪中送碳。清单2中的措施片断只需一个类名就可以制作一个Java类。这样,我们就可以凭借类 和表的名称来识别那些可以处理惩罚从表中抽取出的记录的结构函数。操作尺度的JDBC类,可以容 易地得到所有表的表名,在此,我们将要充实操作这个Java小能力。只要简朴地为每个数据库表 开发一个Java类,使类名和表名彼此匹配,无论何时,每当从表中抽取出一笔记录的时候,通过 将表名通报给Class.forName(),措施将自动生成一个工具。
Class c = class.forName("Person");
Person p = (Person)c.newInstance();
System。out.println("... just created a " + c.getName();
清单2:一个简朴的Class.forName()例子
然而,此处尚有一些问题。由于对 某些特定的类来说,forName()函数需要挪用参数为void的结构函数,所以不能将RecordSet变 量直接通报给结构函数。在这里,我们需要一个初始化函数,把从数据库中抽取出的记录作 为RESULTSET参数,将其值赋予工具的数据元素。一个好的要领是引入超等类,并将其作为所有 数据库表相关类的通用父类。实际上,这个超等类在数据库查询中充当着重要的脚色,我们将在 下面展示这一点。
查询数据库
#p#分页标题#e#
操作上面的要领可以由记录生成 工具,可是你仍然得用SQL语句来查询数据库,这需要对数据库布局有深入的相识。这照旧没有 办理问题,固然我们可以或许自动地匹配数据库表和类的名字,可是照旧必需手工编写SQL语句。这 就是说每次修改数据库布局后,将不得不手工编辑这些查询语句。不外,我们仍然可以操作前文 所述的要领来越过这个障碍。凡是而言,查询干系数据库时,你将会用到属于主键或索引的字段 名和值。一言弊之,假如或人向你提供了适当的字段名和字段值,你就可以从相应的数据库中抽 取切合要求的记录(或字段)。而DatabaseMetaData工具不单可以被用于检索一系列的表名(见 上所述),并且可以得到一系列的主键及索引字段。上面的问题由此可以迎刃而解。
通过填入一系列适当的(字段名,字段值)对,可以操作相对而言少得多的代码实现对干系数据库的查询。你可以将对子中的所有字段名和数据库中的主健及索引字段相匹配。每当你找到了名字列表中相应的主健或索引字 段,可以按拍照应的数值来生成一个SQL语句,执行它来获取RecordSet,并通过Class.forName()结构机制将功效转化为工具。
实现这一想法要求可以以(名,值)对的方法对与数据库表相关的每个类的数据元素举办存取。可是这种要领只有通过上节所述的通用父类才气趋于完美。清单3和4操作伪码暗示了这一要领。
Open the database connection
清单3: 初始化数据库毗连的伪码
Retrieve a list of user defined tables
for each table
{
Check where there is a corresponding class file
if(it is availabe)
{
load the class file
Retrieve lists of key fields and indeces for this table
Store these lists in hashtables for easy access
}
else throw an exception
}
Take an object A containing a series of (name,value) pairs
for each table T
{
for each (name,value) pair
{
if(name matches primary_key_field or index_field)
store a refrence to both name and value
}
if all key_fields were found
create a query string using key names and values
else if all index_fields were found
create a query string using index names and values
execute the query to obtain a ResultSet
For each record in the ResultSet
{
Create an object of the class associated with table T
initialize the object using the record's contents
Add the object to the results, e。g。, attach it to A
}
}
清单4:描写数据库查询的伪码
Java镜像和Java beans
Java1.1开拓套件(JDK)的引入, 为我们带来了很多强大的新机能,譬喻全新的用户界面接口类。有两个新的JDKAPI尤其值得注 意:镜像机制(java.lang.reflect包)和JavaBeans组件的应用措施接口(java.beans包)。这两 个API将会辅佐我们建设高超的数据库类,使我们可以操作有关类的meta-信息,以此来办理开 发通用数据库类中的问题。
拥有forName()和newInstance() 要领的Class类,仅仅是镜象(reflection)成果的一个简朴例子。真正重要的是,forName()字符 串参数不必需是源措施中呈现的字符串。只要给出一个名字(这个名字可从任那里所取来), 你就可以载入并实例化任何一个类。对付我们的数据库类,我们可以直接从数据库自身的表名 中获得类名。这就是说,与数据库表相关的Java类名并不需要呈此刻源措施中。相应地,当表名 改变或某个表被插手到数据库中时,不需要修改源码,只要确信带有新名字的类已存在你的系 统中。
镜像类意味着可以在及时运行中 获取、存储和处理惩罚Java措施中的类信息。它们的实例可以或许象任何Java工具一样被运用,你可以象 修改字符串和整数一样,去修改类、数据范例、返回范例、要领参照和参数。在源措施级,这个镜 像的观念看起来并没有什么代价——因为可以应用你本身的编码直接存取你所需要的有关类、 要领及参数的所有信息。可是,镜像(reflection)将会在java的编译文件中发挥作 用。JavaBeansAPI的浸染是:通过应用措施的结构机制操作来自于全然差异的开拓者或产商所 编写的类。
JavaBeans类型为类成员的名字 拟定一系列的条例。以确保要领函数的名字能系统地描写它们的成果。任何一个切正当则 的Java类都可以被一个Bean的内化实例(通过镜像)查抄,以展现其行为的重要特征——诸如对 于什么样的事件类将有所响应,以及该类将会发生什么样的事件等等。任何切合这些类型的类 都是高效的Bean,因而是一个组件。在理论上,这意味着你可以从各类来历收集一系列beans,当 需要它们时可以将其其及时地绑在一起。
一个Bean的例子
#p#分页标题#e#
在下面一个名为Translation 的Bean中,有一个结构函数和两个要领来操纵一个名为“language”的属性。这里我想强调的是, 既然你可以通过查抄一个类的代码来相识它的结构函数、要领及属性,那么Bean的内化 器(Introspector)也能做到。
public class Translation extends Object
{
int language;
public translation()
{
}
public int getlanguage()
{
return(language);
}
public void setLanguage( int language)
{
this。language=language;
}
}
清单5:一个很是简朴的Bean
一个BeanIntro spector可以或许提供 许大都组的Property Descriptor实例,这些实例包括所有Bean的属性的范例信息,即例子中 由get/set要领所界说的范例。你可以挪用这些要领(操作reflection)来读或写这些属性。
镜像机制(reflection facilities)为我们查抄原本松散的类和数据库表的完整性提供了的更好要领。实际上,仅仅 通过类名和一个表匹配并不必然可以或许担保一些类内部的一致性。一个与表相关的类显然该当具 备存储数据库表中所有字段的数据元素。一个类大概有适当的名字,但其初始化代码大概会省 略。它大概只有一个正确的名字,而其数据成员大概有差异的名字可能是差异的范例。利用JDBC 的DatabaseMetaData及镜像机制可以查抄它们是否完全匹配!
引用一些JDBC挪用去得到现实中 必需的数据库信息,通过正确的名字去查抄你系统中的类,而且通过镜像去较量表和类的属性, 这实实在在是一块香饼!
结论和启示
JDBC,镜像和Java Beans三者的结 合,可以或许利便地从干系数据库中存取记录并操作记录来初始化组件(不只仅是工具)。为了实现 上述操纵,无需修改你的数据库,只需确认你的类切合Bean类型,并使类属性和表字段彼此匹 配。Beans尚有其它一些简朴的能力,可使编程越发有趣。Beans可以或许提供本身的用户界面组件, 而且Beans类型还包罗一个名为Customizers的对象。你可以引入别的的类专门地去察看、编辑 和自行定制一个Bean类的实例。(关于定制Beans,请参看下一期《定制你的Java》
总之,我们可觉得数据库类编写自 行界说的类,应用措施可以或许从干系数据库中抽取数据,通过实例化某个类获得新的实例,并引入 相关的图形用户界面(GUI)组件来查察和编辑数据。所有这些都由通用代码完成,因而可以或许处理惩罚 任何数据库。操作Java编写的、成果齐全的数据库查察/编辑器正迎我们而来。