MyBatis从入门到精通(二):MyBatis XML方式的基本用法之Select

最近在读刘增辉老师所著的《MyBatis从入门到精通》一书,颇有收获,因而将本身学习的过程以博客形式输出,若有错误,欢迎指正,如帮助到你,不胜荣幸!java

1. 明确需求

书中提到的需求是一个基于角色的权限控制需求(RBAC,即Role-Based Access Control),提到权限管理,相信你们都不陌生,由于大部分的系统都是须要权限管理的,我在上家公司负责的系统之一就是权限系统,设计思路和书中提到的差很少,大体描述以下:mysql

1)权限点用来管理要控制权限的资源,好比某个页面,某个按钮。git

2)建立一个角色,给这个角色分配某些权限点,好比商品模块的全部页面的权限。github

3)新建一个用户,给这个用户分配某些角色。sql

数据关系图以下所示:数据库

2. 数据准备

首先执行以下脚本建立上图中的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);
复制代码

3. 建立实体类

在包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。

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. 源码及参考

源码地址:github.com/zwwhnly/myb…,欢迎下载。

刘增辉《MyBatis从入门到精通》

IntelliJ IDEA中建立xml文件

9. 最后

打个小广告,欢迎扫码关注微信公众号:「申城异乡人」,按期分享Java技术干货,让咱们一块儿进步。

相关文章
相关标签/搜索