第12章 JDBC数据库应用程序设计
12.1 JDBC概述 JDBC(Java Data Base Connectivity,其中文含义是Java数据库连接),可以将它理解为一种数据库存取的综合技术。 JDBC内嵌于Java 中, 提供存取关系数据库的标准库。其主要由两部分组成:JDBC API和JDBC 驱动程序管理器。所有用户界面的生成,事件驱动,对数据库的存取等功能均是由Java 来实现的。 1.数据库概述 数据库是指长期存储在计算机内,有组织、可共享的数据集合。有大量的数据库产品可应用于实际的数据库管理和程序设计。例如,Oracle,SQL Server,DB2,SyBase,FoxPro,Access,MySQL等。其中大量应用的数据库产品是关系型数据库系统,关系数据库有严格的数学理论为基础,数据和数据之间的联系表示为二维表的形式(关系),用户使用直观、简单。
通常按照习惯,将二维表的行称为记录,列称为字段或域。一个数据库由若干表(即关系)构成,其中一些表可表示数据,另一些表可表示数据之间的联系。 例如:设在“学生”数据库中有“学生成绩”表,其内容如表12-1所示: 表12-1“学生成绩”数据表 考号 姓名 性别 出生日期 数学 语文 英语 综合 4201025001 王丹 女 1989-10-15 95 87 88 78 4201025002 李屹 男 1988-12-26 96 80 85 4201025003 赵星 1989-5-3 90 75 84 82 4201025004 黄萍 1989-3-12 79
2.JDBC连接数据库的方式 在Java程序中要对数据库进行各种操作,可使用JDBC技术实现对数据库的连接和访问。本书主要介绍JDBC连接数据库的两种方式:使用JDBC-ODBC桥接器和直接使用某种数据库的驱动程序。 (1)使用JDBC-ODBC Bridge(桥接器) 此种方式利用微软公司定义的ODBC(开放数据库连接)对数据库访问的通用接口来访问操作数据库。因为ODBC支持大多数数据库的连接和访问,JDBC通过JDBC-ODBC桥来将JDBC API转换成ODBC API,进而通过ODBC来存取数据库。
为了使用这种方式连接和访问数据库,需在Windows系统中创建与数据库对应的数据源。 在Windows XP中,可在控制面板中选择“管理工具”,在其中的项目中选择“数据源(ODBC)”,可设置SQL Server 2000 数据库中的数据库 “学生”(在SQL Server中已经建立) 为ODBC数据源。 名称为student的数 据源设置完成后的 情况见图。
(2)使用某种数据库的专用驱动程序 此种方式是JDBC与某种数据库的专用驱动程序相连,不用创建数据源就能存取相应的数据库。如对SQL Server 2000,可以到下面的网页去下载“SQL Server 2000 Driver for JDBC”: http://www.microsoft.com/china/sql/downloads/2000/jdbc.asp 下载后得到一个2MB多的Setup.exe文件,运行该文件将进行驱动的安装。在安装目的文件夹中将创建lib子文件夹,其中包含三个jar文件:msbase、mssqlserver和msutil。按照系统说明书的要求,这三个文件要设置到classpath类搜索路径中。
3.JDBC连接数据库程序设计的主要步骤 应用JDBC设计数据库应用程序,需要使用到较多的Java类与接口,掌握这些类与接口,并遵从一定的操作步骤,就能顺利进行数据库的存取,从而进行数据库应用程序的设计。JDBC的类和方法都包含在java.sql包中,Java数据库应用程序的设计都需要引入java.sql包。下面从基本应用的角度,按照数据库应用程序的设计步骤来介绍JDBC相关类和接口。 (1)装载驱动程序 首先使用Class类的forName方法来装载驱动程序。装载驱动程序要处理异常。两种装载驱动程序的方法如下:
使用ODBC-JDBC桥接器的例: 使用SQL Server 2000驱动程序的例: try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); }catch(java.lang.ClassNotFoundException e) { System.out.println("类未找到错误!"+e); } 使用SQL Server 2000驱动程序的例: Class.forName("com.microsoft.jdbc.sqlserver .SQLServerDriver");
(2)定义连接URL 指定装载的数据库驱动程序后,可用DriverManager类来进行数据源或数据库的连接。DriverManager类是JDBC基础,用来管理JDBC驱动程序。该类有静态的getConnection()方法,用于验证JDBC数据源,并返回接口Connection对象。 使用ODBC-JDBC桥接器的例: Connection con = DriverManager.getConnection ("jdbc:odbc:student","sa","123456"); 其中的"student"为建立的数据源的名称,"sa"和"123456"为安装SQL Server 2000数据库系统时设置的用户名和访问密码(这里可根据实际用户名和密码来进行设置)。
使用SQL Server 2000驱动程序方式的例: Connection con = DriverManager.getConnection ("jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=学生","sa","123456"); 其中的"localhost "表示使用本地服务器(可根据实际名称填写),"1433"为访问SQL数据库的服务器默认端口号,"学生"为SQL Server 2000数据库文件名, "sa"和"123456"的意义为安装SQL Server 2000时设置的用户名和密码(可根据实际情况来填写)。 (3) 获取数据库信息和创建接口Statement对象 数据库连接成功后,可以使用Connection对象的getMetaData方法取得接口DatabaseMetaData(提供大量的方法)对象来了解数据源或数据库的各种信息。
例如: DatabaseMetaData dbmd = con.getMetaData(); // 返回DatabaseMetaData对象 dbmd.getURL() // 获得数据库的URL dbmd.getUserName() // 获得用户名 dbmd.getDriverName() // 获得数据库驱动程序名 dbmd.getDriverVersion() // 获得数据库驱动程序版本 dbmd.getDatabaseProductName()// 获得数据库产品名称 dbmd.getDatabaseProductVersion()//获得数据库产品版本 dbmd.getTables()//获得数据库中数据表的信息(有方法参数) dbmd.getColumns()//获得数据表中各列的信息(有方法参数)
数据库连接成功后,还可以使用向数据库发送访问数据库的SQL语句的方法来存取数据库。JDBC使用接口Statement的对象(或执行数据库存储过程的子接口CallableStatement、发送带参数的SQL语句的子接口PreparedStatement)来发送SQL语句,Statement对象可用Connection的方法createStatement()来返回。例如: Statement st = con.createStatement(); (4) 执行SQL语句以存取数据库 希望执行的SQL语句串作为Statement的方法execute()等的参数向数据库传送,以交给数据库引擎,执行SQL对数据库的访问操作。对数据库访问结果是一个数据表的情况,可将这个表(结果集)存入接口ResultSet的对象。
ResultSet rs = st.executeQuery("select * from 学生成绩"); 提交SQL语句的Statement的方法有execute、executeQuery和executeUpdate等,分别用于不同类型的SQL语句的提交。 executeQuery方法用于产生单个结果集的语句,如查询语句select。executeUpdate方法用于执行SQL语句中不产生结果集的语句,如insert,delete,update等语句和数据定义语句等(它的返回值为本次操作影响数据库表中数据行行数的整数值或零)。execute方法可用于执行产生多个结果集或对数据库进行多个操作的SQL语句(例如,执行数据库的存储过程)。下面是使用executeQuery方法和executeUpdate方法的例子: ResultSet rs = st.executeQuery("select * from 学生成绩"); int n=st.executeUpdate( "delete from 学生成绩 where 综合<60");
(5)对执行SQL语句的结果进行处理 Statement对象将SQL语句封装起来交给数据库引擎,执行select查询语句,将得到结果集ResultSet对象(注意:执行insert、delete、update等查询语句无结果集)。接口ResultSet对象封装了执行SQL语句的返回结果,以后可根据需要,以便在屏幕上显示结果或做进一步的处理。 在执行select语句后的返回结果中,包含了数据表和数据表内容的相关信息。关于数据表的信息可通过ResultSet的getMetaData方法来创建ResultSetMetaData对象,由这个对象可获取所需数据库的信息。
例如: ResultSetMetaData rsmd = rs.getMetaData(); rsmd.getColumnCount() // 获取列(字段)数 rsmd.getColumnName(列号) // 获取指定列(字段)的列名 在ResultSet中,提供了一整套的getXXX方法(如getInt,getString,getFloat,getDouble等)来访问结果集中当前行的不同列(用列序号或列标题作为这些方法的参数)的数据(类型不同时可将字段类型按照XXX类型来进行类型转换),而next方法可使得访问可对不同的行来进行。
例如,可用下面的程序段对学生成绩表的姓名和出生日期进行访问: ResultSet rs = st.executeQuery( "select 姓名,出生日期 from 学生成绩"); while(rs.next()){ System.out.println(rs.getString("姓名") // 用字段(列)名 +" " + rs.getString(2).substring(0,10)); // 用字段(列)序号, //而且将日期类型的字段转换为String类型进行显示。 }
ResultSet自动维护结果集当前数据行的指向光标,首先获得结果集时,指向光标置于结果集的第一行前,以后每调用一次next方法,光标就向下移动一行,这样可按照顺序从第一行到最后一行逐行访问结果集的每一行(访问结束时next返回false值)。 在有些情况下,可能希望任意访问而不是顺序访问结果集的数据行,对某些结果集,可能还希望通过结果集修改数据库的数据,则应在createStatement 方法中加入如下的两个参数,即: Statement st = con.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
如果只希望任意滚动结果集的指向光标,而不修改数据库的数据,则可在上述两个参数中将第二个参数设置为ResultSet 如果只希望任意滚动结果集的指向光标,而不修改数据库的数据,则可在上述两个参数中将第二个参数设置为ResultSet.CONCUR_READ_ONLY。 通过参数的设置,就可以用ResultSet的first(定位到第一行)、last(定位到最后一行)、previous(定位到前一行)、absolute(绝对行号)和relative(相对当前行的行号)等方法来设置当前行,并且还可以根据参数的更新设置,用insertRow(插入行)、deleteRow(删除行)、updateRow(更新行)等方法来对数据库表进行增、删、改的操作。
(6)关闭连接 数据库访问结束,为保证数据库数据的完整性和释放系统资源,应明确地关闭数据库的连接:一般是关闭结果集、关闭数据库访问对象和关闭连接。
12.2 JDBC数据库操作实例 【例12.1】用表12-1的数据在数据库“学生”中建立数据表“学生成绩”,并显示建表以后的结果。在程序中给出的是使用JDBC-ODBC桥接器方式连接数据库的代码,并用注释方式给出了使用直接驱动程序方式连接数据库的代码。(使用直接驱动程序方式时将该语句的注释符号去掉,并将上面有“*”号注释的语句去除或设置为注释。) import java.sql.*; class JdbcDemo{ public static void main(String args[]){ Connection con; // 定义连接类的对象
Statement st; // 为SQL语句的使用定义对象 ResultSet rs; // 定义结果集对象 String user="sa",pass="123456"; // 设置数据库的用户名和密码 try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); // * 驱动程序名 // Class.forName( // "com.microsoft.jdbc.sqlserver.SQLServerDriver"); }catch(ClassNotFoundException e) { System.out.println("类未找到错误!" + e); } con=DriverManager.getConnection(" jdbc:odbc:student",user,pass);// *
//con = DriverManager.getConnection // ("jdbc:microsoft:sqlserver://localhost:1433; // DatabaseName = 学生", user,pass); DatabaseMetaData dbmd = con.getMetaData(); System.out.println("数据库的URL:"+ dbmd.getURL()); System.out.println("数据库用户名:"+ dbmd.getUserName()); System.out.println("数据库驱动程序名:"+ dbmd.getDriverName()); System.out.println("数据库驱动程序版本号:"+ dbmd.getDriverVersion()); System.out.println("数据库产品名:"+ dbmd.getDatabaseProductName()); System.out.println("数据库产品版本号:"+ dbmd.getDatabaseProductVersion());
st=con.createStatement(); String query = "create table 学生成绩("+ "考号 char(10),姓名 char(8), 性别 char(2),出生日期 datetime," + "数学 integer,语文 integer,英语 integer, 综合 integer)"; st.executeUpdate(query); String s1="insert into 学生成绩 values(" +"'4201025001','王丹','女','1989-10-15', 95,87,88,78)"; String s2="insert into 学生成绩 values(" +"'4201025002','李屹','男','1988-12-26', 96,80,88,85)"; String s3="insert into 学生成绩 values(" +"'4201025003','赵星','男','1989-5-3', 90,75,84,82)";
String s4="insert into 学生成绩 values(" +"'4201025004','黄萍','女','1989-3-12',88,79,90,80)"; st.executeUpdate(s1); // 执行SQL语句的提交 st.executeUpdate(s2); st.executeUpdate(s3); st.executeUpdate(s4); rs=st.executeQuery("select * from 学生成绩"); // 查询建表并插入数据的结果 System.out.println(" [学生成绩]表中的数据如下:"); System.out.println(" 考号 姓名 性别 出生日期 数 语 英 综"); while(rs.next()) // 在输出数据间加竖线符进行分隔 System.out.println(rs.getString(1)+"|"// 输出考号 +rs.getString(2)+"|" // 输出姓名 +rs.getString(3)+"|" // 输出性别
+rs.getString(4).substring(0,10)+"|" // 输出出生日期 +rs.getString(5)+"|" // 输出数学成绩 +rs.getString(6)+"|" // 输出语文成绩 +rs.getString(7)+"|" // 输出英语成绩 +rs.getString(8)); // 输出综合成绩 rs.close();st.close();con.close(); }catch(java.sql.SQLException e){ System.out.println("SQL错误!" + e); }
例12.1程序的运行结果如下: 数据库的URL:jdbc:odbc:student 数据库用户名:dbo 数据库驱动程序名:JDBC-ODBC Bridge (SQLSRV32.DLL) 数据库驱动程序版本号:2.0001 (03.81.9030) 数据库产品名:Microsoft SQL Server 数据库产品版本号:08.00.0194 [学生成绩]表中的数据如下: 考号 姓名 性别 出生日期 数 语 英 综 4201025001|王丹 |女|1989-10-15|95|87|88|78 4201025002|李屹 |男|1988-12-26|96|80|88|85 4201025003|赵星 |男|1989-05-03|90|75|84|82 4201025004|黄萍 |女|1989-03-12|88|79|90|80
【例12. 2】设计一个Java数据库应用程序,它能按照菜单(用输出对话框显示)的选项,对例12 import javax.swing.JOptionPane; import java.sql.*; class JdbcDemo1{ public static void main(String args[]){ Connection con; Statement st; ResultSet rs; String user="sa",pass="123456",s,s1,s2,ss;
try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");// * // Class.forName( // "com.microsoft.jdbc.sqlserver.SQLServerDriver"); }catch(ClassNotFoundException e) { System.out.println("类未找到错误!" + e); } con=DriverManager.getConnection( "jdbc:odbc:student",user,pass); //con = DriverManager.getConnection // ("jdbc:microsoft:sqlserver://localhost:1433; // DatabaseName = 学生",user,pass);
while(true){ s=JOptionPane.showInputDialog( "请选择:\n 1.显示记录\n 2.添加记录 \n 3.删除记录\n 4.退出"); st=con.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); // 设置数据库指针可任意移动 rs=st.executeQuery("select * from 学生成绩"); if(s.equals("1")){ s1 = JOptionPane.showInputDialog( "请输入[开始记录号(>=1),记录数]"); String sa[]=s1.split(","); listRecord(rs,Integer.parseInt(sa[0]), Integer.parseInt(sa[1]));
// 调用listRecord显示记录,参数为结果集,开始记录号,显示记录数 }else if(s.equals("2")){ s1 =JOptionPane.showInputDialog( "请输入[ 考号,姓名,性别,出生日期,数学,语文,英语,综合]"); String a[]=s1.split(","); ss="insert into 学生成绩 values('"+a[0]+"','"+a[1]+"','"+a[2]; ss+="','"+a[3]+"',"+a[4]+","+a[5]+","+ a[6]+","+a[7]+")";
JOptionPane.showMessageDialog(null, "添加记录的SQL语句是:\n"+ss); st.executeUpdate(ss); }else if(s.equals("3")){ s1 =JOptionPane.showInputDialog( "请输入要删除记录的学生考号:"); PreparedStatement ps = con.prepareStatement( "delete from 学生成绩 where 考号=?"); // 带参数的SQL语句 ps.setString(1,s1);// 设置参数实际值,1为参数序号 ps.executeUpdate(); }else {
rs.close(); st.close(); con.close(); System.exit(0); } }catch(java.sql.SQLException e){ System.out.println("SQL错误!" + e); // 下面为显示记录的listRecord方法,忽略异常的处理 static void listRecord(ResultSet rs,int start,int n)throws SQLException{ int i=1; String ss=""; rs.absolute(start);
while(i<=n){ ss+=rs.getString(1)+"|"; // 输出考号 ss+=rs.getString(2)+"|"; // 输出姓名 ss+=rs.getString(3)+"|"; // 输出性别 ss+=rs.getString(4).substring(0,10)+"|"; // 输出出生日期 ss+=rs.getString(5)+"|"; // 输出数学成绩 ss+=rs.getString(6)+"|"; // 输出语文成绩 ss+=rs.getString(7)+"|"; // 输出英语成绩 ss+=rs.getString(8); // 输出综合成绩 ss+="\n"; i++; if(!rs.next())break; } JOptionPane.showMessageDialog(null, "显示记录如下:\n"+ss);
【例12.3】设计一个Java数据库GUI应用程序,它能根据提示在一个文本区中输入查询数据库的SQL语句,按“显示” 按钮后在下面的一个表格中显示查询结果。 import java.awt.*; import java.awt.event.*; import java.sql.*; import javax.swing.*; import javax.swing.table.*; class RSTable extends AbstractTableModel { private Connection con; private Statement st; private ResultSet rs; private ResultSetMetaData rsmd; private int numberOfRows;
private String user="sa",pass="123456", qs = "SELECT * FROM 学生成绩"; public RSTable(String query) throws SQLException, ClassNotFoundException{ Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); con = DriverManager.getConnection( "jdbc:odbc:student",user,pass); st = con.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY ); // 设置结果集为可滚动并且是只读的 setQuery(query); } public Class getColumnClass(int column){ try { // 获得结果集列的类名
String className =rsmd.getColumnClassName(column+1); return Class.forName( className ); }catch ( Exception e ) { System.out.println(e); } return Object.class; public int getColumnCount(){ try { return rsmd.getColumnCount();// 获得结果集的列数 }catch ( SQLException se ){ System.out.println(se); return 0; // 出错时返回0列
public String getColumnName( int column ){ try { return rsmd.getColumnName( column + 1 ); // 获得结果集的列名 }catch ( SQLException se ) { System.out.println(se); } return ""; // 出错时返回空字符串 public int getRowCount(){ return numberOfRows; // 获得结果集的行数 } // 获得结果集指定行列处的数据
public Object getValueAt( int row, int column ){ try { rs.absolute( row + 1 ); return rs.getObject( column + 1 ); }catch (SQLException se) { System.out.println(se); } return ""; protected void finalize(){ // 在对象撤销时自动调用来关闭数据库连接等 rs.close(); st.close(); con.close(); }catch ( SQLException se ){ public void setQuery( String query ) throws SQLException{
rs=st.executeQuery(query);//执行查询,返回结果集 rsmd = rs.getMetaData(); // 获得结果集的元数据 rs.last(); // 移动数据表指针到最后一行 numberOfRows = rs.getRow();// 获得最后一行的行号 fireTableStructureChanged();// 更新查询显示 }
public class JdbcDemo2 extends JFrame implements ActionListener{ private RSTable table; private JTextArea jta; public JdbcDemo2(){ super( "JdbcGUI应用程序:显示数据库查询结果" ); // 设置窗口标题 String qs = "select * from 学生成绩"; // 设置初始显示的SQL语句 try { table = new RSTable(qs ); // 创建RSTable对象 jta = new JTextArea( qs, 2, 120 );//设置文本区 jta.setWrapStyleWord( true ); // 设置换行属性
jta.setLineWrap( true ); // 设置文本区换行属性 jta.setFont(new Font("宋体",Font.PLAIN,13));//设置字体 JScrollPane scrollPane = new JScrollPane(jta, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER ); // 设置滚动面板 JLabel jl= new JLabel( "<Html><Font color=red>输入SQL语句:"); JButton jb = new JButton( "<Html><Font color=blue>查询"); JPanel jp=new JPanel(); // 创建面板对象 jp.setLayout(new BorderLayout()); // 设置面板的布局为BorderLayout jp.add(jl,BorderLayout.WEST); // 添加到面板
jp.add(scrollPane,BorderLayout.CENTER); // 添加到面板 jp.add(jb,BorderLayout.EAST); // 添加到面板 JTable resultTable = new JTable(table); resultTable.getTableHeader().setBackground(Color.blue); //设置表格标题背景色为蓝色 resultTable.getTableHeader().setForeground(Color.orange); //设置表格标题前景色为橙色 getContentPane().add( jp, BorderLayout.NORTH ); // 添加面板到窗体 getContentPane().add( new JScrollPane(resultTable), BorderLayout.CENTER ); // 添加表格到窗体
jb.addActionListener(this); // 注册动作监听器 }catch ( ClassNotFoundException e) { JOptionPane.showMessageDialog( null, "数据库驱动程序未找到!","驱动程序未找到", JOptionPane.ERROR_MESSAGE ); System.exit(1); // 终止应用程序 }catch (SQLException e) { JOptionPane.showMessageDialog( null, "数据库错误!", "数据库错误",JOptionPane.ERROR_MESSAGE ); } public void actionPerformed( ActionEvent e ){ try { table.setQuery(jta.getText()); // 将输入在文本区中SQL语句作查询
}catch (SQLException se) {//显示出错信息 JOptionPane.showMessageDialog( null, se.toString(), "数据库错误",JOptionPane.ERROR_MESSAGE ); } public static void main(String args[]){ JdbcDemo2 app = new JdbcDemo2(); app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); app.setSize( 680, 250 ); // 设置初始窗口大小 app.setVisible( true );
例12.3的运行界面如下:
《Java语言程序设计》 电子教案全部完! 谢谢使用!