Mybatis分页插件的实现

本文介绍了一个用于MyBatis的自定义分页插件实现细节,包括配置步骤、使用方法及核心代码解析。该插件通过拦截SQL语句实现动态分页功能,并能自动计算总记录数和总页数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

mybatis插件的使用,新增了两个类,一个是Interceptor,负责拦截并处理数据,一个是PageParams,作为入参

使用方式:

1.配置mybatis的插件

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
		<property name="mapperLocations" value="classpath:sqlConfig/*.xml"></property><!-- 扫描所有的xml文件 -->
		<property name="plugins">
			<array>
				<bean class="com.*.*.*.PagePlugin"></bean>
			</array>
		</property>
	</bean>


2. 在dao中将入参设置为PageParams,例如

List<UserPwd> selectUserPwdByPage(PageParams<UserPwd> param);

3.调用

PageParams<UserPwd> pp = new PageParams<UserPwd>();
		pp.setPage(1);
		pp.setPageSize(10);
		UserPwd userPwd = new UserPwd();
		userPwd.setUserId("123");
		pp.setParam(userPwd);
		List<UserPwd> list = userPwdDao.selectUserPwdByPage(pp);
		
		System.out.println("查出数据"+list.size());
		System.out.println(pp);

查询结果为list,总数和总页数在pp中


4.sql

查询参数放在param中,sql中通过param取出

select <include refid="fields" /> from user_pwd 
		<where>
			<if test="param.userId != null">
				user_id = #{param.userId}
			</if>
		</where>


5. 代码

Interceptor

@Intercepts({
	@Signature(type = StatementHandler.class,
			method = "prepare",
			args = {Connection.class})
})
public class PagePlugin implements Interceptor {

	private Integer defaultPage;
	private Integer defaultPageSize;
	private Boolean defaultUseFlag;
	private Boolean defaultCheckFlag;
	
	
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		StatementHandler stmtHandler = getUnProxyObject(invocation);
		MetaObject metaStatementHandler = SystemMetaObject.forObject(stmtHandler);
		
		String sql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
		//不是select语句
		if(!checkSelect(sql)) {
			return invocation.proceed();
		}
		
		BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
		Object parameterObject = boundSql.getParameterObject();
		PageParams pageParams = getPageParams(parameterObject);
		//没有分页参数,不启用插件
		if(pageParams == null) {
			return invocation.proceed();
		}
		//获取分页参数,获取不到时候使用默认值
		Integer pageNum = pageParams.getPage() == null ? 
					this.defaultPage : pageParams.getPage();
		Integer pageSize = pageParams.getPageSize() == null ?
					this.defaultPageSize : pageParams.getPageSize();
		Boolean useFlag = pageParams.getUseFlag() == null ?
					this.defaultUseFlag : pageParams.getUseFlag();
		Boolean checkFlag = pageParams.getCheckFlag() == null ?
					this.defaultCheckFlag : pageParams.getCheckFlag();
		//不适用分页插件
		if(!useFlag) {
			return invocation.proceed();
		}
		
		int total = getTotal(invocation, metaStatementHandler, boundSql);
		//回填总数到分页参数里
		setTotalToPageParams(pageParams, total, pageSize);
		//检查当前页码的有效性
		checkPage(checkFlag, pageNum, pageParams.getTotalPage());
		//修改sql
		return changeSql(invocation, metaStatementHandler, boundSql, pageNum, pageSize);
	}
	@Override
	public Object plugin(Object statementHandler) {
		return Plugin.wrap(statementHandler, this);
	}

	@Override
	public void setProperties(Properties props) {
		String strDefaultPage = props.getProperty("default.page", "1");
		String strDefaultPageSize = props.getProperty("default.pageSize", "50");
		String strDefaultUseFlag = props.getProperty("default.useFlag", "false");
		String strDefaultCheckFlag = props.getProperty("default.checkFlag", "false");
		
		this.defaultPage = Integer.parseInt(strDefaultPage);
		this.defaultPageSize = Integer.parseInt(strDefaultPageSize);
		this.defaultUseFlag = Boolean.parseBoolean(strDefaultUseFlag);
		this.defaultCheckFlag = Boolean.parseBoolean(strDefaultCheckFlag);

	}
	
	private StatementHandler getUnProxyObject(Invocation ivt) {
		StatementHandler statementHandler = (StatementHandler) ivt.getTarget();
		MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
		Object object = null;
		while(metaStatementHandler.hasGetter("h")) {
			object = metaStatementHandler.getValue("h");
		}
		if(object == null) {
			return statementHandler;
		}
		return (StatementHandler) object;
	}
	
	private boolean checkSelect(String sql) {
		String trimSql = sql.trim();
		int idx = trimSql.toLowerCase().indexOf("select");
		
		return idx == 0;
	}
	/*
	 * **********分解分页参数,这里支持使用Map和@Param注解传递参数,或者pojo继承PageParams,这三种方式都可以
	 * 只拦截入参是PageParams的
	 */
	private PageParams getPageParams(Object parameterObject) {
		if(parameterObject == null) {
			return null;
		}
		PageParams pageParams = null;
//		if(parameterObject instanceof Map) {
//			Map<String, Object> paramMap = (Map<String, Object>) parameterObject;
//			Set<String> keySet = paramMap.keySet();
//			Iterator<String> it = keySet.iterator();
//			while(it.hasNext()) {
//				String key = it.next();
//				Object value = paramMap.get(key);
//				if(value instanceof PageParams) {
//					return (PageParams) value;
//				}
//			}
//		}else if(parameterObject instanceof PageParams) {
//			pageParams = (PageParams) parameterObject;
//		}
		if(parameterObject instanceof PageParams) {
			pageParams = (PageParams) parameterObject;
		}
		
		return pageParams;
		
	}
	
	private int getTotal(Invocation invocation, MetaObject metaStatementHandler, BoundSql boundSql) throws SQLException {
		MappedStatement mappedStatement = (MappedStatement) 
				metaStatementHandler.getValue("delegate.mappedStatement");
		Configuration cfg = mappedStatement.getConfiguration();
		String sql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
		String countSql = "select count(*) as total from ("+sql+") a";
		Connection connection  = (Connection) invocation.getArgs()[0];
		PreparedStatement ps = null;
		int total = 0;
		try {
			ps = connection.prepareStatement(countSql);
			BoundSql countBoundSql = new BoundSql(cfg, countSql, 
					boundSql.getParameterMappings(), 
					boundSql.getParameterObject());
			ParameterHandler handler = new DefaultParameterHandler(mappedStatement, 
					boundSql.getParameterObject(), countBoundSql);
			handler.setParameters(ps);
			ResultSet rs = ps.executeQuery();
			while(rs.next()) {
				total = rs.getInt("total");
			}
		}finally {
			if(ps != null) {
				ps.close();
			}
		}
		return total;
	}
	private void setTotalToPageParams(PageParams pageParams, int total, Integer pageSize) {
		pageParams.setTotal(total);
		int totalPage = total % pageSize == 0 ? total / pageSize : total / pageSize +1;
		pageParams.setTotalPage(totalPage);
		
	}
	private void checkPage(Boolean checkFlag, Integer pageNum, Integer totalPage) throws Exception {
		if(checkFlag) {
			if(pageNum > totalPage) {
				//throw new Exception("查询时报,查询页码【"+pageNum+"】大于总页数【"+totalPage+"】!");
				pageNum = totalPage;
			}
		}
		
	}
	private Object changeSql(Invocation invocation, MetaObject metaStatementHandler, BoundSql boundSql, Integer pageNum,
			Integer pageSize) throws Exception {
		String sql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
		String newSql = "select a.* from ("+sql+") a limit ?, ?";
		metaStatementHandler.setValue("delegate.boundSql.sql", newSql);
		PreparedStatement ps = (PreparedStatement) invocation.proceed();
		int count = ps.getParameterMetaData().getParameterCount();
		ps.setInt(count-1, (pageNum-1)*pageSize);
		ps.setInt(count, pageSize);
		return ps;
	}
}

PageParams

public class PageParams <T>{
	private Integer page;//当前页码
	private Integer pageSize;//每页条数
	private Boolean useFlag = true;//是否启用插件
	private Boolean CheckFlag = true;//是否检测当前页码的有效性
	private Integer total;//当前sql返回总数,插件回填
	private Integer totalPage;//sql以当前分页的总页数,插件回填
	
	private Object param;//查询参数
	private List<T> resultList;//返回参数,可以把结果直接设置进来,直接返回
	
	
	
	public Object getParam() {
		return param;
	}
	public void setParam(Object param) {
		this.param = param;
	}
	public List<T> getResultList() {
		return resultList;
	}
	public void setResultList(List<T> resultList) {
		this.resultList = resultList;
	}
	public Integer getPage() {
		return page;
	}
	public void setPage(Integer page) {
		this.page = page;
	}
	public Integer getPageSize() {
		return pageSize;
	}
	public void setPageSize(Integer pageSize) {
		this.pageSize = pageSize;
	}
	public Boolean getUseFlag() {
		return useFlag;
	}
	public void setUseFlag(Boolean useFlag) {
		this.useFlag = useFlag;
	}
	public Boolean getCheckFlag() {
		return CheckFlag;
	}
	public void setCheckFlag(Boolean checkFlag) {
		CheckFlag = checkFlag;
	}
	public Integer getTotal() {
		return total;
	}
	public void setTotal(Integer total) {
		this.total = total;
	}
	public Integer getTotalPage() {
		return totalPage;
	}
	public void setTotalPage(Integer totalPage) {
		this.totalPage = totalPage;
	}
	@Override
	public String toString() {
		return "PageParams [page=" + page + ", pageSize=" + pageSize + ", useFlag=" + useFlag + ", CheckFlag="
				+ CheckFlag + ", total=" + total + ", totalPage=" + totalPage + "]";
	}
}



### MyBatis 分页插件实现方式 MyBatis分页功能可以通过多种方式进行实现,其中最常见的方式之一是通过第三方插件 **PageHelper** 来完成高效的分页操作。以下是关于如何使用 PageHelper 插件的具体实现方法: #### 1. 添加 Maven 或 Gradle 依赖 为了在项目中集成 PageHelper 插件,首先需要将其依赖项引入到项目的构建文件中。对于基于 Maven 构建的项目,可以在 `pom.xml` 文件中添加如下依赖[^3]: ```xml <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.4.2</version> </dependency> ``` 如果使用的是 Spring Boot,则可以直接利用该 Starter 包自动配置 PageHelper。 --- #### 2. 配置 MyBatis 中的 PageHelper 插件MyBatis 的全局配置文件 `mybatis-config.xml` 中,需注册并初始化 PageHelper 插件的相关参数。具体配置如下所示[^2]: ```xml <configuration> <plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!-- 数据库方言 --> <property name="helperDialect" value="mysql"/> <!-- 是否合理化分页 --> <property name="reasonable" value="true"/> </plugin> </plugins> </configuration> ``` 此部分配置指定了数据库类型以及一些高级选项(如是否启用合理的分页逻辑)。 --- #### 3. 编写代码实现分页查询 在实际开发过程中,可以借助 PageHelper 提供的方法快速实现分页查询。以下是一个典型的 Java 方法示例,展示如何结合 SQL 查询语句执行分页操作: ```java import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; public List<User> getUserList(int pageNum, int pageSize) { // 设置分页参数:pageNum 表示当前页码;pageSize 表示每页显示记录数 PageHelper.startPage(pageNum, pageSize); // 执行查询操作 List<User> userList = userMapper.selectAllUsers(); // 获取分页后的详细信息 PageInfo<User> pageInfo = new PageInfo<>(userList); return userList; } ``` 在此代码片段中,调用了 `PageHelper.startPage()` 方法设置分页条件,并随后执行具体的 Mapper 层查询逻辑。最终可通过 `PageInfo` 类获取更多有关分页的信息,例如总条目数、总页数等。 --- #### 4. 替代方案——RowBounds 方式 除了使用 PageHelper 外,还可以采用 MyBatis 自带的 `RowBounds` 工具类来实现基本的分页需求[^4]。这种方式虽然简单,但在复杂场景下的灵活性较差。下面是一段演示代码: ```java int offset = (currentPage - 1) * pageSize; // 起始位置 RowBounds rowBounds = new RowBounds(offset, pageSize); // 使用 RowBounds 参数传递给 Mapper 接口中的方法 List<User> users = sqlSession.selectList("selectUser", null, rowBounds); ``` 尽管如此,在大多数情况下推荐优先选用 PageHelper 插件,因为它不仅提供了更强大的功能支持,还简化了编码过程。 --- ### 总结 综上所述,无论是通过引入外部插件还是直接运用内置工具,都可以满足不同层次上的分页需求。然而考虑到便捷性和扩展性等因素,建议开发者倾向于采纳像 PageHelper 这样的成熟解决方案[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值