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

本章将通过完成权限管理的常见业务来学习 MyBatis XML方式的基本用法。

这个权限管理控制,采用RBAC(Role-Based Access Control,基于角色的访问控制)方式。

2.1 一个简单的权限控制需求

权限管理的需求:一个用户拥有若干角色,一个角色拥有若干权限,权限就是对某个模块资源的某种操作(增、删、改、查),这便是“用户-角色-权限”的授权模型。

采用RBAC授权模型,用户与角色之间、角色与权限之间,一般是多对多的关系。

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

2.1.1 创建数据库表

在已经创建好的 testmybatis 数据库中执行如下SQL脚本。(如何通过SQL脚本用Navicat管理数据库,请参考我上一篇博客的 1.3.1  准备数据库

执行如下脚本创建上图中的5张表:用户表,角色表,权限表,用户角色关联表,角色权限关联表。

本书中此处没有创建表之间的外键关系,对于表之间的关系,会通过业务逻辑来进行限制。

-- --2.1.1数据库创建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 '角色权限关联表';

-- --2.1.1数据库创建5张表

为了方便后面的测试,接着在表中用SQL脚本插入一些测试数据。

-- --2.1.1插入测试数据
INSERT INTO `sys_user` VALUES ('','admin','','admin@mybatis.tk','管理员',NULL,'2019-07-12 17:00:58');
INSERT INTO `sys_user` VALUES ('','test','','test@mybatis.tk','测试用户',NULL,'2019-07-12 17:01:52'); INSERT INTO `sys_role` VALUES ('','管理员','','','2019-11-17 18:54:48');
INSERT INTO `sys_role` VALUES ('','普通用户','','',current_timestamp); INSERT INTO `sys_user_role` VALUES ('','');
INSERT INTO `sys_user_role` VALUES ('','');
INSERT INTO `sys_user_role` VALUES ('',''); INSERT INTO `sys_privilege` VALUES ('','用户管理','/users');
INSERT INTO `sys_privilege` VALUES ('','角色管理','/roles');
INSERT INTO `sys_privilege` VALUES ('','系统日志','/logs');
INSERT INTO `sys_privilege` VALUES ('','人员维护','/persons');
INSERT INTO `sys_privilege` VALUES ('','单位维护','/companies'); INSERT INTO `sys_role_privilege` VALUES ('','');
INSERT INTO `sys_role_privilege` VALUES ('','');
INSERT INTO `sys_role_privilege` VALUES ('','');
INSERT INTO `sys_role_privilege` VALUES ('','');
INSERT INTO `sys_role_privilege` VALUES ('','');

-- --2.1.1插入测试数据

2.1.2  创建实体类

在包(package) cn.bjut.simple.model 下依次创建这5张数据库表 未来(查询结果)映射的实体类。

用户表

package cn.bjut.simple.model;

import java.util.Date;

public class SysUser {
public Long getId() {
return id;
} public void setId(Long id) {
this.id = id;
} public String getUserName() {
return userName;
} public void setUserName(String userName) {
this.userName = userName;
} public String getUserPassword() {
return userPassword;
} public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
} public String getUserEmail() {
return userEmail;
} public void setUserEmail(String userEmail) {
this.userEmail = userEmail;
} public String getUserInfo() {
return userInfo;
} public void setUserInfo(String userInfo) {
this.userInfo = userInfo;
} public byte[] getHeadImg() {
return headImg;
} public void setHeadImg(byte[] headImg) {
this.headImg = headImg;
} public Date getCreateTime() {
return createTime;
} public void setCreateTime(Date createTime) {
this.createTime = createTime;
} /** * 用户ID */ private Long id;
/** * 用户名 */ private String userName;
/** * 密码 */ private String userPassword;
/** * 邮箱 */ private String userEmail;
/** * 简介 */ private String userInfo;
/** * 头像 */ private byte[] headImg;
/** * 创建时间 */ private Date createTime; }

public class SysUser

用户与角色关联表

package cn.bjut.simple.model;

    /**
* 用户角色关联表
*/
public class SysUserRole {
private Long userId;
private Long roleId; public Long getUserId() {
return userId;
} public Long getRoleId() {
return roleId;
} public void setUserId(Long userId) {
this.userId = userId;
} public void setRoleId(Long roleId) {
this.roleId = roleId;
}
}

public class SysUserRole

角色表

package cn.bjut.simple.model;

import java.util.Date;

/**
* 角色表
*/
public class SysRole {
private Long id;
private String roleName;
private Integer enabled;
private Long createBy;
private Date createTime;
private SysUser user; public Long getId() {
return id;
} public String getRoleName() {
return roleName;
} public Integer getEnabled() {
return enabled;
} public Long getCreateBy() {
return createBy;
} public Date getCreateTime() {
return createTime;
} public SysUser getUser() {
return user;
} public void setId(Long id) {
this.id = id;
} public void setRoleName(String roleName) {
this.roleName = roleName;
} public void setEnabled(Integer enabled) {
this.enabled = enabled;
} public void setCreateBy(Long createBy) {
this.createBy = createBy;
} public void setCreateTime(Date createTime) {
this.createTime = createTime;
} public void setUser(SysUser user) {
this.user = user;
}
}

public class SysRole

权限表

package cn.bjut.simple.model;

/**
* 权限表
*/
public class SysPrivilege {
private Long id;
private String privilegeName;
private String privilegeUrl; public Long getId() {
return id;
} public String getPrivilegeName() {
return privilegeName;
} public String getPrivilegeUrl() {
return privilegeUrl;
} public void setId(Long id) {
this.id = id;
} public void setPrivilegeName(String privilegeName) {
this.privilegeName = privilegeName;
} public void setPrivilegeUrl(String privilegeUrl) {
this.privilegeUrl = privilegeUrl;
}
}

public class SysPrivilege

角色与权限关联表

package cn.bjut.simple.model;

/**
* 角色与权限关联表
*/
public class SysRolePrivilege {
private Long roleId;
private Long privilege; public Long getRoleId() {
return roleId;
} public Long getPrivilege() {
return privilege;
} public void setRoleId(Long roleId) {
this.roleId = roleId;
} public void setPrivilege(Long privilege) {
this.privilege = privilege;
}
}

public class SysRolePrivilege

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

可以参考上面创建Java实体类[JavaBean]的方式依次完成   SysUserRole 、SysRole 、SysPrivilege   、SysRolePrivilege 四个实体类的代码。

另外还可以根据本书在 第5章介绍使用MyBatis官方提供的工具 MyBatis Generator 插件 ,根据数据库表的字段信息自动生成这些实体类。

  • MyBatis默认遵循(从SQL到JAVA)“下划线转驼峰”的命名方式。如sys_user表对应的实体类名是SysUser,数据库字段user_name对应的实体类的变量名是userName。
  •  在实体类中不要使用Java的基本数据类型,基本类型包括 byte、int、short、long、float、doubule、char、boolean。因为Java基本类型会有默认值,例如当某个实体类(对应着一个数据库表)中存在private int age;如果使用age != null进行判断,结果总会为true  会导致很多隐藏的问题。一个特殊的类型“byte[]”不是Java基本数据类型。

2.2  使用接口+XML方式

注意:接口可以配合XML使用,也可以配合注解来使用。SSM整合或SpringBoot开发,一般使用接口+通用Mapper+MyBatis3实现单表CRUD操作。

首先,在 src/main/resources 的 cn.bjut.simple.mapper目录下创建5个表各自对应的XML映射文件,分别为 UserMapper.xml 、RoleMapper 、PrivilegeMapper 、 UserRoleMapper 和 RolePrivilegeMapper.xml 。

然后,在 src/main/java 下面创建包 cn.bjut.simple.mapper 。接着在该包下创建XML文件对应的接口类,分别为 UserMapper.java、RoleMapper、PrivilegeMapper、 UserRoleMapper 和 RolePrivilegeMapper.java 。

为了后续更快速的创建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 namespace="">
</mapper>

此处Mapper映射.xml文件的命名空间 namespace 的值需要配置成接口文件的全限定名称,例如 UserMapper接口对应的 cn.bjut.simple.mapper.UserMapper ,MyBatis内部就是通过这个值将接口和XML映射文件关联起来的。

准备好这几个XML映射文件后,还需要在1.3.2节中创建的 mybatis-config.xml配置文件中的 mappers元素中 配置所有的mapper文件路径。

    <mappers>
<mapper resource="cn/bjut/simple/mapper/CountryMapper.xml"/>
<mapper resource="cn/bjut/simple/mapper/UserMapper.xml"/>
<mapper resource="cn/bjut/simple/mapper/RoleMapper.xml"/>
<mapper resource="cn/bjut/simple/mapper/PrivilegeMapper.xml"/>
<mapper resource="cn/bjut/simple/mapper/UserRoleMapper.xml"/>
<mapper resource="cn/bjut/simple/mapper/RolePrivilegeMapper.xml"/>
</mappers>

使用这种配置方式,最明显的缺点就是,后续如果新增了Mapper.xml映射文件,仍然需要来此处修改mybatis-config文件,不好维护操作麻烦。因此我们修改成如下配置方式,配置一个包名。

<mappers>
<package name="cn.bjut.simple.mapper"/>
</mappers>

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


2.3  SELECT用法

查询单条数据

先写一个根据用户id查询用户信息的方法。在 UserMapper 接口中添加一个 selectById方法,代码如下。

    /**
* 通过id查询用户
*
* @param id
* @return
*/
SysUser selectById(Long id);

然后在对应的UserMapper.xml中添加如下的 <resultMap>和<select>部分的代码。

<?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="cn.bjut.simple.mapper.UserMapper">
<resultMap id="userMap" type="cn.bjut.simple.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="userMap"> //XML中的select标签的id属性值 = “与之对应接口的方法名” 
SELECT * FROM sys_user WHERE id = #{id}
</select> </mapper>

映射XML和接口的命名需要符合如下规则:

  • 标签的id属性值在任何时候都不能出现英文句号“.”,并且同一个命名空间下不能出现重复的id。
  • 接口的方法是可以重载的,所以接口中可以出现多个同名但参数不同的方法,但是XML中id的值不能重复,因而接口中的所有同名方法只会对应着XML中的同一个id的select方法。最常见的用法就是,同名方法中其中一个方法增加一个 RowBound 类型的参数用于实现分页查询

XML 标签和属性的讲解:

  • <select>:映射查询语句使用的标签。
  • id:命名空间中的唯一标识符,可用来代表这条语句。
  • resultMap:用于设置数据库返回值(列)的类型 和Java对象属性的映射关系。
  • SELECT * FROM sys_user WHERE id = #{id}是查询SQL语句。
  • #{id}:MyBatis SQL中使用预编译参数的一种方式,大括号中的id是传入的占位参数名。

在上面的 select 中,使用 resultMap 设置返回值的类型,这里的 userMap 就是上面 <resultMap> 中的 id 属性值,通过 id 引用需要的 <resultMap>。

resultMap 标签用于配置 Java 对象的属性和查询结果列的对应关系,通过 resultMap 中配置的 column 和 property 可以将查询列的值映射到 type 对象的属性上。

上面查询语句用到的resultMap包含的属性和标签讲解:

  • id:必填,并且唯一。在select标签中,resultMap属性的值为此处id所设置的值。
  • type:必填,用于配置查询列所映射到的Java实体类对象。
  • extends: 选填,可以配置当前的 resultMap 继承自其他的 resultMap ,属性值为继承 resultMap 的 id。
  • autoMapping: 选填(true/false),该配置可以覆盖全局的 autoMappingBehavior配置。

以上是 resultMap 的属性, resultMap 包含的所有标签如下。

constructor :配置使用构造方法注入结果,包含以下两个子标签。

  1. idArg:id 参数,标记结果作为 id(唯一值),可以帮助提高整体性能。
  2. arg:注入到构造方法的一个普通结果。

· id:一个 id 结果,标记结果作为 id(唯一值)。

· result:注入到 Java 对象属性的普通结果。

  • association :一个复杂的类型关联,许多结果将包成这种类型。
  • collection :复杂类型的集合。
  • discriminator :根据结果值来决定使用哪个结果映射。
  • case:基于某些值的结果映射。

接着看一下id和result标签包含的属性。

id: 一个id结果,标记结果作为id主键(或唯一值)的字段(可以有多个),它们的属性值是通过setter方法注入的。

result :  注入到JAVA对象属性的普通结果。

  • column:  从数据库中得到的列名,或者列的别名。
  • property:映射到列结果的属性。可以映射一些复杂对象中的属性,例如 “address.street.number” ,这会通过“.”方式的属性嵌套赋值。
  • javaType:  一个Java类的完全限定名,或通过typeAlias配置的类型别名。如果映射到 HashMap,则需要明确地指定 javaType 属性。
  • jdbcType:列对应的数据库类型。JDBC 类型仅仅需要对插入、更新、删除操作可能为空的列进行处理。

接口中定义的返回值类型必须和XML映射Mapper文件中配置的resultType类型一致。否则就会因为类型不一致而抛出异常。

返回值类型是由XML中的resultType(或resultMap中的type)决定的,不是由接口中写的返回值类型决定的。(本章讲XML方式,所以先忽略注解的情况)

查询返回多条数据:

在UserMapper接口中添加 selectAll方法,代码如下。

    /**
* 查询全部用户
*
* @return
*/
List<SysUser> selectAll();

在对应的 UserMapper.xml 中添加如下的<select>部分的代码。

    <select id="selectAll" resultType="cn.bjut.simple.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>

在定义查询接口中方法的返回值时,必须注意查询SQL可能返回的结果数量。若执行的SQL返回多个结果时(>1),必须使用 List<SysUser>作为返回值。

selectById  设置结果映射使用了resultMap标签 中的id值。

selectAll      通过 resultType 直接指定了返回结果的类型。

如果使用 resultType 来设置返回结果的类型,需要在 SQL 中为所有列名和属性名不一致的列设置别名,通过设置别名使最终的查询结果列和 resultType 指定对象的属性名保持一致,进而实现自动映射。

因为数据库和 Java 中的这两种命名方式很常见,因此 MyBatis 还提供了一个全局属性 mapUnderscoreToCamelCase ,通过配置这个属性为 true 可以自动将以下画线方式命名的数据库列映射到 Java 对象的驼峰式命名属性中。

    <settings>
<!--下划线转驼峰-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--配置日志输出接口-->
<setting name="logImpl" value="LOG4J"/> </settings>

mapUnderscoreToCamelCase

使用上述配置的时候,前面的 selectAll 可以改写如下。
    <select id="selectAll" resultType="cn.bjut.simple.model.SysUser">
SELECT id,
user_name ,
user_password ,
user_email ,
user_info ,
head_img ,
create_time
FROM sys_user
</select>

===============================================================================================================================

接下来通过测试用例来验证上面的两个查询。为了方便学习后面的大量测试,此处先根据第1章中的测试提取一个(后面测试类的父类)基础测试类 BaseMapperTest

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

/**
* 基础测试类
*/ 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 ignore) {
ignore.printStackTrace();
}
} public SqlSession getSqlSession(){
return sqlSessionFactory.openSession(); }
}

public class BaseMapperTest

将原来的 CountryMapperTest 测试类修改如下。

public  class CountryMapperTest2 extends  BaseMapperTest {

    @Test
public void testSelectAll(){
SqlSession sqlSession = this.getSqlSession(); //调用本类继承父类的成员方法
try {
List<Country> countryList = sqlSession.selectList("cn.bjut.simple.mapper.ContryMapper.selectAll");
printCountryList(countryList); //调用本类的成员方法可以省略this关键字
} finally {
sqlSession.close(); //不要忘记关闭释放sqlSession
}
} private void printCountryList(List<Country> countryList){
for(Country country : countryList){
System.out.printf("%-4d%4s%4s\n",
country.getId(),
country.getCountryname(),
country.getCountrycode());
}
}
}

public class CountryMapperTest2 extends BaseMapperTest

另外由于在 UserMapper 中添加了一个 selectAll 方法,因此 CountryMapperTest 中的 selectAll 方法不再唯一,调用时必须带上 namespace (命名空间)。

参考 CountryMapperTest 测试类,可以模仿着编写一个 UserMapperTest 测试类,代码如下。

    @Test
public void testSelectById(){
//获取 sqlSession
SqlSession sqlSession = getSqlSession();
try {
//获取 UserMapper 接口
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//调用 selectById 方法,查询 id = 1 的用户
SysUser user = userMapper.selectById(1L);
//user 不为空
Assert.assertNotNull(user);
//userName = admin
Assert.assertEquals("admin", user.getUserName());
} finally {
//不要忘记关闭 sqlSession
sqlSession.close();
}
} @Test
public void testSelectAll(){
SqlSession sqlSession = getSqlSession();
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<SysUser> userList = userMapper.selectAll();
//结果不为空
Assert.assertTrue(userList.size() >0 );
} finally {
sqlSession.close(); //不要忘记关闭sqlSession
}
}

public class UserMapperTest extends BaseMapperTest

// 获取 UserMapper 接口的动态代理的实现类对象

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

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

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

与上面2个SELECT单表查询不同,在实际业务中还需要多表关联查询。下面举例一些更为复杂的用法。

第一种简单的情形:根据用户id获取用户拥有的所有角色,返回的结果为角色集合,结果只有角色的信息,不包含额外的其他字段信息。

这个方法会涉及 sys_user sys_role sys_user_role 这3个表,并且该方法写在上述任何一个对应的Mapper接口中都可以。

例如,我们把这个方法写到(位于src/main/java/cn.bjut.simple.mapper包中的) UserMapper接口中,代码如下。

    /**
* 根据用户id获取角色信息
*
* @param userID
* @return
*/
List<SysRole> selectRolesByUserId(Long userID);

在对应的 UserMapper.xml中添加如下代码。

    <select id="selectRolesByUserId" resultType="cn.bjut.simple.model.SysRole">
select
r.id,
r.role_name roleName,
r.enabled,
r.create_by createBy,
r.create_time createTime
from sys_user u
inner join sys_user_role ur on u.id = ur.user_id
inner join sys_role r on ur.role_id = r.id
where u.id = #{userId}
</select>

虽然这个多表关联的查询中涉及了3个表,但是返回的结果只有sys_role一个表中的信息,所以直接使用 SysRole 作为返回值类型即可。

我们来编写代码对此方法进行测试。(src/main/test/java ......mapper包中 UserMapperTest增添)

    @Test
public void testSelectRolesByUserID(){
SqlSession sqlSession = getSqlSession();
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<SysRole> RoleList = userMapper.selectRolesByUserId(1L);
Assert.assertNotNull(RoleList); //结果不为空
Assert.assertTrue(RoleList.size() >0 );
} finally {
sqlSession.close(); //不要忘记关闭sqlSession
}
}

public void testSelectRolesByUserID()

注:如果报错,很有可能是如下图片中的select语句中‘英文逗号 ,的位置和有无造成的。

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

如果我希望这个查询语句同时返回SysUser表的user_name字段呢,该如何设置resultType?请参考链接文章。(不考虑嵌套的情况)

我们设置一个需求(仅为了说明用法):以第一种情形为基础,假设查询的结果不仅要包含 sys_role 中的信息,还要包含当前用户的部分信息(不考虑嵌套的情况),例如增加查询列 u.user_ name as userName 。这时 resultType 该如何设置呢 ?

方法1:就是在 SysRole 对象中直接添加 userName 属性,这样仍然使用 SysRole 作为返回值。

方法2新建扩展类(利用继承性),在扩展类中添加userName字段。

public class SysRoleExtend extends SysRole {
private String userName; public String getUserName() {
return userName;
} public void setUserName(String userName) {
this.userName = userName;
}
}

此时需要将映射文件中SELECT语句中的 resultType修改为:cn.bjut.example.model.SysRoleExtend。

这种方式比较适合需要少量额外字段的场景。如果需要其他表的大量字段,可以使用下面的方式3

方法3(推荐使用):新建扩展类,在扩展类中直接添加SysUser实体类的 成员变量属性字段。

package  cn.bjut.example.model;

public class SysRoleExtend extends SysRole {

//引用数据类型是另一个 实体类
private SysUser user; public SysUser getSysUser() {
return sysUser;
} public void setSysUser(SysUser sysUser) {
this.sysUser = sysUser;
}
}

此时需要将resultType修改为:cn.bjut.example.model.SysRoleExtend。

在XML映射Mapper文件中

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

这里设置别名的时候,使用的是 user.属性名 ,user是SysRole中刚刚增加的属性 ,userName和userEmail是SysUser对象中的属性 ,通过这种方式可以直接将值赋给user字段中的属性。

    @Test
public void testSelectRolesByUserID2(){
SqlSession sqlSession = getSqlSession();
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<SysRoleExtend> RoleList = userMapper.selectRolesByUserId2(1L);
Assert.assertNotNull(RoleList); //结果不为空
Assert.assertTrue(RoleList.size() >0 );
} finally {
sqlSession.close(); //不要忘记关闭sqlSession
}
}

public void testSelectRolesByUserID2

输出日志如下。

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

以上是两种简单方式的介绍,在本书后续章节中还会介绍通过 resultMap 处理这种嵌套对象的方式。

============================================================================

end

上一篇:SQLServer DBA 三十问(加强版)


下一篇:我的Vue之小功能统计