MyBatis系列(二):MyBatis XML方式的基本用法之Select


1. 明確需求

書中提到的需求是一個基於角色的權限控制需求(RBAC,即Role-Based Access Control),提到權限管理,相信大家都不陌生,因為大部分的系統都是需要權限管理的,我在上家公司負責的系統之一就是權限系統,設計思路和書中提到的差不多,大致描述如下:

1)權限點用來管理要控制權限的資源,比如某個頁面,某個按鈕。

2)創建一個角色,給這個角色分配某些權限點,比如商品模塊的所有頁面的權限。

3)新建一個用戶,給這個用戶分配某些角色。

數據關系圖如下所示:

2. 數據准備

首先執行如下腳本創建上圖中的5張表:用戶表,角色表,權限表,用戶角色關聯表,角色權限關聯表。

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);

3. 創建實體類

在包com.zwwhnly.mybatisaction.model下依次創建這5張表對應的實體類:

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。

也可以按照文末提供的源碼地址下載下源代碼。

注意事項:

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。

4. 創建Mapper.xml文件

在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();
}

5. 創建Mapper接口

找到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

6. select用法

6.1 查詢單條數據

假設我們需要通過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 代碼講解:

  • select:映射查詢語句使用的標簽。
  • id:查詢語句的唯一標識符,可用來代表這條語句。
  • resultMap:用於設置數據庫返回列和Java對象的映射關系。
  • SELECT * FROM sys_user WHERE id = #{id}是查詢語句。
  • {id}:MyBatis SQL中使用預編譯參數的一種方式,大括號中的id代表傳入的參數名。

resultMap標簽用於配置Java對象的屬性和查詢結果列的對應關系,通過resultMap中配置的column和property可以將查詢列的值映射到type對象的屬性上。

上面查詢語句用到的resultMap標簽講解:

  • id:必填且唯一。select標簽resultMap屬性的值為此處id設置的值。
  • type:必填。用於配置查詢列所映射到的Java對象模型。
  • column:從數據庫中得到的列名或者列的別名。
  • property:要映射到的列結果的屬性,即Java對象模型的屬性。
  • jdbcType:列對應的數據庫類型。

6.2 查詢多條數據

假設我們需要查詢所有用戶的信息,首先,我們需要打開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>

7. 單元測試

新建基礎測試類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

8. 源碼及參考

源碼地址:https://github.com/zwwhnly/mybatis-action.git,歡迎下載。

劉增輝《MyBatis從入門到精通》

IntelliJ IDEA中創建xml文件

原創不易,如果覺得文章能學到東西的話,歡迎點個贊、評個論、關個注,這是我堅持寫作的最大動力。

如果有興趣,歡迎添加我的微信:zwwhnly,等你來聊技術、職場、工作等話題(PS:我是一名奮斗在上海的程序員)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM