Mybatis在使用Mapper接口进行编程时,底层采用了动态代理机制,表面上是调用的Mapper接口,而实际上是通过动态代理调用的SqlSession的对应方法,其最终会获得一个代理了Mapper接口的MapperProxy对象。MapperProxy对象在调用Mapper接口方法时会把传递的参数做一个转换,然后把转换后的参数作为入参传递给SqlSession对应的操作方法(如selectOne、insert等)。转换过程可以参考MapperMethod的execute()方法实现。简单来说是以下规则:
♦单个参数:可以接受基本类型,对象类型,集合类型的值。这种情况MyBatis可直接使用这个参数,不需要经过任何处理。
♦多个参数:任意多个参数,都会被MyBatis重新包装成一个Map传入。Map的key是param1,param2,0,1…,值就是参数的值。
♦命名参数:为参数使用@Param起一个名字,MyBatis就会将这些参数封装进map中,key就是我们自己指定的名字。
♦POJO:当这些参数属于我们业务POJO时,我们直接传递POJO。
♦Map:我们也可以封装多个参数为map,直接传递。
本节我们将介绍一下,MyBatis是如何处理单个参数和多个参数的?
在MySQL下新建数据库表t_user,并插入一条数据
CREATE TABLE t_user (
id int(10) NOT NULL AUTO_INCREMENT,
loginId varchar(20) DEFAULT NULL,
userName varchar(100) DEFAULT NULL,
role varchar(255) DEFAULT NULL,
note varchar(255) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
INSERT INTO t_user(loginId,userName,role,note) VALUES ('queen', '奎恩', '海贼王副把手', '专门负责提鞋的。。。');
@Test
public void testOneParam() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try {
UserMapper mapper = openSession.getMapper(UserMapper.class);
User user = mapper.findUserById(1);
System.out.println(user);
} finally {
openSession.close();
}
}
运行测试,控制台打印如下:
2017-08-04 23:07:28,536 [main] [com.queen.mybatis.mapper.UserMapper.findUserById]-[DEBUG] ==> Preparing: select id, loginId, userName, role, note from t_user where id = ?
2017-08-04 23:07:28,589 [main] [com.queen.mybatis.mapper.UserMapper.findUserById]-[DEBUG] ==> Parameters: 1(Integer)
2017-08-04 23:07:28,626 [main] [com.queen.mybatis.mapper.UserMapper.findUserById]-[DEBUG] <== Total: 1
User [id=1, loginId=queen, userName=奎恩, role=海贼王副把手, note=专门负责提鞋的。。。]
其实,当你传递单个参数时,#{xxx}里面这个“xxx”无论你写什么都一样,因为只有一个参数,你可以试着修改UserMapper.xml文件中where id = #{idabcd},如下:
测试查询,同样可以查出数据。单个参数下,MyBatis不会做任何处理。那如果是多个参数呢?
同样是查询User信息,但是我们以ID和userName两个参数去查询,在UserMapper.java中新增查询方法,如下:
/**
* 根据ID和用户名查找用户
* @param id
* @param userName
* @return
*/
public User findUserByIdAndUserName(int id, String userName);
在UserMapper.xnl增加配置如下
编写MyBatisTest.java测试类,新增测试方法testTwoParam
@Test
public void testTwoParam() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try {
UserMapper mapper = openSession.getMapper(UserMapper.class);
User user = mapper.findUserByIdAndUserName(1, "奎恩");
System.out.println(user);
} finally {
openSession.close();
}
}
大家想一想,当我们传递两个参数的时候,这种方式编写能成功吗?我们先试着运行一下testTwoParam方法,看啊看控制台打印何种结果?运行结果如下:
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [1, 0, param1, param2]
### Cause: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [1, 0, param1, param2]
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:26)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:111)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:102)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:66)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:68)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:52)
at com.sun.proxy.$Proxy2.findUserByIdAndUserName(Unknown Source)
at com.queen.mybatis.MyBatisTest.testTwoParam(MyBatisTest.java:46)
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)
很不凑巧,大失所望,控制台报错:“Parameter ‘id’ not found. Available parameters are [1, 0, param1, param2]”,说绑定异常,参数ID没有找到,有效的参数是[1, 0, param1, param2]。这是什么意思呢?其实MyBatis在处理多个参数的时候,MyBatis会做特殊处理,多个参数会被封装成一个map,map中是这么存值的
map.put("param1","传入的参数值1");
map.put("param2","传入的参数值2");
map.put("param3","传入的参数值3");
.
.
.
map.put("paramN","传入的参数值N");
#{}就是从map中获取指定的key值。这个时候我们要将UserMapper.xml中查询方法做如下修改:
或者
测试查询,控制台打印如下:
2017-08-04 23:41:38,401 [main] [com.queen.mybatis.mapper.UserMapper.findUserByIdAndUserName]-[DEBUG] ==> Preparing: select id, loginId, userName, role, note from t_user where id = ? and userName=?
2017-08-04 23:41:38,466 [main] [com.queen.mybatis.mapper.UserMapper.findUserByIdAndUserName]-[DEBUG] ==> Parameters: 1(Integer), 奎恩(String)
2017-08-04 23:41:38,503 [main] [com.queen.mybatis.mapper.UserMapper.findUserByIdAndUserName]-[DEBUG] <== Total: 1
User [id=1, loginId=queen, userName=奎恩, role=海贼王副把手, note=专门负责提鞋的。。。]
虽然这两种方式,都能取到值,但是给人的感觉比较怪怪的,看起来跟我们平时的写法不太一样,按照大家预想where id = #{id} and userName=#{userName} 这种写法方式,是最让人舒服的。为了解决这种方式使用,MyBatis提供了命名参数的方式,在传递参数时,明确指出封装参数时map的key,这样我们在XML中就能使用where id = #{id} and userName=#{userName} 这种写法来传递参数了。
=======欢迎大家拍砖,小手一抖,多多点赞哟!=======
版权声明:本文为博主原创文章,允许转载,但转载必须标明出处。