以常见用户角色/权限控制为例,学习MyBatis XML方式基本用法。
一、前期准备
1、创建数据库表及初始数据
(1)用户表(用户ID、用户名、密码、邮箱、简介、头像、创建时间)
-- ---------------------------- -- 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 NULL COMMENT '邮箱', `user_info` text COMMENT '简介', `head_img` blob COMMENT '头像', `create_time` datetime DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1002 DEFAULT CHARSET=utf8 COMMENT='用户表'; -- ---------------------------- -- Records of sys_user -- ---------------------------- INSERT INTO `sys_user` VALUES ('1', 'admin', '123456', 'admin@mybatis.tk', '管理员', null, '2016-04-01 17:00:58'); INSERT INTO `sys_user` VALUES ('1001', 'test', '123456', 'test@mybatis.tk', '测试用户', null, '2016-04-01 17:01:52');
(2)角色表(角色ID、角色名、有效标志、创建人、创建时间)
-- ---------------------------- -- 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', '2016-04-01 17:02:14'); INSERT INTO `sys_role` VALUES ('2', '普通用户', '1', '1', '2016-04-01 17:02:34');
(3)权限表(权限ID、权限名称、权限URL)
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(50) 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');
(4)用户角色关联表(用户ID、角色ID)
-- ---------------------------- -- 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');
(5)角色权限关联表
-- ---------------------------- -- 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');
2、创建实体类
在tk.mybatis.simple.model中分别创建SysUser、SysRole、SysPrivilege、SysUserRole、SysRolePrivilege。
需要注意的是:在数据库表sys_user中,属性head_img(头像)在实际业务场景一般为图片,在Java实体类中,属性类型对应为byte[]。
3、XML方式
Mybatis从3.0开始支持使用Java的动态代理直接通过接口来调用相应的方法,不需要提供接口的实现类,更不需要在实现类中使用SqlSession以命名空间间接调用。
当接口参数大于1时,可通过参数注解@Param设置参数名字,或将使用参数封装为实体类对象,均可将参数注入对应接口中。
在数据库表及实体类创建完成的前提下,在src/main/resources的tk.mybatis.simple.mapper目录下创建5个表对应的XML文件,分别为:UserMapper.xml、RoleMapper.xml、PrivilegeMapper.xml、UserRoleMapper.xml、RolePrivilegeMapper.xml,在src/main/java的tk.mybatis.simple.mapper包中创建5个接口类:UserMapper.java、RoleMapper.java、PrivilegeMapper.java、UserRoleMapper.java、RolePrivilegeMapper.java。如下图所示:
以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 namespace="tk.mybatis.simple.mapper.UserMapper"> </mapper>
在MyBatis中,是通过根节点<mapper>中的namespace属性将接口和XML关联起来,namespace的属性值需配置成接口的全限定名称,对UserMapper而言,则是tk.mybatis.simple.mapper.UserMapper。
参照UserMapper.xml,将其他4个XML内容补充即可。
然后,在mybatis-config.xml中配置所有的mapper:
<mappers> <package name="tk.mybatis.simple.mapper"/> </mappers>
通过在mappers中配置package节点,会先查找tk.mybatis.simple.mapper包下的所有接口,循环对接口进行如下操作:
(1)判断接口对应的命名空间是否已经配置过,如果配置过就抛出异常,没有配置过就继续进行接下来的操作。
(2)加载接口对应的XML映射文件,将接口全限定名转换为路径,例如,将接口tk.mybatis.simple.mapper.UserMapper转换为tk/mybatis/simple/mapper/UserMapper.xml,以.xml为后缀搜索XML资源,如果找到就解析XML。
(3)处理接口中的注解方法。
二、select用法
1、通过ID查询对象
在UserMapper接口中添加selectById方法:
/** * 通过id查询用户 * @param id * @return */ SysUser selectById(Long id);
在UserMapper.xml中添加<resultMap>和<select>部分代码:
<resultMap id="userMap" type="tk.mybatis.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"> select * from sys_user where id = #{id} </select>
通过以上代码可以发现,XML中的select标签的id属性值和定义的接口方法名是一样的,如果接口方法没有和XML中的id属性值相对应,启动程序会报错。
XML和接口的命名映射规则如下:
(1)当只使用XML而不使用接口的时候,namespace的值可以设置为任意不重复的名称;
(2)标签的id属性值在任何时候都不能出现英文句号“.”,并且同一个命名空间下不能出现重复的id;
(3)接口方法是可以重载的,所以接口中可以出现多个同名但参数不同的方法,但是XML中的id的值不能重复,因而接口中的所有同名方法会对应着XML中的同一个id的方法。最常见用法为,同名方法中其中一个方法增加RowBound类型参数来实现分页查询。
2、resultMap标签
resultMap标签用于配置Java对象的属性和查询结果列的对应关系,通过resultMap中配置的column和property可以将查询列的值映射到type对象的属性上。
resultMap包含属性如下:
- id:必填,唯一。在select标签中,resultMap制定的值即为此处id所设置的值。
- type:必填,用于配置查询列所映射到的Java对象类型。
- extends:选填,可以配置当前的resultMap继承自其他的resultMap,属性值为继承resultMap的id。
- autoMapping:选填,可选值为true或false,用于配置是否启用非映射字段(没有在resultMap中配置的字段)的自动映射功能,改配置可以覆盖全局的autoMappingBehavior配置。
resultMap包含标签如下:
- constructor:配置使用构造方法注入结果,包含以下两个子标签。
- idArg:id参数,标记结果作为id(唯一值),可以帮助提高整体性能。
- arg:注入到构造方法的一个普通结果。
- id:一个id结果,标记结果作为id(唯一值),可以帮助提高整体性能。
- result:注入到Java对象属性的普通结果。
- association:一个复杂的类型关联,一般用于一对一关联。
- collection:复杂类型的集合,一般用于一对多关联。
- discrimination:根据结果值来决定使用哪个结果映射。
- case:基于某些值得结果映射。
id和result标签包含的属性:
- column:从数据库中得到的列名,或者是列的别名。
- property:映射到列结果的属性,可以映射简单的属性,也可以映射一些复杂对象中的属性(通过“.”方式的属性嵌套赋值)。
- javaType:一个Java类的全限定名,或一个类型别名(通过typeAlias配置或者默认的类型),如果映射到一个JavaBean,MyBatis通常可以自动判断属性的类型。如果映射到HashMap,则需要明确地制定javaType属性。
- jdbcType:列对应的数据库类型。
- typeHandler:使用这个属性可以覆盖默认的类型处理器。这个属性值是类的完全限定名或类型别名。
接口中定义的返回值类型必须和XML中配置的resultMap类型一致,否则会因为类型不一致而抛出异常。返回值类型是由XML中的resultMap决定的,,不是由接口中写的返回值类型决定的。
3、查询结果集
在UserMapper接口中添加selectAll方法:
/** * 查询所有用户 * @return */ List<SysUser> selectAll();
在UserMapper.xml中添加如下<select>代码:
<select id="selectAll" resultType="tk.mybatis.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>
selectById中使用resultMap来设置结果映射,而selectAll通过resultType直接指定了返回结果的类型。如果使用resultType来设置返回结果的类型,需要在SQL中为所有列名和属性名不一致的列设置别名,通过设置别名使最终的查询结果和resultMap指定对象的属性名保持一致,进而实现自动映射。
注意:property属性或别名要和对象中属性的名字相同,在实际匹配时,MyBatis会先将两者都转化为大写形式,然后再判断是否相同。
由于在mybatis-configx.xml中,配置了一个全局属性mapUnderscoreToCamelCase,可以自动将以下划线命名的数据库列映射到Java对象的驼峰式命名属性中。因此,在以上<select>标签中的对数据库列所设置别名可以去掉。
4、运行单表查询程序
在src/test/java的tk.mybatis.simple.mapper包中新建测试类UserMapperTest.java,内容如下:
public class UserMapperTest extends BaseMapperTest { @Test public void testSelectById() { SqlSession sqlSession = getSqlSession(); try { // 获取UserMapper接口 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 调用selectById方法,查询id = 1的用户 SysUser user = userMapper.selectById(1l); // user不为空 Assert.assertEquals("admin", user.getUserName()); } finally { sqlSession.close(); } } @Test public void testSelectAll() { SqlSession sqlSession = getSqlSession(); try { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 调用selectAll方法,查询id = 1的用户 List<SysUser> userList = userMapper.selectAll(); // user不为空 Assert.assertNotNull(userList); // 用户数量大于0个 Assert.assertTrue(userList.size() > 0); } finally { sqlSession.close(); } } }
运行以上测试方法,控制台输出如下结果:
5、关联查询
(1)根据用户id获取用户,返回结果为只包含角色信息的集合
在UserMapper中添加接口方法:
/** * 根据用户id获取角色信息 * @param userId * @return */ List<SysRole> selectRolesByUserId(Long userId);
在对应UserMapper.xml中添加<select>节点:
<select id="selectRolesByUserId" resultType="tk.mybatis.simple.model.SysRole"> select r.id, r.role_name roleName, r.enabled 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>
(2)根据用户id获取用户,除角色信息外,还包含用户名信息
在SysRole中添加对应属性:
/** * 用户信息 */ private SysUser user;
修改XML中的selectRolesByUserId方法,通过“user.属性名”将查询的user信息直接赋值给user字段中的属性:
<select id="selectRolesByUserId" resultType="tk.mybatis.simple.model.SysRole"> select r.id, r.role_name roleName, r.enabled enabled, r.create_by createBy, r.create_time createTime, u.user_name as "user.userName", u.user_email as "user.userEmail" 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 </select>
6、运行关联查询程序
在UserMapperTest.java测试类中添加测试方法:
@Test public void testSelectRolesByUserId() { SqlSession sqlSession = getSqlSession(); try { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 调用testSelectRolesByUserId方法查询的用户的角色 List<SysRole> roleList = userMapper.selectRolesByUserId(1L); // user不为空 Assert.assertNotNull(roleList); // 用户数量大于0个 Assert.assertTrue(roleList.size() > 0); } finally { sqlSession.close(); } }
运行以上方法,控制台输出结果如下:
三、insert用法
1、简单insert方法
在UserMapper中添加如下方法:
/** * 新增用户 * @param sysUser * @return */ int insert(SysUser sysUser);
在UserMapper.xml中添加:
<insert id="insert"> insert into sys_user( id, user_name, user_password, user_email, user_info, head_img, create_time ) values ( #{id}, #{userName}, #{userPassword}, #{userEmail}, #{userInfo}, #{headImg, jdbcType = BLOB}, #{createTime, jdbcType = TIMESTAMP} ) </insert>
2、insert标签
insert标签包含以下属性:
- id:命名空间中唯一标识符,可用来代表这条语句。
- parameterType:即将传入的语句参数的完全限定类名或别名
- flushCache:默认为true,任何时候只要调用,都会清空一级缓存和二级缓存。
- timeout:设置在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。
- statementType:对于STATEMENT、PREPARED、CALLABLE,MyBatis会分别使用对应的statement、preparedStatement、CallableStatement,默认为PREPARED。
- userGeneratedKeys:默认为false。如果设置为true,MyBatis会使用JDBC的getGeneratedKeys方法来取出由数据库内部生成的主键。
- keyProperty:MyBatis通过getGeneratedKeys获取主键值后将要赋值的属性名。
- keyColumn:仅对INSERT和UPDATE有用,通过生成的键值设置表中的列名,这个设置仅在某些数据库中是必须的,当主键列不是表中的第一列时需要设置。
- databaseId:如果设置了databaseIdProvider,MyBatis会加载所有的不带databaseId的或匹配当前databaseId的语句。
3、运行简单insert程序
在UserMapperTest.java中添加如下测试方法:
@Test public void testInsert() { SqlSession sqlSession = getSqlSession(); try { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 创建一个user对象 SysUser user = new SysUser(); user.setUserName("test1"); user.setUserPassword("123456"); user.setUserEmail("test@mybatis.tk"); user.setUserInfo("test info"); // 正常情况下应该插入一张图片存到byte数组中 user.setHeadImg(new byte[] {1, 2, 3}); user.setCreateTime(new Date()); // 将新建的对象插入数据库中,特别注意这里的返回值result是执行的SQL影响的行数 int result = userMapper.insert(user); // 只插入1条数据 Assert.assertEquals(1, result); // id为null,没有给id赋值,并且没有配置回写的id的值 Assert.assertNull(user.getId()); } finally { // 为了不影响其他测试,这里选择回滚 // 由于默认的sqlSessionFactory.openSession()是不自动提交的 // 因此不手动执行commit也不会提交到数据库 sqlSession.rollback(); sqlSession.close(); } }
运行得到控制台输出:
4、使用JDBC方式返回主键自增的值
在UserMapper接口中添加insert2方法:
/** * 新增用户-使用useGeneratedKeys * @param sysUser * @return */ int insert2(SysUser sysUser);
在XML中新增一个insert2方法:
<insert id="insert2" useGeneratedKeys="true" keyProperty="id"> insert into sys_user( user_name, user_password, user_email, user_info, head_img, create_time ) values ( #{userName}, #{userPassword}, #{userEmail}, #{userInfo}, #{headImg, jdbcType = BLOB}, #{createTime, jdbcType = TIMESTAMP} ) </insert>
useGeneratedKeys设置为true后,MyBatis会使用JDBC的getGeneratedKeys方法取出由数据库内部生成的主键,并将其赋值给keyProperty配置的id属性。当需要设置多个属性时,使用逗号隔开,这种情况下通常还需要设置keyColumn属性,按顺序指定数据库的列,这里列的值会和keyProperty配置的属性一一对应。
下面通过测试方法来验证以上内容:
@Test public void testInsert2() { SqlSession sqlSession = getSqlSession(); try { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 创建一个user对象 SysUser user = new SysUser(); user.setUserName("test1"); user.setUserPassword("123456"); user.setUserEmail("test@mybatis.tk"); user.setUserInfo("test info"); user.setHeadImg(new byte[] {1, 2, 3}); user.setCreateTime(new Date()); int result = userMapper.insert2(user); // 只插入1条数据 Assert.assertEquals(1, result); Assert.assertNotNull(user.getId()); } finally { sqlSession.rollback(); sqlSession.close(); } }
5、使用selectKey返回主键的值
有些数据库不提供主键自增的功能,而是使用序列得到一个值,然后将这个值赋给id,再将数据插入数据库,这种情况需要使用<selectKey>标签来获取主键的值。
在接口和XML中分别添加insert3:
/** * 新增用户-使用selecctKey方式 * @param sysUser * @return */ int insert3(SysUser sysUser);
<insert id="insert3"> insert into sys_user( user_name, user_password, user_email, user_info, head_img, create_time ) values ( #{userName}, #{userPassword}, #{userEmail}, #{userInfo}, #{headImg, jdbcType = BLOB}, #{createTime, jdbcType = TIMESTAMP} ) <selectKey keyColumn="id" resultType="long" keyProperty="id" order="AFTER"> select last_insert_id() </selectKey> </insert>
在MySQL数据库中,order属性设置的值是AFTER,因为当前记录的主键值在insert语句执行成功后才能获取到。而在Oracle数据库中,order的值要设置为BEFORE,因为Oracle中需要先从序列中获取值,然后将值作为主键插入到数据库中。
四、update用法
在UserMapper接口中添加updateById方法:
/** * 根据主键更新 * @param sysUser * @return */ int updateById(SysUser sysUser);
在UserMapper.xml中添加<update>标签:
<update id="updateById"> update sys_user set user_name = #{userName}, user_password = #{userPassword}, user_email = #{userEmail}, user_info = #{userInfo}, head_img = #{headImg, jdbcType = BLOB}, create_time = #{createTime, jdbcType = TIMESTAMP} where id = #{id} </update>
在UserMapperTest中添加测试方法:
@Test public void testUpdateById() { SqlSession sqlSession = getSqlSession(); try { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 从数据库查询1个user对象 SysUser user = userMapper.selectById(1L); // 当前userName为admin Assert.assertEquals("admin", user.getUserName()); // 修改用户名 user.setUserName("admin_test"); // 修改邮箱 user.setUserEmail("test@mybatis.tk"); // 更新数据,特别注意,这里的返回值result是执行的SQL影响的行数 int result = userMapper.updateById(user); // 只更新1条数据 Assert.assertEquals(1, result); // 根据当前id查询修改后的数据 user = userMapper.selectById(1L); // 修改后的名字是admin_test Assert.assertEquals("admin_test", user.getUserName()); } finally { sqlSession.rollback(); sqlSession.close(); } }
运行程序验证:
五、delete用法
在UserMapper中添加deleteById方法:
/** * 通过主键删除 * @param id * @return */ int deleteById(Long id);
在UserMapper.xml中添加<delete>标签:
<delete id="deleteById"> delete from sys_user where id = #{id} </delete>
在UserMapperTest中添加测试方法:
@Test public void testDeleteById() { SqlSession sqlSession = getSqlSession(); try { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 从数据库查询出1个user对象,根据id=1查询 SysUser user1 = userMapper.selectById(1L); // 现在还能查询出user对象 Assert.assertNotNull(user1); // 调用方法删除 Assert.assertEquals(1, userMapper.deleteById(1L)); // 再次查询,这时应该没有值,为null Assert.assertNull(userMapper.selectById(1L)); // 使用SysUser参数再进行一次测试,根据id = 1001查询 SysUser user2 = userMapper.selectById(1001L); // 现在还能查询出user对象 Assert.assertNotNull(user2); // 调用方法删除 Assert.assertEquals(1, userMapper.deleteById(user2)); // 再次查询,这时应该没有值,为null Assert.assertNull(userMapper.selectById(1001L)); } finally { sqlSession.rollback(); sqlSession.close(); } }
运行程序验证:
六、多个接口参数用法
对于Mapper接口参数包含多个的情况,可以将多个参数合并到一个JavaBean中,或者使用Map类型作为参数、使用@param注解。
对于某些查询而言,查询参数并不适合封装成一个JavaBean,因此将多个参数合并到JavaBean的用法只适用于特定场景。
在使用Map类型作为参数的接口方法,key-value需手动创建并赋值。
以@Param注解传参为例,在UserMapper中添加以下方法:
/** * 根据用户id和角色的enabled状态获取用户的角色 * @param userId * @param enabled * @return */ List<SysRole> selectRolesByUserIdAndRoleEnabled(Long userId, Integer enabled);
在XML中添加对应的<select>标签:
<select id="selectRolesByUserIdAndRoleEnabled" resultType="tk.mybatis.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} and r.enabled = #{enabled} </select>
新增对应测试方法:
@Test public void testSelectRolesByUserIdAndRoleEnabled() { SqlSession sqlSession = getSqlSession(); try { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 调用selectRolesByUserIdAndRoleEnabled方法查询用户的角色 List<SysRole> userList = userMapper.selectRolesByUserIdAndRoleEnabled(1L, 1); // 结果不为空 Assert.assertNotNull(userList); // 角色的数量大于0 Assert.assertTrue(userList.size() > 0); } finally { sqlSession.close(); } }
运行程序,控制台显示如下输出:
在新增方法中,使用了两个参数userId、enabled,但是从以上错误日志可以看出,可用参数只有0、1、param1、param2,并无所需的userId和enabled。
按照提示,可以将XML中参数替换为#{0}、#{1}或者#{param1}、#{param2},但是在参数数量、名称或位置调整时,不易于维护。
此时,对接口方法做如下修改,参数以@param进行注解,并将其value与XML中传入参数相对应:
/** * 根据用户id和角色的enabled状态获取用户的角色 * @param userId * @param enabled * @return */ List<SysRole> selectRolesByUserIdAndRoleEnabled(@Param("userId") Long userId, @Param("enabled") Integer enabled);
再运行测试方法,控制显示如下:
给参数配置@param注解后,MyBatis会自动将参数封装成Map类型,并传入SQL中使用。
以上内容整理自《MyBatis从入门到精通》