MyBatis笔记(2)-参数获取以及使用举例

参数值获取

在之前的SQL书写中,我们都是直接将SQL写死在映射文件中,但是实际应用中,我们的SQL是动态变化的,体现在我们可能需要获取一些相关参数,然后拼接在SQL中。MyBatis也给我们提供了参数值获取的方式。

在MyBatis中,参数值获取的方式分为两种,${}以及#{}

  • ${}:使用字符串拼接的方式拼接SQL,如果使用字符串类型或者日期类型字段,则需要手动增加单引号
  • #{}:使用占位符赋值的方式拼接SQL,如果使用字符串类型或者日期类型字段,则会自动添加单引号

根据参数类型和个数的不同,我们访问参数的方式也不同,下面分别进行说明。

单个参数访问

如果Mapper接口中的方法传入的是单个字面量类型,则可以直接使用${}或者#{}进行访问,括号中写任何内容都是可以访问到的。

1
2
3
4
5
6
<!-- User getUserByName(String name)-->
<select id="getUserByName" resultType="com.syh.bean.User">
select *
from users
where username = #{name}
</select>

多个参数访问

如果Mapper接口中的方法传入的是多个字面量类型,则MyBatis会以两种方式进行管理,具体来说,是将这些参数按照顺序保存在Map中。

  • 第一种方式中Map的Key为arg0、arg1、...;Value为对应位置的参数
  • 第二种方式中Map的Key为param1、param2、...;Value为对应位置的参数

在访问对应参数的使用,我们在括号中可以通过Key来访问到对应的参数。使用第一种方式的arg0,arg1

1
2
3
4
5
6
7
<!--User getUserByAgeAndGender(int age, char gender)-->
<select id="getUserByAgeAndGender" resultType="com.syh.bean.User">
select *
from users
where age = #{arg0}
and gender = #{arg1}
</select>

或者使用第二种方式的param1,param2

1
2
3
4
5
6
7
<!--User getUserByAgeAndGender(int age, char gender)-->
<select id="getUserByAgeAndGender" resultType="com.syh.bean.User">
select *
from users
where age = #{param1}
and gender = #{param2}
</select>

两种Key可以进行混用,但是并不推荐这种方式,容易混淆。

上面的方式是由MyBatis将多个参数保存成了一个Map,实际上我们要传入多个参数的时候,也可以自己将其包装成一个Map,之后只需要访问Map的Key就可以获取到相对应的值。

在映射文件中的定义如下:

1
2
3
4
5
6
7
<!--User getUserByMapInfo(Map<String, Object> info)-->
<select id="getUserByMapInfo" resultType="com.syh.bean.User">
select *
from users
where username = #{username}
and age = #{age}
</select>

测试代码如下:

1
2
3
4
Map<String, Object> map = new HashMap<>();
map.put("username", "root");
map.put("age", 23);
User user = userMapper.getUserByMapInfo(map);

实体类访问

如果Mapper接口中的方法参数为一个实体类对象的时候,我们可以通过访问属性名来访问对应的成员变量值,然后进行拼接。

注意这里属性和成员变量的区别:

  • 成员变量与定义有关,是我们直接写在类内部的
  • 属性则与get和set方法有关,将方法中的get和set去除,并将首字母小写得到的就是属性名

如果按照规范为每个成员变量设置get和set方法,那么属性名和成员变量的名称应该是对应相同的。

这里的User实体类定义如下:

1
2
3
4
5
6
7
8
9
10
11
public class User {
private int id;
private String username;
private String password;
private int age;
private String gender;
private String email;

// ...
// 省略了构造方法、get set方法、toString方法等
}

在映射文件中定义如下:

1
2
3
4
5
6
7
8
9
10
<!--User getUserByUser(User user)-->
<select id="getUserByUser" resultType="com.syh.bean.User">
select *
from users
where username = #{username}
and password = #{password}
and age = #{age}
and gender = #{gender}
and email = #{email}
</select>

使用@Param表示参数

还有一种方式是我们可以通过注解的方式来表示Mapper中的方法接口,在注解中指定我们希望它的访问方式,后续在SQL中我们就能够通过对应的方式来访问参数了。

在接口中我们定义如下内容:

1
User getUserByIdAndName(@Param("user_id") int id,@Param("user_name") String username);

在映射文件中,我们可以通过user_iduser_name来访问对应的内容:

1
2
3
4
5
6
7
<!--User getUserByIdAndName(@Param("user_id") int id,@Param("user_name") String username);-->
<select id="getUserByIdAndName" resultType="com.syh.bean.User">
select *
from users
where id = #{user_id}
and username = #{user_name}
</select>

在这种情况下,MyBatis也是使用了两种方式来保存参数:

  • 第一种方式中Map的Key为我们设置的参数;Value为对应位置的参数
  • 第二种方式中Map的Key为param1、param2、...;Value为对应位置的参数

最终我们推荐都是使用@Param来表示参数,这样也无需考虑多种情况的不同,都使用统一的方式进行访问。

查询功能举例

下面我们可以利用参数值获取功能来完成一些查询功能:

查询一个实体类对象

根据用户名称查询用户信息:

1
2
3
4
5
6
<!-- User getUserByName(String name)-->
<select id="getUserByName" resultType="com.syh.bean.User">
select *
from users
where username = #{name}
</select>

查询一个List集合

查询用户信息成为一个List集合:

1
2
3
4
5
<!-- List<User> getUserList()-->
<select id="getUserList" resultType="com.syh.bean.User">
select *
from users
</select>

查询单个数据

查询单个数据指的是查询得到一个int值或者其他字面量,例如这里查询用户的总数。

1
2
3
4
5
<!--int getSumOfUsers()-->
<select id="getSumOfUsers" resultType="int">
select count(*)
from users
</select>

上面需要设置返回类型,我们可以写全类名java.lang.Integer,但是在MyBatis中提供了相应的类型别名,例如java.lang.Integer的别名为int或者integerint的别名为_int或者_integerMap的别名为mapList的别名为list。注意这里设置的是别名,别名是不区分大小写的。

相关的别名参数可以在官方文档中查询得到配置_MyBatis|类型别名

查询单个记录为Map

我们也可以将一条记录查询成为Map类型,对应Map的Key就是表格中的字段名,Value就是对应的值。

1
2
3
4
5
6
<!--Map<String, Object> getUserToMap(@Param("id") int id)-->
<select id="getUserToMap" resultType="map">
select *
from users
where id = #{id}
</select>

指的注意的是,这里查询出的结果中,如果有字段的值为null,是不会保存到结果Map中的,但是如果是查询单个记录为实体类,那么对应的值上是会保存null的。

查询多条记录为Map

查询多条记录为Map有两种方式。

第一种方式是将每一条记录都查询成一个Map,然后将多个Map放入List队列中。如下面所示,注意接口的返回值类型以及标签中的resultType

1
2
3
4
5
<!--List<Map<String, Object>> getUsersToMap()-->
<select id="getUsersToMap" resultType="map">
select *
from users
</select>

第二种方式是返回一个Map,Map的Value又是一个Map。这种情况下则需要设置这个大Map的Key是什么,这也就是下面注解@MapKey的作用,其中的值为表中的字段名。同样注意下面的接口返回值类型以及标签中的resultType

接口方法说明如下:

1
2
@MapKey("id")
Map<String, Object> getAllUserToMap();

映射文件中的语句如下:

1
2
3
4
5
<!--Map<String, Object> getAllUserToMap()-->
<select id="getAllUserToMap" resultType="map">
select *
from users
</select>

查询结果如下:

1
2
3
4
5
{
2={password=123456, gender=男, id=2, age=23, email=654321@haha.com, username=root},
3={password=normal, gender=男, id=3, age=2, email=333333@haha.com, username=normal},
4={password=123123, gender=女, id=4, age=18, email=123456@haha.com, username=admin}
}

特殊SQL

上面的SQL中,我们获取参数使用的都是#{},采用占位符的方式拼接。我们大多数情况使用的的确是这种方式,但是也有一些情况下我们需要使用字符串拼接的操作,或者其他的特殊操作,下面就进行相关说明。

主要需要注意的问题就是#{}使用占位符,如果传入字符串的话会自动添加引号,而${}直接使用字符串拼接,不会进行引号的添加。在使用过程中,注意这点就可以解决很多问题

模糊查询

下面我们要完成一个模糊查询的功能,查找用户名中含有对应字符的用户。由于我们需要使用到%,_等占位符,因此在SQL查询语句中要直接写字符串,但是考虑到#{}的方式会增加引号,因此不能直接使用,不过我们可以通过其他方式进行完成,如下所示:

使用字符拼接:

1
2
3
4
5
6
<!--List<User> getUsersLike(@Param("likeStr") String likeStr)-->
<select id="getUsersLike" resultType="com.syh.bean.User">
select *
from users
where username like '%${likeStr}%'
</select>

当然还有其他方式也能够完成对应操作:

1
2
3
4
5
select * from users
where username like concat('%', #{likeStr}, '%')
// 或者
select * from users
where username like "%"#{likeStr}"%"

批量删除

在批量删除功能中,我们可以传入一个以逗号,分隔的id列表,然后直接拼接到括号当中,使用${}

1
2
3
4
<!--int deleteBatch(@Param("ids") String ids)-->
<delete id="deleteBatch">
delete from users where id in (${ids})
</delete>

这里不能使用占位符,因为占位符会自动添加引号,导致格式错误。

动态设置表名称

动态设置表名称表示我们需要传入需要查询的表名称,这部分也是只能使用字符串拼接来完成的。

1
2
3
4
<!--List<User> getAllUserFrom(@Param("tableName") String tableName)-->
<select id="getAllUserFrom" resultType="com.syh.bean.User">
select * from ${tableName}
</select>

获取自增主键

获取自增主键描述的是下面的功能。假如我们向表中添加了一个User,其中的id列是被设置为自增主键。在添加完成之后,我们希望能够获得刚刚添加的这个User对应的自增主键的值。

在MyBatis中为我们提供了相应的功能:

1
2
3
4
5
<!--int insertUserAndGetId(@Param("user") User user)-->
<insert id="insertUserAndGetId" useGeneratedKeys="true" keyProperty="id">
insert into users
values (null, #{user.username}, #{user.password}, #{user.age}, #{user.gender}, #{user.email})
</insert>

其中,我们需要在标签中增加useGeneratedKeys以及keyProperty

  • useGeneratedKeys:是否使用自增的主键
  • keyProperty:需要将自增主键的值设置到User对象的哪个属性

由于insert语句的返回值只能是受影响的行数,所以只能通过其他的方式来得到自增主键值。MyBatis会将当前插入行对应的自增主键设置到我们的实体类中,设置的属性则由keyProperty来指定。像上面的例子,在插入这条记录之后,对应的自增主键的值会被设置到User类中的id上。

我们可以进行测试,关键测试代码如下:

1
2
3
User user = new User(0, "test", "test", 36, "女", "test@haha.com");
userMapper.insertUserAndGetId(user);
System.out.println(user.getId());

输出结果如下,可以看到我们现在获取user的id属性,它已经被设置为自增的主键值了。

1
2
3
4
DEBUG 10-11 16:18:39,173 ==>  Preparing: insert into users values (null, ?, ?, ?, ?, ?) (BaseJdbcLogger.java:137) 
DEBUG 10-11 16:18:39,204 ==> Parameters: test(String), test(String), 36(Integer), 女(String), test@haha.com(String) (BaseJdbcLogger.java:137)
DEBUG 10-11 16:18:39,220 <== Updates: 1 (BaseJdbcLogger.java:137)
5

MyBatis笔记(2)-参数获取以及使用举例
http://example.com/2022/10/11/MyBatis笔记-2-参数获取以及使用举例/
作者
EverNorif
发布于
2022年10月11日
许可协议