最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,頗有收穫,因而將本身學習的過程以博客形式輸出,若有錯誤,歡迎指正,如幫助到你,不勝榮幸!java
書中提到的需求是一個基於角色的權限控制需求(RBAC,即Role-Based Access Control),提到權限管理,相信你們都不陌生,由於大部分的系統都是須要權限管理的,我在上家公司負責的系統之一就是權限系統,設計思路和書中提到的差很少,大體描述以下:mysql
1)權限點用來管理要控制權限的資源,好比某個頁面,某個按鈕。git
2)建立一個角色,給這個角色分配某些權限點,好比商品模塊的全部頁面的權限。github
3)新建一個用戶,給這個用戶分配某些角色。sql
數據關係圖以下所示:數據庫
首先執行以下腳本建立上圖中的5張表:用戶表,角色表,權限表,用戶角色關聯表,角色權限關聯表。apache
CREATE TABLE sys_user ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '用戶ID', user_name VARCHAR(50) COMMENT '用戶名', user_password VARCHAR(50) COMMENT '密碼', user_email VARCHAR(50) COMMENT '郵箱', user_info TEXT COMMENT '簡介', head_img BLOB COMMENT '頭像', create_time DATETIME COMMENT '建立時間', PRIMARY KEY (id) ); ALTER TABLE sys_user COMMENT '用戶表'; CREATE TABLE sys_role ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '角色ID', role_name VARCHAR(50) COMMENT '角色名', enabled INT COMMENT '有效標誌', create_by BIGINT COMMENT '建立人', create_time DATETIME COMMENT '建立時間', PRIMARY KEY (id) ); ALTER TABLE sys_role COMMENT '角色表'; CREATE TABLE sys_privilege ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '權限ID', privilege_name VARCHAR(50) COMMENT '權限名稱', privilege_url VARCHAR(200) COMMENT '權限URL', PRIMARY KEY (id) ); ALTER TABLE sys_privilege COMMENT '權限表'; CREATE TABLE sys_user_role ( user_id BIGINT COMMENT '用戶ID', role_id BIGINT COMMENT '角色ID' ); ALTER TABLE sys_user_role COMMENT '用戶角色關聯表'; CREATE TABLE sys_role_privilege ( role_id BIGINT COMMENT '角色ID', privilege_id BIGINT COMMENT '權限ID' ); ALTER TABLE sys_role_privilege COMMENT '角色權限關聯表';
而後執行以下腳本添加測試數據:微信
INSERT INTO sys_user VALUES (1,'admin','123456','admin@mybatis.tk','管理員',NULL,current_timestamp); INSERT INTO sys_user VALUES (1001,'test','123456','test@mybatis.tk','測試用戶',NULL,current_timestamp); INSERT INTO sys_role VALUES (1,'管理員',1,1,current_timestamp); INSERT INTO sys_role VALUES (2,'普通用戶',1,1,current_timestamp); INSERT INTO sys_user_role VALUES (1,1); INSERT INTO sys_user_role VALUES (1,2); INSERT INTO sys_user_role VALUES (1001,2); INSERT INTO sys_privilege VALUES (1,'用戶管理','/users'); INSERT INTO sys_privilege VALUES (2,'角色管理','/roles'); INSERT INTO sys_privilege VALUES (3,'系統日誌','/logs'); INSERT INTO sys_privilege VALUES (4,'人員維護','/persons'); INSERT INTO sys_privilege VALUES (5,'單位維護','/companies'); INSERT INTO sys_role_privilege VALUES (1,1); INSERT INTO sys_role_privilege VALUES (1,2); INSERT INTO sys_role_privilege VALUES (1,3); INSERT INTO sys_role_privilege VALUES (2,4); INSERT INTO sys_role_privilege VALUES (2,5);
在包com.zwwhnly.mybatisaction.model下依次建立這5張表對應的實體類:session
package com.zwwhnly.mybatisaction.model; import java.util.Date; /** * 用戶表 */ public class SysUser { /** * 用戶ID */ private Long id; /** * 用戶名 */ private String userName; /** * 密碼 */ private String userPassword; /** * 郵箱 */ private String userEmail; /** * 簡介 */ private String userInfo; /** * 頭像 */ private byte[] headImg; /** * 建立時間 */ private Date createTime; // 按Alt+Insert快捷鍵生成get和set方法 }
package com.zwwhnly.mybatisaction.model; import java.util.Date; /** * 角色表 */ public class SysRole { /** * 角色ID */ private Long id; /** * 角色名 */ private String roleName; /** * 有效標誌 */ private Integer enabled; /** * 建立人 */ private Long createBy; /** * 建立時間 */ private Date createTime; // 按Alt+Insert快捷鍵生成get和set方法 }
能夠參考相似的命名方式建立SysPrivilege.java,SysUserRole.java,SysRolePrivilege.java。mybatis
也能夠按照文末提供的源碼地址下載下源代碼。
注意事項:
1)MyBatis默認遵循「下劃線轉駝峯」命名方式。
如sys_user表對應的實體類名是Sys_User,數據庫字段user_name對應的實體類字段是userName。
2)在實體類中不要使用Java的基本類型,基本類型包括byte、int、short、long、float、doubule、char、boolean。
由於Java中的基本類型會有默認值,例如當某個類中存在private int age;字段時,age的默認值爲0,因此沒法知足age爲null的狀況,若是使用age !=null,結果總爲ture,會致使一些隱藏的bug。
在src/main/resources下的com/zwwhnly/mybatisaction/mapper目錄下依次建立5張表對應的Mapper.xml文件。
爲了後續更快速的建立Mapper.xml文件,咱們能夠按照以下步驟添加模版:
上圖中的內容框中輸入如下內容:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper> </mapper>
而後選中目錄,右鍵新增文件,以下圖所示:
剛生成的SysUserMapper.xml內容以下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper> </mapper>
咱們只須要給mapper標籤添加個namespace屬性便可:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.zwwhnly.mybatisaction.mapper.SysUserMapper"> </mapper>
按照一樣的方式依次建立SysRoleMapper.xml,SysPrivilegeMapper.xml,SysUserRoleMapper.xml和SysRolePrivilegeMapper.xml。
建立完成後,打開咱們在上篇博客中建立的mybatis-config.xml文件,修改
<mappers> <mapper resource="com/zwwhnly/mybatisaction/mapper/CountryMapper.xml"/> <mapper resource="com/zwwhnly/mybatisaction/mapper/SysUserMapper.xml"/> <mapper resource="com/zwwhnly/mybatisaction/mapper/SysRoleMapper.xml"/> <mapper resource="com/zwwhnly/mybatisaction/mapper/SysPrivilegeMapper.xml"/> <mapper resource="com/zwwhnly/mybatisaction/mapper/SysUserRoleMapper.xml"/> <mapper resource="com/zwwhnly/mybatisaction/mapper/SysRolePrivilegeMapper.xml"/> </mappers>
使用這種方式,最明顯的缺點就是,咱們後續若是新增了Mapper.xml文件,仍然須要來修改文件,很是很差維護,所以咱們修改爲以下配置方式,配置一個包名:
<mappers> <package name="com.zwwhnly.mybatisaction.mapper"/> </mappers>
修改完成後,運行上篇博客中的單元測試CountryMapperTest,發現執行報以下錯誤:
報錯的緣由是上篇博客中,咱們並無爲CountryMapper.xml文件建立對應的接口,使用包名配置方式後,就須要建立,因此解決方案就是在src/main/java下新建包com.zwwhnly.mybatisaction.mapper下,而後在該包下新建接口CountryMapper,而後在接口中添加方法selectAll()。
package com.zwwhnly.mybatisaction.mapper; import com.zwwhnly.mybatisaction.model.Country; import java.util.List; public interface CountryMapper { /** * 查詢所有國家 * * @return */ List<Country> selectAll(); }
找到src/main/java目錄下的包com.zwwhnly.mybatisaction.mapper,在該包下建立XML文件對應的接口類,分別爲SysUserMapper.java,SysRoleMapper.java,SysPrivilegeMapper.java,SysUserRoleMapper.java,SysRolePrivilegeMapper.java。
這裏只展現下SysUserMapper.java的代碼:
package com.zwwhnly.mybatisaction.mapper; public interface SysUserMapper { }
注意事項:當Mapper接口和XML文件關聯的時候,命名空間namespace的值須要配置成接口的全限定名稱,MyBatis內部就是經過這個值將接口和XML關聯起來的。
例如SysUserMapper.xml中配置的namespace就com.zwwhnly.mybatisaction.mapper.SysUserMapper
假設咱們須要經過id查詢用戶的信息,首先,咱們須要打開SysUserMapper.java接口定義方法:
/** * 經過id查詢用戶 * * @param id * @return */ SysUser selectById(Long id);
而後打開對應的SysUserMapper.xml文件添加以下內容:
<resultMap id="sysUserMap" type="com.zwwhnly.mybatisaction.model.SysUser"> <id property="id" column="id"/> <result property="userName" column="user_name"/> <result property="userPassword" column="user_password"/> <result property="userEmail" column="user_email"/> <result property="userInfo" column="user_info"/> <result property="headImg" column="head_img" jdbcType="BLOB"/> <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/> </resultMap> <select id="selectById" resultMap="sysUserMap"> SELECT * FROM sys_user WHERE id = #{id} </select>
說明:
1)MyBatis經過select標籤的id屬性值和接口的名稱進行關聯。
2)標籤的id屬性值不能出現英文句號"."。
3)標籤的id屬性值在同一個命名空間下不能重複。
4)由於接口方法是能夠重載的,因此接口中能夠出現多個同名但參數不一樣的方法,可是XML中id的值不能重複,所以接口中的全部同名方法會對應着XML中的同一個id的方法。
爲了驗證第2點,咱們將selectById修改爲select.ById:
<select id="select.ById" resultMap="sysUserMap"> SELECT * FROM sys_user WHERE id = #{id} </select>
此時若是調用該方法,會報以下錯誤:
爲了驗證第3點,咱們將XML內容修改成以下:
<select id="selectById" resultMap="sysUserMap"> SELECT * FROM sys_user WHERE id = #{id} </select> <select id="selectById" resultMap="sysUserMap"> SELECT * FROM sys_user WHERE id = #{id} </select>
此時若是調用該方法,會報以下錯誤:
XML 代碼講解:
resultMap標籤用於配置Java對象的屬性和查詢結果列的對應關係,經過resultMap中配置的column和property能夠將查詢列的值映射到type對象的屬性上。
上面查詢語句用到的resultMap標籤講解:
假設咱們須要查詢全部用戶的信息,首先,咱們須要打開SysUserMapper.java接口定義方法:
/** * 查詢所有用戶 * * @return */ List<SysUser> selectAll();
而後打開對應的SysUserMapper.xml文件添加以下內容:
<select id="selectAll" resultType="com.zwwhnly.mybatisaction.model.SysUser"> SELECT id, user_name userName, user_password userPassword, user_email userEmail, user_info userInfo, head_img headImg, create_time createTime FROM sys_user </select>
注意事項:這裏咱們並無使用resultMap屬性來設置要返回結果的類型,而是經過resultType屬性直接指定
要返回結果的類型,使用此種方式須要設置查詢列的別名,別名要和resultType指定對象的屬性名保持一致,
進而實現自動映射。
MyBatis提供了一個全局屬性mapUnderscoreToCamelCase,將這個屬性的值設置爲ture能夠自動將如下劃線命名的數據庫列映射到Java對象的駝峯式命名屬性中。
那麼如何打開呢?
方法是打開咱們在上篇博客中新建的mybatis-config文件,在settings節點添加以下配置:
<settings> <!--其餘配置--> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
此時,前面的selectAll語句能夠簡化爲以下。
<select id="selectAll" resultType="com.zwwhnly.mybatisaction.model.SysUser"> SELECT id, user_name, user_password, user_email, user_info, head_img, create_time FROM sys_user </select>
新建基礎測試類BaseMapperTest,代碼以下。
package com.zwwhnly.mybatisaction.mapper; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.BeforeClass; import java.io.IOException; import java.io.Reader; /** * 基礎測試類 */ public class BaseMapperTest { private static SqlSessionFactory sqlSessionFactory; @BeforeClass public static void init() { try { Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); reader.close(); } catch (IOException e) { e.printStackTrace(); } } public SqlSession getSqlSession() { return sqlSessionFactory.openSession(); } }
將上篇博客中的CountryMapperTest類代碼修改成以下。
package com.zwwhnly.mybatisaction.mapper; import com.zwwhnly.mybatisaction.model.Country; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.List; public class CountryMapperTest extends BaseMapperTest { @Test public void testSelectAll() { SqlSession sqlSession = getSqlSession(); try { List<Country> countryList = sqlSession.selectList("com.zwwhnly.mybatisaction.mapper.CountryMapper.selectAll"); printCountryList(countryList); } finally { sqlSession.close(); } } private void printCountryList(List<Country> countryList) { for (Country country : countryList) { System.out.printf("%-4d%4s%4s\n", country.getId(), country.getCountryname(), country.getCountrycode()); } } }
修改點:
1)繼承基礎測試類BaseMapperTest,調用基類getSqlSession()方法便可獲取SqlSession對象,實現代碼重用。
2)selectList()方法的參數值由selectAll修改成com.zwwhnly.mybatisaction.mapper.CountryMapper.selectAll,
由於在SysUserMapper中也添加了一個selectAll()方法,selectAll再也不惟一,所以調用時必須帶上namespace。
參考CountryMapperTest測試類新建SysUserMapperTest測試類,代碼以下。
package com.zwwhnly.mybatisaction.mapper; import com.zwwhnly.mybatisaction.model.SysUser; import org.apache.ibatis.session.SqlSession; import org.junit.Assert; import org.junit.Test; import java.util.List; public class SysUserMapperTest extends BaseMapperTest { @Test public void testSelectById() { SqlSession sqlSession = getSqlSession(); try { SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class); SysUser sysUser = sysUserMapper.selectById(1L); Assert.assertNotNull(sysUser); Assert.assertEquals("admin", sysUser.getUserName()); } finally { sqlSession.close(); } } @Test public void testSelectAll() { SqlSession sqlSession = getSqlSession(); try { SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class); List<SysUser> sysUserList = sysUserMapper.selectAll(); Assert.assertNotNull(sysUserList); Assert.assertTrue(sysUserList.size() > 0); } finally { sqlSession.close(); } } }
運行測試類代碼,測試經過,輸出日誌以下所示。
DEBUG [main] - ==> Preparing: SELECT id, user_name, user_password, user_email, user_info, head_img, create_time FROM sys_user
DEBUG [main] - ==> Parameters:
TRACE [main] - <== Columns: id, user_name, user_password, user_email, user_info, head_img, create_time
TRACE [main] - <== Row: 1, admin, 123456, admin@mybatis.tk, <
>, < >, 2019-06-27 18:21:07.0 TRACE [main] - <== Row: 1001, test, 123456, test@mybatis.tk, <
>, < >, 2019-06-27 18:21:07.0 DEBUG [main] - <== Total: 2
DEBUG [main] - ==> Preparing: SELECT * FROM sys_user WHERE id = ?
DEBUG [main] - ==> Parameters: 1(Long)
TRACE [main] - <== Columns: id, user_name, user_password, user_email, user_info, head_img, create_time
TRACE [main] - <== Row: 1, admin, 123456, admin@mybatis.tk, <
>, < >, 2019-06-27 18:21:07.0 DEBUG [main] - <== Total: 1
源碼地址:https://github.com/zwwhnly/mybatis-action.git,歡迎下載。
劉增輝《MyBatis從入門到精通》
打個小廣告,歡迎掃碼關注微信公衆號:「申城異鄉人」,按期分享Java技術乾貨,讓咱們一塊兒進步。