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);
	}
}