设为首页 收藏本站
查看: 770|回复: 0

[经验分享] mybatis分页实现2

[复制链接]
发表于 2016-11-26 07:57:58 | 显示全部楼层 |阅读模式
前一篇写了如何实现sql分页
http://icefire.iyunv.com/blogs/1028915
引用
本文对直接使用sqlsession的方式无参考价值,本文只针对只写接口和XML文件的方式

这一篇讲,如何实现一个mapping的方法访问,自动返回page数据,也就是自读计算分页总数。
先看成果对比效果
原先

public interface HelloMapping {
public List<Hello> findAll(RowBounds rowBounds);
public Long findAllCount();
}


public String list(@ModelAttribute Pagination<Hello> pagination,Model model) {
pagination.setTotal(helloMapping.findAllCount());
pagination.setList(helloMapping.findAll(pagination.newRowBounds())));
model.addAttribute("paging", pagination);
return "/hello/view/list";
}


现在

public interface HelloMapping {
public Pagination<Hello> findAll(Pagination<Hello> pagination);
}


public String list(@ModelAttribute Pagination<Hello> pagination,Model model) {
model.addAttribute("paging", helloMapping.findAll(pagination));
return "/hello/view/list";
}


如果看源码,会发现,mapping接口自动代理涉及Configuration,MapperRegistry,MapperProxy,MapperMethod。
其中比较可惜的是,MapperProxy,MapperMethod完全无法重用,只能参考其中代码自己来实现了。还有那个mybatis通过的spring集成类SqlSessionFactoryBean,由于要实现自己Configuration,也导致SqlSessionFactoryBean无法重用。这点上,和spring比较起来,就差太多了。
MyConfiguration

public class MyConfiguration extends Configuration {
protected MapperRegistry mapperRegistry = new PaginationMapperRegistry(this);
public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
@SuppressWarnings("rawtypes")
public boolean hasMapper(Class type) {
return mapperRegistry.hasMapper(type);
}
}


PaginationMapperRegistry

public class PaginationMapperRegistry extends MapperRegistry {
public PaginationMapperRegistry(Configuration config) {
super(config);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
if (!hasMapper(type)) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return PaginationMapperProxy.newMapperProxy(type, sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
}


PaginationMapperProxy

public class PaginationMapperProxy implements InvocationHandler {
private static final Set<String> OBJECT_METHODS = new HashSet<String>() {
private static final long serialVersionUID = -1782950882770203583L;
{
add("toString");
add("getClass");
add("hashCode");
add("equals");
add("wait");
add("notify");
add("notifyAll");
}
};
private boolean isObjectMethod(Method method) {
return OBJECT_METHODS.contains(method.getName());
}
private SqlSession sqlSession;
private <T> PaginationMapperProxy(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (isObjectMethod(method)) {
return null;
}
final Class<?> declaringInterface = findDeclaringInterface(proxy, method);
if (Pagination.class.isAssignableFrom(method.getReturnType())) {
// 分页处理
return new PaginationMapperMethod(declaringInterface, method, sqlSession).execute(args);
}
// 原处理方式
final MapperMethod mapperMethod = new MapperMethod(declaringInterface, method, sqlSession);
final Object result = mapperMethod.execute(args);
if (result == null && method.getReturnType().isPrimitive()) {
throw new BindingException(
"Mapper method '"
+ method.getName()
+ "' ("
+ method.getDeclaringClass()
+ ") attempted to return null from a method with a primitive return type ("
+ method.getReturnType() + ").");
}
return result;
}
private Class<?> findDeclaringInterface(Object proxy, Method method) {
Class<?> declaringInterface = null;
for (Class<?> iface : proxy.getClass().getInterfaces()) {
Method m = ReflectionUtils.findMethod(iface, method.getName(), method.getParameterTypes());
if (m != null) {
declaringInterface = iface;
}
}
if (declaringInterface == null) {
throw new BindingException(
"Could not find interface with the given method " + method);
}
return declaringInterface;
}
@SuppressWarnings("unchecked")
public static <T> T newMapperProxy(Class<T> mapperInterface,
SqlSession sqlSession) {
ClassLoader classLoader = mapperInterface.getClassLoader();
Class<?>[] interfaces = new Class[] { mapperInterface };
PaginationMapperProxy proxy = new PaginationMapperProxy(sqlSession);
return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);
}
}


PaginationMapperMethod

public class PaginationMapperMethod {
private SqlSession sqlSession;
private Configuration config;
private SqlCommandType type;
private String commandName;
private String commandCountName;
private Class<?> declaringInterface;
private Method method;
private Integer rowBoundsIndex;
private Integer paginationIndex;
private List<String> paramNames;
private List<Integer> paramPositions;
private boolean hasNamedParameters;
public PaginationMapperMethod(Class<?> declaringInterface, Method method,
SqlSession sqlSession) {
paramNames = new ArrayList<String>();
paramPositions = new ArrayList<Integer>();
this.sqlSession = sqlSession;
this.method = method;
this.config = sqlSession.getConfiguration();
this.hasNamedParameters = false;
this.declaringInterface = declaringInterface;
setupFields();
setupMethodSignature();
setupCommandType();
validateStatement();
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public Object execute(Object[] args) {
final Object param = getParam(args);
Pagination<Object> page;
RowBounds rowBounds;
if (paginationIndex != null) {
page = (Pagination) args[paginationIndex];
rowBounds = page.newRowBounds();
}
else if (rowBoundsIndex != null) {
rowBounds = (RowBounds) args[rowBoundsIndex];
page = new Pagination<Object>(rowBounds);
}
else {
throw new BindingException("Invalid bound statement (not found rowBounds or pagination in paramenters)");
}
page.setTotal(executeForCount(param));
page.setList(executeForList(param, rowBounds));
return page;
}
private long executeForCount(Object param) {
Number result = (Number) sqlSession.selectOne(commandCountName, param);
return result.longValue();
}
@SuppressWarnings("rawtypes")
private List executeForList(Object param, RowBounds rowBounds) {
return sqlSession.selectList(commandName, param, rowBounds);
}
private Object getParam(Object[] args) {
final int paramCount = paramPositions.size();
if (args == null || paramCount == 0) {
return null;
} else if (!hasNamedParameters && paramCount == 1) {
return args[paramPositions.get(0)];
} else {
Map<String, Object> param = new HashMap<String, Object>();
for (int i = 0; i < paramCount; i++) {
param.put(paramNames.get(i), args[paramPositions.get(i)]);
}
return param;
}
}
private void setupMethodSignature() {
final Class<?>[] argTypes = method.getParameterTypes();
for (int i = 0; i < argTypes.length; i++) {
if (Pagination.class.isAssignableFrom(argTypes)) {
paginationIndex = i;
}
else if (RowBounds.class.isAssignableFrom(argTypes)) {
rowBoundsIndex = i;
} else {
String paramName = String.valueOf(paramPositions.size());
paramName = getParamNameFromAnnotation(i, paramName);
paramNames.add(paramName);
paramPositions.add(i);
}
}
}
private String getParamNameFromAnnotation(int i, String paramName) {
Object[] paramAnnos = method.getParameterAnnotations();
for (Object paramAnno : paramAnnos) {
if (paramAnno instanceof Param) {
hasNamedParameters = true;
paramName = ((Param) paramAnno).value();
}
}
return paramName;
}
private void setupFields() {
commandName = declaringInterface.getName() + "." + method.getName();
commandCountName = commandName + "Count"; // 命名约定
}
private void setupCommandType() {
MappedStatement ms = config.getMappedStatement(commandName);
type = ms.getSqlCommandType();
if (type != SqlCommandType.SELECT) {
throw new BindingException("Unsupport execution method for: " + commandName);
}
}
private void validateStatement() {
if (!config.hasStatement(commandName)) {
throw new BindingException("Invalid bound statement (not found): " + commandName);
}
if (!config.hasStatement(commandCountName)) {
throw new BindingException("Invalid bound statement (not found): " + commandCountName);
}
}
}


具体某个查询的SQL配置

<select id="findAll" resultMap="helloResultMap">
select * from HELLO
</select>
<select id="findAllCount" resultType="long">
select count(*) from HELLO
</select>


补代码Pagination,分页辅助类

public class Pagination<T> {
private long total;
private int pagesize;
private List<T> list = Collections.emptyList();
private int offset;
private int limit;
private int page;
public Pagination() {
this(1, 15);
}
public Pagination(int page) {
this(page, 15);
}
public Pagination(int page, int limit) {
setPage(page);
setLimit(limit);
}
public Pagination(RowBounds rowBounds) {
this.limit = rowBounds.getLimit();
this.offset = rowBounds.getOffset();
this.page = offset / limit + 1;
}
public void setPage(int page) {
if (page < 0) {
page = 1;
}
this.page = page;
onInit();
}
public void setLimit(int limit) {
if (limit < 1) {
limit = 15;
}
this.limit = limit;
onInit();
}
protected void onInit() {
offset = (page - 1) * limit;
}
protected void onSetRowsize() {
pagesize = (int) (total / limit);
if (total % limit > 0) {
pagesize ++;
}
if (page > pagesize) {
page = pagesize;
onInit();
}
}
protected void onSetList() {
if (list == null || list.isEmpty()) {
total = 0;
page = 1;
offset = 0;
}
}
public long getTotal() {
return total;
}
public void setTotal(long rowsize) {
this.total = rowsize;
onSetRowsize();
}
public int getPagesize() {
return pagesize;
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
onSetList();
}
public int getOffset() {
return offset;
}
public int getLimit() {
return limit;
}
public int getPage() {
return page;
}
public RowBounds newRowBounds() {
return new RowBounds(getOffset(), getLimit());
}
}

目前无可避免的需要两段sql。在考虑如何实现自动生成count(*),但目前做到这样,已经达到自己的预期了。暂时先就这样吧!

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-305632-1-1.html 上篇帖子: SpringMVC与Mybatis集成开发环境搭建 下篇帖子: Mybatis使用之NEIS项目
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表