CH18-性能优化

动态条件

通过传入参数动态组装条件语句时,条件可能均为空,常见做法是使用 where 1=1 作为占位符,避免所有条件为空时语法错误:

<select id="getFruitInfo" resultType="Fruit">
  SELECT * FROM Fruit
  WHERE 1=1
  <if test="id!= null">
    and id = #{id}
  </if> 
  <if test="name!= null">
    and name = #{name}
  </if> 
</select>

如果 id 和 name 均为 null,则整个语句变成了:

SELECT * FROM Fruit WHERE 1=1

这时数据库就无法使用索引等查询优化策略,被迫对每行数据进行扫描,如果表很大,则性能消耗巨大,推荐的做法是使用 WHERE 标签:

<select id="getFruitInfo" resultType="Fruit">
  SELECT * FROM Fruit
  <WHERE>
  <if test="id!= null">
    id = #{id}
  </if> 
  <if test="name!= null">
    and name = #{name}
  </if> 
  </WHERE>
</select>

where 元素知道只有在一个以上的 if 条件有值的情况下才去插入 “where” 子句,且最后的内容如果是 “AND” 或 “OR” 开头的,where 元素也能识别并将它们去除。

批量插入

在使用 foreach 执行批量插入时,如果条目很多,会生成一个很长的 sql 语句,然后再使用实际的条目数据带入,构造出要执行的 sql,这个过程会很慢。

推荐的做法是使用 BATCH 模式执行批量插入:

@Service
public class PersonService {

    @Autowired
    private SqlSessionFactory sqlSessionFactory;
    
    public void insertPersions(List<Person> persons) {
        try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
            PersonMapper mapper = session.getMapper(PersonMapper.class);
            for (Person person : persons) {
                mapper.insert(person);
            }
            
            session.commit();
            session.clearCache();
        }
    }
}

WHERE IN foreach

<select id="getUserInfo" resultType="com.test.UserList">
  SELECT age FROM user
  WHERE
  <if test="userName!= null and userName.size() >0">
    user_name IN
    <foreach collection="userName" item="value" separator="," open="(" close=")">
      #{value}
    </foreach>
  </if>
</select>

但列表可能为空,所以总是在调用之前判空。或者有些同学会以如下方式兼容:

<select id="getUserInfo" resultType="com.test.UserList">
  SELECT age FROM user
  WHERE
  <if test="userName!= null and userName.size() >0">
    user_name IN
    <foreach collection="userName" item="value" separator="," open="(" close=")">
      #{value}
    </foreach>
  </if>
  <if test="userName!= null and userName.size() ==0">
    1=2
  </if>
  <if test="userName!= null and userName.size() ==1">
    user_name = #{users[0].userName}
  </if>
</select>

基于 IN 的性能问题,可以将 IN 改为 JOIN。