1、背景:前阶段做了一个优化,旧代码逻辑是执行一个定时任务clover,每次去数据库查询数据,查出数据后组装入参循环调用其它平台的一个接口,原逻辑是一次性把所有数据都查出,然后后端代码去做筛选,由于目前数据量不是很大所以还勉强满足要求,为了防止后续数据量大导致慢SQL,故优化SQL,在SQL上增加筛选条件和索引的同时,在后端做了物理分页,防止数据量过大导致查询效率降低。
2、由于我这里多处用到该方法,我是封装到一个Service中用于公共调用的方法,提供物理分页,下面用到的User是对应数据库的实体类(我这里为了避免敏感数据都用User来代替):
@Setter
@Getter
public class User{
/** 主键 */
private Long id;
@NotNull(message = "学号,不能为空")
private Long classId;
/** 年级 */
private String grade;
/** 每天打卡时间 */
private Long clockIn;
}
3、下面Page类是公司内部封装的分页插件,其实原理和PageHelper一样,实际开发对照PageHelper的属性开发即可,下面用到Page类中的几个属性:start是起始条数,length是每次查询的条数,类似于PageHelper插件中的startPage中的两个属性,totalRecords是执行当前SQL满足条件的总条数(数据会在第一次执行SQL后,查出总条数封装到Page的totalRecords中)。
4、SQL层面需要传入Page类和对应筛选的实体类,所以方法中需要传入对应的筛选条件的实体(全都封装到User对象中即可),依次贴出相关代码:
1)Manager接口层:
public interface UserManager {
List<User> queryUserList(Page<User> page, User user);
}
2)Manager实现类:
@Component("userManager")
public class UserManagerImpl implements UserManager {
@Resource
private UserDao userDao;
@Override
public List<User> queryUserList(Page<User> page, User user) {
return userDao.queryUserList(page, user);
}
}
3)Dao层:
public interface UserDao {
List<User> queryUserList(@Param("page") Page<User> page, @Param("po") User user);
}
4)mapper.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.demo.User">
<resultMap id="BaseResultMap" type="User">
<result column="id" jdbcType="BIGINT" property="id" />
<result column="class_id" jdbcType="BIGINT" property="classIn" />
<result column="grade" jdbcType="VARCHAR" property="grade" />
<result column="clock_in" jdbcType="BIGINT" property="clockIn" />
</resultMap>
<select id="queryUserList" resultMap="BaseResultMap" parameterType="User">
select
<include refid="Base_Column_List"/>
from user
where
yn = 1
and clockIn != ''
and grade != ''
<if test="po.grade != null">
and grade = #{po.grade,jdbcType=VARCHAR}
</if>
<if test="po.clockIn != null">
and clock_in < #{po.clockIn,jdbcType=BIGINT}
</if>
</select>
</mapper>
5)Service层接口:
public interface UserService {
List<User> selectData(User user);
}
6)Service实现类:物理分页,下面我是每页查询100条数据(也可以做成全局变量,通过配置文件动态替换)
@Slf4j
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserManager userManager;
@Override
public List<User> selectData(User user) {
//分页条件:从0开始,每页100条数据
Page<User> commonPage = new Page<User>();
commonPage.setStart(0);
commonPage.setLength(100);
//先查询前100条记录,并获取到满足条件的总条数
List<User> users = userManager.queryUserList(commonPage, user);
if(CollectionUtils.isEmpty(users)) {
return null;
}
//创建结果集
List<User> resultList = new ArrayList<User>(users);
int totalRecords = commonPage.getiTotalRecords();
//如果总条数在100以内,则直接返回结果集
if(totalRecords <= 100) {
return resultList;
}
//将超出的部分数据查询出来,组装结果一起返回
//1.先计算出可以整除的次数
int num = totalRecords / 100;
//用于记录开始条数
int displayStart = 100;
if(num > 1) {
//从第2次开始查询,因为前100条记录已经查询过了
for(int i = 2; i <= num; i++) {
displayStart += 100;
commonPage.setStart(100 * (i-1));
List<User> extraList = userManager.queryUserList(commonPage, user);
if(CollectionUtils.isEmpty(extraList)) {
continue;
}
resultList.addAll(extraList);
}
}
//2.计算出余数即最后一次需要查询的条数
int remainder = totalRecords % 100;
if(remainder != 0) {
commonPage.setStart(displayStart);
commonPage.setLength(remainder);
List<User> extraList = userManager.queryUserList(commonPage, user);
if(CollectionUtils.isNotEmpty(extraList)) {
resultList.addAll(extraList);
}
}
return resultList;
}
}
7)具体调用
public class Demo {
@Value("${demo.clockIn:3600}")
private Long clockInScreen;
@Resource
private UserService userService;
public List<User> selectData() {
//构建筛选条件
User user = new User();
long currentSecond = System.currentTimeMillis() / 1000;
user.setClockIn(currentSecond + clockInScreen);
user.setGrade(1);
return userService.selectData(user);
}
}