深入理解ResultSet
ResultSet的概念
ResultSet,数据库结果集的数据表,通常通过执行查询数据库的语句生成。
如图7-4所示,ResultSet 对象具有指向其当前数据行的指针。最初,指针被置于第一行之前。next 方法将指针移动到下一行;因为该方法在 ResultSet 对象中没有下一行时返回 false,所以可以在 while 循环中使用它来迭代结果集。
图7-4 ResultSet中的指针
默认的 ResultSet 对象不可更新,仅有一个向前移动的指针。因此,只能迭代它一次,并且只能按从第一行到最后一行的顺序进行。可以生成可滚动和/或可更新的 ResultSet 对象。以下代码片段(其中 con 为有效的 Connection 对象)演示了如何生成可滚动且不受其他更新影响的、可更新的结果集。
Statement stmt = con.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE); ResultSet rs = stmt.executeQuery("SELECT a, b FROM TABLE2");
ResultSet 接口提供用于从当前行检索列值的获取方法(getBoolean、getLong 等)。可以使用列的索引编号或列的名称检索值。一般情况下,使用列索引较为高效。列从 1 开始编号。为了获得最大的可移植性,应该按从左到右的顺序读取每行中的结果集列,而且每列只能读取一次。 对于获取方法,JDBC 驱动程序尝试将基础数据转换为在获取方法中指定的 Java 类型,并返回适当的 Java 值。JDBC 规范有一个表,显示允许的从 SQL 类型到供 ResultSet 获取方法使用的 Java 类型的映射关系。
用作获取方法的输入的列名称不区分大小写。用列名称调用获取方法时,如果多个列具有这一名称,则返回第一个匹配列的值。列名称选项在生成结果集的 SQL 查询中使用列名称时使用。对于没有在查询中显式命名的列,最好使用列编号。如果使用列名称,程序员无法保证名称实际所指的就是预期的列。
获取数据库元数据
在我们前面使用JDBC来处理数据库的接口主要有三个,即Connection,PreparedStatement和ResultSet这三个,而对于这三个接口,还可以获取不同类型的元数据,通过这些元数据类获得一些数据库的信息。
元数据(MetaData),即定义数据的数据。打个比方,就好像我们要想搜索一首歌(歌本身是数据),而我们可以通过歌名,作者,专辑等信息来搜索,那么这些歌名,作者,专辑等等就是这首歌的元数据。因此数据库的元数据就是一些注明数据库信息的数据。
- 由Connection对象的getMetaData()方法获取的是DatabaseMetaData对象。
- 由PreparedStatement对象的getParameterMetaData ()方法获取的是ParameterMetaData对象。
- 由ResultSet对象的getMetaData()方法获取的是ResultSetMetaData对象。
DatabaseMetaData是由Connection对象通过getMetaData方法获取而来,主要封装了是对数据库本身的一些整体综合信息,例如数据库的产品名称,数据库的版本号,数据库的URL,是否支持事务等等,能获取的信息比较多,具体可以参考DatabaseMetaData的API文档。以下有一些关于DatabaseMetaData的常用方法:
- getDatabaseProductName:获取数据库的产品名称
- getDatabaseProductName:获取数据库的版本号
- getUserName:获取数据库的用户名
- getURL:获取数据库连接的URL
- getDriverName:获取数据库的驱动名称
- driverVersion:获取数据库的驱动版本号
- isReadOnly:查看数据库是否只允许读操作
- supportsTransactions:查看数据库是否支持事务
如果简单地获取一些常用的数据库综合信息,可以进行如下编码:
TestSQL t =new TestSQL(); t.getConn();//建立mysql连接 DatabaseMetaData dbMetaData= t.conn.getMetaData(); //获取数据库产品名称 String productName= dbMetaData.getDatabaseProductName(); System.out.println("数据库产品名称:"+productName); //获取数据库版本号 String productVersion= dbMetaData.getDatabaseProductVersion(); //获取数据库用户名 System.out.println("数据库版本号:"+productVersion); String userName= dbMetaData.getUserName(); System.out.println("用户名:"+userName); //获取数据库连接URL String userUrl= dbMetaData.getURL(); System.out.println("获取数据库连接URL:"+userUrl); //获取数据库驱动 String driverName= dbMetaData.getDriverName(); System.out.println("获取数据库驱动:"+driverName); //获取数据库驱动版本号 String driverVersion= dbMetaData.getDriverVersion(); System.out.println("获取数据库驱动版本号:"+driverVersion); //查看数据库是否是只读的 boolean isReadOnly= dbMetaData.isReadOnly(); System.out.println("数据库是否只读?"+isReadOnly); //查看数据库是否支持事务操作 boolean supportsTransactions= dbMetaData.supportsTransactions(); System.out.println("查看数据库是否支持事务操作:"+supportsTransactions);
其中,conn是已获取的连接,上述代码运行结果如下:
数据库产品名称:MySQL 数据库版本号:5.5.65-MariaDB 用户名:root@10.0.0.1 获取数据库连接URL:jdbc:mysql://10.0.0.30:3306/cloud?characterEncoding=utf8 获取数据库驱动:MySQL Connector Java 获取数据库驱动版本号:mysql-connector-java-5.1.39 ( Revision: 3289a357af6d09ecc1a10fd3c26e95183e5790ad ) 数据库是否只读?false 查看数据库是否支持事务操作:true
拓展与提高
在任务案例的代码中,对主界面代码和数据库访问代码进行了分离,提高了代码的可读性,但是,如果把一些逻辑代码和主界面混在一起,就极大地影响了代码的可读性、可维护性、可扩展性和执行效率。因此,业务逻辑代码需要独立出来,放在一个单独的类中,完成业务功能的同时担任主界面和数据库访问代码的桥梁,就好像饭店里的服务员和采购员之间,需要一个厨师一样,明确的人员分工不仅提高了运作效率,也降低了人员变动的风险。这种把数据访问代码、业务逻辑代码和页面代码相互分离的软件设计模式,就是当前比较流行的分层架构模式。
在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结构。微软推荐的分层式结构一般分为三层,从下至上分别为:数据访问层、业务逻辑层、表示层。其中,数据访问层由数据库操作相关的代码组成,业务逻辑层由逻辑处理相关的代码组成,表示层由界面组成;它们之间通过方法的参数和返回值进行数据的传递。我们可以如图7-4所示,对代码进行优化。
知识点提炼
- ResultSet 对象的列的编号、类型和属性由 ResultSet.getMetaData 方法返回的 ResulSetMetaData 对象提供。
- 当生成 ResultSet 对象的 Statement 对象关闭、重新执行或用来从多个结果的序列检索下一个结果时,ResultSet 对象会自动关闭。
- 元数据(Metadata)是描述其它数据的数据(data about other data),或者说是用于提供某种资源的有关信息的结构数据(structured data)。元数据是描述信息资源或数据等对象的数据,其使用目的在于:识别资源;评价资源;追踪资源在使用过程中的变化;实现简单高效地管理大量网络化数据;实现信息资源的有效发现、查找、一体化组织和对使用资源的有效管理。
习题
一、选择题
1、哪个不是JDBC用到的接口和类? ( )
A. System B. Class C. Connection D. ResultSet
2、使用Connection 的哪个方法可以建立一个PreparedStatement接口?( )
A. createPrepareStatement() B. prepareStatement()
C. createPreparedStatement() D. preparedStatement()
3、在JDBC编程中执行完下列SQL语句SELECT name, rank, serialNo FROM employee,能得到rs的第一列数据的代码是哪两个? ( )
A. rs.getString(0) B. rs.getString("name")
C. rs.getString(1) D. rs.getString("ename")
4、下面的选项加载MySQL驱动正确的是哪一个? ( )
A. Class.forname("com.mysql.JdbcDriver")
B. Class.forname("com.mysql.jdbc.Driver")
C. Class.forname("com.mysql.driver.Driver")
D. Class.forname("com.mysql.jdbc.MySQLDriver")
5、如果为下列预编译SQL的第三个问号赋值,那么正确的选项是哪一个? ( )
UPDATE emp SET ename=?,job=?,salary=? WHERE empno=?;
A. pst.setInt("3",2000) B. pst.setInt(3,2000)
C. pst.setFloat("salary",2000) D.pst.setString("salary","2000")
二、简答题
1、结合实际案例说明Eclipse中利用JDBC技术连接到数据库的一般步骤。
2、列出Statement组件用于执行SQL语句的三个主要方法,并说明其作用。
三、编程题
数据表user,结构如下
完成用户注册功能。
返回 Java程序设计