一、存储过程概述:
存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。
存储过程的优缺点:
优点:
①重复使用。存储过程可以重复使用,从而可以减少数据库开发人员的工作量。
②减少网络流量。存储过程位于服务器上,调用的时候只需要传递存储过程的名称以及参数就可以了,因此降低了网络传输的数据量。
③安全性。参数化的存储过程可以防止SQL注入式攻击,而且可以将Grant、Deny以及Revoke权限应用于存储过程。
简单地讲:
1.存储过程只在创造时进行编译,以后每次执行存储过程都不需再重新编译,而一般SQL语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度。
2.当对数据库进行复杂操作时(如对多个表进行Update,Insert,Query,Delete时),可将此复杂操作用存储过程封装起来与数据库提供的事务处理结合一起使用。
3.存储过程可以重复使用,可减少数据库开发人员的工作量
4.安全性高,可设定只有某些用户才具有对指定存储过程的使用权
有一点需要注意的是,一些网上盛传的所谓的存储过程要比sql语句执行更快的说法,实际上是个误解,并没有根据,包括微软内部的人也不认可这一点,所以不能作为正式的优点,希望大家能够认识到这一点。
缺点:
1:调试麻烦,但是用 PL/SQL Developer 调试很方便!弥补这个缺点。
2:移植问题,数据库端代码当然是与数据库相关的。但是如果是做工程型项目,基本不存在移植问题。
3:重新编译问题,因为后端代码是运行前编译的,如果带有引用关系的对象发生改变时,受影响的存储过程、包将需要重新编译(不过也可以设置成运行时刻自动编译)。
4: 如果在一个程序系统中大量的使用存储过程,到程序交付使用的时候随着用户需求的增加会导致数据结构的变化,接着就是系统的相关问题了,最后如果用户想维护该系统可以说是很难很难、而且代价是空前的,维护起来更麻烦。
二、存储过程的使用:
① 基本的创建、删除的语法:
创建:
CREATE [DEFINER = { user | CURRENT_USER }] PROCEDURE sp_name ([proc_parameter[,...]]) [characteristic ...] routine_body
proc_parameter: [ IN | OUT | INOUT ] param_name type
type: Any valid MySQL data type
characteristic: LANGUAGE SQL | [NOT] DETERMINISTIC | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA } | SQL SECURITY { DEFINER | INVOKER } | COMMENT 'string'
routine_body: Valid SQL procedure statement
sp_name 存储过程的名字
proc_parameter 指定参数为IN, OUT,或INOUT
characteristic 特征子句
routine_body 包含合法的SQL过程语句删除:
DROP {PROCEDURE | FUNCTION} [IF EXISTS] sp_name
这个语句被用来移除一个存储程序或函数。即,从服务器移除一个制定的子程序。在MySQL 5.1中,你必须有ALTER ROUTINE权限才可用此子程序。这个权限被自动授予子程序的创建者。 IF EXISTS 子句是一个MySQL的扩展。如果程序或函数不存在,它防止发生错误
show procedure status; 查看存储过程状态② 使用示例 example
student表结构:
mysql> select * from student; +----------+----------+---------------+-----------+----+ | nickname | password | email | address | id | +----------+----------+---------------+-----------+----+ | elgin | 12345 | dddffg@qq.com | xinbei9-2 | 1 | | elgin | 12345 | dddffg@qq.com | xinbei9-2 | 2 | | seth | 12345 | dddffg@qq.com | xinbei9-2 | 3 | | 3344 | ddff3322 | dddffg@qq.com | xinbei9-2 | 4 | | ddddfff | 12345 | dddffg@qq.com | xinbei9-2 | 5 | | jdbcName | jdbc1234 | dddffg@qq.com | xinbei9-2 | 6 | +----------+----------+---------------+-----------+----+创建一个向student表插入数据的存储过程:
mysql> create procedure addStudent(nickname varchar(50),pwd varchar(50),email varchar(50),address varchar(50)) -> begin -> insert into student(nickname,password,email,address)values(nickname,pwd,email,address);end;执行之后,通过show procedure status 命令查看存储过程是否创建成功:
mysql> show procedure status; +------+------------+-----------+----------------+---------------------+---------------------+---------------+---------+----------------------+----------------------+--------------------+ | Db | Name | Type | Definer | Modified | Created | Security_type | Comment | character_set_client | collation_connection | Database Collation | +------+------------+-----------+----------------+---------------------+---------------------+---------------+---------+----------------------+----------------------+--------------------+ | test | addStudent | PROCEDURE | root@localhost | 2015-12-09 14:10:37 | 2015-12-09 14:10:37 | DEFINER | | utf8 | utf8_general_ci | latin1_swedish_ci | +------+------------+-----------+----------------+---------------------+---------------------+---------------+---------+----------------------+----------------------+--------------------+ 1 row in set调用addStudent存储过程:
mysql> call addStudent('testProcedure','procedure1234','procedure123@163.com','china.js'); Query OK, 1 row affected执行完毕之后查看student表数据,发现通过参数传递进去的数据已插入表中:
mysql> select * from student; +---------------+---------------+----------------------+-----------+----+ | nickname | password | email | address | id | +---------------+---------------+----------------------+-----------+----+ | elgin | 12345 | dddffg@qq.com | xinbei9-2 | 1 | | elgin | 12345 | dddffg@qq.com | xinbei9-2 | 2 | | seth | 12345 | dddffg@qq.com | xinbei9-2 | 3 | | 3344 | ddff3322 | dddffg@qq.com | xinbei9-2 | 4 | | ddddfff | 12345 | dddffg@qq.com | xinbei9-2 | 5 | | jdbcName | jdbc1234 | dddffg@qq.com | xinbei9-2 | 6 | | testProcedure | procedure1234 | procedure123@163.com | china.js | 7 | +---------------+---------------+----------------------+-----------+----+ 7 rows in set创建一个查询数据的存储过程getStudent:
mysql> create procedure getStudent(sid int) -> begin -> select * from student where id>sid;end; Query OK, 0 rows affected调用执行此存储过程,可以看到id大于4的记录都被列出来了:
mysql> call getstudent(4); +---------------+---------------+----------------------+-----------+----+ | nickname | password | email | address | id | +---------------+---------------+----------------------+-----------+----+ | ddddfff | 12345 | dddffg@qq.com | xinbei9-2 | 5 | | jdbcName | jdbc1234 | dddffg@qq.com | xinbei9-2 | 6 | | testProcedure | procedure1234 | procedure123@163.com | china.js | 7 | +---------------+---------------+----------------------+-----------+----+ 3 rows in set三、JDBC调用存储过程:
DBUtil类用来获取Connection:
public class DBUtil { public static Connection getConnection(){ try { //加载数据库驱动 Class.forName("com.mysql.jdbc.Driver"); Connection con=(Connection) DriverManager.getConnection("jdbc:mysql://127.0.0.1/test", "root", "root"); return con; } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } return null; } }测试类Test:
public class Test { public static void main(String[] args) { try { testProcedureUpdate(); testProcedureQuery(); } catch (SQLException e) { e.printStackTrace(); } } /** * JDBC调用存储过程update * @throws SQLException */ public static void testProcedureUpdate() throws SQLException{ String sql="call addStudent(?,?,?,?)"; Connection con=DBUtil.getConnection(); CallableStatement cs = null; try { cs = con.prepareCall(sql); cs.setString(1, "jdbcName"); cs.setString(2, "jdbc1234"); cs.setString(3, "jdbc1234@gmail.com"); cs.setString(4, "china.js"); int count=cs.executeUpdate(); System.out.println(count); } catch (SQLException e) { e.printStackTrace(); } finally { con.close(); cs.close(); } } /** * JDBC调用存储过程query * @throws SQLException */ public static void testProcedureQuery() throws SQLException{ String sql="call getStudent(?)"; Connection con=DBUtil.getConnection(); CallableStatement cs = null; try { cs = con.prepareCall(sql); cs.setInt(1, 4); ResultSet rs=cs.executeQuery(); while(rs.next()){ String username=rs.getString(1); String password=rs.getString(2); String email=rs.getString(3); String address=rs.getString(4); int id=rs.getInt(5); System.out.println(id +" ^ "+ username +" ^ "+ password +" ^ "+ email +" ^ "+ address); } } catch (SQLException e) { e.printStackTrace(); } finally{ con.close(); cs.close(); } } }程序运行结果:
1 5 ^ ddddfff ^ 12345 ^ dddffg@qq.com ^ xinbei9-2 6 ^ jdbcName ^ jdbc1234 ^ dddffg@qq.com ^ xinbei9-2 7 ^ testProcedure ^ procedure1234 ^ procedure123@163.com ^ china.js 9 ^ jdbcName ^ jdbc1234 ^ jdbc1234@gmail.com ^ china.js