概述
我们前面几篇文章,所列举的接口方法中只有一个参数,参数的类型可以分为两种
1. 基本类型
2. JavaBean
当参数是一个基本类型的时候,它在XML文件中对应的SQL语句只会使用一个参数,比如delete方法
当参数类型是一个JavaBean类型的时候,它在XML文件中对应的SQL语句中会有多个参数,比如 insert 、 update
在实际应用中使用多个参数的情况再常见不过来。 前面的博文,我们有个方法是将多个参数合并到一个JavaBean中,并使用这个Bean作为接口方法的参数。 这种方法虽然方便,单并不适合全部的情况,因为不可能只为了两三个参数去创建新的JavaBean。
因此对于参数比较少的情况,有两种方式可以采用
- 使用Map类型作为参数
- 使用@Param注解
使用Map类型作为参数需要手工创建MAP并且对参数进行赋值,并不简洁。这里推荐使用@Param注解的方式
使用Map类型作为参数需要手工创建MAP并且对参数进行赋值,并不简洁。这里推荐使用@Param注解的方式
模拟场景:根据用户ID和角色的enable状态来查询用户的所有角色
工程结构
多个基本类型参数的场景
我们先来看下如果在接口中使用多个参数但不是用Param注解的情况
1.UserRoleMapper接口增加接口方法
package com.artisan.mybatis.xml.mapper; import java.util.List; import com.artisan.mybatis.xml.domain.SysRole; public interface UserRoleMapper { /** * * * @Title: selectSysRolesByUserIdAndRoleEnable * * @Description: 根据用户ID和角色的Enable属性查询角色 * * @param userId * @param enable * @return * * @return: List<SysRole> */ List<SysRole> selectSysRolesByUserIdAndRoleEnable(Long userId, Integer enable); }
2.UserRoleMapper.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.UserRoleMapper"> <select id="selectSysRolesByUserIdAndRoleEnable" resultType="com.artisan.mybatis.xml.domain.SysRole"> SELECT c.id, c.role_name roleName, c.enabled, c.create_by createBy, c.create_time createTime FROM sys_user a INNER JOIN sys_user_role b ON a.id = b.user_id INNER JOIN sys_role c ON b.role_id = c.id WHERE a.id = #{userId} AND c.enabled = #{enabled} </select> </mapper>
3.单元测试
package com.artisan.mybatis.xml.mapper; import java.util.List; import org.apache.ibatis.session.SqlSession; import org.apache.log4j.Logger; import org.junit.Assert; import org.junit.Test; import com.artisan.mybatis.xml.domain.SysRole; /** * * * @ClassName: UserRoleMapperTest * * @Description: UserRoleMapperTest测试类 * * @author: Mr.Yang * * @date: 2018年4月16日 下午3:34:45 */ public class UserRoleMapperTest extends BaseMapperTest { private static final Logger logger = Logger.getLogger(UserRoleMapperTest.class); private SqlSession sqlSession; @Test public void selectSysRolesByUserIdAndRoleEnableTest(){ logger.info("selectSysRolesByUserIdAndRoleEnableTest"); try { // 获取SqlSession sqlSession = getSqlSession(); // 获取UserRoleMapper UserRoleMapper userRoleMapper = sqlSession.getMapper(UserRoleMapper.class); // 通过接口调用方法 List<SysRole> roleList = userRoleMapper.selectSysRolesByUserIdAndRoleEnable((long) 1, 1); // 期望roleList不为空 Assert.assertNotNull(roleList); // 期望roleList> 0 Assert.assertTrue(roleList.size() > 0); for (SysRole sysRole : roleList) { logger.info(sysRole); } } catch (Exception e) { e.printStackTrace(); }finally{ sqlSession.close(); logger.info("sqlSession close successfully "); } } }
选中方法,执行单元测试
会得到如下异常
org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: org.apache.ibatis.binding.BindingException: Parameter 'userId' not found. Available parameters are [1, 0, param1, param2] ### Cause: org.apache.ibatis.binding.BindingException: Parameter 'userId' not found. Available parameters are [1, 0, param1, param2] at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:122) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:113) at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:122) at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:64) at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:53) at com.sun.proxy.$Proxy6.selectSysRolesByUserIdAndRoleEnable(Unknown Source) at com.artisan.mybatis.xml.mapper.UserRoleMapperTest.selectSysRolesByUserIdAndRoleEnableTest(UserRoleMapperTest.java:40) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) Caused by: org.apache.ibatis.binding.BindingException: Parameter 'userId' not found. Available parameters are [1, 0, param1, param2] at org.apache.ibatis.binding.MapperMethod$ParamMap.get(MapperMethod.java:168) at org.apache.ibatis.reflection.wrapper.MapWrapper.get(MapWrapper.java:45) at org.apache.ibatis.reflection.MetaObject.getValue(MetaObject.java:122) at org.apache.ibatis.executor.BaseExecutor.createCacheKey(BaseExecutor.java:212) at org.apache.ibatis.executor.CachingExecutor.createCacheKey(CachingExecutor.java:139) at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:81) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:120)
重点信息
### Cause: org.apache.ibatis.binding.BindingException: Parameter 'userId' not found. Available parameters are [1, 0, param1, param2]
这个错误标识,xml可用的参数只有 0、1、param1、param2 , 没有userId.
1, 0, param1, param2 都是Mybatis根据参数位置自定义的名字,如果将xml中的#{userId} 改为#{0}或者#{param1} ,将#{enbale}改为#{1}或者#{param2} ,这个方法是可以被正常调用的 。 这里只是为了演示,实际上并不建议这么做。
修复
现在在接口方法的参数前添加@Param注解
package com.artisan.mybatis.xml.mapper; import java.util.List; import org.apache.ibatis.annotations.Param; import com.artisan.mybatis.xml.domain.SysRole; public interface UserRoleMapper { /** * * * @Title: selectSysRolesByUserIdAndRoleEnable * * @Description: 根据用户ID和角色的Enable属性查询角色 * * @param userId * @param enable * @return * * @return: List<SysRole> */ List<SysRole> selectSysRolesByUserIdAndRoleEnable(@Param("userId") Long userId, @Param("enable") Integer enable); }
重新运行单元测试
2018-04-16 19:43:22,404 INFO [main] (BaseMapperTest.java:26) - sessionFactory bulit successfully 2018-04-16 19:43:22,408 INFO [main] (BaseMapperTest.java:29) - reader close successfully 2018-04-16 19:43:22,412 INFO [main] (UserRoleMapperTest.java:31) - selectSysRolesByUserIdAndRoleEnableTest 2018-04-16 19:43:22,910 DEBUG [main] (BaseJdbcLogger.java:142) - ==> Preparing: SELECT c.id, c.role_name roleName, c.enabled, c.create_by createBy, c.create_time createTime FROM sys_user a INNER JOIN sys_user_role b ON a.id = b.user_id INNER JOIN sys_role c ON b.role_id = c.id WHERE a.id = ? AND c.enabled = ? 2018-04-16 19:43:22,988 DEBUG [main] (BaseJdbcLogger.java:142) - ==> Parameters: 1(Long), 1(Integer) 2018-04-16 19:43:23,025 TRACE [main] (BaseJdbcLogger.java:148) - <== Columns: id, roleName, enabled, createBy, createTime 2018-04-16 19:43:23,026 TRACE [main] (BaseJdbcLogger.java:148) - <== Row: 1, 管理员, 1, 1, 2018-04-13 21:12:46.0 2018-04-16 19:43:23,031 TRACE [main] (BaseJdbcLogger.java:148) - <== Row: 2, 普通用户, 1, 1, 2018-04-13 21:12:46.0 2018-04-16 19:43:23,032 DEBUG [main] (BaseJdbcLogger.java:142) - <== Total: 2 2018-04-16 19:43:23,033 INFO [main] (UserRoleMapperTest.java:48) - SysRole [id=1, roleName=管理员, enabled=1, createBy=1, createTime=Fri Apr 13 21:12:46 BOT 2018, user=null, privilegeList=null] 2018-04-16 19:43:23,037 INFO [main] (UserRoleMapperTest.java:48) - SysRole [id=2, roleName=普通用户, enabled=1, createBy=1, createTime=Fri Apr 13 21:12:46 BOT 2018, user=null, privilegeList=null] 2018-04-16 19:43:23,039 INFO [main] (UserRoleMapperTest.java:54) - sqlSession close successfully
测试通过了,这时的XML文件中对应的SQL的可用参数变成了【userId,enable,param1,param2】 ,如果#{userId} 改为#{param1} ,将#{enbale}改为#{param2}也是可以通过的。
给参数配置@Param注解后,mybatis就会自动将参数封装成Map类型,@Param注解值会作为Map的key 因此在SQL部分就会通过配置的注解值来使用参数。
多个参数为JavaBean类型场景
1. UserRoleMapper接口增加接口方法
// 传递多个JavaBean的场景 List<SysRole> selectSysRolesByUserIdAndRoleEnable2(@Param("sysUser") SysUser sysUser, @Param("sysRole") SysRole sysRole);
这个时候,在XML中就不能直接使用#{userId}和#{enabled}了,而是要通过点取值方式使用 #{sysUser.id} 和 #{sysRole.enabled} 从两个JavaBean中取出指定属性的值。
2.UserRoleMapper.xml配置SQL
<!-- 传递多个Bean的场景 --> <select id="selectSysRolesByUserIdAndRoleEnable2" resultType="com.artisan.mybatis.xml.domain.SysRole"> SELECT c.id, c.role_name roleName, c.enabled, c.create_by createBy, c.create_time createTime FROM sys_user a INNER JOIN sys_user_role b ON a.id = b.user_id INNER JOIN sys_role c ON b.role_id = c.id WHERE a.id = #{sysUser.id} AND c.enabled = #{sysRole.enabled} </select>
3.单元测试
@Test public void selectSysRolesByUserIdAndRoleEnable2Test() { logger.info("selectSysRolesByUserIdAndRoleEnable2Test"); try { // 获取SqlSession sqlSession = getSqlSession(); // 获取UserRoleMapper UserRoleMapper userRoleMapper = sqlSession.getMapper(UserRoleMapper.class); // 构造SysUser SysUser sysUser = new SysUser(); sysUser.setId((long) 1); // 构造SysRole SysRole sysRole = new SysRole(); sysRole.setEnabled(1); // 通过接口调用方法 List<SysRole> roleList = userRoleMapper.selectSysRolesByUserIdAndRoleEnable2(sysUser, sysRole); // 期望roleList不为空 Assert.assertNotNull(roleList); // 期望roleList> 0 Assert.assertTrue(roleList.size() > 0); for (SysRole sysRole2 : roleList) { logger.info(sysRole2); } } catch (Exception e) { e.printStackTrace(); }finally{ sqlSession.close(); logger.info("sqlSession close successfully "); } }
日志
2018-04-16 20:13:06,731 INFO [main] (BaseMapperTest.java:26) - sessionFactory bulit successfully 2018-04-16 20:13:06,734 INFO [main] (BaseMapperTest.java:29) - reader close successfully 2018-04-16 20:13:06,738 INFO [main] (UserRoleMapperTest.java:62) - selectSysRolesByUserIdAndRoleEnable2Test 2018-04-16 20:13:07,361 DEBUG [main] (BaseJdbcLogger.java:142) - ==> Preparing: SELECT c.id, c.role_name roleName, c.enabled, c.create_by createBy, c.create_time createTime FROM sys_user a INNER JOIN sys_user_role b ON a.id = b.user_id INNER JOIN sys_role c ON b.role_id = c.id WHERE a.id = ? AND c.enabled = ? 2018-04-16 20:13:07,449 DEBUG [main] (BaseJdbcLogger.java:142) - ==> Parameters: 1(Long), 1(Integer) 2018-04-16 20:13:07,483 TRACE [main] (BaseJdbcLogger.java:148) - <== Columns: id, roleName, enabled, createBy, createTime 2018-04-16 20:13:07,484 TRACE [main] (BaseJdbcLogger.java:148) - <== Row: 1, 管理员, 1, 1, 2018-04-13 21:12:46.0 2018-04-16 20:13:07,487 TRACE [main] (BaseJdbcLogger.java:148) - <== Row: 2, 普通用户, 1, 1, 2018-04-13 21:12:46.0 2018-04-16 20:13:07,488 DEBUG [main] (BaseJdbcLogger.java:142) - <== Total: 2 2018-04-16 20:13:07,489 INFO [main] (UserRoleMapperTest.java:86) - SysRole [id=1, roleName=管理员, enabled=1, createBy=1, createTime=Fri Apr 13 21:12:46 BOT 2018, user=null, privilegeList=null] 2018-04-16 20:13:07,494 INFO [main] (UserRoleMapperTest.java:86) - SysRole [id=2, roleName=普通用户, enabled=1, createBy=1, createTime=Fri Apr 13 21:12:46 BOT 2018, user=null, privilegeList=null] 2018-04-16 20:13:07,495 INFO [main] (UserRoleMapperTest.java:92) - sqlSession close successfully
小结
除了上述常用的类型,接口的参数还可以是集合或者数组,后续介绍。