前言:由于本人最近在项目中看到太多其他人在for里面写查询的案例(看着头痛.....),所以就想着把自己遇到的问题提出来总结一下(以下的案例和代码仅做参考)。希望这篇文章对大家写代码的有所帮助。
1.分页里面的for循环查询
方法一:条件分割
public List<SysUserVO> selectUserAll(SysUser user) {
List<SysUserVO> vos = new ArrayList<>();
//分页查询
List<SysUser> users = sysUserMapper.selectUserAll(user);
//将分页数据中需要翻译的值过滤出来
Set<Integer> classId = users.stream().map(SysUser::getClassId).collect(Collectors.toSet());
//查询需要翻译的值
List<SysClassInfo> sysClassInfos = classInfoMapper.selectClassInfoAllById(classId);
//将需要翻译的值转成Map
Map<Integer, String> classInfoMap = sysClassInfos.stream().collect(Collectors.toMap(SysClassInfo::getId, SysClassInfo::getName));
//组装数据
users.forEach(data -> {
SysUserVO vo = new SysUserVO();
BeanUtils.copyProperties(data,vo);
vo.setClassName(classInfoMap.get(data.getClassId()));
vos.add(vo);
});
return vos;
}
分析:假设我们分页的数据只有10条,那么也就是我们主表查询出来的数据只有10条数据要进行翻译(页面展示数据),这个时候如果在循环里面写查询一个个的去翻译,那么就会和数据库进行10此交互。那如果数据更大呢?50条?100条?岂不是要查50次?100次?效率可想而知。所以这个时候我们可以通过上面的方法进行优化:
将我们主表中需要翻译的值通过流的方式过滤一下,把它转换成另一个我们需要翻译的从表条件,这个时候我们只需要去从表查询我们需要翻译的那几个值就行了,然后再将它转换成map的形式方便我们取值翻译,最后组装一下就是我们最终需要的分页数据了。
这样做的好处就是:最终我们去数据库交换的次数只有两次。相比之下,我们程序处理的数据肯定是比去数据库一条一条的检索翻译快得多。
那这个时候就有小伙伴问了,那如果我们需要翻译的条件过多的时候,岂不是每次都要进行这样分割的操作,那样的话,代码会不会太臃肿了。问得好!下面我开始介绍第二种写法:
方法二:建视图或自写SQL
自写SQL(以Mybatis为例):
<select id="selectUserAll1" resultMap="SysUserVO">
SELECT
a.*,
b.`name` AS className
FROM
( SELECT * FROM t_user LIMIT #{beginPage},#{endPage} ) AS a
LEFT JOIN ( SELECT * FROM t_class_info LIMIT #{beginPage},#{endPage} ) AS b ON a.class_id = b.id
WHERE
1 =1
<if test="user.id != null">
AND a.id = #{user.id}
</if>
<if test="user.name != null and user.name != ''">
AND a.name = #{user.name}
</if>
<if test="user.sex != null and user.sex != ''">
AND a.sex = #{user.sex}
</if>
<if test="user.height != null">
AND a.height = #{user.height}
</if>
<if test="user.age != null">
AND a.age = #{user.age}
</if>
<if test="user.number != null and user.number != ''">
AND a.number = #{user.number}
</if>
<if test="user.birthday != null">
AND a.birthday = #{user.birthday}
</if>
<if test="user.classId != null">
AND a.class_id = #{user.classId}
</if>
</select>
dao层:
List<SysUserVO> selectUserAll1(@Param("user") SysUser user,@Param("beginPage")Integer beginPage,@Param("endPage")Integer endPage);
servie层:
public List<SysUserVO> selectUserAll1(SysUser user) {
return sysUserMapper.selectUserAll1(user,0,10);
}
注意:此处我们只是模拟分页的查询条件
分析:这种方式我们可以在service层省去大量的数据处理的操作,但是,弊端也很明显:那就是xml文件不太好维护,如果判断条件没有写好的话,很容易出现问题。而且如果遇到比较复杂的查询,非常考验sql功底。
如果不想在在Mybatis里面写这么复杂的判断条件或者用的是其他的框架(如JPA,Hibernate)。可以考虑在在数据库建视图,原理和自写SQL类似:
代码:
<select id="selectUserAll2" resultMap="SysUserVO">
select * from t_class_user_view where 1=1
<if test="user.id != null">
AND a.id = #{user.id}
</if>
<if test="user.name != null and user.name != ''">
AND a.name = #{user.name}
</if>
<if test="user.sex != null and user.sex != ''">
AND a.sex = #{user.sex}
</if>
<if test="user.height != null">
AND a.height = #{user.height}
</if>
<if test="user.age != null">
AND a.age = #{user.age}
</if>
<if test="user.number != null and user.number != ''">
AND a.number = #{user.number}
</if>
<if test="user.birthday != null">
AND a.birthday = #{user.birthday}
</if>
<if test="user.classId != null">
AND a.class_id = #{user.classId}
</if>
</select>
相信大家肯定这个时候也发现了新的问题,那就是如果用视图的话,在SQL的可塑性就没有那么强了。遇到数据量极大的时候反而没有自写的SQL好用(视图只能在后面带上分页条件)。不过相对的,这种方式对于JPA而言可能是比较好的解决方案。
2.插入时候的for循环
Mybatis插入详见:Mybatis批量插入的几种方式-优快云博客
JPA和Hibernate的插入(如果觉得复杂,其实也可以使用原始的jdbc写,虽然有点复杂,但是效率还是比较高的):有关Hibernate/JPA的批量插入更新_jpa hibernate批量插入@query-优快云博客