1、在MyBatis中使用多个参数
默认情况下,在MyBatis中的抽象方法中,最多只允许有1个参数,如果使用了多个参数,会报告错误,例如:根据用户的id,修改密码为某个变量值,需要执行的SQL语句应该是:
UPDATE t_user SET password=? WHERE id=?
则抽象方法应该是:
Integer updatePasswordById(String password, Integer id);
注意:在MyBatis的接口中,各抽象方法的名称不允许出现重载!
MyBatis框架在处理时,确实始终只能支持1个参数的抽象方法,如果有多个,则无法识别,至于报错时,指示某参数找不到,也是正常的,因为当Java文件编译成class文件后,就会丢失所有局部变量的变量名,也就包括了参数的名称,也就是说,假设某抽象方法中有参数名称叫做id
,在编译后,该参数名也就不存在了!
如果一定要使用多个参数,则可以使用自定义的数据类型封装这些参数,然后,抽象方法的参数就可以只使用自定义的数据类型这1个参数:
Integer updatePasswordById(User user);
或者,也可以使用Map
类型的参数,并把所需要的多个参数全部封装到这个Map
中!
Integer updatePasswordById(Map<String, Object> map);
但是,无论是使用自定义的数据类型,还是使用Map
,在最终调用方法时,都是极不方便的!例如使用User
这样的自定义数据类型作为参数,方法的调用者可以创建出User类型的对象,却可能并不知道需要向User对象中封装哪些属性!使用Map
时也有这样的问题,方法的调用者甚至不知道应该使用什么名称的key
来封装数据!
推荐的解决方案是在每一个参数之前添加@Param
注解,并在注解中配置参数的名称:
Integer updatePasswordById(
@Param("id") Integer id,
@Param("password") String password);
当调用该方法时,MyBatis会根据注解中的名称作为key
,调用时给出的值作为value
,从而将多个参数封装到1个Map中,最终完成对应的数据操作。
<update id="updatePasswordById">
UPDATE
t_user
SET
password=#{password}
WHERE
id=#{id}
</update>
@Test
public void testUpdatePasswordById() {
UserMapper mapper = ctx.getBean("userMapper", UserMapper.class);
Integer id = 1;
String password = "000000";
Integer rows = mapper.updatePasswordById(id, password);
System.out.println("rows = " + rows);
}
注意:凡在MyBatis的接口中的抽象方法,如果方法的参数超过1个,必须使用
@Param
注解,并且,在配置的映射中,#{}
占位符中的名称就必须是注解中使用的名称!
2、动态SQL
MyBatis中的动态SQL指的是:在配置SQL映射时,可以使用某些特定的标签,实现简单的判断、循环等操作,使得最终执行的SQL语句是可以由参数不同而发生变化的。
以“批量删除”为例,被删除的数据有几条,id值分别是多少,都是未知的,在设计抽象方法时,可以设计为:
Integer deleteUserByIds(List<Integer> ids);
对于若干个id,可以使用数组类型,也可以使用List类型,来封装这些id的值。
在配置映射时,可以使用<foreach>
节点来遍历抽象方法中的参数,以动态的生成SQL语句:
<delete id="deleteUserByIds">
DELETE FROM
t_user
WHERE
id
IN (
<foreach collection="list" item="id" separator=",">
#{id}
</foreach>)
</delete>
在配置<foreach>
节点时,各属性的配置:
-
collection:被遍历的对象,当抽象方法的参数只有1个时,如果参数是List类型,则取值为
list
,如果参数是数组类型,则取值为array
,当抽象方法的参数超过1个时,参数必然是添加了@Param
注解的,此处取值就必须是注解中配置的名称; -
item:遍历过程中使用的变量名,以JAVA中的增强for循环为例,可能写成
for(Student stu : students)
,其中的stu
就是遍历过程中使用的变量名,在MyBatis的动态SQL中,这个item
属性的取值就相当于stu
,并且,在<foreach>
节点内部,使用占位符时,也应该使用这个名称来进行占位; -
seperator:分隔符,在
IN
语句中,分隔符就是逗号(,
); -
open / close:整个遍历产生的代码的最左侧字符串和最右侧字符串,例如:
当配置IN语法时没有写括号,可以配置为open="(" close=")"
。
测试:
@Test
public void testDeleteUserByIds() {
UserMapper mapper = ctx.getBean("userMapper", UserMapper.class);
List<Integer> ids = new ArrayList<Integer>();
ids.add(11);
ids.add(13);
ids.add(14);
Integer rows = mapper.deleteUserByIds(ids);
System.out.println("rows = " + rows);
}
3、使用MyBatis实现关联表查询
首先,创建部门信息表:t_department,该表中包含id和部门名称(name),并向该表中插入不少于3条数据。
CREATE TABLE t_department (
id INT AUTO_INCREMENT COMMENT 'id',
name VARCHAR(20) NOT NULL COMMENT '部门名称',
PRIMARY KEY (id)
)DEFAULT CHARSET=UTF8;
INSERT INTO t_department (name) VALUES ('销售部'),('产品部'),('财务部');
然后,修改用户信息表,添加部门id字段,并为每个用户分配部门。
ALTER TABLE t_user ADD did INT;
UPDATE t_user SET did=1 WHERE id IN (1,4);
UPDATE t_user SET did=2 WHERE id IN (7,9);
UPDATE t_user SET did=3 WHERE id IN (10);
3.1. 练习
需求: 根据id查询某用户的信息,要求显示该用户所归属的部门的名称。
需要执行的SQL语句大致是:
<select id="findUserById" resultType="cn.tedu.mybatis.UserVO">
SELECT
t_user.id,username,
password,age,
phone,email,
did,name
FROM
t_user
LEFT JOIN
t_department
ON
t_user.did=t_department.id
WHERE
t_user.id=#{id}
</select>
此前的查询结果可以很好的封装到User
类型的对象中,是因为查询结果中的列名与User
类中的属性名是完全一致,所以,MyBatis框架可以根据名称对应的方式,将查询结果封装起来!
目前,使用了关联表查询,很显然,User
类中的属性并不足以封装此次查询结果中的所有数据,通常,像User
这样的实体类是根据某张数据表的结构来设计,也就意味着只要是关联查询,一定没有匹配的实体类可以封装结果!在这种情况下,应该自定义VO(Value Object)类,用于匹配查询结果,并封装查询结果。
public class UserVO {
private Integer id;
private String username;
private String password;
private Integer age;
private String phone;
private String email;
private Integer departmentId;
private String departmentName;
// Get/Set/toString
VO类的编写方式与实体类几乎一致,只不过,实体类是对应某数据表的,而VO类是对应查询结果的,这2种类型的类,在语法和内容上相似度极高,只是定位不同而已。
则在UserMapper
接口中添加抽象方法:
UserVO findUserById(Integer id);
MyBatis要求查询结果中的列名(注意:不是数据表的字段名) 与 返回值类型中的属性名保持一致,所以,当名称不一致时,应该在查询的字段列表中取别名:
<select id="findUserById" resultType="cn.tedu.mybatis.UserVO">
SELECT
t_user.id,username,
password,age,
phone,email,
did AS departmentId,
name AS departmentName
FROM
t_user
LEFT JOIN
t_department
ON
t_user.did=t_department.id
WHERE
t_user.id=#{id}
</select>
测试:
@Test
public void testFindUserById() {
UserMapper mapper = ctx.getBean("userMapper", UserMapper.class);
Integer id = 7;
UserVO user = mapper.findUserById(id);
System.out.println(user);
}