MyBatis中XML 映射文件中常見的標簽說明
SQL 映射文件只有很少的幾個頂級元素(按照應被定義的順序列出):
cache ? 對給定命名空間的緩存配置。 cache-ref ? 對其他命名空間緩存配置的引用。 resultMap ? 是最復雜也是最強大的元素,用來描述如何從數(shù)據(jù)庫結(jié)果集中來加載對象。 parameterMap ? 已被廢棄!老式風格的參數(shù)映射。更好的辦法是使用內(nèi)聯(lián)參數(shù),此元素可能在將來被移除。 sql ? 可被其他語句引用的可重用語句塊。 insert ? 映射插入語句 update ? 映射更新語句 delete ? 映射刪除語句 select ? 映射查詢語句select<select parameterType='int' resultType='hashmap'> SELECT * FROM PERSON WHERE ID = #{id} </select>
這個語句被稱作 selectPerson,接受一個 int(或 Integer)類型的參數(shù),并返回一個 HashMap 類型的對象,其中的鍵是列名,值便是結(jié)果行中的對應值。
注意參數(shù)符號:#{id}
這就告訴 MyBatis 創(chuàng)建一個預處理語句(PreparedStatement)參數(shù),在 JDBC 中,這樣的一個參數(shù)在 SQL 中會由一個“?”來標識,并被傳遞到一個新的預處理語句中,就像這樣:
// 近似的 JDBC 代碼,非 MyBatis 代碼... String selectPerson = 'SELECT * FROM PERSON WHERE ID=?'; PreparedStatement ps = conn.prepareStatement(selectPerson); ps.setInt(1,id); <select parameterType='int' parameterMap='deprecated' resultType='hashmap' resultMap='personResultMap' flushCache='false' useCache='true' timeout='10' fetchSize='256' statementType='PREPARED' resultSetType='FORWARD_ONLY'>屬性 描述 id 在命名空間中唯一的標識符,可以被用來引用這條語句。 parameterType 將會傳入這條語句的參數(shù)類的完全限定名或別名。這個屬性是可選的,因為 MyBatis 可以通過類型處理器(TypeHandler) 推斷出具體傳入語句的參數(shù),默認值為未設(shè)置(unset)。 parameterMap 這是引用外部 parameterMap 的已經(jīng)被廢棄的方法。請使用內(nèi)聯(lián)參數(shù)映射和 parameterType 屬性。 resultType 從這條語句中返回的期望類型的類的完全限定名或別名。 注意如果返回的是集合,那應該設(shè)置為集合包含的類型,而不是集合本身。可以使用 resultType 或 resultMap,但不能同時使用。 resultMap 外部 resultMap 的命名引用。結(jié)果集的映射是 MyBatis 最強大的特性,如果你對其理解透徹,許多復雜映射的情形都能迎刃而解。可以使用 resultMap 或 resultType,但不能同時使用。 flushCache 將其設(shè)置為 true 后,只要語句被調(diào)用,都會導致本地緩存和二級緩存被清空,默認值:false。 useCache 將其設(shè)置為 true 后,將會導致本條語句的結(jié)果被二級緩存緩存起來,默認值:對 select 元素為 true。 timeout 這個設(shè)置是在拋出異常之前,驅(qū)動程序等待數(shù)據(jù)庫返回請求結(jié)果的秒數(shù)。默認值為未設(shè)置(unset)(依賴驅(qū)動)。 fetchSize 這是一個給驅(qū)動的提示,嘗試讓驅(qū)動程序每次批量返回的結(jié)果行數(shù)和這個設(shè)置值相等。 默認值為未設(shè)置(unset)(依賴驅(qū)動)。 statementType STATEMENT,PREPARED 或 CALLABLE 中的一個。這會讓 MyBatis 分別使用 Statement,PreparedStatement 或 CallableStatement,默認值:PREPARED。 resultSetType FORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等價于 unset) 中的一個,默認值為 unset (依賴驅(qū)動)。 databaseId 如果配置了數(shù)據(jù)庫廠商標識(databaseIdProvider),MyBatis 會加載所有的不帶 databaseId 或匹配當前 databaseId 的語句;如果帶或者不帶的語句都有,則不帶的會被忽略。 resultOrdered 這個設(shè)置僅針對嵌套結(jié)果 select 語句適用:如果為 true,就是假設(shè)包含了嵌套結(jié)果集或是分組,這樣的話當返回一個主結(jié)果行的時候,就不會發(fā)生有對前面結(jié)果集的引用的情況。 這就使得在獲取嵌套的結(jié)果集的時候不至于導致內(nèi)存不夠用。默認值:false。 resultSets 這個設(shè)置僅對多結(jié)果集的情況適用。它將列出語句執(zhí)行后返回的結(jié)果集并給每個結(jié)果集一個名稱,名稱是逗號分隔的。 insert, update 和 delete
數(shù)據(jù)變更語句 insert,update 和 delete 的實現(xiàn)非常接近:
<insert parameterType='domain.blog.Author' flushCache='true' statementType='PREPARED' keyProperty='' keyColumn='' useGeneratedKeys='' timeout='20'> <update parameterType='domain.blog.Author' flushCache='true' statementType='PREPARED' timeout='20'> <delete parameterType='domain.blog.Author' flushCache='true' statementType='PREPARED' timeout='20'>屬性 描述 id 命名空間中的唯一標識符,可被用來代表這條語句。 parameterType 將要傳入語句的參數(shù)的完全限定類名或別名。這個屬性是可選的,因為 MyBatis 可以通過類型處理器推斷出具體傳入語句的參數(shù),默認值為未設(shè)置(unset)。 parameterMap 這是引用外部 parameterMap 的已經(jīng)被廢棄的方法。請使用內(nèi)聯(lián)參數(shù)映射和 parameterType 屬性。 flushCache 將其設(shè)置為 true 后,只要語句被調(diào)用,都會導致本地緩存和二級緩存被清空,默認值:true(對于 insert、update 和 delete 語句)。 timeout 這個設(shè)置是在拋出異常之前,驅(qū)動程序等待數(shù)據(jù)庫返回請求結(jié)果的秒數(shù)。默認值為未設(shè)置(unset)(依賴驅(qū)動)。 statementType STATEMENT,PREPARED 或 CALLABLE 的一個。這會讓 MyBatis 分別使用 Statement,PreparedStatement 或 CallableStatement,默認值:PREPARED。 useGeneratedKeys (僅對 insert 和 update 有用)這會令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法來取出由數(shù)據(jù)庫內(nèi)部生成的主鍵(比如:像 MySQL 和 SQL Server 這樣的關(guān)系數(shù)據(jù)庫管理系統(tǒng)的自動遞增字段),默認值:false。 keyProperty (僅對 insert 和 update 有用)唯一標記一個屬性,MyBatis 會通過 getGeneratedKeys 的返回值或者通過 insert 語句的 selectKey 子元素設(shè)置它的鍵值,默認值:未設(shè)置(unset)。如果希望得到多個生成的列,也可以是逗號分隔的屬性名稱列表。 keyColumn (僅對 insert 和 update 有用)通過生成的鍵值設(shè)置表中的列名,這個設(shè)置僅在某些數(shù)據(jù)庫(像 PostgreSQL)是必須的,當主鍵列不是表中的第一列的時候需要設(shè)置。如果希望使用多個生成的列,也可以設(shè)置為逗號分隔的屬性名稱列表。 databaseId 如果配置了數(shù)據(jù)庫廠商標識(databaseIdProvider),MyBatis 會加載所有的不帶 databaseId 或匹配當前 databaseId 的語句;如果帶或者不帶的語句都有,則不帶的會被忽略。 生成主鍵
<selectKey keyProperty='id' resultType='int' order='BEFORE' statementType='PREPARED'>屬性 描述 keyProperty selectKey 語句結(jié)果應該被設(shè)置的目標屬性。如果希望得到多個生成的列,也可以是逗號分隔的屬性名稱列表。 keyColumn 匹配屬性的返回結(jié)果集中的列名稱。如果希望得到多個生成的列,也可以是逗號分隔的屬性名稱列表。 resultType 結(jié)果的類型。MyBatis 通常可以推斷出來,但是為了更加精確,寫上也不會有什么問題。MyBatis 允許將任何簡單類型用作主鍵的類型,包括字符串。如果希望作用于多個生成的列,則可以使用一個包含期望屬性的 Object 或一個 Map。 order 這可以被設(shè)置為 BEFORE 或 AFTER。如果設(shè)置為 BEFORE,那么它會首先生成主鍵,設(shè)置 keyProperty 然后執(zhí)行插入語句。如果設(shè)置為 AFTER,那么先執(zhí)行插入語句,然后是 selectKey 中的語句 - 這和 Oracle 數(shù)據(jù)庫的行為相似,在插入語句內(nèi)部可能有嵌入索引調(diào)用。 statementType 與前面相同,MyBatis 支持 STATEMENT,PREPARED 和 CALLABLE 語句的映射類型,分別代表 PreparedStatement 和 CallableStatement 類型。
<selectKey resultType='java.lang.Long' order='AFTER' keyProperty='id'> SELECT LAST_INSERT_ID() AS id </selectKey>sql
這個元素可以被用來定義可重用的 SQL 代碼段,這些 SQL 代碼可以被包含在其他語句中。它可以(在加載的時候)被靜態(tài)地設(shè)置參數(shù)。 在不同的包含語句中可以設(shè)置不同的值到參數(shù)占位符上。比如:
<sql id='userColumns'> ${alias}.id,${alias}.username,${alias}.password </sql> <select resultType='map'> select <include refid='userColumns'><property name='alias' value='t1'/></include>, <include refid='userColumns'><property name='alias' value='t2'/></include> from some_table t1 cross join some_table t2 </select>結(jié)果映射
resultMap 元素是 MyBatis 中最重要最強大的元素,在一些情形下允許你進行一些 JDBC 不支持的操作。實際上,在為一些比如連接的復雜語句編寫映射代碼的時候,一份 resultMap 能夠代替實現(xiàn)同等功能的長達數(shù)千行的代碼。ResultMap 的設(shè)計思想是,對于簡單的語句根本不需要配置顯式的結(jié)果映射,而對于復雜一點的語句只需要描述它們的關(guān)系就行了。
在JavaBean中定義一個有 3 個屬性:id,username 和 hashedPassword的類,然后在mapper.xml中,這些屬性會對應到 select 語句中的列名,這樣的一個 JavaBean 可以被映射到 ResultSet,就像映射到 HashMap 一樣簡單。
<select resultType='com.someapp.model.User'> select id, username, hashedPassword from some_table where id = #{id} </select>
像第三中的第三個例子,也可以定義外部resultMap,這也是解決列名不匹配的另外一種方式。
<resultMap type='User'> <id property='id' column='user_id' /> <result property='username' column='user_name'/> <result property='password' column='hashed_password'/> </resultMap> <select resultMap='userResultMap'> select user_id, user_name, hashed_password from some_table where id = #{id} </select>結(jié)果映射(resultMap) constructor-用于在實例化類時,注入結(jié)果到構(gòu)造方法中 idArg - ID 參數(shù);標記出作為 ID 的結(jié)果可以幫助提高整體性能 arg - 將被注入到構(gòu)造方法的一個普通結(jié)果 id ? 一個 ID 結(jié)果;標記出作為 ID 的結(jié)果可以幫助提高整體性能 result ? 注入到字段或 JavaBean 屬性的普通結(jié)果 association? 一個復雜類型的關(guān)聯(lián);許多結(jié)果將包裝成這種類型 嵌套結(jié)果映射 ? 關(guān)聯(lián)本身可以是一個 resultMap 元素,或者從別處引用一個 collection? 一個復雜類型的集合 嵌套結(jié)果映射 ? 集合本身可以是一個 resultMap 元素,或者從別處引用一個 discriminator? 使用結(jié)果值來決定使用哪個resultMap case? 基于某些值的結(jié)果映射 嵌套結(jié)果映射 ? case 本身可以是一個 resultMap 元素,因此可以具有相同的結(jié)構(gòu)和元素,或者從別處引用一個。屬性 描述 id 當前命名空間中的一個唯一標識,用于標識一個結(jié)果映射。 type 類的完全限定名, 或者一個類型別名(關(guān)于內(nèi)置的類型別名,可以參考上面的表格)。 autoMapping 如果設(shè)置這個屬性,MyBatis將會為本結(jié)果映射開啟或者關(guān)閉自動映射。 這個屬性會覆蓋全局的屬性 autoMappingBehavior。默認值:未設(shè)置(unset)。 id & result
<id property='id' column='post_id'/> <result property='subject' column='post_subject'/>
這些是結(jié)果映射最基本的內(nèi)容。id 和 result 元素都將一個列的值映射到一個簡單數(shù)據(jù)類型(String, int, double, Date 等)的屬性或字段。
這兩者之間的唯一不同是,id 元素表示的結(jié)果將是對象的標識屬性,這會在比較對象實例時用到。 這樣可以提高整體的性能,尤其是進行緩存和嵌套結(jié)果映射(也就是連接映射)的時候。
屬性 描述 property 映射到列結(jié)果的字段或?qū)傩浴H绻脕砥ヅ涞?JavaBean 存在給定名字的屬性,那么它將會被使用。否則 MyBatis 將會尋找給定名稱的字段。 無論是哪一種情形,你都可以使用通常的點式分隔形式進行復雜屬性導航。 比如,你可以這樣映射一些簡單的東西:“username”,或者映射到一些復雜的東西上:“address.street.number”。 column 數(shù)據(jù)庫中的列名,或者是列的別名。一般情況下,這和傳遞給 resultSet.getString(columnName) 方法的參數(shù)一樣。 javaType 一個 Java 類的完全限定名,或一個類型別名(關(guān)于內(nèi)置的類型別名,可以參考上面的表格)。 如果你映射到一個 JavaBean,MyBatis 通常可以推斷類型。然而,如果你映射到的是 HashMap,那么你應該明確地指定 javaType 來保證行為與期望的相一致。 jdbcType JDBC 類型,所支持的 JDBC 類型參見這個表格之后的“支持的 JDBC 類型”。 只需要在可能執(zhí)行插入、更新和刪除的且允許空值的列上指定 JDBC 類型。這是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 編程,你需要對可能存在空值的列指定這個類型。 typeHandler 我們在前面討論過默認的類型處理器。使用這個屬性,你可以覆蓋默認的類型處理器。 這個屬性值是一個類型處理器實現(xiàn)類的完全限定名,或者是類型別名。 緩存默認情況下,只啟用了本地的會話緩存,它僅僅對一個會話中的數(shù)據(jù)進行緩存。 要啟用全局的二級緩存,只需要在你的 SQL 映射文件中添加一行:<cache/>
基本上就是這樣。這個簡單語句的效果如下:
映射語句文件中的所有 select 語句的結(jié)果將會被緩存。 映射語句文件中的所有 insert、update 和 delete 語句會刷新緩存。 緩存會使用最近最少使用算法(LRU, Least Recently Used)算法來清除不需要的緩存。 緩存不會定時進行刷新(也就是說,沒有刷新間隔)。 緩存會保存列表或?qū)ο螅o論查詢方法返回哪種)的 1024 個引用。 緩存會被視為讀/寫緩存,這意味著獲取到的對象并不是共享的,可以安全地被調(diào)用者修改,而不干擾其他調(diào)用者或線程所做的潛在修改。提示 緩存只作用于 cache 標簽所在的映射文件中的語句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的語句將不會被默認緩存。你需要使用 @CacheNamespaceRef 注解指定緩存作用域。
這些屬性可以通過 cache 元素的屬性來修改。比如:
<cache eviction='FIFO' flushInterval='60000' size='512' readOnly='true'/>
這個更高級的配置創(chuàng)建了一個 FIFO 緩存,每隔 60 秒刷新,最多可以存儲結(jié)果對象或列表的 512 個引用,而且返回的對象被認為是只讀的,因此對它們進行修改可能會在不同線程中的調(diào)用者產(chǎn)生沖突。
可用的清除策略有:
LRU ? 最近最少使用:移除最長時間不被使用的對象。 FIFO ? 先進先出:按對象進入緩存的順序來移除它們。 SOFT ? 軟引用:基于垃圾回收器狀態(tài)和軟引用規(guī)則移除對象。 WEAK ? 弱引用:更積極地基于垃圾收集器狀態(tài)和弱引用規(guī)則移除對象。默認的清除策略是 LRU。
flushInterval(刷新間隔)屬性可以被設(shè)置為任意的正整數(shù),設(shè)置的值應該是一個以毫秒為單位的合理時間量。 默認情況是不設(shè)置,也就是沒有刷新間隔,緩存僅僅會在調(diào)用語句時刷新。
size(引用數(shù)目)屬性可以被設(shè)置為任意正整數(shù),要注意欲緩存對象的大小和運行環(huán)境中可用的內(nèi)存資源。默認值是 1024。
readOnly(只讀)屬性可以被設(shè)置為 true 或 false。只讀的緩存會給所有調(diào)用者返回緩存對象的相同實例。 因此這些對象不能被修改。這就提供了可觀的性能提升。而可讀寫的緩存會(通過序列化)返回緩存對象的拷貝。 速度上會慢一些,但是更安全,因此默認值是 false。
提示 二級緩存是事務性的。這意味著,當 SqlSession 完成并提交時,或是完成并回滾,但沒有執(zhí)行 flushCache=true 的 insert/delete/update 語句時,緩存會獲得更新。
使用自定義緩存除了上述自定義緩存的方式,你也可以通過實現(xiàn)你自己的緩存,或為其他第三方緩存方案創(chuàng)建適配器,來完全覆蓋緩存行為。
<cache type='com.domain.something.MyCustomCache'/>
例子:
type 屬性指定的類必須實現(xiàn) org.mybatis.cache.Cache 接口,且提供一個接受 String 參數(shù)作為 id 的構(gòu)造器。 這個接口是 MyBatis 框架中許多復雜的接口之一,但是行為卻非常簡單。
public interface Cache { String getId(); int getSize(); void putObject(Object key, Object value); Object getObject(Object key); boolean hasKey(Object key); Object removeObject(Object key); void clear(); }動態(tài)SQL Mybatis 動態(tài) SQL ,可以讓我們在 XML 映射文件內(nèi),以 XML 標簽的形式編寫動態(tài) SQL ,完成邏輯判斷和動態(tài)拼接 SQL 的功能。 Mybatis 提供了 9 種動態(tài) SQL 標簽:<if />、<choose />、<when />、<otherwise />、<trim />、<where />、<set />、<foreach />、<bind /> 。 其執(zhí)行原理為,使用 OGNL 的表達式,從 SQL 參數(shù)對象中計算表達式的值,根據(jù)表達式的值動態(tài)拼接 SQL ,以此來完成動態(tài) SQL 的功能。if
動態(tài) SQL 通常要做的事情是根據(jù)條件包含 where 子句的一部分。比如:
<selectresultType='Blog'> SELECT * FROM BLOG WHERE state = ‘ACTIVE’ <if test='title != null'> AND title like #{title} </if> </select>
這條語句提供了一種可選的查找文本功能。如果沒有傳入“title”,那么所有處于“ACTIVE”狀態(tài)的BLOG都會返回;反之若傳入了“title”,那么就會對“title”一列進行模糊查找并返回 BLOG 結(jié)果(“title”參數(shù)值是可以包含一些掩碼或通配符的)。
通過“title”和“author”兩個參數(shù)進行可選搜索:
<selectresultType='Blog'> SELECT * FROM BLOG WHERE state = ‘ACTIVE’ <if test='title != null'> AND title like #{title} </if> <if test='author != null and author.name != null'> AND author_name like #{author.name} </if> </select>choose, when, otherwise
有時我們不想應用到所有的條件語句,而只想從中擇其一項。針對這種情況,MyBatis 提供了 choose 元素,它有點像 Java 中的 switch 語句。
這次變?yōu)樘峁┝恕皌itle”就按“title”查找,提供了“author”就按“author”查找的情形,若兩者都沒有提供,就返回所有符合條件的 BLOG(實際情況可能是由管理員按一定策略選出 BLOG 列表,而不是返回大量無意義的隨機結(jié)果)。
<selectresultType='Blog'> SELECT * FROM BLOG WHERE state = ‘ACTIVE’ <choose> <when test='title != null'> AND title like #{title} </when> <when test='author != null and author.name != null'> AND author_name like #{author.name} </when> <otherwise> AND featured = 1 </otherwise> </choose> </select>trim, where, set
上面的幾個例子,如果條件不滿足的話,會拼湊成不成一條sql語句,導致無法查詢,如:SELECT * FROM BLOG WHERE
<selectresultType='Blog'> SELECT * FROM BLOG <where> <if test='state != null'> state = #{state} </if> <if test='title != null'> AND title like #{title} </if> <if test='author != null and author.name != null'> AND author_name like #{author.name} </if> </where> </select>
where 元素只會在至少有一個子元素的條件返回 SQL 子句的情況下才去插入“WHERE”子句。而且,若語句的開頭為“AND”或“OR”,where 元素也會將它們?nèi)コ?/p>
如果 where 元素沒有按正常套路出牌,我們可以通過自定義 trim 元素來定制 where 元素的功能。比如,和 where 元素等價的自定義 trim 元素為:
<trim prefix='WHERE' prefixOverrides='AND |OR '> ... </trim>
prefixOverrides 屬性會忽略通過管道分隔的文本序列(注意此例中的空格也是必要的)。它的作用是移除所有指定在 prefixOverrides 屬性中的內(nèi)容,并且插入 prefix 屬性中指定的內(nèi)容。
類似的用于動態(tài)更新語句的解決方案叫做 set。set 元素可以用于動態(tài)包含需要更新的列,而舍去其它的。比如:
<update id='updateAuthorIfNecessary'> update Author <set> <if test='username != null'>username=#{username},</if> <if test='password != null'>password=#{password},</if> <if test='email != null'>email=#{email},</if> <if test='bio != null'>bio=#{bio}</if> </set> where id=#{id} </update>
這里,set 元素會動態(tài)前置 SET 關(guān)鍵字,同時也會刪掉無關(guān)的逗號,因為用了條件語句之后很可能就會在生成的 SQL 語句的后面留下這些逗號。(譯者注:因為用的是“if”元素,若最后一個“if”沒有匹配上而前面的匹配上,SQL 語句的最后就會有一個逗號遺留)
若你對 set 元素等價的自定義 trim 元素的代碼感興趣,那這就是它的真面目:
<trim prefix='SET' suffixOverrides=','> ... </trim>
注意這里我們刪去的是后綴值,同時添加了前綴值。
foreach動態(tài) SQL 的另外一個常用的操作需求是對一個集合進行遍歷,通常是在構(gòu)建 IN 條件語句的時候。比如:
<select resultType='domain.blog.Post'> SELECT * FROM POST P WHERE ID in <foreach item='item' index='index' collection='list' open='(' separator=',' close=')'> #{item} </foreach> </select>
foreach 元素的功能非常強大,它允許你指定一個集合,聲明可以在元素體內(nèi)使用的集合項(item)和索引(index)變量。它也允許你指定開頭與結(jié)尾的字符串以及在迭代結(jié)果之間放置分隔符。這個元素是很智能的,因此它不會偶然地附加多余的分隔符。
注意
可以將任何可迭代對象(如 List、Set 等)、Map 對象或者數(shù)組對象傳遞給 foreach 作為集合參數(shù)。當使用可迭代對象或者數(shù)組時,index 是當前迭代的次數(shù),item 的值是本次迭代獲取的元素。當使用 Map 對象(或者 Map.Entry 對象的集合)時,index 是鍵,item 是值。
bindbind 元素可以從 OGNL 表達式中創(chuàng)建一個變量并將其綁定到上下文。比如:
<select resultType='Blog'> <bind name='pattern' value='’%’ + _parameter.getTitle() + ’%’' /> SELECT * FROM BLOG WHERE title LIKE #{pattern} </select>
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持好吧啦網(wǎng)。
相關(guān)文章:
1. MySQL 字符串函數(shù):字符串截取2. Microsoft Office Access修改代碼字體大小的方法3. MySQL中InnoDB和MyISAM類型的差別4. MySQL 8.0 之索引跳躍掃描(Index Skip Scan)5. 恢復從 Access 2000、 Access 2002 或 Access 2003 中數(shù)據(jù)庫刪除表的方法6. Mysql入門系列:安排預防性的維護MYSQL數(shù)據(jù)庫服務器7. mysql數(shù)據(jù)存放的位置在哪8. SQLServer的內(nèi)存管理架構(gòu)詳解9. 在SQL Server中用XQuery分解XML數(shù)據(jù)10. Mysql 用戶權(quán)限管理實現(xiàn)
