项目要求根据天进行分表,可是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
调用前,先建立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'; 后,问题获得解决
总结:
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;