MyBatis学习总结(一)

1 #{} 和 ${} 的区别是什么?

  1. #{}是预编译处理,$ {}是字符串替换。

  2. mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;mybatis在处理 $ { } 时,就是把 ${ } 替换成变量的值。

  3. 使用 #{} 可以有效的防止SQL注入,提高系统安全性。

2 SQL注入举例

${}不能有效防止SQL注入,因为它只是简单的字符串替换,没有进行转义和处理,容易受到攻击者的注入攻击,下面是一个例子:

假设SQL语句为:

String sql = "SELECT * FROM users WHERE username = '${username}' AND password = '${password}'";

如果username和password参数中包含恶意的SQL语句(如' OR 1=1 #),那么SQL注入攻击就会成功,从而导致查询到所有用户的信息。攻击者可以向程序传递如下参数:

String username = "' OR 1=1 #";
String password = "' OR 1=1 #";

经过简单的替换后,SQL语句变为:

String sql = "SELECT * FROM users WHERE username = '' OR 1=1 #' AND password = '' OR 1=1 #'";

此时,SQL语句的逻辑就被攻击者修改过了,由原来的查询指定用户,变为了查询所有用户。同时,#号也被用来注释掉后面的内容,避免程序出现错误。

因此,在实际应用中,应该尽量使用#{}占位符,避免使用${}占位符,从而提高系统的安全性。

3 xml 映射文件中,除了常见的 select、insert、update、delete 标签之外,还有哪些标签?

还有很多其他的标签, 、 、 、 、 ,加上动态 sql 的 9 个标签, trim|where|set|foreach|if|choose|when|otherwise|bind 等,其中 为 sql 片段标签,通过 标签引入 sql 片段, 为不支持自增的主键生成策略标签。

4 MyBatis 是如何进行分页的?分页插件的原理是什么?

4.1 使用 RowBounds 对象进行分页

使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内存分页,而非物理分页;

Mybatis 中的 RowBounds 对象确实是对结果集执行的内存分页,而不是类似数据库的物理分页。因为 MyBatis 中的 RowBounds 仅仅是将查询 SQL 语句的返回结果集放在了一个 List 集合中,然后通过 List.subList(start, end) 的方式截取需要的分页数据,这样的方式存在一定的缺陷:

  • 对于大批量数据的查询,需要将整个结果集先查询出来,再在内存中进行分页,存在占用内存过大的问题。
  • MyBatis 的实现机制仅仅是简单的将结果集按照分页参数进行截取,如果结果集发生改变,比如新增、删除、排序等操作,再使用同样的分页参数再次查询时,可能会有数据重复或遗漏的问题。

4.2 在 sql 内直接书写带有物理分页的参数来完成物理分页功能

在 SQL 语句中直接写物理分页的参数虽然可以实现物理分页,但是需要自己手动编写 SQL 语句,在编写过程中容易出现分页参数的计算错误,同时也会让 SQL 语句变得较为冗长,不易维护。而且不同数据库的分页方法也有所不同,需要根据具体的数据库类型做出相应的调整。

4.3 使用分页插件来完成物理分页

分页插件可以更方便地实现物理分页功能。分页插件通常会拦截 MyBatis 执行的 SQL 语句,在其中自动添加分页相关的参数和语句,提供了统一的分页接口,支持多个数据库类型的分页功能,使用起来更加方便。同时,一些优秀的分页插件还可以对查询结果进行缓存、预处理等优化操作,进一步提升分页查询的效率和性能。

4.4 示例代码

4.4.1 以下是使用 RowBounds 对象实现分页查询的示例代码

public interface UserMapper {
    List<User> selectUsersWithRowBounds(RowBounds rowBounds);
}
<select id="selectUsersWithRowBounds" resultMap="userResultMap">
  SELECT * FROM user ORDER BY id
select>
// 使用 RowBounds 对象实现分页查询
int pageNum = 2;
int pageSize = 10;
RowBounds rowBounds = new RowBounds(pageSize * (pageNum - 1), pageSize);
List<User> users = userMapper.selectUsersWithRowBounds(rowBounds);

4.4.2 以下是在 SQL 语句中直接使用物理分页参数的示例代码

<select id="selectUsersWithPhysicalPaging" resultMap="userResultMap">
  SELECT * FROM (
    SELECT *, ROW_NUMBER() OVER(ORDER BY id) AS row_number FROM user
  ) AS t
  WHERE t.row_number BETWEEN #{startRow} AND #{endRow}
select>
// 使用 SQL 语句实现物理分页查询
int pageNum = 2;
int pageSize = 10;
int startRow = pageSize * (pageNum - 1) + 1;
int endRow = pageSize * pageNum;
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("startRow", startRow);
paramMap.put("endRow", endRow);
List<User> users = userMapper.selectUsersWithPhysicalPaging(paramMap);

4.4.3 以下是使用 MyBatis 分页插件实现物理分页查询的示例代码(以 PageHelper 为例)

// 配置 MyBatis 分页插件
@Configuration
public class MyBatisConfig {

    @Bean
    public PageInterceptor pageHelper() {
        PageInterceptor pageInterceptor = new PageInterceptor();
        Properties properties = new Properties();
        properties.setProperty("helperDialect", "mysql");
        properties.setProperty("reasonable", "true");
        properties.setProperty("supportMethodsArguments", "true");
        pageInterceptor.setProperties(properties);
        return pageInterceptor;
    }

}
// 使用 MyBatis 分页插件实现物理分页查询
int pageNum = 2;
int pageSize = 10;
PageHelper.startPage(pageNum, pageSize);
List<User> users = userMapper.selectUsersWithPageHelper();

其中,PageHelper.startPage(pageNum, pageSize) 表示开启分页功能,将后续的查询操作进行物理分页。

4.5 分页插件的基本原理

分页插件的基本原理是使用 MyBatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 sql,然后重写 sql,根据 dialect 方言,添加对应的物理分页语句和物理分页参数。

5 MyBatis 动态 sql 是做什么的?

MyBatis 动态 sql 可以让我们在 xml 映射文件内,以标签的形式编写动态 sql,完成逻辑判断和动态拼接 sql 的功能。

6 动态 sql 的执行原理

使用 OGNL(Object-Graph Navigation Language) 从 sql 参数对象中计算表达式的值,根据表达式的值动态拼接 sql,以此来完成动态 sql 的功能。

7 都有哪些动态 sql?

MyBatis 的动态 SQL 语句包括:

  • if 元素:可用于判断是否应该包含某个元素;
  • choose 元素:类似于 Java 语言的 switch 语句,可根据条件包含其中的某个分支;
  • when 元素:choose 元素下的子元素,类似于 Java 语言中的 case 语句;
  • otherwise 元素:choose 元素下的子元素,类似于 Java 语言中的 default 语句;
  • trim 元素:用于过滤 SQL 语句片段的前缀、后缀或者分隔符;
  • where 元素:用于组合 where 条件语句,仅在第一个条件存在时加入“WHERE”关键字;
  • set 元素:用于组合 SQL 更新操作时的 set 语句;
  • foreach 元素:对一个集合进行遍历,生成多条 SQL 语句;
  • bind 元素:可以将一个 OGNL 表达式绑定到一个变量上,在之后的 SQL 语句中可以使用该变量。

你可能感兴趣的:(mybatis,学习,java)