第6章 数据库应用编程
第6章 数据库应用编程
-
掌握ODBC数据库连接的工作原理及配置方式。
-
掌握JDBC数据库连接的工作原理及程序结构。
-
掌握数据库存储过程、触发器、游标的程序结构。
-
掌握数据库数据Web呈现技术及MyBatis数据访问技术。
6.1 数据库连接技术
ODBC的技术背景
- 一般情况下,通常选用特定的DBMS管理系统;
- 网络环境下,开发应用时通常需要对多种数据库的实现互连和资源共享;
- 如果从一种DBMS改变到另一种DBMS,就意味着要重写应用程序;
- 而开发对应的多种应用,在不同的DBMS上运行的应用程序, 显然不可取
- Microsoft推出了开放式数据库互连技术(Open DataBase Connectivity,简写为 ODBC)。
- ——ODBC实现了应用程序对多种不同DBMS的数据库的访问
- ——ODBC定义了一套基于SQL的、公共的、与数据库无关的API(应用程 序设计接口);
- ——使每个应用程序利用相同的源代码就可访问不同的数据库系统,存取多个数据库中的数据;
ODBC的层次结构

- 客户端数据库应用程序:
- 请求与数据源的连接和会话
- 向数据源发送SQL请求
- ODBC应用程序接口: 是一种使用ODBC技术实现应用程序与数据库互连的标准接口
- ODBC驱动程序管理器: 用于管理系统中的各种驱动程序。
- 驱动程序
- 是一个用于支持 ODBC函数调用的模块,通 常是一个动态链接库DLL。
- SQL Server DBMS:用户管理数据库的管理软件
- SQL Server DB:① 用户创建的数据库; ② 用于存储用户数据、对象。
应用程序使用ODBC访问数据库的步骤
- 首先必须用ODBC管理器注册一个数据源;
- 管理器根据数据源提供的数据库位置、数据库类型及ODBC驱动程序等信息,建立起ODBC与数据连接
- 应用程序只需将数据源名提供给ODBC,ODBC就能建立起与相应数据库的连接;
- 这样,应用程序就可以通过驱动程序管理器与数据库交换信息;
- 驱动程序管理器负责将应用程序对ODBC API的调用传递给正确的驱动程序;
- 驱动程序在执行完相应的SQL操作后,将结果通过驱动程序管理器返回给应用程序
使用ODBC管理器配置数据源
首先必须在数据库厂商的官网下载与你的数据库对应的驱动程序;
例如:名字为psqlodbc_x64.msi的驱动程序支持PostgreSQL 64位数据库,安装它。
JDBC编程
JDBC(Java DataBase Connectivity,Java数据库连接)技术的简称 ,是一种用于执行SQL语句的Java API。
- 这个API由java.sql.*包中的一些类和接口组成,
- 注意:使用JDBC访问数据库需要相应数据库的JDBC驱动程序。

JDBC工作原理

JDBC程序访问数据库步骤
应用程序:Eclipse

(1)加载驱动
PostgreSQL数据库驱动程序加载语句
Class.forName("org.postgresql.Driver");
(2)建立连接
与PostgreSQL数据库建立连接:
String URL = "jdbc:postgresql://localhost:5432/testdb";
String userName = "myuser";
String passWord = "sa";
connection conn = DriverManager.getConnection(URL,userName,passWord);//调用方法,建立连接
(3)创建Statement对象
Statement stmt = conn.createStatement();
(4)执行SQL语句
主要掌握两种执行SQL语句的方法
-
executeQuery():可执行指定的静态select语句
-
返回结果集
-
例如向staff表中插入一条数据的代码即向数据库发送SQL命令,代码示例如下。
-
Statement stmt = conn.createStatement() ; String sql = "INSERT INTO public.Student (sid, sname, sex, classid)" + " VALUES ('2017001003', ’张山’, ’男’, 'C2017001')"; stmt.executeUpdate(sql);
-
-
executeUpdate() 可用来更新数据,包括插入和删除等操作;
-
返回值是一个整数,即不同步更新记录的条数。(update、insert、delete语句)。
-
Statement stmt = conn.createStatement(); String sql = "INSERT INTO public.student (sid, sname, gender, birthday, major, phone)" + " VALUES ('2017001', '张山', '男', '1998-10-10','软件工程','13602810001')"; stmt.executeUpdate(sql);
-
(5)ResultSet保存结果集
调用Statement对象的executeQuery()方法,会得到ResultSet 对象。
ResultSet 对象具有可以指向当前数据行的指针。
用ResultSet.next()方法,可以使指针指向下一行。如果next()方法返回null,则表示下一行中没有数据存在。
并且它通过一套get()方法提供了对查询结果的数据访问。(这些 get()方法可以访问当前行中的不同列)
Statement stmt = conn.createStatement();
String sql = "SELECT id, name, age FROM company";
ResultSet rs = stmt.executeQuery(sql);
(6)关闭连接
作为一种好的编程风格,不需要对象时,应该显式地关闭它们。
rs.close(); //关闭结果集对象
stmt.close(); //关闭执行对象
conn.close(); //关闭连接对象
在Eclipse下使用JDBC连接数据库
在Eclipse环境中创建应用项目,然后在项目中配置JDBC驱动程序包
在项目中创建Java类,并按照下列步骤在程序中访问数据库。
官网下载JDBC驱动程序包
在Eclipse中创建工程
在PSQL工程中创建jdbclib目录,并且将这个下载的程序包拷贝到这个目录
eclipse如何导入MySQL的JDBC驱动jar包?
eclipse如何导入MySQL的JDBC驱动jar包-百度经验 (baidu.com)
Java使用JDBC连接数据库的例子
package test1;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class Class1 {
public static void main( String args[] ){
Connection conn = null;
Statement stmt = null;
String URL = "jdbc:postgresql://localhost:5432/CAR"; //这里的CAR是首先自己要创建的数据库名,必须与数据库里已有的那个大小写一致
String userName = "postgres"; //必须用posrgres
String password = "root";
try {
Class.forName( "org.postgresql.Driver" );
conn = DriverManager. getConnection(URL , userName, password );
System.out.println( "opened database successfully");
stmt = (Statement) conn.createStatement();
/****************************更改这个car为对应的数据库名************************/
String sql = "select * from car" ; //查询数据的sql语句
stmt = (Statement) conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
System.out.println("最后的查询结果为: ");
while (rs.next()) { //判断是否还有下一个数据
/****************************更改要打印的字符,为对应的数据库的列************************/
//输出查到的记录的各个字段的值
System.out.println(rs.getString("caid") +" " +rs.getString( "caplate") +" "
+ rs.getString( "cacolor") +" " + rs.getString( "castate") );
}
stmt.close();
conn.close();
}catch ( Exception e ) {
System.err.println( e.getClass().getName()+": "+ e.getMessage() );
System.exit(0);}
}
}
当出现bug:用户 ??? 密码??? 说明数据库打不开,可能是因为大小写不一致
使用JDBC连接PostgreSQL数据库并完成一些操作
完成Student表的创建和删除,插入数据、查询数据和删除数据等操作。
package test1;
import java.sql.*; //加载Java包java.sql.*
public class Class1 {
// 创建静态全局变量
static Connection conn;
static Statement st;
public static void main(String[] args) {
createtable(); //创建数据表
insert(); //插入记录
query(); //查询记录并显示
update(); //更新记录数据
query(); //查询记录并显示
delete(); //删除记录
droptable(); //删除数据表
}
/* 创建学生表Student*/
public static void createtable() {
conn = getConnection(); // 首先要获取连接,即连接到数据库
try {
String sql = "CREATE TABLE Student" +
"( sid character(13) NOT NULL, " +
"sname character varying(30) NOT NULL, " +
"sex character(2)NOT NULL, " +
"classid character(11), "+
"CONSTRAINT sid_pkey PRIMARY KEY (sid), "+
"CONSTRAINT gCheck CHECK (sex = ANY (ARRAY[’男’, ’女’])))";
st = (Statement) conn.createStatement();// 创建Statement对象
st.executeUpdate(sql); // 执行创建表的操作
System.out.println("成功创建Student表 ");
conn.close(); //关闭数据库连接
} catch (SQLException e) {
System.out.println("创建表操作失败" + e.getMessage());
}
}
/* 删除学生表Student*/
public static void droptable() {
conn = getConnection(); // 首先要获取连接,即连接到数据库
try {
String sql = "DROP TABLE Student";
st = (Statement) conn.createStatement(); // 创建Statement对象
st.executeUpdate(sql); // 执行删除表的操作
System.out.println("成功删除Student表 ");
conn.close(); //关闭数据库连接
} catch (SQLException e) {
System.out.println("删除操作失败" + e.getMessage());
}
}
/* 插入数据记录,并输出插入的数据记录数*/
public static void insert() {
conn = getConnection(); // 首先要获取连接,即连接到数据库
try {
String sql="INSERT INTO public.Student(sid,sname,sex,classid)"+ " VALUES ('2017001003',’张山’, ’男’, C2017001')";
st = (Statement) conn.createStatement();
//创建执行SQL的Statement对象
int count = st.executeUpdate(sql); // 执行插入的SQL语句并返回插入条数
System.out.println("向Student表中插入 " + count + " 条数据");
conn.close(); //关闭数据库连接
} catch (SQLException e) {
System.out.println("插入数据失败" + e.getMessage());
}
}
/* 更新符合要求的记录,并返回更新的记录数目*/
public static void update() {
conn = getConnection(); //同样要获取连接,即连接到数据库
try {
String sql = "update Student set sid='2017001012 where sname = ’张山’";
st = (Statement) conn.createStatement();
int count = st.executeUpdate(sql);// 执行更新的SQL语句,返回更新记录数
System.out.println("Student表中更新 " + count + " 条数据");
conn.close(); //关闭数据库连接
} catch (SQLException e) {
System.out.println("更新数据失败");
}
}
/* 查询数据库,输出符合要求的记录的情况*/
public static void query() {
conn = getConnection(); //同样要获取连接,即连接到数据库
try {
String sql = "select * from Student"; // 查询数据的SQL语句
st = (Statement) conn.createStatement();
ResultSet rs = st.executeQuery(sql);
System.out.println("最后的查询结果:");
while (rs.next())
{ // 判断是否还有下一个数据
// 根据字段名获取相应的值
String vsid = rs.getString("sid");
String vname = rs.getString("sname");
String vsex = rs.getString("sex");
String vclassid = rs.getString("classid");
//输出查到的记录的各个字段的值
System.out.println(vsid+" "+vname + " " +vsex + " " +vclassid);
}
conn.close(); //关闭数据库连接
} catch (SQLException e) {
System.out.println("查询数据失败");
}
}
/* 删除符合要求的记录,输出情况*/
public static void delete() {
conn = getConnection(); //同样要获取连接,即连接到数据库
try {
String sql = "delete from Student where sname = ’张山’";
st = (Statement) conn.createStatement();
int count = st.executeUpdate(sql);
// 执行SQL删除语句,返回删除数据的数量
System.out.println("Student表中删除 " + count + " 条数据\n");
conn.close(); //关闭数据库连接
} catch (SQLException e) {
System.out.println("删除数据失败");
}
}
/* 获取数据库连接的函数*/
public static Connection getConnection() {
Connection con = null; //创建用于连接数据库的Connection对象
String URL = "jdbc:postgresql://localhost:5432/testdb";
String userName = "root";
String passWord = "root";
try {
Class.forName("org.postgresql.Driver");// 加载PostgreSQL数据驱动
con = DriverManager.getConnection(URL, userName, passWord);
// 创建与数据库连接
} catch (Exception e) {
System.out.println("数据库连接失败" + e.getMessage());
}
return con; //返回所建立的数据库连接
}
}
6.2 Java Web数据库编程
Java web简介
• Java web开发运行环境
• Servlet技术
• JSP
• JavaBean
• MyBatis
一、 WEB数据库系统技术
以浏览器/服务器(B/S)模式为平台,其运行模式:
浏览器 —— Web服务器 —— 数据库
Java web
Java 的 Web 框架虽然各不相同,但遵循特定的 J2EE 规范:使用 Servlet 或者 Filter 拦截请求,使用 MVC 的思想 设计架构,使用XML 或 Annotation 实现配置,运用 Java 面向对象的特点,面向对象实现请求和响应的流程
二、Java web开发运行环境
Web 服务器主要用来接收客户端发送的请求和响应客户端请求。
JavaWeb 应用程序需要Servlet 容器,容器的主要 作用就是调用 Java 程序处理用户发送的请求,并响应指定的资源。
开发 JavaWeb 项目,Web 服务器和 Servlet 容 器是必需的,两者通常是合在一起的
数据库应用程序结构 三层架构(3-tier Architecture)
所谓三层体系架构是在客户端与数据库之间加入了一个中间件层,也叫组 件层。三层是指逻辑上的三层。三层体系的应用程序将业务规则、数据访 问、合法性校验等工作放到了中间层进行处理。通常来说,客户端不与数 据库直接进行交互,而是与中间层建立连接,再经由中间层与数据库进行 交互。
- 表现层(UI):通俗讲就是展现给用户的界面,即用户在使用一个 系统的时候他的所见所得。
- 业务逻辑层(BLL):针对具体问题的操作,也可以说是对数据层的 操作,对数据业务逻辑处理。
- 数据访问层(DAL):该层所做事务直接操作数据库,针对数据的增加、 删除、修改、查找等
数据库应用程序结构 MVC

三、 Servlet技术
Servlet 是运行在服务器端的程序
• Servlet 被 Web 服务器加载和执行
• Servlet 从客户端(通过 Web 服务器)接收请求,执行某种操作,然后返回结果。
Servlet的工作流程

四、JSP技术
JSP(JavaServer Pages)是在普通HTML中嵌入了Java程序片段
JSP网页Java程序片段可以操纵数据库、重新定向网页以及发送E-mail等,实现建立动态网站所需要的功能。
所有程序操作都在服务器端执行,网络上传送给客户端的仅是得到的结果,这样大大降低了对客户浏览器的要求,
即使客户浏览器端不支持Java,也可以访问JSP网页

五、JavaBean技术
HTML 代码与Java 代码相分离,而使用Java代码封装的类,就是一个JavaBean组件
·在JavaWeb 开发中,可以使用JavaBean组件来完成
六、MyBatis访问数据库
6.3 存储过程编程
什么是存储过程
就是C语言的函数!
创建的语法
CREATE [ OR REPLACE ] FUNCTION name()
RETURNS XXX
AS $XXX$ //$$声明实际的开始
DECLARE
-- 声明段
BEGIN
--函数体语句
END;
$XXX$ LANGUAGE lang_name; //$$ 表明代码的结束, LANGUAGE后面指明所用的编程语言
参数解释:
OR REPLACE :覆盖同名的存储过程;
RETURNS:返回值;
RETURNS TABLE:返回二维表
创建 示例
创建一个名为countRecords()的存储过程,用于统计STUDENT表的记录数。
CREATE OR REPLACE FUNCTION countRecords()
RETURNS integer
AS $count$
declare
count integer;
BEGIN
SELECT count(*) into count FROM STUDENT;
RETURN count;
END;
$count$ LANGUAGE plpgsql;
编写存储过程统计数据库原理( CID='1288264844')及应用不及格人数,代码如下:
create or replace function count_1()
returns integer as $count$
declare
count integer;
begin
select count(*) into count from GRADE
where Score<60 and CID='1288264844';
return count;
end
$count$ language plpgsql;
执行存储过程
语法形式如下:
select 存储过程名(参数);
例如: select countRecords ();
或者: select * from 存储过程名(参数);
又例如: select * from countRecords ( );
用一个存储过程调用其它存储过程,语法形式如下:
select into 自定义变量 from 存储过程名(参数);
CREATE OR REPLACE FUNCTION testExec()
returns integer AS $$
declare
rec integer;
BEGIN
select into rec countRecords();
//如果不关心countRecords()的返回值,则可用 PERFORM countRecords() 代替;
return rec;
END;
$$ LANGUAGE plpgsql;
删除存储过程
参数:
CASCADE:级联删除依赖于存储过程的对象(如触发器)。
RESTRICT:如果有任何依赖对象存在,则拒绝删除该函数;这个是缺省值。
例子:假如需要删除前面定义的存储过程 testExec()
DROP FUNCTION IF EXISTS testExec()
PL/SQL基本语法
1、声明局部变量。
变量声明的语法如下:
declare
变量名 变量类型;
例如:
declare
count intger;
rec RECORD ;//注:声明变量为记录类型,RECORD不是真正的数据类型,只是一个占位符。
2、条件语句
在PL/pgSQL中有以下三种形式的条件语句,与其他高级语言的条件语句意义相同。
1). IF-THEN
IF boolean-expression THEN
statements
END IF;
2). IF-THEN-ELSE
IF boolean-expression THEN
statements
ELSE
3). IF-THEN-ELSIF-ELSE
IF boolean-expression THEN
statements
ELSIF boolean-expression THEN
statements
ELSIF boolean-expression THEN
statements
ELSE
statements
END IF;
3、循环语句
**LOOP 语句(EXIT CONTINUE) ** 例如:
LOOP
count=count+1;
EXIT WHEN count >100;
END LOOP;
WHILE例
WHILE amount_owed > 0 AND balance > 0 LOOP
do something
END LOOP;
FOR 例
FOR i IN 1...10 LOOP
RAISE NOTICE 'i IS %', i;
END LOOP;
FOR i IN REVERSE 10...1 LOOP
do something
END LOOP;
4、遍历命令结果
FOR record_or_row IN query LOOP
statements
END LOOP ;
FOR循环可以遍历命令的结果并操作相应的数据,例如:
declare
rec RECORD ;
FOR rec IN SELECT sid , sname FROM student LOOP
raise notice ‘%-,%-’,rec.sid, rec.sname;
END LOOP;
存储过程的优缺点
1、使用存储过程的优点
- (1) 减少网络通信量
- (2) 执行速度更快
- (3) 更强的适应性
- (4) 降低了业务实现与应用程序的耦合
- (5) 降低了开发的复杂性
- (6) 保护数据库元信息
- (7) 增强了数据库的安全性
2、使用存储过程的缺点
- (1) SQL本身是一种结构化查询语言,而存储过程本质上是过程化的程序;
- 面对复杂的业务逻辑,过程化处理逻辑相对比较复杂;
- 而SQL语言的优势是面向数据查询而非业务逻辑的处理。
- (2) 更新存储过程的参数代码麻烦
- (3) 开发调试复杂
- (4) 可移植性差
6.4 触发器编程
- 掌握postgreSQL触发器创建、修改、删除的方法
一、触发器的基本概念
触发器是一种存储过程(函数),可由某操作触发自动执行。
触发器本身是一个特殊的事务单位。
触发器的特点
- 与表相关联:必须定义在表或视图上。
- 自动触发:由执行INSERT、DELETE、UPDATE操作时触发
- 不能直接调用,也不能传递或接受参数
- 是事务的一部分:触发器和触发语句作为可在触发器内回滚的单个事务。
触发器的分类
按触发的语句分为:
- INSERT触发器
- DELETE触发器
- UPDATE触发器
即用户一旦在应用中执行了这三种动作,数据库的触发器会有相应的反应。
触发器执行的次数可分:
- 语句级触发器:
- 由关键字FOR EACH STATEMENT声明
- 在触发器作用的表上执行一条SQL语句时,该触发器只执行一次
- 行级触发器:
- 由关键字FOR EACH ROW标记的触发器
- 当触发器作用的表的数据发生变化时,每变化一行就会执行一次触发器。
- 例如,在学生成绩表表执行DELETE语句删除记录时,如果删除了20条记录,将导致 DELETE触发器被执行20 次。
按触发的时间分为三类:
- BEFORE触发器:在触发事件之前执行触发器。
- AFTER触发器:在触发事件之后执行触发器。
- INSTEAD OF触发器:当触发事件发生后,执行触发器中指定的函数,而不是执行产生触发事件的SQL 语句
- 注:在表或视图上,对于INSERT、UPDATE 或 DELETE 三种触发事件,每种最多可以定义一个INSTEAD OF 触发器
触发器相关的特殊变量
- NEW
- 数据类型是RECORD。
- 对于行级触发器,它存有INSERT或UPDATE操作产生的新的数据行。
- 对于语句级触发器,它的值是NULL。
- OLD
- 数据类型是RECORD。
- 对于行级触发器,它存有被UPDATE或DELETE操作修改或删除的旧的数据行。
- 对于语句级触发器,它的值是NULL。
- TG_OP
- 数据类型是text;
- 是值为INSERT、UPDATE、DELETE 的一个字符串,它说明触发器是为哪个操作引发。
二、安装触发器的语法
CREATE TRIGGER 触发器名()
{ BEFORE | AFTER | INSTEAD OF }
ON 表名
[ FOR [ EACH ] { ROW | STATEMENT } ]
EXECUTE PROCEDURE 存储过程名 ( 参数列表 )
(1)指明所定义的触发器名
(2) BEFORE | AFTER | INSTEAD OF 指明触发器被触发的时间
(3) ON 表名 指明触发器所依附的表
(4) FOR EACH { ROW | STATEMENT } 指明触发器被触发的次数
(5) EXECUTE PROCEDURE 存储过程名 ( 参数列表 ) 指明触发时所执行的存储过程
三、创建触发器的基本步骤:
- (1)创建触发器所要依附的表
- (2)创建触发器被触发时所要执行的触发器函数,该函数的类型必须是TRINGER型,是触发器的执行函数。
- (3)创建触发器
四、创建触发器的示例
假设有stu_score表存储学生的课程成绩,其表结构如下
CREATE TABLE stu_score
(
sid character(10) NOT NULL,
cid character(10) NOT NULL,
score numeric(5,1),
CONSTRAINT stu_score_pkey PRIMARY KEY (sid, cid)
)
对表中插入如下数据:
INSERT INTO stu_score VALUES('14101','1201',85);
INSERT INTO stu_score VALUES('14101','1301',85);
为了防止非法修改stu_score表的课程成绩,创建audit_score表记录stu_score表 的成绩变化,其表结构如下:
CREATE TABLE audit_score
(
username character(20) ,
sid character(10) ,
cid character(10) ,
updatetime text ,
oldscore numeric(5,1),
newscore numeric(5,1)
)
创建触发器函数score_audit()
CREATE OR REPLACE FUNCTION score_audit() RETURNS TRIGGER AS $score_audit$
BEGIN
IF (TG_oP = 'DELETE' )THEN
INSERT INTO audit_score SELECT user,old.sid,old.cid,now(),OLD.score ;
RETURN OLD;
ELSIF (TG_oP = 'UPDATE' ) THEN
INSERT INTO audit_score SELECT user,old.sid,old.cid,now(),old.score,new.score
where old.sid=new.sid and old.cid=new.cid;
RETURN NEW;
ELSIF (TG_OP = 'INSERT')THEN
INSERT INTO audit_score SELECT user,new.sid,new.cid,now(),null,new.score;
RETURN NEw;
END IF;
RETURN NULL;END;
$score_audit$ LANGUAGE plpgsql;
接下来在stu_score表上安装触发器score_audit_triger
CREATE TRIGGER score_audit_triger
AFTER INSERT OR UPDATE OR DELETE ON stu_score
FOR EACH ROW EXECUTE PROCEDURE score_audit();
五、验证触发器的执行
将课程号为1201的课程成绩增加1分
update stu_score set score=score+1 where cid='1201';
依次查看两个表即可。
六、触发器的修改
ALTER TRIGGER old_name ON table_name RENAME TO new_name
例如:将上述定义的触发器改名为score_audit_trig
ALTER TRIGGER score_audit_trigger ON stu_score RENAME TO score_audit_trig;
七、触发器的删除
DROP TRIGGER [ IF EXISTS ] name ON table_name [ CASCADE | RESTRICT ]
主要参数说明:
(1)IF EXISTS:如果指定的触发器不存在,那么发出提示而不是抛出错误。
(2)name:要删除的触发器名。
(3)table_name:触发器定义所依附的表的名称。
(5)CASCADE:级联删除依赖此触发器的对象。
(6)RESTRICT:如果有依赖对象存在,那么拒绝删除。该参数缺省是拒绝删除。
例如:将上述触发器score_audit_trig删除,同时级联删除依赖触发器的对象。
DROP TRIGGER IF EXISTS score_audit_trig ON stu_score CASCADE;
6.5 游标编程
一、游标的基本概念
- 游标(Cursor)是一种临时的数据库对象;
- 用来存放从数据库表中查询返回的数据记录;
- 提供了从结果集中提取并分别处理每一条记录的机制;
- 游标总是与一条SQL查询语句相关联;
- 游标包括:SQL语言的查询结果,指向特定记录的指针。
二、声明游标
(1)在存储过程中游标类型的变量。例如:游标变量 refcursor;此时,游标变量还没有绑定查询语句,因此不能访问游标变量。
(2) 游标声明语法:
游标名 REFCURSOR [ ( arguments ) ] FOR SELECT;
其中arguments为由逗号分隔的参数列表,用于打开游标时向游标传递参数,类似于函数的形式参数;
返回的值存储在游标变量中。
游标声明的示例
(1)curStudent REFCURSOR FOR SELECT * FROM student;
(2)curStudentOne REFCURSOR (key integer) IS SELECT * FROM student WHERE SID = key;
三、打开游标
(1)OPEN FOR 其声明形式,例如:
OPEN curVars1 FOR SELECT * FROM student WHERE SID = mykey;
(2)OPEN FOR EXECUTE 其声明形式为:
OPEN unbound_cursor FOR EXECUTE query-string;
打开未绑定的游标变量。EXECUTE将动态执行查询字符串。
例如:
OPEN curVars1 FOR EXECUTE 'SELECT * FROM ' || quote_ident($1);
注意:$1是指由存储过程传递的第1个参数。
(3)打开一个绑定的游标,其声明形式为:
OPEN bound_cursor [ ( argument_values ) ];
仅适用于绑定的游标变量,只有当该变量在声明时包含接收参数,才能以传递参数的形式打开该游标,参数将传入到游标声明的查询语句中,例如:
OPEN curStudent;
OPEN curStudentOne (‘20160230302001’);
四、使用游标
FETCH命令从游标中读取下一行记录的数据到目标中,读取成功与否,可通过PL/SQL内置系统变量FOUND来判断。
例如:
FETCH curVars1 INTO rowvar; --rowvar为行变量
FETCH curStudent INTO SID, Sname, sex;
请注意:游标的属性列必须与 目标列的数量一致,并且类型兼容
五、关闭游标
CLOSE curVars1;
当游标数据不再需要时,需要关闭游标,以释放其占有的系统资源,主要是释放游标数据所占用的内存资源,
六、在存储过程中使用游标的示例1
1、下面例子使用不带参数的游标,查询student表的学号、学生姓名和性别
CREATE OR REPLACE FUNCTION cursorDemo ( )
returns boolean as $BODY$
Declare --定义变量及游标
unbound_refcursor refcursor; --声明游标变量
vsid varchar; --学号变量
vsname varchar; --姓名变量
vsgender varchar; --性别变量
begin --函数开始
open unbound_refcursor for execute 'select sid,sname ,sex from student'; --打开
loop --开始循环
fetch unbound_refcursor into vsid,vsname ,vsgender;--从游标中取值给变量
if found then --检查从游标中是取到数据
raise notice '% ,% ,% ' , vsid ,vsname , vsgender;
else
exit;
end if;
end loop; --结束循环
close unbound_refcursor; --关闭游标
raise notice'取数据循环结束...'; --打印消息
return true; --为函数返回布尔值
exception when others then raise exception 'error(%) ' , sqlerrm;-- sqlerrm错误代码变量,抛出异常end; --结束
$BODY$ LANGUAGE plpgsql;--规定语言
2、下面示例使用带参数的游标,从成绩表中查询分数大于某给定值的学号和课程号
create or replace function cusorDemo2 (myscore int)
returns void as
$$
declare
vstuscore stu_score%ROWTYPE; --定义与表stu_score结构相同的行变量
--定义带有一个输入参数的游标
vstucursor cursor( invalue int)
for select sid,cid,score from stu_score where score>=invalue order by sid;
begin
open vstucursor (myscore) ;--从外部传入参数给游标
loop fetch vstucursor into vstuscore;
exit when not found; --假如没有检索到数据,结束循环处理
raise notice '%,%,% ' , vstuscore.sid,vstuscore.cid,vstuscore.score;
end loop;
close vstucursor;--关闭游标
end;
$$ language plpgsql;
select cusorDemo2 (85)
6.6 嵌入式SQL编程
掌握嵌入SQL的使用方式
一、嵌入式SQL与宿主语言
- SQL语言具有很强的查询处理能力,而逻辑表达的能力很弱,界面编程能力也很弱。
- 如JAVA,C/C++等高级语言具有很强逻辑表达能力,能实现复杂的处理逻辑,同时具有较强的用户界面实现功能。
- 为了使程序语言同时具有它的优点,在JAVA、C/C++等高级语言中嵌入SQL语句,称高级语言为宿主语言。
- 由DBMS的预编译器扫描识别处理SQL语句,把SQL语句转换成主语言调用语句,以使主语言编译程序能识别它,最后由主语言的编译程序将整个源程序编译成目标码,然后连接(Link)处理生成装载模块。
二、嵌入式SQL的处理过程

三、嵌入式SQL与主语言之间的通信
将SQL嵌入到高级语言中混合编程,SQL语句负责操纵数据库,高级语言语句负责控制流程。
这时程序中会含有两种不同的计算模型的语句,从而需要二者之间建立通信。
数据库工作单元与源程序工作单元之间的通信主要包括:
(1)向主语言传递SQL语句的执行状态信息,使主语言能够根据此信息控制程序流程,主要用SQL通信区实现
(2)主语言向SQL语句提供参数,主要用主变量实现
(3)将SQL语句查询数据库的结果传回主语言处理,主要用主变量和游标实现
四、嵌入SQL的使用规定
(1)在程序中要区分SQL语句和宿主语言语句
嵌入式SQL语句的格式如:
EXEC SQL <SQL语句> END_EXEC
结束标志在不同的宿主语言中可能是不同的,在C和PASCAL语言程序中规定结束标志为“;”,而非“END_EXEC”。
(2)SQL的集合处理方式与宿主语言单记录处理方式之间的协调
由于SQL语句处理的是记录集合,而宿主语言语句一次只能处理一条记录,因此需要使用游标(Cursor)机制,把集合操作转换为单记录处理方式。
游标是与某一查询结果相联系的符号名,与游标有关的SQL语句如下:
1)游标定义语句(DECLARE)。其格式如下:
EXEC SQL DECLARE <游标名> CURSOR FOR
< SELECT 语句>
END EXEC
2)游标打开语句(OPEN)。
该语句在执行游标定义中的SELECT语句,同时游标处于活动状态。游标是一个指针,此时指向查询结果的第一行之前。
OPEN语句的格式如下:
EXEC SQL OPEN <游标名>
END_EXEC
3)游标读取数据语句(FETCH)。
此时游标推进一行,并把游标指向的行(即当前行)中的值取出并送到共享变量,其格式如下:
EXEC SQL FETCH FROM <游标名> INTO <变量表>
END_EXEC
变量表由逗号分开的共享变量组成。
FETCH语句通常置于宿主语言程序的循环结构中,以逐一处理查询结果中的每一个元组。

浙公网安备 33010602011771号