在MyBatis中,处理特殊SQL查询时,需要格外注意SQL注入的风险以及参数的绑定方式。下面将详细介绍几种常见的特殊SQL执行场景,并提供相应的 MyBatis 实现方式及注意事项。
一、模糊查询
/**
* 根据用户名进行模糊查询
* @param username
* @return java.util.List<com.example.mybatis.pojo.User>
*/
List<User> getUserByLike(@Param("username") String username);
模糊查询是数据库中常见的查询方式,MyBatis提供了两种主要的参数绑定方式来实现模糊查询:
- 使用
${}
(不推荐,存在SQL注入风险)
xml
<select id="getUserByLike" resultType="User">
select * from t_user where username like '%${username}%'
</select>
注意:这种方式直接将变量内容拼接到SQL语句中,存在SQL注入的风险。
- 使用
concat
函数和#{}
(推荐)
xml
<select id="getUserByLike" resultType="User">
select * from t_user where username like concat('%', #{username}, '%')
</select>
或者在某些数据库中,可以使用 ||
代替 concat
函数:
xml
<select id="getUserByLike" resultType="User">
select * from t_user where username like '%' || #{username} || '%'
</select>
使用 #{}
的方式可以确保参数被正确预编译,防止SQL注入。
二、批量删除
在 MyBatis 中进行批量删除时,需要特别注意参数的绑定方式。由于 #{}
会将参数值作为整体进行预编译,因此不能直接在 IN 子句中使用。
delete from t_user where id in ('1,2,3')
这样是将 1,2,3
看做是一个整体,只有 id 为 1,2,3
的数据会被删除。
正确的做法是使用 ${}
或者将参数拆分为多个 #{}
:
- 使用
${}
(注意SQL注入风险)
xml
<delete id="deleteMore">
delete from t_user where id in (${ids.replaceAll(",","','")})
</delete>
即
delete from t_user where id in ('1','2','3')
注意:在Java代码中,需要确保 ids 字符串中的每个ID都被单引号包围,并且用逗号分隔。
- 使用
foreach
元素和#{}
(推荐)
xml
<delete id="deleteMore">
delete from t_user
<where>
<foreach item="id" collection="ids" open="id in (" separator="," close=")">
#{id}
</foreach>
</where>
</delete>
这种方式可以确保每个ID都被单独预编译,避免了SQL注入的风险。
三、动态设置表名
在某些场景下,可能需要动态地设置表名。由于表名无法被预编译,因此只能使用 ${}
来绑定参数:
/**
* 查询指定表中的数据
* @param tableName
* @return java.util.List<com.atguigu.mybatis.pojo.User>
*/
List<User> getUserByTable(@Param("tableName") String tableName);
xml
<select id="getUserByTable" resultType="User">
select * from ${tableName}
</select>
注意:在使用这种方式时,需要确保传入的表名是可信的,否则将面临SQL注入的风险。一种常见的做法是在后端代码中定义一个包含所有有效表名的白名单,并检查传入的表名是否在该白名单中。
四、添加功能获取自增的主键
-
使用场景
t_clazz(clazz_id,clazz_name)
t_student(student_id,student_name,clazz_id)
- 添加班级信息
- 获取新添加的班级的id
- 为班级分配学生,即将某学的班级id修改为新添加的班级的id
在 mapper.xml
中设置两个属性:
useGeneratedKeys
:设置使用自增的主键keyProperty
:因为增删改有统一的返回值(受影响的行数),因此只能将获取的自增的主键放在传输的参数user对象的某个属性中
/**
* 添加用户信息
* @param user
*/
void insertUser(User user);
<!--void insertUser(User user);-->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into t_user values (null,#{username},#{password},#{age},#{sex},#{email})
</insert>
//测试类
@Test
public void insertUser() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
SQLMapper mapper = sqlSession.getMapper(SQLMapper.class);
User user = new User(null, "ton", "123", 23, "男", "[email protected]");
mapper.insertUser(user);
System.out.println(user);
// 输出:user{id=10, username='ton', password='123', age=23, sex='男', email='[email protected]'},自增主键存放到了user的id属性中
}
总结
在处理特殊 SQL 查询时,我们需要根据具体的场景选择合适的参数绑定方式。
-
对于模糊查询和批量删除等场景,推荐使用
#{}
来确保参数被正确预编译,防止SQL注入。 -
对于需要动态设置表名等场景,由于无法预编译表名,只能使用
${}
来绑定参数,但需要确保传入的参数是可信的。