第七章 对数据库的操作 本章介绍页面与数据库之间的通讯.根据需要,将页面数据保存到数据库中,或者将数据库中的数据展现在页面上,或者执行数据事务处理:数据查询和数据更新。 学习目标 理解数据源 熟悉创建数据源的步骤 理解JDBC-ODBC桥接器 熟悉数据查询技术 熟悉数据更新技术
7.1 数据源 java应用程序与数据库的连接方式有四种,本书中采用JDBC-ODBC方式连接数据库.这种连接方式分三个步骤:首先,创建一个数据源,其次,加载JDBC-ODBC驱动程序,第三步,建立一个到数据库的连接。 数据源是对数据库的一种映射。我们可以把数据源理解为数据库本身,一个数据源对应一个数据库。
本书以Microsoft Access数据库为例。假设已创建了数据库db. mdb(保存在E:\db 本书以Microsoft Access数据库为例。假设已创建了数据库db.mdb(保存在E:\db.mdb目录下)。假设采用的是windows2000操作系统,我们为该数据库创建数据源的步骤如下:
1.在"管理工具"中选择"ODBC数据源" ,如图7-1所示。
2.双击"ODBC数据源"图标,出现如图7-2所示界面。该图显示了"User DSN"选项卡中的已有数据源的名称。
3.选择"System DSN"选项卡,单击"Add"按钮,增加新的数据源,如图7-3所示。此对话框,为新增加的数据源选择驱动程序。 图7-3 选择驱动程序
4.因为要访问 Microsoft Access数据库,选择"Micosoft Access Driver( 4.因为要访问 Microsoft Access数据库,选择"Micosoft Access Driver(*.mdb) "选项,单击"完成"按钮(为数据源选择了驱动程序),出现了设置数据源具体项目的对话框,如图7-4所示。 图7-4 配置数据源
5.在"数据源名(N) "后的文本框中为数据源起一个名字,这里起的名字是grade(可以起别的名字),点击 "选择(S)…"按钮,为数据源grade选择数据库,这里选择的数据库是E:\db.mdb。配置情况如图7-5所示。 图7-5 配置情况
6.最后点击 "确定" 按钮,完成了数据源的配置,出现对话框如图7-6所示。在该对话框中,点击"确定" 按钮,完成数据源grade的配置。 图7-6 配置完成
7.2 JDBC-ODBC桥接器 JDBC-ODBC桥接器就是把应用程序与数据源连接起来的驱动程序。因此,创建了数据源以后,还要加载JDBC-ODBC桥接器,即加载驱动程序。
下面是加载驱动程序的方式: try{ Class.forName("sun .jdbc.odbc.JdbcOdbcDriver"); } chatch(ClassNotFoundException e) {} 通过Class类的静态方法forName(String driver) 加载JDBC-ODBC桥接器。
7.3 数据库连接 创建了数据源,加载了驱动程序,应用程序还是不能连接到数据库。应用程序要访问数据库,还必须创建一个到数据库的连接。即创建一个连接对象。下面是创建连接对象的静态方法: public static Connection getConnection(String url,String user, String password) throws SQLException
假设数据源名是 “grade”,用户登录系统的帐号是user/password。下面是获得连接对象的方法: Connection conn=DriverManager.getConnection(“jdbc:odbc:grade”, user, password) ; 或者 Connection conn=DriverManager.getConnection(“jdbc:odbc:grade”) ; getConnection()方法是DriverManager类的一个静态方法。对于Access数据库连接,可以不要帐号参数就能建立连接对象。
7.4 数据库事务处理 一般来说,数据库事务处理分两种:一种是数据查询,第二种是数据更新。数据更新包括数据插入、修改和删除。
假设已经连接到某数据库,创建的连接对象是conn。则数据查询和数据更新步骤如下。 1.数据查询 通过以下两个步骤,获得查询结果集rs。 (1) 创建语句对象 Statement stmt=conn.createStatement(int type,int concurrency ) ; (2)获得查询结果集 String sql="select * from tablename where expression" ; //SQL查询字符串 ResultSet rs=stmt.executeQuery(sql); // 获得结果集rs
2.数据更新 通过以下两个步骤,实现数据更新。 (1).创建语句对象 Statement stmt=conn.createStatement(int type,int concurrency ) ; (2).执行更新 String sql="sqlStatement " ; //插入或修改或删除SQL字符串 int number=stmt.executeUpdate(sql) ; //执行更新操作
说明:在数据查询、数据更新事务中,一般采用无参的createStatement()方法创建语句对象。如果事务是随机查询、游动查询和用结果集更新数据库,则应采用createStatement(int type,int concurrency )方法创建语句对象。下面是对该方法参数的说明。
type 的取值决定滚动方式,即结果集中的游标是否能上下滚动。取值如下: ResultSet.TYPE_FORWORD_ONLY 结果集的游标只能向下滚动。 ResultSet.TYPE_SCROLL_INSENSITIVE 结果集的游标可以上下移动,当数据库变化时,当前结果集不变。 ResultSet.TYPE_SCROLL_SENSITIVE 返回可滚动的结果集。当数据库变化时,当前结果集同步改变。 concurrency的取值决定是否能用结果集更新数据库。concurrency取值: ResultSet.CONCUR_READ_ONLY 不能用结果集更新数据库中的表。 ResultSet.CONCUR_UPDATABLE 能用结果集更新数据库中的表。
我们可以使用同一个Statement对象来执行查询和更新操作(修改/添加/删除),但是,需要注意,用Statement对象获取结果集的操作,必须在用Statement对象执行更新的操作之前,否则,执行更新的操作会破坏Statement对象获取的结果集。
7.5 数据查询 按照查询的方式不同,将查询分为顺序查询、游动查询、随机查询、参数查询、排序查询、使用通配符查询。
7.5.1 顺序查询 这种查询中,获得的结果集(ResultSet对象)中的游标只能一行行地向下移动。既不能向上移动游标, 也不能跳行移动游标。 ResultSet对象由若干行组成。ResultSet对象一次只能看到一个数据行,使用next()方法,使游标移到下一行记录。 ResultSet对象可以用字段索引(第一列是1,第二列是2等等)为参数,获得对应的字段值(记录中的数据项),如方法getXxx(int columnIndex);也可以用字段名为参数,获得对应的字段值(记录中的数据项),如方法getXxx(String columnName)。ResultSet对象的常用方法如表7-1所示。
表7-1 ResultSet类的常用方法 返回类型 方法名称 boolean next() byte getByte(int columnIndex) Date getDate(int columnIndex) Double getDouble(int columnIndex) Float getFloat(int columnIndex) int getInt(int columnIndex) Long getLong(int columnIndex) String getString(int columnIndex)
byte getByte(String columnName) Date getDate(String columnName) Double getDouble(String columnName) Float getFloat(String columnName) int getInt(String columnName) Long getLong(String columnName) String getString(String columnName)
程序结构: 程序算法: [例子7.1]查询英语成绩及格的学生。学生成绩保存在表(students)中,表结构为 sudents(number,name,math,english,phics)。在db.mdb库中创建表students。 程序结构: 程序名:ex7_1.jsp 程序算法: 1.分别声明连接类型,语句类型,结果集类型的变量:con/sql/rs 。 2.加载驱动程序"sun.jdbc.odbc.JdbcOdbcDriver"。 3.获取连接对象:con 4.获取语句对象:sql 5.获取结果集对象:rs 6.输出表头 7.输出结果集(rs)中的所有记录
7.5.2 游动查询 有时,我们需要在结果集中前后移动游标,以便获取某条记录。这时,我们必须返回一个可滚动的结果集。为了获取可滚动的结果集,我们必须使用下述方法先获得一个Statement对象: Statement stmt= conn.createStatement(int type,int concurrency ) ; 通过上述Statement对象获得的结果集是可滚动结果集。可滚动结果集(ResultSet)还可用到的方法如下:
public boolean previous() 将游标向上移动,该方法返回boolean型数据,当移动结果集第一行之前时返回false。 public void beforeFirst() 将游标移到结果集的初始位置,即在第一行之前。 public void afterLast() 将游标移到结果集最后一行之后 public void first() 将游标移到结果集的第一行。 public void last() 将游标移到结果集的最后一行。 public boolean isAfterLast() 判断游标是否在最后一行之后。 public boolean isBeforeFirst() 判断游标是否在第一行之前。
public boolean isFirst() 判断游标是否指向结果集的第一行。 public boolean isLast() 判断游标是否指向结果集的最后一行。 public int getRow() 得到当前游标所指向的行号。行号从1开始,如果结果集没有行,返回0。 public boolean absolute(int row) 将游标移到参数row指定的行号。 说明:如果row取负值,就是倒数的行数。asolute(-1)表示移到最后一行,asolute(-2)表示移到倒数第二行。当移到第一行前面或最后一行的后面时,该方法返回false。
[例子7.2]从表(students)的最后一行开始,以逆序方式输出记录。然后单独输出物理表中的第5条记录。 问题分析: 从题目要求可知,结果集中的游标必须能上下游动,因此,必须用带参数的createStatement(int type,int concurrency )方法创建Statement类型的对象。 程序结构: 程序名:ex7_2.jsp
程序算法: 1. 分别声明连接类型,语句类型,结果集类型的变量: con/sql/rs 。 2. 加载驱动程序"sun.jdbc.odbc.JdbcOdbcDriver"。 3. 获取连接对象:con 4. 获取语句对象:sql 5. 获取结果集对象:rs 6. 游标移到最后一行 7. 获取最后一行的行号 8. 输出记录数 9. 输出表头 10.将游标移动到最后一行之后 11.逆序输出输出结果集(rs)中的所有记录(从表的最后一行开始输出记录) 12.将游标移到第5条记录 13.输出第5条记录。
7.5.3 随机查询 [例子7.3]从学生表(students)中随机抽取4条记录,并计算4条记录的数学成绩的平均值。 问题分析: 产生1到num之间的随机数计算公式如下: int i=(int)(Math.random()*num+1); i的值是1到num之间的随机数。在程序中,根据该随机数,把游标移到相应的行,并输出该行。 程序结构: 程序名:ex7_3.jsp
程序算法: 1.分别声明连接类型,语句类型,结果集类型的变量:con/sql/rs 。 2.加载驱动程序"sun.jdbc.odbc.JdbcOdbcDriver"。 3.获取连接对象:con 4.获取语句对象:sql 5.获取结果集对象:rs 6.游标移到最后一行 7.获取表中记录总数:number 8.向vector中添加number个Integer型对象(该对象保存了表的行号)。 9.输出表头 10.从vector中抽取四个数字对象,以每个数字对象为行号,输出该行号对应的记录。 11.求出4条记录数学成绩的平均值,并输出平均值。
7.5.4参数查询 [例子7.4]对学生表(students)分别按姓名查询和按成绩查询。 问题分析: 由三个页面完成此功能。一个页面提供查询输入界面;一个页面按姓名查询数据;一个页面按成绩查询数据。 程序结构: ex7_4.jsp页面提供查询条件输入窗口,按姓名查询时,其姓名参数被提交给byname.jsp页面,按成绩查询时,其成绩参数被提交给byscore.jsp页面。byname.jsp页面查询出指定姓名的学生;byscore.jsp页面查询出指定成绩的学生。三个页面的交互关系如图7-7所示。
1.创建一个表单,该表单包含一个文本框,文 本框用于录入姓名。 2.创建一个表单,该表单包含四个文本框,四 个文本框用于录入英语成绩和 ex7_4.jsp 页面 byname.jsp byscore.jsp 按姓名查询 按分数查询 图7-7 页面交互关系 程序算法: ex7_4.jsp: 1.创建一个表单,该表单包含一个文本框,文 本框用于录入姓名。 2.创建一个表单,该表单包含四个文本框,四 个文本框用于录入英语成绩和 3.数学成绩的查询范围。
byname.jsp: 1.获取表单中的姓名参数:name 2.分别声明连接类型,语句类型,结果集类型的变量:con/sql/rs 。 3.加载驱动程序"sun.jdbc.odbc.JdbcOdbcDriver"。 4.获取连接对象:con 5.获取语句对象:sql 6.构造SQL语句字符串:condition 7.获取结果集对象:rs 8.输出表头 9.输出结果集(rs)中的所有记录
byscore.jsp: 1.获取表单中的英语和数学成绩范围:englishmax/englishmin; mathmax/mathmin 2.分别声明连接类型,语句类型,结果集类型的变量:con/sql/rs 。 3.加载驱动程序"sun.jdbc.odbc.JdbcOdbcDriver"。 4.获取连接对象:con 5.获取语句对象:sql 6.构造SQL语句字符串:condition 7.获取结果集对象:rs 8.输出表头 9.输出结果集(rs)中的所有记录
7.5.5排序查询 程序结构: 程序算法: 可以在SQL语句中使用order by子句对查询记录进行排序。 [例子7.5] 对学生表(students)分别按姓名、英语成绩、总成绩进行排序,输出成绩。 程序结构: ex7_5.jsp页面将排序方式提交给byorder.jsp页面;byorder.jsp页面根据排序方式,查询数据并输出。 程序算法: ex7_5.jsp: 创建一个表单,包含三个单选按钮,每个单选按钮代表一种排序方式。
byorder.jsp: 1.从表单中获取排序方式,保存到变量name中。 2. 分别声明连接类型,语句类型,结果集类型的变量:con/sql/rs 。 3.加载驱动程序"sun.jdbc.odbc.JdbcOdbcDriver"。 4.获取连接对象:con 5.获取语句对象:sql 6.构造SQL语句字符串(condition),该串指定了记录的排序方式。 7.获取结果集对象:rs 8.输出表头 9.输出结果集(rs)中的所有记录
7.5.6 使用通配符查询 可以用SQL语句操作符 "like " 进行模式匹配。用"%"代替一个或者多个字符,用一个下划线" _"代替一个字符。例如,下面的SQL语句查询姓氏为" 王"的记录。 Select * from students where name like ‘王%’
[例子7.6]按姓氏查询表(students)。 程序结构: ex7_6.jsp页面将姓氏参数提交给byname2.jsp页面;byname2.jsp页面按姓氏通配符查询数据。两页面的交互关系如图7-8所示。 ex7_6.jsp 页面 byname2.jsp 查询 图7-8 页面交互关系 程序算法: ex7_6.jsp: 创建一个表单,该表单包含一个文本框,可在此文本框中录入姓氏参数。
byname2.jsp 1.获取姓名参数:name 2.创建查询通配字符串:name="%"+name+"%" 3.分别声明连接类型,语句类型,结果集类型的变量:con/sql/rs 。 4.加载驱动程序"sun.jdbc.odbc.JdbcOdbcDriver"。 5.获取连接对象:con 6.获取语句对象:sql 7.构造SQL语句字符串:condition 8.获取结果集对象:rs 9.输出表头 10.输出结果集(rs)中的所有记录
7.6 数据更新 7.6.1 修改数据 数据更新操作包括修改数据、添加数据、删除数据。 7.6 数据更新 数据更新操作包括修改数据、添加数据、删除数据。 7.6.1 修改数据 修改数据是根据表中某一关键字,修改满足某些条件的记录。 [例子7.7]以姓名为关键字,修改表(students)中的某些数据。 程序结构: ex7_7.jsp页面将要修改的数据提交给newResult.jsp页面;newResult.jsp页面执行数据修改,并将修改后的表数据显示出来。两页面的交互关系如图7-9所示。
ex7_7.jsp 页面 newResult.jsp 执行修改 图7-9 页面交互关系 程序算法: ex7_7.jsp 1.创建表单,该表单包含4个文本框,分别用于录入关键字姓名(name),数学成绩(math),英语成绩(english),物理成绩(physics)。 2.输出修改前表中的数据。
newResult.jsp: 1.从表单中获取关键字姓名:name 2.从表单中获取修改后的成绩:(newMath/newEnglish/newPhysics) 3.分别声明连接类型,语句类型,结果集类型的变量:con/sql/rs 。 4.加载驱动程序"sun.jdbc.odbc.JdbcOdbcDriver"。 5.获取连接对象:con 6.获取语句对象:sql 7.构造三个SQL语句字符串:condition1/ condition2/ condition3 8.执行修改操作 9.显示修改后的表的记录。
7.6.2添加数据 [例子7.8] 向表(students)中添加记录。 程序结构: ex7_8.jsp页面将录入的数据提交给newDatabase.jsp页面;newDatabase.jsp页将数据添加到表(students)中,并显示添加数据后表中的记录。两个页面的交互关系如图7-10所示。 添加记录 ex7_8.jsp 页面 newDatabase.jsp 页面 图7-10 页面交互关系
程序算法: ex7_8.jsp: 1. 创建一个表单,包含5个文本框,分别用于录入学号(number),姓 名(name),数学(math),英语(english),物理(physics)成绩。 2.输出添加记录前表(students)中的记录。 newDatabase.jsp: 1.从表单中获取录入的数据分别保存到以下变量中:number/name/m/e/p。 2.分别声明连接类型,语句类型,结果集类型的变量:con/sql/rs 。 3.加载驱动程序"sun.jdbc.odbc.JdbcOdbcDriver"。 4.获取连接对象:con 5.获取语句对象:sql 6.构造SQL插入语句字符串:condition。 7.执行添加操作 8.输出添加记录后的表中全部数据。
7.6.3删除数据 [例子7.9] 以学号为关键字,删除表(students)中的某些数据。 程序结构: ex7_9.jsp页面将要删除的关键字值提交给delete.jsp页面;delete.jsp页面根据关键字的值,删除表中的相应记录。两个页面的交互关系如图7-11所示。 ex7_8.jsp 页面 delete.jsp 删除记录 图7-11 页面交互关系
程序算法: ex7_9.jsp: 1.创建一个表单,该表单包含一个文本框(接受学号关键字值的录入)。 2 .输出删除数据前表的数据。 delete.jsp: 1 .从表单获取关键字段学号的值:number。 2.分别声明连接类型,语句类型,结果集类型的变量:con/sql/rs 。 3.加载驱动程序"sun.jdbc.odbc.JdbcOdbcDriver"。 4.获取连接对象:con 。 5.获取语句对象:sql 。 6.构造SQL删除语句字符串:deleteALL 。 7.执行删除操作 。 8.输出删除记录后的表数据。
7.7 分页显示记录 当要显示的数据记录太多时,必须采用分页技术。这里对数据库表中的记录进行分页显示。
[例子7.10]分页显示表(students)中的数据。 问题分析: 假设要显示的总记录数为num,每页显示记录数为pageSize,则总页数(pageCount)的计算公式如下: 如果num除以pageSize的余数大于0,则总页数等于num除以pageSize的商加1。 如果num除以pageSize的余数等于0, 则总页数等于num除以pageSize的商。 即 pageCount =(num%pageSize)==0 ? (num/pageSize) : (num/pageSize+1) 如果准备显示第k页的内容,应当把游标移到第(k-1)* pageSize+1条记录处。
程序结构: 程序名:ex7_10.jsp 程序算法: 1.设置页面大小:pageSize=8 (表示每页8条记录)。 2.创建一个表单,该表单包含一文本框,在此框中输入客户想显示的记录的页码号。 3.计算表中记录总数:numRow。 4.计算分页后总页数: pageCount。 5.从表单中获取要显示记录的页码号:showPage。 6.计算showPage页的第一条记录在表中的行号:posion。 7.使游标指向posion。 8.从当前游标开始,输出pageSize条记录。
7.8 网上投票 [例子7.11]创建一个网上投票系统。 问题分析: 首先要创建两个表。一个是ip表,该表用来存放投票人的ip地址,表的结构是ip(ip);一个是candidate表,该表用来存放候选人名单及候选人得票数,表的结构是candidate(name,count)。 该系统由三个页面组成。一个页面展示投票界面;一个页面将客户投票选择保存到candidate表中,实现投票统计;一个页面显示投票结果。
程序结构: ex7_11.jsp页面将客户的投票选择提交给vote.jsp页面;vote.jsp页面将客户的投票选择保存到candidate表中,并将客户的ip地址保存到ip表中;showvote.jsp页面查看投票结果。三个页面的交互关系如图7-12所示。 ex7_11.jsp 页面 vote.jsp showvote.jsp 投票 查看投票结果 图7-12 页面交互关系
程序算法: ex7_11.jsp: 1.构造一个StringBuffer类型的空对象:nameList 2.分别声明连接类型,语句类型,结果集类型的变量:con/sql/rs 。 3.加载驱动程序"sun.jdbc.odbc.JdbcOdbcDriver"。 4.获取连接对象:con 5.获取语句对象:sql 6.获取结果集对象:rs 7.把构成表单的字符串加入nameList对象中 8.把构成表格的字符串加入nameList对象中(在表单中加入一个表格) 9.把构成表头的字符串加入nameList对象中(表格的表头) 10.把rs中的name值和单选按钮分别以字符串的格式加入nameList对 象中 11.关闭连接对象:con 12. 将nameList对象中的字符串输出,即输出表单(以表格的方式显示表单数据),该表单为客户提供投票选择(从第1到12步,创建第1个表单) 13.创建第2个表单,该表单查看投票情况
vote.jsp: 1.声明一个类变量,统计投票的总数:total 2.定义一个同步方法countTotal(),实现投票总数total加1 3.定义标识(vote),用vote标识客户是否有权投票。vote值为true,表示有权投票,vote值为false表示无权投票,给vote赋初值为true(允许投票)。. 4.从表单获取客户选择的候选人:name 5.从表单获取投票客户的ip地址 6.加载驱动程序 7.在ip表中查找是否有与本次投票客户有相同的ip地址,如果ip表中已有与本次投票客户相同的ip地址,则给标识vote赋值为false(不允许投票) 8.判断vote的值是否为true 9.如果vote的值为true(允许客户投票),则总投票数加1;候选人票数加1;投票客户的ip地址写入ip表中。如果vote的值为false(不允许投票,说明已经投过票了),提示客户"您已经投过票了"。 showvote.jsp: 将候选人得票数输出。
本章小结 本章介绍了页面与数据库之间的通讯。通过实例讲解了JSP页面对数据库的主要操作,包括:数据库的各种查询、数据录入、修改、删除等操作。
心得体会 (1)如果使用oracle数据库时,需要更改Tomcat服务器的端口为8090,因为oracle数据库使用的端口也是8080。 (2)连接数据库可以用数据库本身的驱动程序,也可以用JDBC-ODBC桥接器等方法。对于初学者建议用桥接器方法。 (3)创建连接对象时,例如”jdbc:odbc:grade”中grade并不是指数据库名,又或者是表名,而是在设置数据源时的名字。 (4)如果是采用无参的createStatement()方法创建语句对象,在查询时,游标只能够顺序向下移动。例如:while(rs.next()){ a=rs.getString(1);c=rs.getString(3);b=rs.getString(2);}那么a和c能得到数据库的信息,由于游标已经移动到第三项,所以b就不能得到数据库信息了。如果需要游标上下移动,则在创建语句对象时应采用带参数的createStatement(int type,int concurrency)方法。
心得体会 (5)使用sql语句时要注意,每项的值如果是字符型要用“”号或‘’引用,condition=“select * from Student where Stu_Name=‘张三’”; rs=sql.executeQuery(condition); (6)如果在使用sql语句时要带变量的处理, String name=“张三”; condition=“select * from Student where Stu_Name=‘”+name+”’”; rs=sql.executeQuery(condition);