先放入mysql存储过程,便于大家了解一下存储过程的整体代码结构:
CREATE PROCEDURE `get_branch_path`(IN company CHAR(32)) BEGIN DECLARE _branchid CHAR(32) ; DECLARE _branchpath LONGTEXT; DECLARE _currentid CHAR(32); DECLARE fetchSeqOK boolean; DECLARE cur1 CURSOR FOR SELECT branchid FROM t_branch WHERE companyid=company; DECLARE CONTINUE HANDLER FOR NOT FOUND SET fetchSeqOK=TRUE; SET fetchSeqOK=FALSE; DROP TEMPORARY TABLE IF EXISTS temp_table; CREATE TEMPORARY TABLE IF NOT EXISTS temp_table ( branchid CHAR(32), branchpath LONGTEXT ); OPEN cur1; FETCH cur1 INTO _currentid; fetchSeqLoop:LOOP IF fetchSeqOK THEN LEAVE fetchSeqLoop; ELSE SET _branchid = _currentid; SET _branchpath = NULL; CALL generate_branch_path(_currentid, _branchpath); INSERT INTO temp_table VALUES(_branchid, _branchpath); FETCH cur1 INTO _currentid; END IF; END LOOP; CLOSE cur1; SELECT branchid,branchpath FROM temp_table; DROP TABLE temp_table; END
CREATE PROCEDURE `generate_branch_path`(INOUT `_subid` varchar(255),INOUT `_subpath` longtext) BEGIN DECLARE _path LONGTEXT; DECLARE _parentid CHAR(32); DECLARE fetchSeqOK boolean; DECLARE cur2 CURSOR FOR SELECT `name`,parentid FROM t_branch WHERE branchid=_subid; SET @@max_sp_recursion_depth = 20; OPEN cur2; FETCH cur2 INTO _path,_parentid; IF( LEFT(_parentid,1) <> 0 ) THEN SET _subid = _parentid; IF (_subpath IS NULL) THEN SET _subpath = _path; ELSE SET _subpath = CONCAT(_path,'/',_subpath); END IF; CALL generate_branch_path(_subid,_subpath); ELSE IF (_subpath IS NULL) THEN SET _subpath = _path; ELSE SET _subpath = CONCAT(_path,'/',_subpath); END IF; END IF; END
上述两个存储过程所需要的数据表及测试数据:
CREATE TABLE `t_branch` ( `branchid` char(32) NOT NULL, `companyid` char(32) NOT NULL, `name` varchar(50) NOT NULL, `parentid` char(32) NOT NULL, `sortid` int(11) NOT NULL, `remark` varchar(200) DEFAULT NULL, `isshare` int(11) DEFAULT NULL, `lastupdatetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `createtime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', PRIMARY KEY (`branchid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `t_branch` VALUES ('402880ff2d6f871a012d6f871cf30000','402880ff2d5f53cd012d5f53d43d0000','销售部门','0','2','就是湖北销售部门!','0','2011-01-11 18:13:35','2011-01-10 18:46:32'), ('402880ff2d6f871a012d6f8788210001','402880ff2d5f53cd012d5f53d43d0000','hbdev','402880ff2d744a06012d74531f8e0002','1','就是湖北研发','1','2011-01-11 17:16:42','2011-01-10 18:47:00'), ('402880ff2d6f871a012d6f8816f30002','402880ff2d5f53cd012d5f53d43d0000','abc team','402880ff2d6f871a012d6f8788210001','2','就是 abc team!','1','2011-01-11 18:21:34','2011-01-10 18:47:36'), ('402880ff2d744a06012d745231660000','402880ff2d5f53cd012d5f53d43d0000','人力资源部','0','3','就是湖北人力资源部!','0','2011-01-11 18:18:11','2011-01-11 17:06:47'), ('402880ff2d744a06012d7452930e0001','402880ff2d5f53cd012d5f53d43d0000','市场部','0','4','就是湖北市场部!','0','2011-01-11 18:18:11','2011-01-11 17:07:12'), ('402880ff2d744a06012d74531f8e0002','402880ff2d5f53cd012d5f53d43d0000','PTC','0','1','就是EFG!','0','2011-01-11 17:17:23','2011-01-11 17:07:48'), ('402880ff2d749155012d7492e8e70000','402880ff2d5f53cd012d5f53d43d0000','HDK team','402880ff2d6f871a012d6f8788210001','1','就是HDK team!','0','2011-01-11 18:17:28','2011-01-11 18:17:28'), ('ff8080812e3792bb012e50492fad0018','402880ff2d5f53cd012d5f53d43d0000','新部门','402880ff2d6f871a012d6f871cf30000','1','','0','2011-02-23 10:11:37','2011-02-23 10:11:37');
1.1存储过程代码结构:
CREATE PROCEDURE 存储过程名(参数列表,有三种类型:IN,OUT,INOUT) BEGIN 1.变量和条件声明 2. Cursor声明 3. Handler声明 4. 程序代码 END
注意:
由括号包围的参数列必须总数存在的。如果没有参数,也要使用一个空参数列()。每个参数默认都是一个IN参数,可在参数名之间加入OUT,INOUT等关键字指定为其他类型。
变量和条件声明不需放在Cursor声明等代码块之前,否则该存储过程编译不能通过。
1.2变量声明
DECLARE _branchid CHAR(32) ; DECLARE 关键字 _branchid 变量名 CHAR(32) 变量类型
1.3指针和Handler声明
定义游标 cur1 CURSOR FOR SELECT branchid FROM t_branch WHERE companyid=company; 使用游标 open cur1; fetch数据 FETCH cur1 INTO _currentid; 关闭游标 close cur1; 用到游标cur1,都会离不开循环语句。 一般使用Loop和while来进行循环语句的编写。 这里使用Loop为例 fetchSeqLoop:LOOP fetch cur1 into _currentid; end Loop; 现在是死循环,还没有退出的条件,那么在这里和oracle有区别,OraclePL/SQL的指针有个隐性变量%notfound, Mysql是通过一个Error handler的声明来进行判断的, DECLARE CONTINUE HANDLER FOR NOT FOUND SET fetchSeqOK=TRUE; 在Mysql里当游标遍历溢出时,会出现一个预定义的NOT FOUND的Error,我们处理这个Error并定义一个continue的handler就可以了, 关于Mysql Error handler可以查询Mysql手册。定义一个flag,在NOT FOUND,标示Flag,在Loop里以这个flag为结束循环的判断就可以了。 具体代码可以参考上面的存储过程:get_branch_path(); 注意:在循环语句中,记得使用FETCH cur1 into _currentid来进行指针的数据移动,否则指针会一直指向你初次赋值的地方,而不指向下一个值。
1.4程序代码
根据上述定义的变量,临时表,指针等运用循环语句,IF/ELSE语句,函数等内容完成一定的业务逻辑。
IF语句的结构: IF 逻辑表达式 THEN 程序语句; ELSE 程序语句; END IF; WHILE语句的结构: WHILE 逻辑表达式 DO 程序语句; END WHILE; LOOP语句的结构: LOOP 程序语句; END LOOP; 可以给代码块加lebel,这样END匹配比较直观,还可以用LEAVE语句来终结代码块: fetchSeqLoop:LOOP IF fetchSeqOK THEN LEAVE fetchSeqLoop; ELSE 程序语句; END IF; END LOOP;
2.1存储过程调用
CALL generate_branch_path(_currentid, _branchpath);
存储过程中的参数有三种IN,OUT,INOUT.具体用法可以看看下面的这篇文章,就能够明白:
http://www.blogjava.net/nonels/archive/2009/04/22/233324.html
2.2存储过程的结果集返回
上述存储过程中,创建了一个临时表temp_table,用于存储调用方(持久层中间件:hibenate,ibatis,JDBC)所需要的结果集。如果不考虑临时表的回收问题,在调用方可以在调用存储过程之后,再直接用sql语句查询该临时表获取结果集。但考虑到临时表的回收问题,需要在存储过程结束时,删除该临时表,并返回该结果集。使用如下代码即可达到目的。
SELECT branchid,branchpath FROM temp_table; DROP TABLE temp_table; 在有了上面的SELECT语句之后,在DAO层调用存储过程之后,通过代码就可以获取结果集了。 使用JDBC等接口代码完成结果集的返回。 String url = "jdbc:mysql://127.0.0.1:3306/test"; try { DriverManager.registerDriver(new com.mysql.jdbc.Driver()); Connection conn = DriverManager.getConnection(url, "root",""); CallableStatement stmt = conn.prepareCall("{call xx()}"); stmt.execute(); ResultSet rs = (ResultSet)stmt.getResultSet(); while(rs.next()){ System.out.print (rs.getString(1)); System.out.print (" , "); System.out.println (rs.getString(2)); } } catch (SQLException e) { e.printStackTrace(); }
行了,关于mysql总结的经验主要就是这些了,上述代码很多借鉴了其他的一些文章,有雷同之处还望大家见谅。关于上述贴出的存储过程,数据表等是可以编译通过,并运行的。大家想学习的话,可以拷贝下来测试一下。