MySQL存储过程

项目要求根据天进行分表,可是DBA要求建表要控制权限。表结构固定,所以要求不直接给建表权限,只能经过存储过程来建表。java

思路:将建表的语句作成存储过程,而后在须要建表是,调用存储过程建立分表。mysql

建立存储过程

-- procedure.sql
DELIMITER $$

DROP PROCEDURE IF EXISTS `new_dove_table`;$$
CREATE DEFINER=`root`@`%` PROCEDURE `new_dove_table`(IN tname varchar(200), OUT res int)
BEGIN
SET @sqlcmd = CONCAT('CREATE TABLE IF NOT EXISTS `', tname, '` (`id` bigint(20) NOT NULL, `content` mediumtext COLLATE utf8mb4_unicode_ci, `time` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `time_idx` (`time`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci');
PREPARE stmt FROM @sqlcmd;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET res=1;
END$$

DELIMITER ;

tname是传递的表名称,IN表明是传入的参数,类型为字符串。 res是返回的应答,OUT表明返回的结构,类型为数字。sql

EXECUTE stmt; 执行建表SQL数据库

登陆mysql: mysql -h127.0.0.1 -P3306 -uroot -pless

msyql> source procedure.sql

java调用存储过程

调用前,先建立test的帐户,用户测试 mysql -uroot -S tmp/mysql.sokcide

mysql> grant select,insert,update,delete,execute on test.* to 'test'@'%' identified by '123456';
mysql> FLUSH PRIVILEGES;

使用JDBC进行调用,可使用C3P0库。测试

public class ProcedureTest {
    static Connection conn = null;  

    public static void main(String[] args) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            //创建JDBC链接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","test","123456");   
        } catch (Exception e) {  
            System.out.println("数据库异常!");  
            e.printStackTrace();  
        }
        
        callProcedure();
    } 
            
    public static void callProcedure() {
        String sqlx = "{CALL `new_dove_table`(?,?)}";  
        try {  
            //建立一个JDBC声明
            CallableStatement stmt = conn.prepareCall(sqlx);
            //设置传入的参数
            stmt.setString(1, "test_table"); 
            //注册执行结果
            stmt.registerOutParameter(2, Types.INTEGER);
            //执行存储过程
            stmt.executeUpdate(); 
            
            System.out.println(" test mysql procedure : " + stmt.getInt(2));
        } catch (SQLException e) {  
            e.printStackTrace();  
        } finally {  
            //预防性关闭链接(避免异常发生时在try语句块关闭链接没有执行)  
            try {  
                if (conn != null) conn.close();  
            } catch (SQLException e) {  
                e.printStackTrace();  
            }  
        }  
    }
}

能够执行程序后,查看表是否建立成功。ui

遇到的问题

在实际过程当中遇到如下报错:url

java.sql.SQLException: The user specified as a definer ('root'@'%') does not exist
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1073)
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3593)
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3525)
	at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1986)
	at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2140)
	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2626)
	at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2111)
	at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2407)
	at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2325)
	at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2310)
	at com.mysql.jdbc.CallableStatement.executeUpdate(CallableStatement.java:973)
	at jdbc.mysql.ProcedureTest.callProcedure(ProcedureTest.java:37)
	at jdbc.mysql.ProcedureTest.main(ProcedureTest.java:24)

分析多是权限问题,查看下procedure信息code

mysql> show procedure status\G;
*************************** 1. row ***************************
                  Db: test
                Name: new_dove_table
                Type: PROCEDURE
             Definer: root@%
            Modified: 2015-12-15 20:23:30
             Created: 2015-12-15 20:11:49
       Security_type: DEFINER
             Comment: 
character_set_client: latin1
collation_connection: latin1_swedish_ci
  Database Collation: latin1_swedish_ci
6 rows in set (0.00 sec)

ERROR: 
No query specified

mysql>

security_type为definer,也即只有建立者才有权限。修改权限

mysql> ALTER PROCEDURE `new_dove_table` SQL SECURITY INVOKER;
Query OK, 0 rows affected (0.00 sec)

mysql>

执行完毕后,能够正常调用和建立表。

上线问题

上线后,遇到一个报错,致使没法建立表

[2015-12-15 17:24:57] ERROR ~ checkQueueExist exception: User does not have access to metadata required to determine stored procedure parameter types. If rights can not be granted, configure connection with "no
AccessToProcedureBodies=true" to have driver generate parameters that represent INOUT strings irregardless of actual parameter types.
java.sql.SQLException: User does not have access to metadata required to determine stored procedure parameter types. If rights can not be granted, configure connection with "noAccessToProcedureBodies=true" to h
ave driver generate parameters that represent INOUT strings irregardless of actual parameter types.
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1073)
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:987)
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:982)
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:927)
        at com.mysql.jdbc.DatabaseMetaData.getCallStmtParameterTypes(DatabaseMetaData.java:1630)
        at com.mysql.jdbc.DatabaseMetaData.getProcedureOrFunctionColumns(DatabaseMetaData.java:4246)
        at com.mysql.jdbc.DatabaseMetaData.getProcedureColumns(DatabaseMetaData.java:4087)
        at com.mysql.jdbc.CallableStatement.determineParameterTypes(CallableStatement.java:845)
        at com.mysql.jdbc.CallableStatement.<init>(CallableStatement.java:626)
        at com.mysql.jdbc.JDBC4CallableStatement.<init>(JDBC4CallableStatement.java:47)
        at com.mysql.jdbc.Util.handleNewInstance(Util.java:407)
        at com.mysql.jdbc.CallableStatement.getInstance(CallableStatement.java:522)
        at com.mysql.jdbc.ConnectionImpl.parseCallableStatement(ConnectionImpl.java:4008)
        at com.mysql.jdbc.ConnectionImpl.prepareCall(ConnectionImpl.java:4092)
        at com.mysql.jdbc.ConnectionImpl.prepareCall(ConnectionImpl.java:4066)
        at helper.mysql.MysqlHelper.executeNewQueueTableProcedure(MysqlHelper.java:53)
        at storage.impl.mysql.MySQLDataStorage.createQueueTable(MySQLDataStorage.java:193)
        at storage.impl.db.DBDataStorage.checkQueueExist(DBDataStorage.java:49)
        at server.SubscriberManager.checkQueueExist(SubscriberManager.java:142)
        at command.impl.PullCommand.run(PullCommand.java:103)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)

经过分析异常仍是权限问题,可是为什么两次的权限问题不同? 咱们就在线上mysql url添加了 noAccessToProcedureBodies=true 参数,可是依然存在此报错。 虽然执行了 ALTER PROCEDURE new_dove_table SQL SECURITY INVOKER; 报错依然存在。

mysql> show procedure status\G;
*************************** 1. row ***************************
                  Db: test
                Name: new_dove_table
                Type: PROCEDURE
             Definer: root@localhost
            Modified: 2015-12-15 20:23:30
             Created: 2015-12-15 20:11:49
       Security_type: DEFINER
             Comment: 
character_set_client: latin1
collation_connection: latin1_swedish_ci
  Database Collation: latin1_swedish_ci
6 rows in set (0.00 sec)

ERROR: 
No query specified

mysql>

对比测试环境和线上环境,发现 definer有差别,一个是root@%,一个是root@localhost。 DBA在线上执行:GRANT SELECT ON mysql.proc TO 'test'@'localhost'; 后,问题获得解决

总结:

  • 若是definer是root@localhost,须要执行 GRANT SELECT ON mysql.proc TO 'test'@'%';
  • 若是definer是root@%,须要执行 ALTER PROCEDURE new_dove_table SQL SECURITY INVOKER;

缘由: 存储过程的执行流程: 一、查找存储过程 存储过程就在mysql.proc中,查询时须要根据Security_type的类型进行权限检查。 若是Security_type值为INVOKER,则已经具备查询mysql.proc的权限,所以不须要GRANT SELECT ON mysql.proc TO 'user'@'%';赋权限若是若是Security_type值为DEFINER,则须要看调用者是否具备存储过程的权限,默认没有查询权限,所以须要GRANT SELECT ON mysql.proc TO 'user'@'%';赋权限

二、解析存储过程的参数 解析存储过程传递参数和存储过程定义是否符合。

三、执行存储过程 检查执行者的权限,执行者由Security_type决定。若是是INVOKER则,检查存储过程调用者的权限和存储过程内要求的权限是否匹配; 若是是DEFINER,检查存储过程的DEFINER权限和存储过程内要求的权限是否匹配。

扩展信息

存储过程的执行权限

mysql> grant execute on procedure test.new_dove_table to 'test'@'%';

mysql> revoke execute procedure test.new_dove_table from test;

JDBC链接执行MySQL存储过程报权限错误

相关文章
相关标签/搜索