MyBatis分页插件实现
Posted claindoc
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatis分页插件实现相关的知识,希望对你有一定的参考价值。
日常开发中,MyBatis已经成为数据持久层实现的重要角色,以下就是一个使用MyBatis开发的一个分页插件的实现。关于Mybatis的插件概念可以查看MyBatis官网
查看官网教程可以得知,MyBatis允许客户对以下类的方法进行拦截。
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。
// ExamplePlugin.java
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
}
}
<!-- mybatis-config.xml -->
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
对官网教程的解读:
- 用户自定义拦截器类需要实现Interceptor接口。
- 指定拦截器需要拦截的方法。
ExamplePlugin
是对Executor
的update方法进行拦截。查看Executor
源码如下:
int update(MappedStatement ms, Object parameter) throws SQLException; - 配置文件
以下为分页插件的实现,是对Executor
Java <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
方法的拦截。
1.定义拦截器类,实现Interceptor
接口,指定拦截的方法。
@Intercepts({ @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }) })
public class PageInterceptor implements Interceptor {
static int MAPPED_STATEMENT_INDEX = 0;
static int PARAMETER_INDEX = 1;
static int ROWBOUNDS_INDEX = 2;
static int RESULT_HANDLER_INDEX = 3;
Dialect dialect;
public Object intercept(Invocation invocation) throws Throwable {
processIntercept(invocation.getArgs());
return invocation.proceed();
}
void processIntercept(final Object[] queryArgs) {
MappedStatement ms = (MappedStatement) queryArgs[MAPPED_STATEMENT_INDEX];
Object parameter = queryArgs[PARAMETER_INDEX];
// 获取分页信息RowBounds
final RowBounds rowBounds = (RowBounds) queryArgs[ROWBOUNDS_INDEX];
int pageCurrent = rowBounds.getOffset();
int pageSize = rowBounds.getLimit();
// 获取NAMESPACE和方法
String[] nameSpaceId = ms.getId().split("\\.");
if (!ArrayUtils.isEmpty(nameSpaceId) && nameSpaceId[nameSpaceId.length - 1].startsWith("query")) {
if (dialect.supportsLimit() && (pageCurrent != RowBounds.NO_ROW_OFFSET || pageSize != RowBounds.NO_ROW_LIMIT)) {
BoundSql boundSql = ms.getBoundSql(parameter);
String sql = boundSql.getSql().trim();
if (dialect.supportsLimitOffset()) {
sql = dialect.getPageSql(sql, pageCurrent, pageSize);
pageCurrent = RowBounds.NO_ROW_OFFSET;
} else {
sql = dialect.getPageSql(sql, 0, pageSize);
}
pageSize = RowBounds.NO_ROW_LIMIT;
queryArgs[ROWBOUNDS_INDEX] = new RowBounds(pageCurrent, pageSize);
BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), sql, boundSql.getParameterMappings(), boundSql.getParameterObject());
MappedStatement newMs = copyFromMappedStatement(ms, new BoundSqlSqlSource(newBoundSql));
//需要将metaParameters赋值过去..
MetaObject countBsObject = SystemMetaObject.forObject(newBoundSql);
MetaObject boundSqlObject = SystemMetaObject.forObject(boundSql);
countBsObject.setValue("metaParameters",boundSqlObject.getValue("metaParameters"));
queryArgs[MAPPED_STATEMENT_INDEX] = newMs;
}
}
}
/** @see MapperBuilderAssistant */
private MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource) {
Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());
builder.resource(ms.getResource());
builder.fetchSize(ms.getFetchSize());
builder.statementType(ms.getStatementType());
builder.keyGenerator(ms.getKeyGenerator());
builder.keyProperty(ms.getKeyProperties() == null ? null : ms.getKeyProperties()[0]);
builder.timeout(ms.getTimeout());
builder.parameterMap(ms.getParameterMap());
builder.resultMaps(ms.getResultMaps());
builder.resultSetType(ms.getResultSetType());
builder.cache(ms.getCache());
builder.flushCacheRequired(ms.isFlushCacheRequired());
builder.useCache(ms.isUseCache());
return builder.build();
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
try {
String dialectClassValue =properties.getProperty("dialectClass");
if("com.page.dialect.mysqlDialect".equals(dialectClassValue)){
dialect = (Dialect) Class.forName(MySqlDialect.class.getCanonicalName()).newInstance();
}else{
dialect = (Dialect) Class.forName(OracleDialect.class.getCanonicalName()).newInstance();
}
} catch (Exception e) {
throw new RuntimeException("cannot create dialect instance by dialectClass : " + OracleDialect.class.getCanonicalName(), e);
}
}
public static class BoundSqlSqlSource implements SqlSource {
BoundSql boundSql;
public BoundSqlSqlSource(BoundSql boundSql) {
this.boundSql = boundSql;
}
public BoundSql getBoundSql(Object parameterObject) {
return boundSql;
}
}
}
/**
* 类似HIBERNATE的Dialect,但只精简出分页部分
*/
public class Dialect {
/**
* 将SQL变成分页SQL语句</br>
* 拆分原始SQL,拼装需要的分页SQL.
*/
public String getPageSql(String sql, int pageCurrent, int pageSize) {
throw new UnsupportedOperationException("paged queries not supported");
}
public boolean supportsLimit() {
return false;
}
public boolean supportsLimitOffset() {
return supportsLimit();
}
}
public class MySqlDialect extends Dialect {
public String getPageSql(String sql, int pageCurrent, int pageSize) {
// 原始SQL
String orgSql = sql.trim();
// 重置当前页
pageCurrent = pageCurrent - 1;
//起始数据行
int offset=pageCurrent*pageSize;
// 最终SQL
StringBuffer finaleSql = new StringBuffer();
finaleSql.append("select * from ( ");
// 追加原始SQL
finaleSql.append(orgSql);
//用limit分页
finaleSql.append(" limit "+String.valueOf(offset)+","+String.valueOf(pageSize));
finaleSql.append(" ) as tabletotal");
return finaleSql.toString();
}
public boolean supportsLimit() {
return true;
}
public boolean supportsLimitOffset() {
return true;
}
}
public class OracleDialect extends Dialect {
public String getPageSql(String sql, int pageCurrent, int pageSize) {
// 原始SQL
String orgSql = sql.trim();
// 重置当前页
pageCurrent = pageCurrent - 1;
// 最终SQL
StringBuffer finaleSql = new StringBuffer();
// 如果当前页>0
finaleSql.append("select * from (select * from ( select row_.*, rownum rownum_ from ( ");
// 追加原始SQL
finaleSql.append(orgSql);
// 如果当前页>0
if (pageCurrent > 0) {
finaleSql.append(" ) row_ )) where rownum_ <= " + ((pageCurrent * pageSize) + "+" + pageSize) + " and rownum_ > "
+ String.valueOf(pageCurrent * pageSize));
} else {// 如果当前页<=0
finaleSql.append(" ) row_ )) where rownum_ <= " + String.valueOf(pageSize));
}
return finaleSql.toString();
}
public boolean supportsLimit() {
return true;
}
public boolean supportsLimitOffset() {
return true;
}
}
public class SystemMetaObject {
public static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
public static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
public static final MetaObject NULL_META_OBJECT = MetaObject.forObject(NullObject.class, DEFAULT_OBJECT_FACTORY,
DEFAULT_OBJECT_WRAPPER_FACTORY);
private SystemMetaObject() {
// Prevent Instantiation of Static Class
}
private static class NullObject {
}
public static MetaObject forObject(Object object) {
return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);
}
}
<configuration>
<properties>
<property name="dialect" value="oracle" />
</properties>
<settings>
<setting name="lazyLoadingEnabled" value="true" />
<setting name="jdbcTypeForNull" value="VARCHAR" />
</settings>
<plugins>
<plugin interceptor="com.page.interceptor.PageInterceptor">
<property name="dialectClass" value="com..page.dialect.OracleDialect" />
</plugin>
</plugins>
</configuration>
以上是关于MyBatis分页插件实现的主要内容,如果未能解决你的问题,请参考以下文章