第19章 数据库技术
数据库是大多数企业所采取的数据存储形式,而Java为数据库编程提供了强大的支持。Sun公司提供了用Java语言编写成的Java与数据库的接口规范JDBC(Java DataBase Connectivity), JDBC API定义了Java中的类和接口,表示数据库连接、SQL指令、结果集合等。它允许Java程序员发送SQL指令并处理结果。
实例182 连接各种数据库
本实例将介绍连接数据库的方法。以MySQL数据库为例,首先实现了一个通用的方法,然后针对不同类型的数据库的特点,实现了更方便的连接数据库的方法。在运行实例前,需要先建立一个MySQL数据库。SQL脚本如下:
create database myuser; GRANT all on myuser.* to root@127.0.0.1 IDENTIFIED BY 'root';
此SQL语句的作用是创建了名字为myuser的数据库,并可以用root账户访问该数据库,访问密码也是root。本实例就以刚创建的myuser数据库为例。
技术要点
连接数据库的技术要点如下:
• 为数据连接注册驱动程序,如Class.forName("com.mysql.jdbc.Driver")。
• 根据数据库的URL、用户名和密码创建数据库连接,如Connection conn =DriverManager. getConnection("jdbc:mysql://localhost:3306/myuser","root","root")。
• 通过Connection对象的createStatement方法创建Statement或它的子类,如Statement st =con.createStatement()。
• 创建Statement之后,就可以使用Statement的对象调用executeQuery(String sql)方法执行查询sql语句。
• 调用close()来关闭Statement对象和Connection对象。
实现步骤
(1)新建一个类名为JDBC_Conntion.java。
(2)代码如下所示:
package chp19; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class JDBC_Conntion { //获得数据库连接 public static Connection getConnection(String driverClassName, String dbURL, String userName, String password) throws ClassNotFoundException, SQLException { Connection con = null; //加载连接数据库的驱动类 Class.forName(driverClassName); //用用户名、密码连接数据库 con = DriverManager.getConnection(dbURL, userName, password); return con; } //获得Oracle数据库的连接 public static Connection getOracle_Con(String dricerClassName, String serverHost, String serverPort, String dbName, String userName, String password) throws ClassNotFoundException, SQLException { //如果没有提供这些连接参数,则用默认值 if (dricerClassName == null) { dricerClassName = "oracle.jdbc.driver.OracleDriver"; } if (serverHost == null) { serverHost = "127.0.0.1"; } if (serverPort == null) { serverPort = "1521"; } //构建访问Oracle数据库的URL String dbURL = "jdbc:oracle:thin:@" + serverHost + ":" + serverPort + ":" + dbName; return getConnection(dricerClassName, dbURL, userName, password); } //获得DB2数据库的连接 public static Connection getDB2_Con(String dricerClassName, String serverHost, String serverPort, String dbName, String userName, String password) throws ClassNotFoundException, SQLException { //如果没有提供这些连接参数,则用默认值 if (dricerClassName == null) { dricerClassName = "com.ibm.db2.jdbc.app.DB2Driver"; } if (serverHost == null) { serverHost = "127.0.0.1"; } if (serverPort == null) { serverPort = "5000"; } //构建访问DB2数据库的URL String dbURL = "jdbc:db2://" + serverHost + ":" + serverPort + "/" + dbName; return getConnection(dricerClassName, dbURL, userName, password); } //获得SQL Server数据库的连接 public static Connection getsqlServer_Con(String dricerClassName, String serverHost, String serverPort, String dbName, String userName, String password) throws ClassNotFoundException, SQLException { //如果没有提供这些连接参数,则用默认值 if (dricerClassName == null) { dricerClassName = "com.microsoft.jdbc.sqlserver.SQLServerDriver"; } if (serverHost == null) { serverHost = "127.0.0.1"; } if (serverPort == null) { serverPort = "1433"; } //构建访问SQL Server数据库的URL String dbURL = "jdbc:microsoft:sqlserver://" + serverHost + ":" + serverPort + "; DatabaseName=" + dbName; return getConnection(dricerClassName, dbURL, userName, password); } //获得MySQL数据库的连接 public static Connection getmysql_Con(String dricerClassName, String serverHost, String serverPort, String dbName, String userName, String password) throws ClassNotFoundException, SQLException { //如果没有提供这些连接参数,则用默认值 if (dricerClassName == null) { dricerClassName = "com.mysql.jdbc.Driver"; } if (serverHost == null) { serverHost = "127.0.0.1"; } if (serverPort == null) { serverPort = "3306"; } //构建访问SQL Server数据库的URL String dbURL = "jdbc:mysql://" + serverHost + ":" + serverPort + "/" + dbName; return getConnection(dricerClassName, dbURL, userName, password); } //获得Sybase数据库的连接 public static Connection getsybase_Con(String dricerClassName, String serverHost, String serverPort, String dbName, String userName, String password) throws ClassNotFoundException, SQLException { //如果没有提供这些连接参数,则用默认值 if (dricerClassName == null) { dricerClassName = "com.sybase.jdbc3.jdbc.SybDriver"; } if (serverHost == null) { serverHost = "127.0.0.1"; } if (serverPort == null) { serverPort = "5007"; } //构建访问SQL Server数据库的URL String dbURL = "jdbc:sybase:Tds:" + serverHost + ":" + serverPort + "/" + dbName; return getConnection(dricerClassName, dbURL, userName, password); } public static void main(String[] args) throws ClassNotFoundException, SQLException { String mysql_Dirver = "com.mysql.jdbc.Driver"; String dateBase = "myuser"; String userName = "root"; String password = "root"; Connection con = JDBC_Conntion.getmysql_Con(mysql_Dirver, null, null, dateBase, userName, password); System.out.println("通过getmysql_Con连接方法:"); System.out.println("**** MySQL数据库连接成功!*****"); con.close(); System.out.println("###### 成功关闭MySQL数据库! #####\n"); System.out.println("通过getConnection连接方法:"); String url = "jdbc:mysql://127.0.0.1:3306/" + dateBase; con = getConnection(mysql_Dirver, url, userName, password); System.out.println("@@@@@@@ 连接MySQL数据库成功!@@@@@@@@"); con.close(); System.out.println("$$$$$$$$ 成功关闭与MySQL数据库的连接!$$$$$$$"); } }
图19-1 运行结果
(3)运行结果如图19-1所示。
源程序解读
(1)getConnection方法是连接数据库最通用的方法,用户提供数据库的驱动类、数据库的URL、用户名和密码,便可以获得数据库的连接。
(2)getOracle_Con方法获得Oracle数据库的连接,用户提供数据库的驱动类、数据库服务的IP地址(域名)、数据库服务的端口、数据库名、用户名和密码,便可以获得与Oracle数据库的连接。Oracle的驱动程序为oracle.jdbc.driver.OracleDriver,默认的数据库端口为1521。
(3)getDB2_Con方法获得DB2数据库的连接,用户提供数据库的驱动类、数据库服务的IP地址(域名)、数据库服务的端口、数据库名、用户名和密码,便可以获得与DB2数据库的连接。DB2的驱动程序为com.ibm.db2.jdbc.app.DB2Driver,默认的数据库端口为5000。
(4)getsqlServer_Con方法获得SQL Server数据库的连接,用户提供数据库的驱动类、数据库服务的IP地址(域名)、数据库服务的端口、数据库名、用户名和密码,便可以获得与SQL Server数据库的连接。SQL Server的驱动程序为com.microsoft.jdbc.sqlserver. SQLServerDriver,默认的数据库端口为1433。
(5)getmysql_Con方法获得MySQL数据库的连接,用户提供数据库的驱动类、数据库服务的IP地址(域名)、数据库服务的端口、数据库名、用户名和密码,便可以获得与MySQL数据库的连接。MySQL的驱动程序为com.mysql.jdbc.Driver,默认的数据库端口为3306。
(6)getsybase_Con方法获得Sybase数据库的连接,用户提供数据库的驱动类、数据库服务的IP地址(域名)、数据库服务的端口、数据库名、用户名和密码,便可以获得与Sybase数据库的连接。Sybase的驱动程序为com.sybase.jdbc3.jdbc.SybDriver,默认的数据库端口为5007。
(7)在不使用数据库连接时,要及时调用Connection的close方法,关闭数据库连接。
实例183 创建表结构
在实例182中创建了数据库myuser,并通过JDBC连接到MySQL数据库,接下来就可以对库中的表进行操作。本实例介绍如何在数据库中创建表结构并在程序中获取表中的信息。
技术要点
获取数据库和数据表元数据的技术要点如下:
• 通过create table表名(字段名字段类型长度)的sql形式创建表结构。
• ResultSetMetaData接口的作用是获取ResultSet对象中列的类型和属性信息。由于ResultSetMetaData是接口,所以没有构造方法,故不能使用new来创建ResultSetMetaData对象,但是可以通过ResultSet的getMetaData()方法创建,如ResultSetMetaData md =rs.getMetaData()。
• 调用ResultSetMetaData的getColumnCount()可以返回ResultSet对象中的列数。
• 调用ResultSetMetaData的getColumnName(int column)可以获取指定列的名称,其中column指的是列数。
• 调用ResultSetMetaData的getColumnTypeName(int column)返回检索指定列的数据库特定的类型名称,其中column指的是列数。
• 调用ResultSetMetaData的isNullable(int column)的作用是说明指定列中的值是否可以为null,其中column指的是列数。
实现步骤
(1)新建一个类名为Structure.java。
(2)代码如下所示:
create table staff( id INT PRIMARY KEY NOT NULL UNIQUE AUTO_INCREMENT, //主键 name VARCHAR(50) NOT NULL UNIQUE, //姓名 age INT(3) UNSIGNED NOT NULL, //年龄 sex VARCHAR(3) NOT NULL UNIQUE, //性别 address VARCHAR(50) NOT NULL UNIQUE, //居住地址 depart VARCHAR(50) NOT NULL UNIQUE, //部门 worklen VARCHAR(10) NOT NULL UNIQUE, //工龄 wage VARCHAR(20) //工资 ); //在myuser数据库中创建了一个名字为staff的数据表,它的基本信息如上。 package chp19; import java.sql.Connection; import java.sql.*; import java.sql.DriverManager; public class Structure { //显示数据表的信息,主要是列的信息。con表示与数据库的连接,tableName表示数据表名 public static void Show_Data(Connection con, String tableName){ String sql = "SELECT * FROM " + tableName; Statement sm = null; try { sm = con.createStatement(); ResultSet rs = sm.executeQuery(sql); //首先获得表的所有数据 ResultSetMetaData md = rs.getMetaData(); //得到结果集的元数据 System.out.println("数据表" + tableName + "的表结构如下:"); int columnCount = md.getColumnCount(); //表的列数 System.out.println(); StringBuffer sbf = new StringBuffer(""); sbf.append("序号\t列名\t\t").append("类型\t\t"); sbf.append("是否为空\t\t"); System.out.println(sbf); sbf.delete(0, sbf.length()); //输出列的属性信息 for (int i=1; i<=columnCount; i++){ sbf.append(i).append("\t"); sbf.append(md.getColumnName(i)).append("\t\t"); sbf.append(md.getColumnTypeName(i)).append("\t\t"); sbf.append(md.isNullable(i)); System.out.println(sbf); sbf.delete(0, sbf.length()); } System.out.println("此表共有 " + columnCount+" 列"); rs.close(); } catch (SQLException e) { e.printStackTrace(); } finally { //关闭Statement if (sm != null){ try { sm.close(); } catch (SQLException e1) { e1.printStackTrace(); } } } } public static void main(String[] args) throws ClassNotFoundException, SQLException { String tableName = "staff"; Connection con = null; try { //获得数据库连接 con = getConnection(); System.out.println(); //显示数据表的元信息 Show_Data(con, tableName); } finally { //关闭数据库连接 if (con != null) { try { con.close(); } catch (SQLException e1) { e1.printStackTrace(); } } } } public static Connection getConnection() { Connection con = null; try { Class.forName("com.mysql.jdbc.Driver"); con = DriverManager.getConnection( "jdbc:mysql://localhost:3306/myuser", "root", "root"); } catch (Exception e) { e.printStackTrace(); } return con; } }
图19-2 表结构的运行结果
(3)运行结果的初始页面如图19-2所示。
图19-3 查看表结构里的数据
(4)通过SQL语句查询表结构里的数据,如图19-3所示。
源程序解读
Show_Data()方法实现了获取数据表元数据的功能。首先调用Connection的createStatement方法,创建一个java.sql.Statement对象,它用于执行静态 SQL 语句并返回执行结果;调用Statement的executeQuery方法,执行查询操作,获得数据表的所有数据,得到的结果集是一个ResultSet对象,该对象里包含了数据表的所有列和行的数据;调用ResultSet的getMetaData方法,获得描述结果集元数据的ResultSetMetaData对象,该对象实质上描述了数据表的元数据。
ResultSetMetaData的getColumnCount方法获得数据表定义的列数,getColumnName方法获得某列的列名,getColumnTypeName方法获得某列的类型名,isNullable方法判断某列是否允许为null。ResultSetMetaData还提供了很多方法,这里不一一列出。
注意
在读取完结果集中的数据后,需要调用ResultSet的close方法关闭该结果集,因为,每个ResultSet都与数据库存在一个连接,然后调用Statement的close方法,再调用Connection的close方法,即可彻底地关闭连接。这三个关闭是数据库编程中必须注意的,先后顺序不能改变。
实例184 表数据的基本操作
本实例介绍在Java中使用静态SQL语句操作数据库,包括添加、更新、删除和查询等基本操作。
技术要点
表数据的基本操作的技术要点如下:
• Statement类用于执行静态SQL语句并返回它所生成结果的对象。值得注意的是,一个Statement对象只能打开一个ResultSet对象。
• Statement类的execute(String sql)方法执行给定的SQL语句,它的返回值类型为boolean,表示如果第一个结果为ResultSet对象,则返回 true;如果其为更新计数或者不存在任何结果,则返回false。
• Statement类的executeQuery(String sql)方法用于执行查询SQL语句,返回查询的结果集,一个ResultSet对象。
• ResultSet的next()方法的作用是将指针从当前位置向下移动一行。它的返回值类型为boolean,表示如果新的当前行不为null,则返回true;否则返回false。此方法常用于while语句中作为判断是否存在数据的依据。
• ResultSet的getString(String columnName)方法的作用是以字符串的形式返回ResultSet对象的当前行中指定列名的值。
• Statement类的executeUpdate(String sql)方法用于执行更新SQL语句,包括插入、修改和删除语句,返回一个int值,表示更新的行的计数。
实现步骤
(1)新建一个类名为Table_Data.java。
(2)代码如下所示:
package chp19; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import com.mysql.jdbc.Statement; public class Table_Data { private static Connection conect; private static Statement st; public static void main(String[] args) { insert(); query(); update(); query(); delete(); query(); } public static void insert() { //插入 conect = getConnection(); //获取连接 try { Stringsql = "INSERTINTOstaff(name,age,sex,address,depart,worklen,wage)" + " VALUES ('lucy', 27, 'w', 'china','Personnel','3','2000')"; //插入数据的sQL语句 st = (Statement) conect.createStatement(); //创建Statement对象 int count = st.executeUpdate(sql); //返回插入数据的个数 System.out.println("向staff表中插入 " + count + " 条数据"); } catch (SQLException e) { System.out.println("插入数据失败" + e.getMessage()); } } public static void update() { //更新 conect = getConnection(); //获取连接 try { String sql = "update staff set wage='2500' where name = 'lucy'"; //更新数据的SQL语句 st = (Statement) conect.createStatement(); //创建Statement对象 int count = st.executeUpdate(sql); //返回更新数据的个数 System.out.println("staff表中更新 " + count + " 条数据"); } catch (SQLException e) { System.out.println("更新数据失败"); } } public static void query() { //查询 conect = getConnection(); //获取连接 try { String sql = "select * from staff"; //查询数据的SQL语句 st = (Statement) conect.createStatement(); //创建Statement对象 ResultSet rs = st.executeQuery(sql); //返回查询数据的结果集 while (rs.next()) { //判断是否还有下一个数据 //根据字段名获取相应的值 String name = rs.getString("name"); int age = rs.getInt("age"); String sex = rs.getString("sex"); String address = rs.getString("address"); String depart = rs.getString("depart"); String worklen = rs.getString("worklen"); String wage = rs.getString("wage"); System.out.println(name + " " + age + " " + sex + " " + address + " " + depart + " " + worklen + " " + wage); } System.out.println("\n"); } catch (SQLException e) { System.out.println("查询数据失败"); } } public static void delete() { //删除 conect = getConnection(); //获取连接 try { String sql = "delete from staff where name = 'lucy'"; //删除数据的SQL语句 st = (Statement) conect.createStatement(); //创建Statement对象 int count = st.executeUpdate(sql); //返回删除数据的个数 System.out.println("staff表中删除 " + count + " 条数据"); } catch (SQLException e) { System.out.println("删除数据失败"); } } public static Connection getConnection() { Connection con = null; try { Class.forName("com.mysql.jdbc.Driver"); //加载MySQL数据库驱动 con = DriverManager.getConnection( "jdbc:mysql://localhost:3306/myuser", "root", "root"); //创建数据连接 } catch (Exception e) { System.out.println("数据库连接失败"); } return con; } }
(3)运行结果如图19-4所示。
图19-4 表中数据运行结果
源程序解读
(1)query方法演示数据库的查询,创建数据库连接Connection和待执行的SQL语句,通过Statement的executeQuery方法执行SQL语句,返回一个ResultSet对象。再调用ResultSet的next()方法,根据字段名将数据取出,并打印在控制台上。在遍历ResultSet时,必须保证与之相关的Statement和Connection对象是活的,即没有被关闭,否则,就不能读取ResultSet中的数据。
注意
一个Statement对象同时只能有一个结果集在活动,即使没有调用ResultSet的close方法,只要打开了第二个结果集,就隐含着对上一个结果集的关闭。所以,如果想要同时对多个结果集进行操作,就要创建多个Statement对象,如果不需要同时对多个结果集进行操作,可以使用一个Statement对象,顺序地打开结果集。
(2)insert()方法演示数据库的插入,创建数据库连接Connection和待执行的SQL语句,根据Connection创建Statement对象,再调用Statement的executeUpdate方法执行SQL语句,返回此次被插入的行数。关闭连接。
(3)update()方法演示数据库的更新,创建数据库连接Connection和待执行的SQL语句,根据Connection创建Statement对象,再调用Statement的executeUpdate方法执行SQL语句,返回此次被更新的行数。关闭连接。
(4)delete()方法演示数据库的删除,创建数据库连接Connection和待执行的SQL语句,根据Connection创建Statement对象,再调用Statement的executeUpdate方法执行SQL语句,返回此次被删除的行数。关闭连接。
(5)getConnection()方法封装了数据库的连接方式。如果在程序中需要使用数据库,直接调用此方法即可,方便使用。
实例185 批处理
本实例将演示如何一次执行多条SQL语句,这些SQL语句可以是插入语句、更新语句和删除语句。本实例以myuser数据库中的staff表为例。
技术要点
批处理的技术要点如下:
• DatabaseMetaData接口是描述有关数据库的整体综合信息,由于DatabaseMetaData是接口,所以没有构造方法,故不能使用new来创建DatabaseMetaData对象,但是可以通过Connection的getMetaData()方法创建。例如:DatabaseMetaData md=con.getMetaData()。
• DatabaseMetaData类的supportsBatchUpdates方法用于判断此数据库是否支持批量更新。其返回值类型为boolean,如果此数据库支持批量更新,则返回true;否则返回false。
• Statement的addBatch(String sql)方法将给定的SQL命令添加到此Statement对象的当前命令列表中,此方法可多次调用。
• Statement的executeBatch()方法的作用是将一批命令提交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组。
实现步骤
(1)新建一个类名为Batch.java。
(2)代码如下所示:
package chp19; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class Batch { //判断数据库是否支持批处理 public static boolean support_Batch(Connection con) { try { //得到数据库的元数据 DatabaseMetaData md = con.getMetaData(); return md.supportsBatchUpdates(); } catch (SQLException e) { e.printStackTrace(); } return false; } //执行一批SQL语句 public static int[] startBatch(Connection con, String[] sqls) throws Exception { if (sqls == null) { return null; } Statement sm = null; try { sm = con.createStatement(); for (int i = 0; i < sqls.length; i++) { sm.addBatch(sqls[i]); //将所有的SQL语句添加到Statement中 } //一次执行多条SQL语句 return sm.executeBatch(); } catch (SQLException e) { e.printStackTrace(); } finally { sm.close(); } return null; } public static void main(String[] args) throws Exception { System.out.println("没有执行批处理时的数据为:"); query(); String[] sqls = new String[3]; sqls[0] = "UPDATE staff SET depart='Personnel' where name='mali'"; sqls[1] = "INSERT INTO staff (name, age, sex,address, depart, worklen,wage) VALUES ('lili', 27, 'w', 'china','Technology','2','2300')"; sqls[2] = "DELETE FROM staff where name='marry'"; Connection con = null; try { con = getConnection(); //获得数据库连接 boolean Batch_Flag = support_Batch(con); //判断是否支持批处理 System.out.println("支持批处理?" + Batch_Flag); if (Batch_Flag) { int[] results = startBatch(con, sqls); //执行一批SQL语句 //分析执行的结果 for (int i = 0; i < sqls.length; i++) { if (results[i] >= 0) { System.out.println("语句: " + sqls[i] + " 执行成功,影响了" + results[i] + "行数据"); } else if (results[i] == Statement.SUCCESS_NO_INFO) { System.out.println("语句: " + sqls[i] + " 执行成功,影响的行数未知"); } else if (results[i] == Statement.EXECUTE_FAILED) { System.out.println("语句: " + sqls[i] + " 执行失败"); } } } } catch (ClassNotFoundException e1) { throw e1; } catch (SQLException e2) { throw e2; } finally { con.close(); //关闭数据库连接 } System.out.println("执行批处理后的数据为:"); query(); } public static Connection getConnection() { //数据库连接 Connection con = null; try { Class.forName("com.mysql.jdbc.Driver"); //加载MySQL数据驱动 con = DriverManager.getConnection( "jdbc:mysql://localhost:3306/myuser", "root", "root"); //创建数据连接 } catch (Exception e) { System.out.println("数据库连接失败"); } return con; } public static void query() throws Exception { //查询所有的数据 Connection con = getConnection(); Statement st = con.createStatement(); ResultSet rs = st.executeQuery("select * from staff"); while (rs.next()) { String name = rs.getString("name"); int age = rs.getInt("age"); String sex = rs.getString("sex"); String address = rs.getString("address"); String depart = rs.getString("depart"); String worklen = rs.getString("worklen"); String wage = rs.getString("wage"); System.out.println(name + " " + age + " " + sex + " " + address + " " + depart + " " + worklen + " " + wage); } } }
(3)运行结果如图19-5所示。
图19-5 批处理运行结果
源程序解读
(1)support_Batch()方法判断数据库是否支持SQL语句的批处理。通过Connection的getMetaData方法获得数据库的元数据对象DatabaseMetaData,再调用DatabaseMetaData supportsBatchUpdates方法判断数据库是否支持批处理。
(2)startBatch()方法执行一组SQL语句。首先创建执行SQL语句的Statement对象,通过Statement类的addBatch方法将待执行SQL语句添加到执行缓冲区中,再调用executeBatch方法将执行缓冲区中的SQL语句全部执行,返回一个整型数组,如果数组元素的值大于等于0,则表示该语句执行成功,该值表示了执行该SQL语句修改的记录的行数;如果数组元素的值等于Statement.SUCCESS_NO_INFO常量,表示该语句也执行成功,但不知道具体修改了多少条记录;如果数组元素的值等于Statement.EXECUTE_FAILED常量,表示该语句执行失败。
(3)getConnection()方法封装了数据库的连接方式。如果在程序中需要使用数据库,直接调用此方法即可。
(4)query()方法的作用是查询数据库,传入执行查询语句的Statement对象和待执行的SQL语句,通过Statement的executeQuery方法执行SQL语句,返回一个ResultSet对象。再调用ResultSet的next()方法,根据字段名将数据取出,并打印在控制台上。
实例186 事务处理
在执行多条SQL语句的时候,不能保证所有语句都执行成功。但是有些数据表间会存在着一些关联,例如:一条数据需要同时插入A表和B表中,也就是说A表和B表中的数据是一一对应的关系,如果在执行的过程中,对A表插入成功而对B表插入失败,两表中的对应关系失衡,将会对后面的工作造成很大的影响。为了解决类似的问题,就需要对数据库作一系列的修改,修改之间具有依赖关系,要么所有的语句全部执行成功,要么所有语句全部执行失败,这就是事务的概念。本实例将演示事务的提交与回滚。
技术要点
提交和回滚事务的技术要点如下:
• DatabaseMetaData的supportsTransactions()方法可以判断数据库是否支持事务。它的返回值类型为boolean,如果事务受支持,则返回true;否则返回false。
• 数据库连接Connection对象默认是自动提交的。为了控制事务提交,则需在事务提交前,调用Connection的setAutoCommit(boolean autoCommit)方法关闭数据库连接的自动提交模式,其中参数autoCommit为true,表示启用自动提交模式;为false,表示禁用该模式。以后只有调用Connection的commit方法,才会将所有的操作提交到数据库。
• Connection的rollback方法,执行回滚操作,其作用是如果一同进行操作的语句,有一条出现了错误,那么其他的语句也会操作无效。也就是说,要么一起都操作成功,要么就一起全部操作失败。使用此方法时,需要注意的是只能在setAutoCommit(false)的模式下使用。
实现步骤
(1)新建一个类名为Transaction.java。
(2)代码如下所示:
package chp19; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; /** * 判断数据库是否支持事务,如果支持,如何实现事务的提交与回滚。 * MySQL中如果要使用事务,必须使用InnoDB存储引擎,在创建表时,后面加上ENGINE=InnoDB。 * MySQL默认的存储引擎是MyISAM,不支持事务 */ public class Transaction { //判断数据库是否支持事务 public static boolean isTransaction(Connection con) { try { //得到数据库的元数据 DatabaseMetaData md = con.getMetaData(); return md.supportsTransactions(); } catch (SQLException e) { e.printStackTrace(); } return false; } //将一组SQL语句放在一个事务里执行,要么全部执行通过,要么全部不执行 public static void Begin_Transaction(Connection con, String[] sqls) throws Exception { if (sqls == null) { return; } Statement sm = null; try { //事务开始 System.out.println("事务开始!"); //设置连接不自动提交,即用该连接进行的操作都不更新到数据库 con.setAutoCommit(false); sm = con.createStatement(); for (int i = 0; i < sqls.length; i++) { //执行SQL语句,但是没更新到数据库 sm.execute(sqls[i]); } //提交,立即更新到数据库 System.out.println("事务提交!"); con.commit(); System.out.println("事务结束!"); //事务结束 } catch (SQLException e) { try { //出现异常时,进行回滚,取消前面执行的操作 System.out.println("事务执行失败,进行回滚!\n"); con.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } finally { sm.close(); } } public static void main(String[] args) throws Exception { String[] sqls = new String[3]; sqls[0] = "UPDATE staff_info SET wage='2500' where name='lucy'"; sqls[1] = "INSERT INTO staff_info (name, age, sex,address, depart, worklen,wage)" + " VALUES ('hahaxiao', 27, 'w', 'china','Personnel','3','2000')"; //执行这条语句会引起错误,因为表staff_info不允许name列重复 sqls[2] = "INSERTINTOuserMess(name,password,nich,compass,sex,address,mail,phone) " +"values('nini','nini','mali','nini','w','changchun','ml@sina.com','1315588459');"; Connection con = null; try { //获得数据库连接 con = getConnection(); //判断是否支持批处理 boolean isTransaction = Transaction.isTransaction(con); System.out.println("支持事务?" + isTransaction); if (isTransaction) { //执行事务 Transaction.Begin_Transaction(con, sqls); } } catch (Exception e) { e.printStackTrace(); } finally { con.close(); //关闭数据库连接 } query_staff(); query_userMess(); } public static Connection getConnection() { //数据库连接 Connection con = null; try { Class.forName("com.mysql.jdbc.Driver"); //加载MySQL数据库驱动 con = DriverManager.getConnection( "jdbc:mysql://localhost:3306/myuser", "root", "root"); //创建数据连接 } catch (Exception e) { System.out.println("数据库连接失败"); } return con; } public static void query_staff() { //查询表staff_info Connection conect = getConnection(); //获取连接 System.out.println("表staff_info的全部记录为:"); try { String sql = "select * from staff_info"; //查询数据的SQL语句 Statement st = (Statement) conect.createStatement();//创建Statement对象 ResultSet rs = st.executeQuery(sql); //返回查询数据的结果集 while (rs.next()) { //判断是否还有下一个数据 //根据字段名获取相应的值 String name = rs.getString("name"); int age = rs.getInt("age"); String sex = rs.getString("sex"); String address = rs.getString("address"); String depart = rs.getString("depart"); String worklen = rs.getString("worklen"); String wage = rs.getString("wage"); System.out.println(name + " " + age + " " + sex + " " + address + " " + depart + " " + worklen + " " + wage); } System.out.println(); } catch (SQLException e) { System.out.println("查询数据失败"); } } public static void query_userMess() { //查询表userMess Connection conect = getConnection(); //获取连接 System.out.println("表userMess的全部记录为:"); try { String sql = "select * from userMess"; //查询数据的SQL语句 Statement st = (Statement) conect.createStatement();//创建Statement对象 ResultSet rs = st.executeQuery(sql); //返回查询数据的结果集 while (rs.next()) { //判断是否还有下一个数据 //根据字段名获取相应的值 String name = rs.getString("name"); String password = rs.getString("password"); String nich = rs.getString("nich"); String sex = rs.getString("sex"); String address = rs.getString("address"); String mail = rs.getString("mail"); String phone = rs.getString("phone"); System.out.println(name + " " + password + " " + nich + " " + sex + " " + address + " " + mail + " " + phone); } System.out.println(); } catch (SQLException e) { System.out.println("查询数据失败" + e.getMessage()); } } }
(3)运行结果:在没有进行事务处理之前,先看看表staff和表userMess的所有记录,如图19-6所示。
(4)由于在程序中表staff的插入语句有误,所以事务执行失败,产生回滚,加了相应的事务处理的其他语句也没有执行,如图19-7所示。
图19-6 没有处理前的表记录
(5)若将程序中的错句改为sqls[1] = "INSERT INTO staff_info (name, age, sex,address, depart, worklen,wage)"+ " VALUES ('haha', 27, 'w', 'china','Personnel','3','2000')",则事务执行成功,如图19-8所示。
图19-7 事务执行失败
图19-8 事务执行成功
源程序解读
(1)isTransaction()方法判断数据库是否支持事务。调用Connection的getMetaData方法获得数据库的DatabaseMetaData,再调用DatabaseMetaData的supportsTransactions方法判断数据库是否支持事务。
(2)Begin_Transaction()方法在事务中执行一组SQL语句。con.setAutoCommit(false)语句将数据库连接设置为不自动提交,然后用Statement对象依次执行SQL语句数组,如果所有执行都没有异常,表示事务中的所有操作都会执行成功,于是调用Connect的commit方法提交事务,将修改更新到数据库上。如果在用Statement执行SQL语句时,有一条执行发生异常,便调用Connect的rollback方法执行回滚,之前成功执行的SQL语句也将无效。
(3)在main()方法中,创建并初始化一个String数组,数组里装有3个SQL语句,分别为在staff表插入、在staff_info表插入和更新staff_info表。
(4)query_staff()查询表staff_info,创建数据库连接Connection,创建Statement对象,执行executeQuery(sql);方法返回ResultSet结果集,然后根据字段名获取相应的值,打印在控制台上。
(5)query_userMess()查询表userMess。查询过程可以参考query_staff()方法。
实例187 Applet连接数据库
本实例是一个通过JDBC连接后台数据库的例子,用JApplet来显示。环境的介绍:Java编写使用的是Eclipse 3.4.1,数据库MySQL 5.0,JDK版本是1.6,Tomcat 5.0.28。在“JDBC驱动”列表框中选择com.mysql.jdbc.Driver,在“数据库地址”文本框中输入jdbc:mysql://localhost/xx,在“用户名”文本框中输入用户名root,再输入密码,就可以进入了。
技术要点
Applet连接数据库的技术要点如下:
• JApplet类的用法。
• JDBC的Connection接口对象的使用。
• GetConnnection()方法。
• 元数据类ResultSetMetaData的使用。
实现步骤
(1)新建一个类名为JDBC_Applet.java。
(2)代码如下所示:
package chp19; import java.awt.Color; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.UnsupportedEncodingException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import javax.swing.JFrame; import javax.swing.SwingUtilities; public class JDBC_Applet extends javax.swing.JApplet { final static private String[] jdbcDriver = { "com.mysql.jdbc.Driver", "com.informix.jdbc.IfxDriver", "sun.jdbc.odbc.JdbcOdbcDriver", "com.borland.datastore.jdbc.DataStoreDriver", "com.sybase.jdbc.SybDriver", "oracle.jdbc.driver.OracleDriver", "COM.ibm.db2.jdbc.net.DB2Driver", "interbase.interclient.Driver", "weblogic.jdbc.mssqlserver4.Driver" }; private boolean connected = false; //判断是否连接成功 private Connection connection = null; private ResultSet rs = null; private String query = null; //查询的SQL语句 private String rsLine = null; private String driver = null; //数据库驱动程序 private String url = null; //数据库连接url private String user = null; //数据库用户名 private String password = null; //数据库用户密码 public JDBC_Applet() { Com_init(); Post_Init(); } private void Post_Init() { for (int i = 0; i < jdbcDriver.length; i++) { cbDriver.addItem(jdbcDriver[i]); } } private void Com_init() { jScrollPane1 = new javax.swing.JScrollPane(); taResponse = new javax.swing.JTextArea(); //显示SQL查询结果的文本区域 jPanel2 = new javax.swing.JPanel(); jPanel1 = new javax.swing.JPanel(); jLabel6 = new javax.swing.JLabel(); tfSql = new javax.swing.JTextField(); //输入SQL语句的文本区域 btnExecute = new javax.swing.JButton(); //SQL执行按钮 jPanel3 = new javax.swing.JPanel(); jLabel3 = new javax.swing.JLabel(); jPanel4 = new javax.swing.JPanel(); cbDriver = new javax.swing.JComboBox(); //数据库驱动列表框 jLabel7 = new javax.swing.JLabel(); tfUrl = new javax.swing.JTextField(); //连接数据库的url jLabel9 = new javax.swing.JLabel(); tfUser = new javax.swing.JTextField(); //录入用户名的文本框 jLabel10 = new javax.swing.JLabel(); tfPassword = new javax.swing.JTextField(); //录入用户密码的文本框 btnConnect = new javax.swing.JButton(); //连接button btnDisconnect = new javax.swing.JButton(); //释放数据库连接的button setFont(new java.awt.Font("Verdana", 0, 16)); this.setForeground(Color.orange); //设置字体 jScrollPane1.setViewportView(taResponse); getContentPane().add(jScrollPane1, java.awt.BorderLayout.CENTER); getContentPane().add(jPanel2, java.awt.BorderLayout.EAST); jLabel6.setText("SQL:"); //label Sql: jPanel1.add(jLabel6); tfSql.setPreferredSize(new java.awt.Dimension(300, 21)); jPanel1.add(tfSql); btnExecute.setText("执行语句"); btnExecute.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { btnExecuteActionPerformed(evt); } }); jPanel1.add(btnExecute); //将jPanel1放到上面 getContentPane().add(jPanel1, java.awt.BorderLayout.SOUTH); jPanel3.setPreferredSize(new java.awt.Dimension(550, 100)); jPanel3.setMinimumSize(new java.awt.Dimension(550, 100)); jPanel3.setMaximumSize(new java.awt.Dimension(550, 100)); jLabel3.setText("JDBC 驱动:"); jPanel3.add(jLabel3); //label JDBC Driver: jPanel3.add(jPanel4); cbDriver.setPreferredSize(new java.awt.Dimension(450, 26)); cbDriver.setMinimumSize(new java.awt.Dimension(100, 26)); jPanel3.add(cbDriver); jLabel7.setText("数据库地址:"); jPanel3.add(jLabel7); //label Database URL: tfUrl.setPreferredSize(new java.awt.Dimension(450, 21)); jPanel3.add(tfUrl); jLabel9.setText("用户名:"); jPanel3.add(jLabel9); //label User: tfUser.setPreferredSize(new java.awt.Dimension(100, 21)); jPanel3.add(tfUser); jLabel10.setText("密码:"); jPanel3.add(jLabel10); tfPassword.setPreferredSize(new java.awt.Dimension(100, 21)); jPanel3.add(tfPassword); btnConnect.setPreferredSize(new java.awt.Dimension(89, 27)); btnConnect.setMaximumSize(new java.awt.Dimension(89, 27)); btnConnect.setText("连接"); btnConnect.setMinimumSize(new java.awt.Dimension(89, 27)); btnConnect.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { btnConnectActionPerformed(evt); } }); jPanel3.add(btnConnect); btnDisconnect.setText("断开"); btnDisconnect.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { btnDisconnectActionPerformed(evt); } }); jPanel3.add(btnDisconnect); //放在布局管理器的北边 getContentPane().add(jPanel3, java.awt.BorderLayout.NORTH); } //执行查询的SQL语句 private void btnExecuteActionPerformed(java.awt.event.ActionEvent evt) { if (!connected) { SwingUtilities.invokeLater(new Runnable() { public void run() { taResponse.append("没有可连接的数据库.\n"); } }); } else { if (connection == null) { SwingUtilities.invokeLater(new Runnable() { public void run() { taResponse.append("连接数据库错误.\n"); } }); } else { try { query = tfSql.getText(); Statement stmt = connection.createStatement(); SwingUtilities.invokeLater(new Runnable() { public void run() { taResponse.append("正在查询: " + query + "\n"); } }); rs = stmt.executeQuery(query); //使用元数据 ResultSetMetaData rsmd = rs.getMetaData(); //还使用了元数据来判断有多少个字段 int count = rsmd.getColumnCount(); int i; rsLine = "\n 表的结构如下:\n"; for (int it = 1; it <= count; it++) { rsLine += rsmd.getColumnName(it) + " "; } rsLine += "\n"; while (rs.next()) { for (i = 1; i <= count; i++) { String s; try { s = new String(rs.getString(i).getBytes( "ISO-8859-1"), "GBK"); rsLine += s; } catch (UnsupportedEncodingException e) { } } rsLine += "\n"; } rsLine += "\n"; stmt.close(); SwingUtilities.invokeLater(new Runnable() { public void run() { taResponse.append(rsLine); } }); } catch (SQLException e) { SwingUtilities.invokeLater(new Runnable() { public void run() { taResponse.append("查询失败.\n"); } }); e.printStackTrace(); } } } } //释放数据库连接 private void btnDisconnectActionPerformed(java.awt.event.ActionEvent evt) { if (connected) { try { if (connection != null) { connection.close(); connection = null; SwingUtilities.invokeLater(new Runnable() { public void run() { taResponse.append("数据库断开.\n"); } }); } } catch (SQLException e) { SwingUtilities.invokeLater(new Runnable() { public void run() { taResponse.append("数据库断开错误.\n"); } }); e.printStackTrace(); } connected = false; driver = null; url = null; user = null; password = null; } else { SwingUtilities.invokeLater(new Runnable() { public void run() { taResponse.append("数据库已断开.\n"); } }); } } //连接数据库 private void btnConnectActionPerformed(java.awt.event.ActionEvent evt) { if (connected) { taResponse.append("数据库已连接.\n"); } else { driver = (String) cbDriver.getSelectedItem(); url = tfUrl.getText(); user = tfUser.getText(); password = tfPassword.getText(); try { SwingUtilities.invokeLater(new Runnable() { public void run() { taResponse .append("Using JDBC driver: " + driver + "\n"); } }); //注册数据库驱动:通过jdbc的方式连接数据库 Class.forName(driver).newInstance(); connection = DriverManager.getConnection(url, user, password); //连接数据库 if (connection != null) { SwingUtilities.invokeLater(new Runnable() { public void run() { taResponse.append("数据库 " + url + " connected.\n"); } }); connected = true; } } catch (ClassNotFoundException e) { SwingUtilities.invokeLater(new Runnable() { public void run() { taResponse.append("不能加载驱动.\n"); } }); e.printStackTrace(); } catch (SQLException e) { SwingUtilities.invokeLater(new Runnable() { public void run() { taResponse.append("不能连接数据库.\n"); } }); e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } } private javax.swing.JScrollPane jScrollPane1; private javax.swing.JTextArea taResponse; private javax.swing.JPanel jPanel2; private javax.swing.JPanel jPanel1; private javax.swing.JLabel jLabel6; private javax.swing.JTextField tfSql; private javax.swing.JButton btnExecute; private javax.swing.JPanel jPanel3; private javax.swing.JLabel jLabel3; private javax.swing.JPanel jPanel4; private javax.swing.JComboBox cbDriver; private javax.swing.JLabel jLabel7; private javax.swing.JTextField tfUrl; private javax.swing.JLabel jLabel9; private javax.swing.JTextField tfUser; private javax.swing.JLabel jLabel10; private javax.swing.JTextField tfPassword; private javax.swing.JButton btnConnect; private javax.swing.JButton btnDisconnect; public static void main(String[] args) { JFrame frame = new JFrame("通过JApplet连接数据库..."); JDBC_Applet hwj = new JDBC_Applet(); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); frame.getContentPane().add(hwj); hwj.init(); frame.setSize(400, 300); frame.setVisible(true); } }
(3)运行结果如图19-9所示。
图19-9 运行结果界面
源程序解读
(1)Post_Init()方法为ComboBox()下拉列表添加选项。
(2)Com_init()方法的主要功能是初始化Applet的布局和组件显示。例如,创建显示SQL查询结果的文本区域JTextArea和用于输入SQL语句的文本区域JTextField以及执行SQL查询的按钮。
(3)btnExecuteActionPerformed()方法执行查询的SQL语句的事件监听,并将表结构和表中的数据显示在JTextArea中。
实例188 简单的JDBC连接
JDBC是由一个驱动程序管理器对多个不同数据库的驱动程序的管理。Java应用程序之所以可以对指定的开源数据库进行数据操作,实际上是使用JDBC管理器通过加载不同的JDBC驱动程序访问不同的JDBC驱动程序,从而实现了对不同数据库的访问。
技术要点
进行JDBC数据库的连接的技术要点如下:
• 加载注册驱动程序。
• 取得对数据库的连接。
• DriverManager类的作用。
• Connection接口的使用。
• Statement类的使用方法。
• ResultSet接口的操作。
实现步骤
(1)新建一个类名为JDBC.java。
(2)代码如下所示:
package chp19; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; public class JDBC { public static void main(String args[]) throws Exception { Connection conn = null; Statement stmt = null; ResultSet rst = null; String s=""; try { Class.forName("com.mysql.jdbc.Driver"); //注册驱动程序 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/myuser", "root","root"); //获得连接 stmt = conn.createStatement(); //创建会话声明 rst = stmt.executeQuery("select * from user"); while(rst.next()){ System.out.print(s=newString(rst.getString(1).getBytes("ISO-8859-1"),"GBK")+"--"); System.out.print(s=newString(rst.getString(2).getBytes("ISO-8859-1"),"GBK")+"--"); System.out.print(s=newString(rst.getString(3).getBytes("ISO-8859-1"),"GBK")+"--"); System.out.print(s=newString(rst.getString(4).getBytes("ISO-8859-1"),"GBK")); System.out.println(); } //执行SQL语句 } catch (ClassNotFoundException e) { System.out.println(e.getMessage()); } catch (SQLException e) { e.printStackTrace(); System.out.println(e.getErrorCode()); } finally { //关闭所有的连接 try{ if(rst !=null) rst.close(); }catch(SQLException e){ } try { if (stmt != null) stmt.close(); } catch (SQLException e) { } try { if (conn != null) conn.close(); } catch (SQLException e) { } } } }
(3)运行结果如图19-10所示。
图19-10 运行结果
源程序解读
(1)用Class.forName(“数据库驱动”);加载数据库驱动。
(2)DriverManager.getConnection(数据库的连接地址);创建数据库连接。
(3)DriverManager类是负责管理JDBC的驱动程序。使用JDBC驱动程序之前,必须先将驱动程序加载并向DriverManager注册后才可以使用,同时提供方法来建立与数据库的连接。
(4)Connection接口是负责维护JSP/Java数据库程序和数据库之间的联机。
(5)Connection的createStatement()可以创建一个Statement对象,通过Statement类所提供的方法,可以利用标准的SQL命令,对数据库进行新增、删除或修改操作。
(6)Statement的executeQuery()方法可以创建一个ResultSet,其作用是负责存储查询数据库的结果。并提供一系列的方法对数据库进行新增、删除和修改操作。也负责维护一个记录指针(Cursor),记录指针指向数据表中的某个记录,通过适当地移动记录指针,可以随心所欲地存取数据库,加强程序的效率。
实例189 RowSet接口
javax.sql.RowSet是ResultSet提供的一个子接口,该接口具备ResultSet的功能,而且提供了更丰富的功能。本实例将介绍RowSet的特点,使用各种RowSet更新staff数据表中的记录。
技术要点
使用RowSet的技术要点如下:
RowSet接口提供一组JavaBeans 属性,这些属性允许配置一个RowSet实例来连接JDBC数据源并从中读取某些数据。RowSet常用的5个子接口,分别是:
• CachedRowSet:是一个数据行的容器,可在内存中缓存其各行,这使得进行操作时无须总是连接到数据源。此外,它还是一个JavaBeans组件,是可滚动、可更新、可序列化的。由于CachedRowSet是接口,所以必须由它的实现类CachedRowSetImpl去创建CachedRowSet对象。
• JdbcRowSet:可以让ResultSet对象可滚动和可更新,即使所用驱动程序和数据库不支持ResultSet的滚动和更新,也可以通过JdbcRowSet对象完成。
• FilteredRowSet:可以灵活地定义过滤条件,在调用有关移动游标的方法时,会使用这个方法进行检测。只有符合过滤规则的数据才会显示出来。
• JoinRowSet:可以在无连接的状态下直接对多个RowSet对象进行SQL的Join语句的合并。
• WebRowSet:封装了读写XML的方法,可以轻松地把数据库的数据持久化到XML或者从XML读取数据写入数据库。
实现步骤
(1)新建一个类名为Use_CachedRowSet.java。
(2)代码如下所示:
package chp19; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import javax.sql.RowSet; import javax.sql.rowset.CachedRowSet; import javax.sql.rowset.FilteredRowSet; import javax.sql.rowset.JdbcRowSet; import javax.sql.rowset.JoinRowSet; import javax.sql.rowset.Predicate; import javax.sql.rowset.WebRowSet; import com.sun.rowset.CachedRowSetImpl; import com.sun.rowset.FilteredRowSetImpl; import com.sun.rowset.JdbcRowSetImpl; import com.sun.rowset.JoinRowSetImpl; import com.sun.rowset.WebRowSetImpl; public class RowSetDemo { /** * 使用CachedRowSet。一旦获得数据,CachedRowSet就可以断开与数据库的连接,直到往数据 库写入数据的时候才需建立连接。 */ public static void Use_CachedRowSet() throws SQLException { CachedRowSet crs = new CachedRowSetImpl(); //设置CachedRowSet的属性,用它可以直接连接数据库 crs.setUrl("jdbc:mysql://localhost:3306/myuser"); crs.setUsername("root"); crs.setPassword("root"); crs.setCommand("select * from staff "); try { //需要先加载驱动,否则在使用执行时会找不到驱动类 Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { } //CachedRowSet的execute方法执行SQL语句,首先会创建与数据库的连接,然后将结果集取 出来,再关闭连接 crs.execute(); //此时的CachedRowSet与数据库是断开连接的 System.out.println("使用CachedRowSet操作数据前:"); Show_Result(crs); //在断开连接的情况下可以操作数据 crs.beforeFirst(); while (crs.next()) { if (crs.getString("name").equals("lili")) { crs.updateInt("age", 30); crs.updateRow(); crs.acceptChanges(); //再调用acceptChanges方法获得与数据库的连接,将修改提交到数据库 break; } } System.out.println("使用CachedRowSet操作数据后:"); Show_Result(crs); crs.close(); } /** * 使用JdbcRowSet。JdbcRowSet功能与ResultSet类似,JdbcRowSet在操作时保持与数据库的连接, * JdbcRowSet返回的结果默认是可以上下滚动和可更新的。 */ public static void Use_JdbcRowSet() throws SQLException { JdbcRowSet jdbcrs = new JdbcRowSetImpl(); //设置JdbcRowSet的属性,用它可以直接连接数据库 jdbcrs.setUrl("jdbc:mysql://localhost:3306/myuser"); jdbcrs.setUsername("root"); jdbcrs.setPassword("root"); jdbcrs.setCommand("select * from staff "); try { //需要先加载驱动,否则在使用执行时会找不到驱动类 Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { } //JdbcRowSet的execute方法执行SQL语句,首先获得数据库连接,再获取结果集, //与CachedRowSet的execute方法不同,执行完后它不关闭连接,它会一直保持该连接,直到调用close方法 jdbcrs.execute(); System.out.println("使用JdbcRowSet操作数据前:"); Show_Result(jdbcrs); //然后操作数据 jdbcrs.beforeFirst(); while (jdbcrs.next()) { if (jdbcrs.getString("name").equals("lili")) { jdbcrs.updateInt("age", 40); //提交到数据库 jdbcrs.updateRow(); //因为它本身是连接到数据库的,所以和CachedRowSet不同,它不需要再次获得连接 break; } } System.out.println("使用JdbcRowSet操作数据后:"); Show_Result(jdbcrs); //关闭结果集,此时关闭数据库连接 jdbcrs.close(); } /** * 使用FilteredRowSet。FilteredRowSet接口中规定了可以设定过滤器,其过滤接口为Predicate接口, * 必须实现Predicate接口中的evaluate方法 */ public static void Use_FilteredRowSet() throws SQLException { FilteredRowSet frs = new FilteredRowSetImpl(); //设置FilteredRowSet的属性,用它可以直接连接数据库 frs.setUrl("jdbc:mysql://localhost:3306/myuser"); frs.setUsername("root"); frs.setPassword("root"); frs.setCommand("select * from staff where wage = '2000'"); try { //需要先加载驱动,否则在使用执行时会找不到驱动类 Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { } frs.execute(); //FilteredRowSet继承CachedRowSet,所以execute方法功能与CachedRowSet的execute方法功能一样 //此时的CachedRowSet与数据库是断开连接的 System.out.println("使用FilteredRowSet过滤数据之前:"); Show_Result(frs); //设置过滤器,过滤器必须实现Predicate接口定义的三个execute方法 frs.setFilter(new Predicate() { public boolean evaluate(RowSet rs) { CachedRowSet crs = (CachedRowSet) rs; try { if (crs.getString("name").equals("mali")) //如果名字为mail则返回true return true; } } catch (SQLException e) { } return false; } //以下是Predicate接口的其他两个方法 public boolean evaluate(Object arg0, int arg1) throws SQLException { return false; } public boolean evaluate(Object arg0, String arg1) throws SQLException { return false; } }); System.out.println("使用FilteredRowSet过滤数据之后:"); Show_Result(frs); frs.close(); } /** * 使用JoinRowSet。JoinRowSet可以将多个RowSet对象进行join合并, * Join的列可以通过每个RowSet通过调用setMatchColumn方法来设置。 * setMatchColumn方式是Joinable接口定义的方法,五种类型的RowSet规定都需要实现该接口。 * JoinRowSet继承CachedRowSet,也不需要保持与数据库的连接。 */ public static void Use_JoinRowSet() throws SQLException { JoinRowSet joinrs = new JoinRowSetImpl(); CachedRowSet crs = new CachedRowSetImpl(); //设置CachedRowSet的属性,用它可以直接连数据库 crs.setUrl("jdbc:mysql://localhost:3306/myuser"); crs.setUsername("root"); crs.setPassword("root"); crs.setCommand("select * from staff"); try { Class.forName("com.mysql.jdbc.Driver"); //需要先加载驱动,否则在使用执行时会找不到驱动类 } catch (ClassNotFoundException e) { } crs.execute();//获取结果集 crs.setMatchColumn("name"); //设置结果集在Join时匹配的列名。 joinrs.addRowSet(crs); //将结果集数据放入JoinRowSet crs.close(); //查另外一个表 crs.setCommand("select name, mail from userMess"); crs.execute(); crs.setMatchColumn("name"); joinrs.addRowSet(crs); crs.close(); System.out.println("使用JoinRowSet对多个结果集进行Join操作:"); //表staff的name列和userMess的name列进行匹配 while (joinrs.next()) { //name属性两具表都有,age属性为表staff所有 System.out.print(joinrs.getString("name") + "\t"); System.out.print(joinrs.getInt("age") + "\t"); //mail属性为userMess所有 System.out.println(joinrs.getString("mail")); } joinrs.close(); } /** * 使用WebRowSet。WebRowSet继承自CachedRowSet,支持XML格式的查询、更新等操作, * 下面将WebRowSet对象输出成XML格式到文件。 */ public static void Use_WebRowSet() throws SQLException { WebRowSet wrs = new WebRowSetImpl(); //设置WebRowSet的属性,用它可以直接连接数据库 wrs.setUrl("jdbc:mysql://localhost:3306/myuser"); wrs.setUsername("root"); wrs.setPassword("root"); wrs.setCommand("select * from staff"); try { Class.forName("com.mysql.jdbc.Driver"); //需要先加载驱动,否则在使用执行时会找不到驱动类 } catch (ClassNotFoundException e) { } wrs.execute(); //获取结果集 //输出到XML文件 try { FileOutputStream out = new FileOutputStream("staff_data.xml"); wrs.writeXml(out); out.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } wrs.close(); } public static void main(String[] args) throws SQLException { RowSetDemo.Use_CachedRowSet(); RowSetDemo.Use_JdbcRowSet(); RowSetDemo.Use_FilteredRowSet(); RowSetDemo.Use_JoinRowSet(); RowSetDemo.Use_WebRowSet(); } public static void Show_Result(ResultSet rs) { //将ResultSet中的数据显示出来 if (rs == null) { return; } try { ResultSetMetaData md = rs.getMetaData(); int columnCount = md.getColumnCount(); //获得该ResultSet中的列数 //如果结果集的指针没有位于第一条记录的前面 //将结果集的指针指向第一条记录的前面 if (!rs.isBeforeFirst()) { rs.beforeFirst(); } //从前往后移动结果集指针,处理每条记录 while (rs.next()) { //每条记录都包含columnCount个列 for (int i = 1; i < columnCount; i++) { //由于不知道该列的类型,所以用getObject方法获取值 System.out.print(rs.getObject(i) + "\t"); } System.out.print(rs.getObject(columnCount) + "\r\n"); } //将指针归位 rs.beforeFirst(); } catch (SQLException e) { e.printStackTrace(); } } }
(3)运行结果如图19-11所示。
图19-11 操作数据的结果
WebRowSet输出的staff_data.xml的内容为:
<?xml version="1.0"?> <webRowSet xmlns="http://java.sun.com/xml/ns/jdbc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/jdbc http://java.sun.com/ xml/ns/jdbc/webrowset.xsd"> <properties> <command>select * from staff</command> <concurrency>1008</concurrency> <datasource> <null /> </datasource> <escape-processing>true</escape-processing> <fetch-direction>1000</fetch-direction> <fetch-size>0</fetch-size> ... <currentRow> <columnValue>5</columnValue> <columnValue>lili</columnValue> <columnValue>40</columnValue> <columnValue>w</columnValue> <columnValue>china</columnValue> <columnValue>Technology</columnValue> <columnValue>2</columnValue> <columnValue>2300</columnValue> </currentRow> </data> </webRowSet>
源程序解读
(1)Use_CachedRowSet()方法演示了CachedRowSet接口的使用,它的一个实现类是com.sun.rowset.CachedRowSetImpl。首先创建数据库连接;然后调用CachedRowSet的execute方法执行SQL命令,获得数据库的数据,再将结果集读取到内存并关闭连接;最后使用CachedRowSet修改数据库记录,但是在将数据修改提交到数据库时,除了要调用RowSet的updateRow方法之外,还需要调用CachedRowSet的acceptChanges方法,因为该方法会创建与数据库的连接。由于在前面已经将数据库连接关闭,所以必须调用acceptChanges方法将修改提交到数据库。
(2)Use_JdbcRowSet()方法演示了JdbcRowSet接口的使用,它的一个实现类是com.sun.rowset.JdbcRowSetImpl。JdbcRowSet在调用execute方法执行SQL命令时,只会创建数据库连接,不会将数据读取到内存,也不会关闭数据库连接,所以在用JdbcRowSet修改数据时,只需要调用updateRow方法便将修改提交到数据库了。
(3)Use_FilteredRowSet()方法演示了FilteredRowSet接口的使用,它的一个实现类是com.sun.rowset.FilteredRowSetImpl。通过setFilter方法为FilteredRowSet设置过滤器,在遍历FilteredRowSet时,将只能看到满足条件的数据,其他数据被过滤掉。
(4)Use_JoinRowSet()方法演示了JoinRowSet接口的使用,它的一个实现类是com.sun. rowset.JoinRowSetImpl。先创建两个CachedRowSet对象,通过CachedRowSet的setMatchColumn方法设置在进行Join操作时匹配的列属性,然后调用JoinRowSet的addRowSet方法将CachedRowSet对象添加到待Join的结果集组中,每调用一次addRowSet方法,都将刚加入的CachedRowSet对象与之前加入的CachedRowSet对象做Join操作。
(5)Use_WebRowSet()方法演示了WebRowSet接口的使用,它的一个实现类是com.sun.rowset.WebRowSetImpl。采用了与Use_CachedRowSet()方法中类似的机制连接数据库。WebRowSet的writeXml方法将结果集的数据以XML的格式输出到流中。
(6)RowSet还定义了ResultSet所不具备的如下方法:
• setUrl方法设置待访问数据库的URL。
• setUsername方法设置访问数据库的用户名;setPassword方法设置访问数据库的密码。
• setCommand方法设置待执行的SQL语句,该语句可以是静态的,也可以是动态的。
• execute方法执行事先设置的SQL语句。
从这些方法中可以看出,在没有创建Connection对象和Statement对象的情况下,也可以使用RowSet访问数据库。
实例190 调用存储过程
SQL语句执行的时候要先编译,然后执行。存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中。用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象,它是SQL语句书写的过程,这个过程经编译和优化后存储在数据库服务器中,应用程序使用时只要调用即可。本实例介绍如何在Java中调用数据库的存储过程。
技术要点
调用存储过程的技术要点如下:
• DatabaseMetaData的getProcedures(String catalog, String schemaPattern, String procedureNamePattern)方法的主要作用是获取与模式和名称相匹配的存储过程。其中,参数catalog表示过程类别;schemaPattern表示过程模式;procedureNamePattern表示过程名称。
• CallableStatement接口的作用是执行SQL存储过程。可以通过Connection的getProcedures方法创建CallableStatement对象。
• PreparedStatement接口表示SQL语句被预编译的时候,存储在PreparedStatement对象中。然后可以使用此对象高效地多次执行该语句。在调用有参数的存储过程时,需要使用动态SQL语句,对于输入参数,使用PreparedStatement提供的set系列方法,为输入参数赋值;对于输出参数,必须使用CallableStatement的registerOutParameter方法注册输出参数的类型。
实现步骤
(1)新建一个类名为Procedure.java。
(2)代码如下所示。
MySQL的存储过程如下所示:
use myuser; delimiter // create procedure my_count_proc (IN wagevalue INT, OUT no INT) //带有一个输入值和一个输出值的存储过程 BEGIN SELECT count(*) INTO no from staff where wage= wagevalue ; end // delimiter ; delimiter // create procedure my_count_proc1 (OUT no INT) //带有一个输出值的存储过程 BEGIN SELECT count(*) INTO no from staff ; end // delimiter ; delimiter // create procedure my_update_proc() //无参存储过程 BEGIN update staff set address='ningxia' where name='lili'; end // delimiter ;
Procedure.java是调用存储过程的Java文件,具体代码如下:
package chp19; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; public class Procedure { //获取、创建、调用数据库的存储过程 //列出数据库中所有的存储过程名 public static void getProcedureName(Connection con) { try { DatabaseMetaData md = con.getMetaData(); //获得数据库的元数据 ResultSet resultSet = md.getProcedures(null, null, "%"); //获得所有的存储过程的描述 System.out.println("数据库现有的存储过程名为:"); //显示存储过程名,位于结果集的第三个字段 while (resultSet.next()) { String procName = resultSet.getString(3); System.out.print(procName + "\t"); } System.out.println(); } catch (SQLException e) { e.printStackTrace(); } } //调用存储过程 public static void callProcedure(Connection con) throws Exception { CallableStatement cs = null; try { //调用无参数的存储过程 cs = con.prepareCall("{call my_update_proc()}"); cs.execute(); cs = con.prepareCall("{call my_count_proc1(?)}"); //调用有一个输出参数的存储过程 cs.registerOutParameter(1, Types.INTEGER); //注册输出参数的类型 cs.execute(); //执行 int write_Param = cs.getInt(1); //获取输出参数的值 System.out.println("my_count_proc1() 执行结果:" + write_Param); //调用有一个输入参数和一个输出参数的存储过程 cs = con.prepareCall("{call my_count_proc(?,?)}"); //注册输出参数的类型 cs.registerOutParameter(2, Types.INTEGER); //设置输入参数的值 cs.setString(1, "2000"); //执行 cs.execute(); //获取输出参数的值 write_Param = cs.getInt(2); System.out.println("my_count_proc 执行结果:" + write_Param); } catch (SQLException e) { e.printStackTrace(); } finally { cs.close(); } } public static void main(String[] args) throws Exception { Connection con = null; try { con = getConnection(); //获得数据库连接 getProcedureName(con); //列出数据库的所有存储过程名 callProcedure(con); //调用存储过程 } catch (Exception e1) { throw e1; } finally { con.close(); //关闭数据库连接 } } public static Connection getConnection() { //数据库连接 Connection con = null; try { Class.forName("com.mysql.jdbc.Driver"); //加载MySQL数据驱动 con = DriverManager.getConnection( "jdbc:mysql://localhost:3306/myuser", "root", "root"); //创建数据连接 } catch (Exception e) { System.out.println("数据库连接失败"); } return con; } }
图19-12 调用存储过程的运行结果
(3)运行结果如图19-12所示。
源程序解读
(1)getProcedureName方法获取数据库的所有存储过程的名字。首先根据Connection对象创建DatabaseMetaData对象,然后调用DatabaseMetaData的getProcedures方法获取数据库所有的存储过程,返回一个ResultSet对象,其中第三列便是存储过程的名字。
(2)callProcedure方法演示了存储过程的调用流程,其流程如下。
• 通过Connection的prepareCall方法创建执行存储过程的CallableStatement对象。
• 调用CallableStatement的execute方法执行存储过程;对于有输入参数的存储过程,使用setInt方法为参数设置整型值。
• 对于带有输出参数的存储过程,可以用registerOutParameter方法为输出参数定义参数类型。
• 调用getInt方法获得输出的整型值。
实例191 图片文件存入数据库
往数据库存取图形文件,需要支持存取二进制文件的数据库。目前的数据库都支持存取二进制文件,本实例就以MySQL为例,在数据库建立一个可以存取二进制文件的表image,并把一张图片存入到该表中。
技术要点
将图片文件存入数据库的技术要点如下:
• 在数据库中图像文件是以byte存储的。
• ByteBuffer的使用方法。
• 图像文件在数据库中存储类型为longblob。
实现步骤
(1)新建一个类名为Image_DataBase.java。
(2)代码如下所示。
创建表image的SQL语句如下:
create table image( id int NOT NULL primary key auto_increment, file_name varchar(255) not null, content longblob);
Image_DataBase.java是保存图像的Java文件,具体代码如下:
package chp19; import java.io.File; import java.io.FileInputStream; import java.nio.ByteBuffer; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; public class Image_DataBase { public static void main(String[] args) { //生成测试类 try { File file = new File("D:/abc/PIC3.bmp"); //将保存的图片生成文件对象 if (Image_byte(null, file)) //如果文件成功读入byte数组则返回true,否者返回false System.out.print("ture"); else System.out.print("False"); } catch (Exception e) { System.out.println("图像生成文件对象失败 " + e.getMessage()); } } public static boolean Image_byte(String sqlstr, File file) {//将图像文件转换成byte[] try { //将文件对象流化,并在内存生成文件大小的缓存区 FileInputStream fin = new FileInputStream(file); //创建字节缓存区,并分文件大小空间 ByteBuffer nbf = ByteBuffer.allocate((int) file.length()); byte[] array = new byte[1024]; int offset = 0, length = 0; //存放字节流 while ((length = fin.read(array)) > 0) { if (length != 1024) nbf.put(array, 0, length); else nbf.put(array); offset += length; } //关闭文件流 fin.close(); byte[] content = nbf.array(); System.out.println(content.length); return LoadImage(sqlstr, content); } catch (Exception e) { System.out.println("图像转换成byte数组失败 " + e.getMessage()); } return false; } private static boolean LoadImage(String sqlstr, byte[] in) { boolean flag = false; if (sqlstr == null) { sqlstr = "select * from image"; try { //获取连接,生成记录集 Connection con = getConnection(); Statement stmt = con.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); ResultSet rs = stmt.executeQuery(sqlstr); //如果记录存在则更新记录 if (rs.next()) { rs.updateBytes(3, in); rs.updateRow(); System.out.println(" updateRow"); //否则插入新记录 } else { rs.moveToInsertRow(); rs.updateString(2, "01"); rs.updateBytes(3, in); rs.insertRow(); System.out.println("insertRow"); } rs.close(); flag = true; //处理异常 } catch (Exception e) { System.out.println("图像存入数据库失败 " + e.getMessage()); } //返回标签 return flag; } return flag; } public static Connection getConnection() { //连接数据库 Connection con = null; try { Class.forName("com.mysql.jdbc.Driver"); //加载MySQL数据驱动 con = DriverManager.getConnection( "jdbc:mysql://localhost:3306/myuser", "root", "root"); //创建数据库连接 } catch (Exception e) { System.out.println("数据库连接失败 " + e.getMessage()); } return con; } }
源程序解读
(1)mage_byte方法的主要作用是判断图像文件是否成功转换成byte[],首先根据传入此图像的路径创建一个文件输入流FileInputStream对象,然后通过该对象的read()方法将图像文件读入指定的byte数组中。
(2)LoadImage方法的主要作用将存有图像文件的byte数组存入数组据中。首先调用getConnection方法获取数据库连接,首先查看该数据库中是否存在数据,若存在则修改其列名为content(列数为3)的内容,否则插入新的数据即执行insert操作。
实例192 数据库图片的输出
数据库中存入了二进制的图片,现在需要读取该文件并将其显示出来。本实例将介绍如何从数据库中将图片取出并显示在页面上。
技术要点
将数据库中的图形二进制数据显示成图片的操作主要有如下几个步骤:
(1)确定需要的图片在数据库中照片的ID。
(2)连接数据库。
(3)将数据库中的图片的二进制信息读入缓冲区。
(4)将图片二进制信息输出到浏览器。
实现步骤
(1)新建一个类名为show_Image.jsp。
(2)代码如下所示:
<%@ page contentType="image/jpeg;charset=GB2312"%> <%@ page import="java.sql.*"%> <%@ page import="java.io.*"%> <%@ page import="com.sun.image.codec.jpeg.*"%> <%@ page import="javax.imageio.*"%> <%@ page import="java.awt.image.*"%> <html> <head> <meta http-equiv="Content-Type" content="image/bmp;charset=GB2312"> <title>show Image</title> </head> <body> <% String sql = "select * from image where file_name ='01'"; Connection conn = null; BufferedInputStream inputImage = null; try { Class.forName("com.mysql.jdbc.Driver"); //加载MySQL数据驱动 conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/myuser", "root", "root"); //创建数据连接 Statement st = conn.createStatement(); ResultSet rs = st.executeQuery(SQL); while (rs.next()) { Blob blob = (Blob) rs.getBlob("content"); inputImage = new BufferedInputStream(blob.getBinaryStream()); } BufferedImage image = null; image = ImageIO.read(inputImage); ServletOutputStream sos = response.getOutputStream(); JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(sos); encoder.encode(image); inputImage.close(); } catch (SQLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } %> </body> </html>
(3)运行结果如图19-13所示。
图19-13 图片输出的运行结果
源程序解读
首先要知道图像文件存入数据库的file_name字段的值,在这里就直接以file_name=01来获取content值,由于content字段在数据库的存储类型为longblob。所以只能通过ResultSet的getBlob()来获取,其返回值为Blob类型的。Blob的getBinaryStream()方法的作用是以流的形式检索此Blob实例指定的BLOB值。通过ImageIO的read()方法生成awt的图像数据缓冲区BufferedImage。最后通过JPEGImageEncoder接口的encode()方法将图片输出在页面上。
实例193 利用console控制台运行类中的汉字处理方案
对于在console上运行的情况,在程序编写时,如果可能遇到中文的输入或含有中文的输出,程序中应该采用字符流来处理输入和输出。具体操作中,任何文件最终都可以转化为字节型,所以通用的方法就是:任何输入/输出流如果可能遇到编码问题,应该首先转化为字节流,然后将字节流按照特定的编码转化为字符流。
技术要点
console控制台汉字处理的技术要点如下:
• 学会在Java程序转码的入口和出口地方限制编码方法。
• 利用InputStreamReader按照指定字符集编码将字节流转换成字符。
• 利用InputStreamWriter按照指定字符集编码将字节流转换成字符。
实现步骤
(1)新建一个类名为Console_Chinese.java。
(2)代码如下所示:
package chp19; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.InputStreamReader; import java.io.OutputStreamWriter; public class Console_Chinese { public static void main(String[] args) throws Exception { String str = "\n汉字测试,这是在程序内部编写的字符串" + "\n"; String string = ""; //设置输入接口按中文编码 BufferedReader stdin = new BufferedReader(new InputStreamReader( System.in, "gb2312")); //设置输出接口按中文编码 BufferedWriter stdout = new BufferedWriter(new OutputStreamWriter( System.out, "gb2312")); stdout.write("请输入:"); stdout.flush(); string = stdin.readLine(); stdout.write("这是从键盘输入的字符串:" + string); stdout.write(str); stdout.flush(); } }
(3)运行结果如图19-14所示。
图19-14 console控制台运行类中的汉字
源程序解读
这种情况,运行该类首先需要JVM支持,即操作系统中必须安装有JRE。运行过程是这样的:
(1)Java启动JVM。
(2)JVM读出操作系统中保存的 class文件并把内容读入内存中,此时内存中为UNICODE格式的class类。
(3)JVM运行该类。如果此时此类需要接收用户输入,则类会默认用 file.encoding编码格式对用户输入的串进行编码并转化为unicode保存入内存(用户可以设置输入流的编码格式)。
(4)程序运行后,产生的字符串(UNICODE编码的)再回交给JVM。
(5)最后JRE把此字符串再转化为file.encoding格式(用户可以设置输出流的编码格式)传递给操作系统显示接口并输出到界面上。
必须确保以上每一步的转化都是正确的编码格式,才能最终不出现乱码现象。
实例194 Servlet中的汉字处理方案
从form表单提交信息到一个JSP页面或者一个Servlet进行处理的时候,提交的中文信息若不加处理就会显示乱码,如一串???。本实例将会介绍在Servlet开发中遇到的中文乱码的问题及解决办法。
技术要点
掌握Servlet处理乱码的两种形式:
• request.setCharacterEncoding("GB2312")
• response.setContentType("text/HTML;charset=GBK")
实现步骤
(1)新建一个类名为Servlet_Chinese.java。
(2)代码如下所示。
Servlet_Chinese.java设置编码格式的Java文件,具体代码如下:
package chp19; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class Servlet_Chinese extends HttpServlet { public void init() throws ServletException { } public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { request.setCharacterEncoding("GB2312"); //设置输入编码格式 response.setContentType("text/HTML;charset=GB2312");//设置输出编码格式 PrintWriter out = response.getWriter(); //使用PrintWriter输出 out.println("<hr>"); out.println("Servlet中的汉字处理方案!"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { request.setCharacterEncoding("GB2312"); //设置输入编码格式 response.setContentType("text/HTML;charset=GB2312"); //设置输出编码格式 String context = request.getParameter("context"); if (context == null) context = ""; PrintWriter out = response.getWriter(); //使用PrintWriter输出 out.println("<hr>"); out.println("<FONT style='COLOR:#87CEFA'><B>你输入的中文字符串是:" + context + "</B></FONT>"); } }
servlet_chinese.jsp显示结果的JSP页面,具体代码如下:
<%@ page language="java" contentType="text/html; charset=GB2312" pageEncoding="GB2312"%> <%request.setCharacterEncoding("GB2312");%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GB2312"> <title>Test Chinese</title> <% String contextPath = request.getContextPath(); %> </head> <body> <form action = "<%= contextPath%>/ServletChinese" name="base" method="POST"> <input name="context" type="text" value="" size="30" /> <input type="Submit" name="submit" value="SendToServlet"> </form> </body> </html>
(3)运行结果:输入汉字的页面如图19-15所示。
(4)对汉字进行处理的页面,如图19-16所示。
图19-15 输入汉字
图19-16 对汉字进行处理
源程序解读
经过编译后,JSP文件也被转化为Servlets类文件,只不过它不像标准的Servlets一样存于classes目录中,它存在于Web容器的临时目录中,所以JSP的运行和Servlet完全一样。
对于Servlets,客户端请求它时,Web容器调用它的JVM来运行Servlet。
(1)JVM把Servlet的class类从系统中读出并装入内存中,内存中是以UNICODE编码的Servlet类的代码。
(2)JVM在内存中运行该Servlet类。
(3)Servlet在运行的过程中,需要接收从客户端传来的字符,如表单输入的值和URL中传入的值。此时如果程序中没有设定接收参数时采用的编码格式,则We b容器会默认采用ISO-8859-1编码格式来接收传入的值,并在JVM中转化为UNICODE格式保存在Web容器的内存中。
(4)Servlet运行后生成输出,输出的字符串是 UNICODE格式的,紧接着,容器将Servlet运行产生的UNICODE格式的串(如HTML语法、用户输出的串等)直接发送到客户端浏览器上并输出给用户,此时如果指定了发送时输出的编码格式,则按指定的编码格式输出到浏览器上。如果没有指定,则默认按ISO-8859-1编码发送到客户的浏览器上。
(5)用户在浏览器中,通过浏览器解析返回的字节流。
最终解决方法是:在编译Servlet类的源程序时,用Encoding指定编码为GBK或GB2312,且在向用户输出时的编码部分用response对象的setContentType(“text/HTML;charset=GBK”),或GB2312来设置输出编码格式,同样在接收用户输入时,用request.setCharacterEncoding (“GB2312”)。这样无论Servlet类移植到什么操作系统中,只要客户端的浏览器支持中文显示,就可以正确显示。
实例195 JSP中的汉字处理方案
在JSP的开发过程中,经常出现中文乱码的问题,如JSP页面显示乱码、表单提交中文时出现乱码等,这些问题一直困扰着很多人。本实例将介绍在JSP开发中遇到的中文乱码的问题及解决办法。
技术要点
掌握JSP处理乱码的三种形式:
• <%@page contentType="text/HTML;charset=gb2312"%>
• <%request.setCharacterEncoding("GB2312");%>
• <%@page pageEncoding="GB2312"%>
实现步骤
(1)新建一个类名为JSP_chinese.jsp。
(2)代码如下所示:
<%@page pageEncoding="GB2312"%> <%@page contentType="text/HTML; charset=gb2312"%> <% request.setCharacterEncoding("GB2312"); %> <% //获取提交参数 String action = request.getParameter("ACTION"); String name = ""; String str = ""; //提取传递的参数值 if (action != null && action.equals("send")) { name = request.getParameter("name"); str = request.getParameter("str"); } %> <HTML> <head> <title>JSPChiese</title> <Script language="javascript"> function aa() { document.base.action = "?ACTION=send&str=书山有路勤为径"; document.base.method = "POST"; document.base.submit(); } </Script> </head> <body topmargin="5"> <form name="base" method="POST" target="_self"> <input type="text" name="name" value="" size="30"> <a href="javascript:aa()">SendToJSP</a> </form> <% //显示传递的中文 if (action != null && action.equals("send")) { out.println("<br><FONT style='COLOR:#87CEFA'><B>你输入的字符为:" + name + "</B></FONT>"); out.println("<br><FONT style='COLOR:#87CEFA'><B>通过URL传入的字符串为:" + str + "</B></FONT>"); } %> </body> </HTML>
由于要用到URL传递中文,需要设置服务器配置文件server.xml。修改Connector的配置,添加新属性URIEncoding=“GB2312”。
<Connector port="8080" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" redirectPort="8443" acceptCount="100" debug="0" connectionTimeout="20000" disableUploadTimeout="true" URIEncoding="GB2312" />
(3)运行结果:启动服务器,在地址栏中输入http://localhost:8080/my/JSP_chinese.jsp,在输入框中输入中文,如图19-17所示。
(4)单击“SendToJSP”按钮,可以看出,无论是URL中的中文还是form提交的中文,都可以正确地输出,如图19-18所示。
图19-17 汉字处理输入
图19-18 JSP中汉字的处理
源程序解读
由于JSP是在运行时由Web容器进行动态编译的,如果没有指定JSP源文件的编码格式,则JSP编译器会获得服务器操作系统的 file.encoding值来对JSP文件编译,所以在不同的操作系统中运行同一段JSP代码,就会出现乱码,其主要原因是容器在编译JSP文件时获取的操作系统的编码不同造成的。
在程序中通常遇到的问题最多的也是此类问题,对于这类问题,我们了解了Java中程序编码转换的原理,解决起来就容易多了。建议的解决办法如下:
(1)要保证JSP向客户端输出时是采用中文编码方式输出的,即无论如何先在JSP源代码中加入以下一行:
<%@page contentType="text/HTML; charset=gb2312"%>
(2)为了让JSP能正确获得传入的参数,在JSP源文件头加入下面一句:
<%request.setCharacterEncoding("GB2312");%>
(3)为了让JSP编译器能正确地解码含有中文字符的JSP文件,需要在JSP源文件中指定JSP源文件的编码格式。具体来说,在JSP源文件头上加入下面的一句即可:
<%@page pageEncoding="GB2312"%>
或
<%@page pageEncoding="GBK"%>
这是JSP规范2.0新增加的指令。建议使用此方法来解决JSP文件中的中文问题。
实例196 Tomcat连接池的配置
在用Servlet或JSP连接数据库的时候,往往数据源会在Tomcat的XML文件中配置。本实例将介绍在Tomcat中对数据连接池的配置。
技术要点
Tomcat连接池的配置的技术要点如下:
• Connection接口的用法。
• Statement类的使用。
实现步骤
(1)新建一个类名为Tomcat_test.java。
(2)代码如下所示:
<%@ page contentType="text/html;charset=gb2312" language="java" import="javax.naming.Context,javax.sql.DataSource,javax.naming.InitialContext,java.sql.*"%> <% DataSource ds = null; try { Context envCtx = new InitialContext(); ds = (DataSource) envCtx .lookup("java:comp/env/jdbc/mydatebase"); if (ds != null) { out . println("<FONT style='COLOR:#87CEFA'><B>connection is ok!</B></FONT>"); out.println("<br>"); Connection conn = ds.getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("select * from userMess"); while (rs.next()) { out.println("<FONT style='COLOR:#6495ED'><B>name:" + rs.getString("name") + "</B></FONT>"); out.println("<FONT style='COLOR:#6495ED'><B>password:" + rs.getString("password") + "</B></FONT>"); out.println("<br>"); } } else out.println("fail!"); } catch (Exception e) { out.println(e); } %>
手动修改server.xml的配置,用文本编辑器打开Tomcat安装目录下的\conf\server.xml,找到以下语句:
<Host name="localhost" debug="0" appBase="webapps" unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false">
在其下面添加Context标签中的内容:
<Host name="localhost" debug="0" appBase="webapps" unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false"> <Context path="/my" docBase="my" debug="5" reloadable="true" crossContext="true"> <Logger className="org.apache.catalina.logger.FileLogger" prefix="localhost_DBTest_log." suffix=".txt" timestamp="true" /> <Resource name="jdbc/mydatebase" auth="Container" type="javax.sql.DataSource" /> <ResourceParams name="jdbc/mydatebase"> <parameter> <name>factory</name> <value> org.apache.commons.dbcp.BasicDataSourceFactory </value> </parameter> <parameter> <name>driverClassName</name> <value>com.mysql.jdbc.Driver</value> </parameter> <parameter> <name>url</name> <value> jdbc:mysql://localhost:3306/myuser </value> </parameter> <parameter> <name>username</name> <value>root</value> </parameter> <parameter> <name>password</name> <value>root</value> </parameter> <parameter> <name>maxActive</name> <value>20</value> </parameter> <parameter> <name>maxIdle</name> <value>10</value> </parameter> <parameter> <name>maxWait</name> <value>10000</value> </parameter> </ResourceParams> </Context> </Host>
然后再打应用程序下的\WEB-INF\下的web.xml,加入以下配置文件:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <resource-ref> <description>MySQL</description> <res-ref-name>jdbc/mydatebase</res-ref-name> <res-type>javax.SQL.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref> </web-app>
(3)运行结果如图19-19所示。
图19-19 Tomcat连接池的配置
源程序解读
在Tomcat中配置数据连接池的时候,需要注意以下几点:
(1)mysql-connector-java-3.1.12-bin.jar,它只能放在Tomcat/common/lib下。
(2)在Tomcat中需要设置根目录时,datasource的设置要特殊写,修改Tomcat安装目录下的conf/server.xml:
• 在<GlobalNamingResources></GlobalNamingResources>之间定义resource
<!-- Oracle --> <Resource name="testDataSource" auth="Container" type="javax.sql.DataSource" factory="org.apache.commons.dbcp.BasicDataSourceFactory" url="jdbc:oracle:thin:@127.0.0.1:1521:Yourdatabase" driverClassName="oracle.jdbc.OracleDriver" username="Yourusername " password="YourPassword" maxWait="3000" maxIdle="100" maxActive="10"/> <!-- DB2 --> <Resource name="testDataSource" auth="Container" type="javax.SQL.DataSource" factory="org.apache.commons.dbcp.BasicDataSourceFactory" url="jdbc:db2://127.0.0.1:50000/ Yourdatabase " driverClassName="com.ibm.db2.jcc.DB2Driver" username="Yourusername " password="YourPassword" maxWait="3000" maxIdle="100" maxActive="10"/> <!-- MSSQL --> <Resource name="testDataSource" auth="Container" type="javax.sql.DataSource" factory="org.apache.commons.dbcp.BasicDataSourceFactory" url="jdbc:jtds:sqlserver://127.0.0.1:1433/ Yourdatabase " driverClassName="net.sourceforge.jtds.jdbc.Driver" username="Yourusername " password="YourPassword" maxWait="3000" maxIdle="100" maxActive="10"/>
• 在<Host></Host>之间定义context
<Context path="" docBase="./cardzhou" debug="0" privileged="true" reloadable="true"> <ResourceLink global="testDataSource" name="testDataSource" type="javax.sql.DataSource"/> </Context>
实例197 MySQL数据库的分页形式
在Java中实现分页的方法有很多,可以用JSP实现,也可以用Servlet来实现。本实例将演示如何利用MySQL数据库自带的分页语句来实现分页操作。
技术要点
MySQL数据库的分页的技术要点如下:
• Preparedstatement()的使用。
• 元数据类ResultSetMetaData的用法。
实现步骤
(1)新建一个类名为QuerySql.java。
(2)代码如下所示。
封装查询SQL的Java类:QuerySql.java。
package chp19; import java.sql.*; import java.util.Hashtable; import java.util.Vector; public class QuerySQL { //传入一个查询SQL语句然后将结果放入Vector中。以字段名为Key,并通过toUpperCase()将字段 名全部转成大字,像下面这样才能取到正确的值:hash.get("FIELD"); public static Vector getQuery(String querySql) { Connection connection = getConnection(); PreparedStatement statement = null; ResultSet resultset = null; Vector vector = new Vector(); try { connection.setReadOnly(true); statement = connection.prepareStatement(querySQL); resultset = statement.executeQuery(); ResultSetMetaData resultsetmetadata = resultset.getMetaData(); int i = resultsetmetadata.getColumnCount(); String as[] = new String[i]; for (int j = 1; j <= i; j++) { as[j - 1] = (resultsetmetadata.getColumnName(j)).toUpperCase(); } //所有的key值均为字段的大写 Hashtable hashtable; for (; resultset.next(); vector.addElement(hashtable)) { hashtable = new Hashtable(); for (int k = 1; k <= i; k++) { Object obj = resultset.getObject(k); String s = as[k - 1]; if (obj != null) hashtable.put(s, obj); else hashtable.put(s, ""); } } } catch (SQLException e) { e.printStackTrace(); } finally { try { if (resultset != null) resultset.close(); } catch (SQLException e) { } try { if (statement != null) statement.close(); } catch (SQLException e) { } try { if (connection != null) connection.close(); } catch (SQLException e) { } } return vector; } //本方法的使用应该注意,只能使用统计函数count(*) public static int getInt(String querySql) { int temp = 0; Connection conn = getConnection(); PreparedStatement statement = null; ResultSet rst = null; try { statement = conn.prepareStatement(querySql); rst = statement.executeQuery(); while (rst.next()) { temp = rst.getInt(1); } } catch (SQLException e) { System.out.println("出现异常:\n" + "QuerySql.getInt(" + querySql + ")\n"); System.out.println("下面是详细信息:\n"); e.printStackTrace(); } finally { try { if (rst != null) rst.close(); } catch (SQLException e) { } try { if (statement != null) statement.close(); } catch (SQLException e) { } try { if (conn != null) conn.close(); } catch (SQLException e) { } } return temp; } public static Connection getConnection() { Connection con = null; try { Class.forName("com.mysql.jdbc.Driver"); con = DriverManager.getConnection( "jdbc:mysql://localhost:3306/myuser", "root", "root"); } catch (Exception e) { e.printStackTrace(); } return con; } }
进行分页处理的JavaBean:Page_JavaBean.java。
package chp19; import java.util.Vector; public class Page_JavaBean { private int curPage; //当前第几页 private int maxPage; //共有多少页 private int maxRowCount; //总共多少行 private int rowsPerPage = 10; //每页有多少行,默认为10行 private Vector vector = new Vector(); //用来存放最后的查询结果 private String countSql = ""; //统计总的查询结果数目的SQL private String selectSql = ""; //查询SQL语句 private String formName = "item2"; //form表单的名字,默认为item public void setFormName(String formName) { this.formName = formName; } public String getFormName() { return this.formName; } public void setRowsPerPage(int rowsPerPage) { this.rowsPerPage = rowsPerPage; } public int getRowsPerPage() { return this.rowsPerPage; } public void setCurPage(int curPage) { this.curPage = curPage; } public int getCurPage() { return this.curPage; } //设置查询总数的SQL语句,当然不是必需的 public void setCountSql(String countSql) { this.countSql = countSql; } public String getCountSql() { return this.countSql; } public void setSelectSql(String selectSql) { this.selectSql = selectSql; } public String getSelectSql() { return this.selectSql; } //返回查询结果的总记录数 public void setMaxRowCount() { this.maxRowCount = QuerySql.getInt(getCountSql()); } public int getMaxRowCount() { return this.maxRowCount; } //根据总数计算总共有多少页 public void setMaxPage() { this.maxPage = (this.maxRowCount % this.rowsPerPage == 0) ? this.maxRowCount / this.rowsPerPage : this.maxRowCount / this.rowsPerPage + 1; } public int getMaxPage() { return this.maxPage; } //返回查询的结果 //修改这个方法,根据不同数据库的特点实现分页显示 public Vector getResult() { setMaxRowCount(); //总结果数 setMaxPage(); //总的页数 //select * from table limit n,m 查询出从n开始的m条记录 String objectSql = this.selectSql + " limit " + (getCurPage() - 1) * getRowsPerPage() + " , " + getRowsPerPage(); Vector vector = QuerySql.getQuery(objectSql); return vector; } //修改这个方法可以改变分页的显示样式 public String getPage() { StringBuffer sbf = new StringBuffer(); //首先输出页面中js实现的页面跳转 sbf.append("<script languange=\"javascript\">").append( "function Jumping(){").append( " document." + getFormName() + ".submit();").append(" return;") .append("}").append("function gotoPage(pagenum){").append( " document." + getFormName() + ".jumpPage.value=pagenum;").append( " document." + getFormName() + ".submit();").append( " return;").append(")").append("</script>"); //在这里可以改变分页显示的样式 sbf.append("<center><table>").append("<tr>").append("<td>").append( "每页" + getRowsPerPage() + "行 ").append( "共" + getMaxRowCount() + "行 ").append( "第" + getCurPage() + "页 ") .append("共" + getMaxPage() + "页").append("<br>").append(""); if (getCurPage() == 1) { sbf.append(" 首页 上一页"); } else { sbf.append("<a href=\"javascript:gotoPage(1)\">首页</a>").append( " <a href=\"javascript:gotoPage(" + (getCurPage() - 1) + ")\">上一页</a>"); } if (getCurPage() == getMaxPage()) { sbf.append(" 下一页 尾页"); } else { sbf.append( "<a href = \"javascript:gotoPage(" + (getCurPage() + 1) + ")\">下一页</a>").append( " <a href = \"javascript:gotoPage(" + getMaxPage() + ")\">尾页</a>"); } sbf.append(" 转到第").append( "<select name=\"jumpPage\" onchange=\"Jumping()\">"); for (int i = 1; i <= getMaxPage(); i++) { if (i == getCurPage()) { sbf.append("<option selected value=\"" + i + "\">" + i + "</option>"); } else { sbf.append("<option value=\"" + i + "\">" + i + "</option>"); } } sbf.append("</select>页").append("</td>").append("</tr>").append( "</table></center>").append(""); return sbf.toString(); } }
用于页面显示的JSP:page.jsp。
<%@ page contentType="text/html;charset=gb2312"%> <%@ page import="chp19.*"%> <html> <head> <title>MySQL分页实例</title> </head> <% String countSql = "select count(*) from date_one "; String jumpPage = (request.getParameter("jumpPage") == null) ? "1" : request.getParameter("jumpPage"); System.out.println(jumpPage + " jumpPage"); String objSql = "select * from date_one "; Page_JavaBean pjb = new Page_JavaBean(); pjb.setRowsPerPage(5); //设置每页显示多少行记录 pjb.setCurPage(Integer.parseInt(jumpPage)); //设置要跳转到哪一页 pjb.setCountSql(countSQL); //设置统计所有记录数的SQL pjb.setSelectSql(objSQL); //设置要查询的SQL pjb.setFormName("item2"); //设置form表单的名字 java.util.Vector vector = pjb.getResult(); //将查询结果放在vector中 %> <body> <form name="item2" method="post" action="page.jsp"> <table border="0" width="70%" align="center"> <tr bgcolor="#87CEFA"> <th> <FONT style='FONT-SIZE:20px;COLOR:#0000CD'>序号</FONT> </th> <th> <FONT style='FONT-SIZE:20px;COLOR:#0000CD'>姓名</FONT> </th> <th> <FONT style='FONT-SIZE:20px;COLOR:#0000CD'>日期</FONT> </th> <th> <FONT style='FONT-SIZE:20px;COLOR:#0000CD'>E-Mail</FONT> </th> <th> <FONT style='FONT-SIZE:20px;COLOR:#0000CD'>主题</FONT> </th> <th> <FONT style='FONT-SIZE:20px;COLOR:#0000CD'>留言</FONT> </th> </tr> <% int start = (Integer.parseInt(jumpPage) - 1) * pjb.getRowsPerPage() + 1; System.out.println(start + " start"); String name = ""; String date = ""; String Email = ""; String Topic = ""; String message = ""; out .println("<CENTER><FONT style='FONT-SIZE:30px;COLOR: #87CEFA'><B>JavaBean实现MySQL的分页实例</B></FONT></CENTER>"); for (int i = 0; i < vector.size() && i < 5; i++) { java.util.Hashtable hash = (java.util.Hashtable) vector .elementAt(i); name = (String) hash.get("NAME"); //通过字段名取数据 date = hash.get("DATE").toString(); Email = (String) hash.get("EMAIL"); Topic = (String) hash.get("TOPIC"); message = (String) hash.get("MESSAGE"); %> <tr bgcolor="#FFFFF0"> <td> <FONT style='FONT-SIZE:18px;COLOR:#6B8E23'><%=start + i%> </FONT> </td> <td> <FONT style='FONT-SIZE:18px;COLOR:#6B8E23'><%=name%> </FONT> </td> <td> <FONT style='FONT-SIZE:18px;COLOR:#6B8E23'><%=date%> </FONT> </td> <td> <FONT style='FONT-SIZE:18px;COLOR:#6B8E23'><%=Email%> </FONT> </td> <td> <FONT style='FONT-SIZE:18px;COLOR:#6B8E23'><%=Topic%> </FONT> </td> <td> <FONT style='FONT-SIZE:18px;COLOR:#6B8E23'><%=message%> </FONT> </td> </tr> <% } %> </table> <%=pjb.getPage()%> </form> </body> </html>
(3)运行结果:打开一个浏览器,在地址栏中输入http://localhost:8080/My_Servlet/date/page.jsp得到的界面,如图19-20所示。
(4)出现上面的页面后,单击“下一页”按钮,就可以查看下一页的内容,如图19-21所示。
图19-20 进入Tomcat后出现的页面
图19-21 单击“下一页”按钮出现的页面
源程序解读
1. QuerySQL类
此类装查询SQL的Java类。getQuery()方法传入一个查询SQL语句然后将结果放入Vector中。以字段名为Key,并通过toUpperCase()将字段名全部转成大字;getInt()用于统计。
2. Page_JavaBean类
此类的主要作用是封装了进行分页操作的各种属性的get和set方法,例如,curPage表示当前第几页,maxPage表示共有多少页,rowsPerPage表示每页有多少行等属性的set和get方法。
3. page.jsp
做数据分页的显示,当单击“上一页”或“下一页”按钮时,跳转到相应页面。
实例198 连接ODBC数据库的Apple程序
本实例是通过连接ODBC数据库,并实现Applet小程序的,可以通过输入的SQL语句来对ODBC数据库进行查询操作。
技术要点
连接ODBC数据库的Apple程序的技术要点如下:
• ODBC数据库的操作语句。
• ODBC数据库的驱动。
• ResultSet接口的用法。
• Statement类的使用。
实现步骤
(1)新建一个类名为JDBC_ODBC.java。
(2)代码如下所示:
package chp19; import java.sql.*; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; public class JDBC_ODBC extends JFrame { private Connection connection; //数据库变量定义 private Statement statement; private ResultSet resultSet; private JTable table; private JTextArea area; private JButton button; public JDBC_ODBC() { super("输入SQL语句,按查询按钮查看结果"); //输出标题 try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); //加载odbc数据驱动 connection = DriverManager .getConnection("jdbc:odbc:myodbc", "", ""); } catch (ClassNotFoundException cnfex) { System.out.println("装载 JDBC/ODBC 驱动程序失败。"); cnfex.printStackTrace(); System.exit(1); } catch (SQLException sqlex) { System.out.println("连接数据库失败"); sqlex.printStackTrace(); System.exit(1); } String test = "select * from staff"; area = new JTextArea(test, 4, 30); button = new JButton("查询"); button.addActionListener(new ActionListener() { //定义Button监听 public void actionPerformed(ActionEvent e) { getTable(); } }); JPanel topPanel = new JPanel(); topPanel.setLayout(new BorderLayout()); //创建布局界面 topPanel.add(new JScrollPane(area), BorderLayout.CENTER); topPanel.add(button, BorderLayout.SOUTH); table = new JTable(); Container ct = getContentPane(); ct.setLayout(new BorderLayout()); ct.add(topPanel, BorderLayout.NORTH); ct.add(table, BorderLayout.CENTER); getTable(); setSize(400, 300); show(); //显示窗口 } private void getTable() { try { String query = area.getText(); //从文本域中获取内容 statement = connection.createStatement(); resultSet = statement.executeQuery(query); //执行SQL语句 QueryResultSet(resultSet); //显示查询结果 } catch (SQLException sqlex) { sqlex.printStackTrace(); } } private void QueryResultSet(ResultSet row) throws SQLException { boolean moreRecords = row.next(); //定位到第一条记录 if (!moreRecords) { //如果没有记录,则显示消息 JOptionPane.showMessageDialog(this, "无记录"); setTitle("无记录显示"); return; } Vector columnHeads = new Vector(); //声明向量对象并实例化向量 Vector vec = new Vector(); try { ResultSetMetaData rsm = row.getMetaData(); for (int i = 1; i <= rsm.getColumnCount(); ++i) columnHeads.addElement(rsm.getColumnName(i));//获取字段的名称 do { vec.addElement(getNextRow(row, rsm)); //获取记录集 } while (row.next()); //显示查询结果 table = new JTable(vec, columnHeads); JScrollPane scroller = new JScrollPane(table); Container c = getContentPane(); c.remove(1); c.add(scroller, BorderLayout.CENTER); c.validate(); } catch (SQLException sqlex) { sqlex.printStackTrace(); } } private Vector getNextRow(ResultSet row, ResultSetMetaData rsm) throws SQLException { Vector currentRow = new Vector(); for (int i = 1; i <= rsm.getColumnCount(); ++i) currentRow.addElement(row.getString(i)); return currentRow; //返回一条记录 } public void disconnect() { try { connection.close(); //关闭数据库连接 } catch (SQLException sqlex) { System.out.println("关闭数据库连接失败"); sqlex.printStackTrace(); } } public static void main(String args[]) { final JDBC_ODBC ben = new JDBC_ODBC(); ben.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { ben.disconnect(); System.exit(0); } }); } }
(3)运行结果如图19-22所示。
(4)若想精确查询,可以在文本域中输出相应的SQL,然后单击“查询”按钮,如图19-23所示。
图19-22 查询全部的记录
图19-23 查询带条件的记录
源程序解读
(1)配置ODBC数据源:在Windows开始菜单中依次选择“控制面板”→“管理工具”→“数据源”,选择添加Access数据源,在添加的时候要记住数据源的名称,它可用于创建数据连接,与MySQL中的数据库的作用类似。
(2)通过以下代码实现JDBC-ODBC桥连接数据库,其中myodbc为数据源的名称。
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); //加载odbc数据驱动 connection = DriverManager .getConnection("jdbc:odbc:myodbc", "", "");
(3)执行SQL语句。在Access数据库中,是可以支持列名为汉字的表结构,所以在创建表的时候不用刻意地用英文来创建表头,例如,在表staff中查询性别为女的员工的信息时, SQL语句可以写成:
select * from staff where 性别='女'