項目要求根據天進行分表,可是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;