- 概述
- 一个简单的权限控制需求
- XML方式概述及步骤
- 1. 首先在src/main/resources下com.artisan.mybatis.xml.mapper目录下创建5个表各自对应的XML文件
- 2. 然后在src/main/java先创建包com.artisan.mybatis.xml.mapper,接着在该包下面创建XML文件对应的接口类
- 3. Mapper.xml 如何编写,我们在 MyBatis-01 MyBatis入门篇中已经简单介绍过了,下面我们以用户表对应的Mapper接口 UserMapper.java为例来演示下接口的编写
- 4. 打开UserMapper.xml 文件,输入如下内容
- 5.准备好这几个XML映射文件后,需要在 MyBatis全局配置文件中的mapper元素配置所有的mapper
概述
我们通过一个简单的权限控制需求,采用RBAC(Role-Based Access Control基于角色的访问控制)方式,通过这个示例我们来学习 MyBatis XML方式的基本用法。
一个简单的权限控制需求
需求: 一个用户拥有若干角色,一个角色拥有若干权限。 权限就是对摸个资源或者模块的某种操作(CRUD). 这样就构成了“用户-角色-权限”的授权模型。
在这种模型中,用户与角色、角色与权限,一般都是多对多的关系
我们先来看下数据模型
创建数据库表
5张表,创建在mysql数据库中。
-- ----------------------------
-- Table structure for sys_privilege
-- ----------------------------
DROP TABLE IF EXISTS `sys_privilege`;
CREATE TABLE `sys_privilege` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '权限ID',
`privilege_name` varchar(50) DEFAULT NULL COMMENT '权限名称',
`privilege_url` varchar(200) DEFAULT NULL COMMENT '权限URL',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='权限表';
-- ----------------------------
-- Records of sys_privilege
-- ----------------------------
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');
-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '角色ID',
`role_name` varchar(50) DEFAULT NULL COMMENT '角色名',
`enabled` int(11) DEFAULT NULL COMMENT '有效标志',
`create_by` bigint(20) DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='角色表';
-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('1', '管理员', '1', '1', SYSDATE());
INSERT INTO `sys_role` VALUES ('2', '普通用户', '1', '1', SYSDATE());
-- ----------------------------
-- Table structure for sys_role_privilege
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_privilege`;
CREATE TABLE `sys_role_privilege` (
`role_id` bigint(20) DEFAULT NULL COMMENT '角色ID',
`privilege_id` bigint(20) DEFAULT NULL COMMENT '权限ID'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色权限关联表';
为了方便对表进行直接操作,此处没有创建表之间的外键关系。 对于表之间的关系,会通过业务逻辑来进行限制
-- ----------------------------
-- Records of sys_role_privilege
-- ----------------------------
INSERT INTO `sys_role_privilege` VALUES ('1', '1');
INSERT INTO `sys_role_privilege` VALUES ('1', '3');
INSERT INTO `sys_role_privilege` VALUES ('1', '2');
INSERT INTO `sys_role_privilege` VALUES ('2', '4');
INSERT INTO `sys_role_privilege` VALUES ('2', '5');
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`user_name` varchar(50) DEFAULT NULL COMMENT '用户名',
`user_password` varchar(50) DEFAULT NULL COMMENT '密码',
`user_email` varchar(50) DEFAULT 'test@artisan.com' COMMENT '邮箱',
`user_info` text COMMENT '简介',
`head_img` blob COMMENT '头像',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1035 DEFAULT CHARSET=utf8 COMMENT='用户表';
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1', 'admin', '123456', 'admin@artisan.com', '管理员用户', 0x1231231230, SYSDATE());
INSERT INTO `sys_user` VALUES ('1001', 'test', '123456', 'test@artisan.com', '测试用户', 0x1231231230, SYSDATE());
-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
`user_id` bigint(20) DEFAULT NULL COMMENT '用户ID',
`role_id` bigint(20) DEFAULT NULL COMMENT '角色ID'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户角色关联表';
为了方便对表进行直接操作,此处没有创建表之间的外键关系。 对于表之间的关系,会通过业务逻辑来进行限制
-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES ('1', '1');
INSERT INTO `sys_user_role` VALUES ('1', '2');
INSERT INTO `sys_user_role` VALUES ('1001', '2');
创建实体类
MyBatis默认遵循“下面线转驼峰”命名方式的规则,所以在创建实体类时一般都按照这种方式进行创建。
首先看下用户表对应的实体类SysUser的代码
package com.artisan.mybatis.xml.domain;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
/**
*
*
* @ClassName: SysUser
*
* @Description: 用户表
*
* @author: Mr.Yang
*
* @date: 2018年4月13日 下午9:24:21
*/
public class SysUser implements Serializable {
private static final long serialVersionUID = 5736486618394472355L;
/**
* 用户ID
*/
private Long id;
/**
* 用户名
*/
private String userName;
/**
* 密码
*/
private String userPassword;
/**
* 邮箱
*/
private String userEmail;
/**
* 简介
*/
private String userInfo;
/**
* 头像
*/
private byte[] headImg;
/**
* 创建时间
*/
private Date createTime;
/**
* 用户角色
*/
private SysRole sysRole;
/**
* 用户的角色集合
*/
private List<SysRole> roleList;
// 省略setter和getter方法
@Override
public String toString() {
return "SysUser [id=" + id + ", userName=" + userName + ", userPassword=" + userPassword + ", userEmail=" + userEmail + ", userInfo=" + userInfo + ", headImg=" + Arrays.toString(headImg)
+ ", createTime=" + createTime + "]";
}
}
对于SysUser实体类,首先需要注意的是命名方式,它的类名和属性名都采用了“下面线转驼峰”方式。 具体采用什么样的命名方式并不重要(方式一致即可)。 在后面使用这些对象的时候,可以通过resultMap对数据库中的列和类的字段配置映射关系
在 MyBatis中,关于数据库字段和Java类型的对应关系,不需要可以的去记,但需要注意的一个特殊的类型,byte[] ,这个类型一般对应数据库中的BLOB、LONGVARBINARY以及一些二进制流相关的字段类型
注意: 由于Java中的基本类型会有默认值,例如当某个类中存在private int age字段时,创建这个类时,age会有个默认值0。 当使用age属性时,它总会有值。 因此在某些情况下,便无法实现age为null . 并且在动态SQL的部分,如果使用age !=null 进行判断,结果总会为true,因而会导致很多隐藏的问题。
所以在实体类中不要使用基本类型。 基本类型包括:byte、int、short、long、float、double、char、boolean
SysRole
package com.artisan.mybatis.xml.domain;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
*
*
* @ClassName: SysRole
*
* @Description: 角色表
*
* @author: Mr.Yang
*
* @date: 2018年4月13日 下午9:29:31
*/
public class SysRole implements Serializable {
private static final long serialVersionUID = 6320941908222932112L;
/**
* 角色ID
*/
private Long id;
/**
* 角色名
*/
private String roleName;
/**
* 有效标志
*/
private Integer enabled;
/**
* 创建人
*/
private String createBy;
/**
* 创建时间
*/
private Date createTime;
/**
* 用户信息
*/
private SysUser user;
/**
* 角色包含的权限列表
*/
List<SysPrivilege> privilegeList;
// 省略setter和getter
@Override
public String toString() {
return "SysRole [id=" + id + ", roleName=" + roleName + ", enabled=" + enabled + ", createBy=" + createBy + ", createTime=" + createTime + ", user=" + user + ", privilegeList="
+ privilegeList + "]";
}
}
SysUserRole
package com.artisan.mybatis.xml.domain;
/**
*
*
* @ClassName: SysUserRole
*
* @Description: 用户角色关联表
*
* @author: Mr.Yang
*
* @date: 2018年4月13日 下午9:44:31
*/
public class SysUserRole {
/**
* 用户ID
*/
private Long userId;
/**
* 角色ID
*/
private Long roleId;
// 省略setter和getter
@Override
public String toString() {
return "SysUserRole [userId=" + userId + ", roleId=" + roleId + "]";
}
}
SysPrivilege
package com.artisan.mybatis.xml.domain;
import java.io.Serializable;
/**
*
*
* @ClassName: SysPrivilege
*
* @Description: 权限表
*
* @author: Mr.Yang
*
* @date: 2018年4月13日 下午9:45:15
*/
public class SysPrivilege implements Serializable {
private static final long serialVersionUID = 6315662516417216377L;
/**
* 权限ID
*/
private Long id;
/**
* 权限名称
*/
private String privilegeName;
/**
* 权限URL
*/
private String privilegeUrl;
// 省略setter和getter
@Override
public String toString() {
return "SysPrivilege [id=" + id + ", privilegeName=" + privilegeName + ", privilegeUrl=" + privilegeUrl + "]";
}
}
SysRolePrivilege
package com.artisan.mybatis.xml.domain;
/**
*
*
* @ClassName: SysRolePrivilege
*
* @Description: 角色权限关联表
*
* @author: Mr.Yang
*
* @date: 2018年4月13日 下午9:46:46
*/
public class SysRolePrivilege {
/**
* 角色ID
*/
private Long roleId;
/**
* 权限ID
*/
private Long privilegeId;
// 省略setter和getter
@Override
public String toString() {
return "SysRolePrivilege [roleId=" + roleId + ", privilegeId=" + privilegeId + "]";
}
}
创建实体类的过程比较枯燥,后面可以通过 MyBatis官方提供的工具 MyBatis Generator( MyBatis代码生成器,简称MBG)根据数据库表的信息自动生成这些类,减少工作量。
XML方式概述及步骤
* MyBatis3.0比2.0一个最大的变化,就是支持使用接口来调用方法。*
以前使用SqlSession通过命名空间调用 MyBatis的方法时,首先需要用到命名空间和方法id组成的字符串来调用对应的方法。 当参数多于1个的时候,需要将参数放到一个Map对象中,通过Map传递多个参数,使用起来不方便,而且无法避免很多重复的代码。
使用接口调用方式就会方便很多, MyBatis使用Java的动态代理可以直接通过接口来调用相应的方法,不需要提供接口的实现类,更不需要在实现类中使用SqlSession以命名空间间接调用。 另外,当有个多个参数的时候,通过参数注解@Parma设置参数的名字省去了手工构造Map参数的过程。 尤其在Spring中使用的时候 ,可以配置为自动扫描所有的接口类,直接将接口注入到需要用到的地方。
我们来看下如何使用 MyBatis的XML方式
1. 首先在src/main/resources下com.artisan.mybatis.xml.mapper目录下创建5个表各自对应的XML文件
2. 然后在src/main/java先创建包com.artisan.mybatis.xml.mapper,接着在该包下面创建XML文件对应的接口类
3. Mapper.xml 如何编写,我们在 MyBatis-01 MyBatis入门篇中已经简单介绍过了,下面我们以用户表对应的Mapper接口 UserMapper.java为例来演示下接口的编写
package com.artisan.mybatis.xml.mapper;
public interface UserMapper {
}
到目前为止,Mapper接口和对应的XML文件都是空的,后续逐步完善接口方法。
4. 打开UserMapper.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接口和XML文件关联的时候, namespace的值就需要配置成接口的全限定名称 -->
<mapper namespace="com.artisan.mybatis.xml.mapper.UserMapper">
</mapper>
需要注意的是mapper标签的namespace属性。 当Mapper接口和XML文件关联的时候,命名空间namespace的值就需要配置成接口的全限定名称。
比如UserMapper接口对应的com.artisan.mybatis.xml.mapper.UserMapper , Mybatis内部就是通过这个值将接口和XML关联起来的。 具体原理后续分析。
按照相同的方式将其他4个Mapper.xml编写完成
5.准备好这几个XML映射文件后,需要在 MyBatis全局配置文件中的mapper元素配置所有的mapper
<!-- 逐一配置,比较繁琐,容易遗漏,接口方式不推荐 -->
<mapper resource="com/artisan/mybatis/xml/mapper/UserMapper.xml"/>
<mapper resource="com/artisan/mybatis/xml/mapper/UserRoleMapper.xml"/>
<mapper resource="com/artisan/mybatis/xml/mapper/RoleMapper.xml"/>
<mapper resource="com/artisan/mybatis/xml/mapper/PrivilegeMapper.xml"/>
<mapper resource="com/artisan/mybatis/xml/mapper/RolePrivilegeMapper.xml"/>
这种方式需要将所有的映射文件一一列举出来,如果增加了新的映射文件,还需要注意在此处进行配置,不推荐。
因为此处所有的XML映射文件都有对应的Mapper接口,所以有一种更加简单的配置方式
<!-- 推荐:通过包的方式配置,mybatis会先查找对应包下的所有的接口 -->
<package name="com.artisan.mybatis.xml.mapper"/>
这种配置方式会先查找com.artisan.mybatis.xml.mapper包下的所有接口,循环对接口进行如下操作:
1. 判断接口对应的命名空间是否已经存在,存在抛出异常,不存在就继续进行接下来的操作
2. 加载接口对应的XML映射文件,将接口全限定名转换为路径,比如将接口com.artisan.mybatis.xml.mapper.UserMapper转换为com/artisan/mybatis/xml/mapper/UserMapper.xml. 以.xml为后缀搜索xml资源,如果找到就解析xml
3. 处理接口中的注解方法
因为这里的接口和xml映射文件完全符合上面操作的第二点,因此直接配置包名就能够自动扫描包下的接口和XML映射文件,省去了很多麻烦。