`
378629846
  • 浏览: 212910 次
  • 性别: Icon_minigender_1
  • 来自: 哈尔滨
社区版块
存档分类
最新评论

LDAP查询分页,基于迭代器的查询分页

    博客分类:
  • java
阅读更多

      LDAP服务器端可以支持分页查询,但是有个前提条件,需要客户端先发送按关键字排序的指令后,才能执行分页查询。排序的过程是比较耗费时间的,需要对服务器做很多优化的操作。

      我的方法是在迭代结果集的时候实现分页,见代码:

 

package ldap.page;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;

/**
 * LDAP分页查询,使用时需要异步统计返回结果集的总行数
 * @author sxl
 *
 */
public class LdapSearch {
	/**
	 * 根据条件查找指定DN的条目下的一层所有属性
	 * 
	 * @param dn
	 *            要查询的BaseDN名称
	 * @param filter
	 *            要查询的过滤字符串
	 * @param returnedAtts
	 *            要查找的属性
	 * @param start
	 *            开始行
	 * @param limit
	 *            分页大小,如果为-1,则返回结果集的总行数
	 * @return 符合查询结果的List
	 */
	public static List searchContextOneByPage(String dn, String filter, String[] returnedAtts, int start, int limit){
		DirContext context = getDirContext("url","username","password");
		if(context == null) return null;
        // 实例化一个搜索器
 		SearchControls constraints = new SearchControls();
		// 设置搜索器的搜索范围为ONELEVEL
		constraints.setSearchScope( SearchControls.ONELEVEL_SCOPE);
		// 设置返回的属性
		if (returnedAtts != null) {
			constraints.setReturningAttributes(returnedAtts);
		}
		try {
			if (filter == null || filter.trim().equals("")) {
				filter = "objectclass=*";
			}
			NamingEnumeration<SearchResult> results = context.search(dn, filter, constraints);
			context.close();
			return searchPage(results,start,limit);
		} catch (NamingException ex) {
			ex.printStackTrace();
			return null;
		}
	}

	/**
	 * 分页查询
	 * @param resultList
	 * @param results
	 * @throws NamingException
	 */
	private static List searchPage(NamingEnumeration<SearchResult> results,int start,int limit) throws NamingException {
		List resultList = new ArrayList();
		int row = 0;
		boolean flag = false;
		while (results != null && results.hasMore()) {
			SearchResult si = results.next();// 取一个条目
			if(limit == -1)//如果limit==-1,只统计总行数
				row++;
			else {
				if(row++ == start) flag = true;//从start行开始取数据
				Attributes attrs = si.getAttributes();
				if (attrs != null && flag) {
					Map resultRowMap = new HashMap();// 一行数据
					for (NamingEnumeration ae = attrs.getAll(); ae.hasMoreElements();) {
						Attribute attr = (Attribute) ae.next();// 获取一个属性
						String attrId = attr.getID();
						Enumeration vals = attr.getAll();
						if (vals != null) {
							ArrayList valList = new ArrayList();
							while (vals.hasMoreElements()) {
								Object obj = vals.nextElement();
								if (obj instanceof String) {
									String _value = (String) obj;
									valList.add(_value);
								} else {
									valList.add(obj);
								}
							}
							resultRowMap.put(attrId, valList);
						}
					}
					resultList.add(resultRowMap);
					if(resultList.size() == limit){
						break;
					}
				}
			}
			
		}
		results.close();
		if(limit == -1)//如果limit为-1,则只返回总行数
			resultList.add(row);
		return resultList;
	}
	/**
	 * 从LDAP中取得一个连接
	 * @return DirContext
	 */
	private static DirContext getDirContext(String url,String userdn,String password){
		Properties mEnv = new Properties();
		mEnv.put(Context.AUTHORITATIVE, "true");
		mEnv.put("com.sun.jndi.ldap.connect.pool", "false");
		mEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
		mEnv.put(Context.PROVIDER_URL, url);
		mEnv.put("com.sun.jndi.ldap.connect.timeout","3000");
		mEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
		mEnv.put(Context.SECURITY_PRINCIPAL, userdn);
		mEnv.put(Context.SECURITY_CREDENTIALS, password);
		DirContext ctx = null;
		try {
			ctx = new InitialLdapContext(mEnv, null);
		} catch (NamingException ex) {
			ex.printStackTrace();
			if(ctx != null)
				try {
					ctx.close();
				} catch (NamingException e) {
					e.printStackTrace();
				}
		}
		return ctx;

	}
}

 使用时,传递start和limit作为分页的参数,这样在迭代结果集的时候进行判断,从start开始一直取limit个元素结束。

但是,这里有一个前提条件,我们要异步来统计结果集的总行数,传递limit为-1。以EXT为例,我们覆盖Ext.PagingToolbar这个类里的一些函数,保存为myPagebar.js。

 

(function (){
	var total = 0;
	var T = Ext.Toolbar;
	Ext.PagingToolbar.prototype.setTotalCount = function(num){
		total = num;
	}
	Ext.PagingToolbar.prototype.getPageData = function(){
		if(total == 0){
			this.inputItem.setDisabled(true);
		}else{
			this.inputItem.setDisabled(false);
			this.last.setDisabled(false);
		}
		return {
		    total : total,
		    activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
		    pages : Math.ceil(total/this.pageSize)
		};
	}
	Ext.PagingToolbar.prototype.onLoad = function(store, r, o){
	    if(!this.rendered){
	        this.dsLoaded = [store, r, o];
	        return;
	    }
	    var p = this.getParams();
	    this.cursor = (o.params && o.params[p.start]) ? o.params[p.start] : 0;
	    var d = this.getPageData(), ap = d.activePage, ps = d.pages;
	
	    this.afterTextItem.setText(String.format(this.afterPageText, d.pages));
	    this.inputItem.setValue(ap);
	    this.first.setDisabled(ap == 1);
	    this.prev.setDisabled(ap == 1);
	    this.next.setDisabled(ap == ps);
	    //this.last.setDisabled(ap == ps);
	    this.refresh.enable();
	    this.updateInfo();
	    this.fireEvent('change', this, d);
	}
	Ext.PagingToolbar.prototype.updateInfo = function(){ 
		if(this.displayItem){
	        var count = this.store.getCount();
	        var msg = count == 0 ?
	            this.emptyMsg :
	            String.format(
	                this.displayMsg,
	                this.cursor+1, this.cursor+count, total
	            );
	        this.displayItem.setText(msg);
	    }
	}
	Ext.PagingToolbar.prototype.changePage = function(page){
	    this.doLoad(((page-1) * this.pageSize).constrain(0, total));
	}
	Ext.PagingToolbar.prototype.moveLast = function(){
	    var extra = total % this.pageSize;
	    this.doLoad(extra ? (total - extra) : total - this.pageSize);
	}
})();

 在jsp里引入myPagebar.js后,按照正常的方式使用Ext.PagingToolbar就可以了。

这样,在使用Ext.grid.GridPanel来显示数据之前,先使用Ajax去后台统计总行数,传递参数里增加start:1,limit:-1。

在Ajax成功返回后,调用Ext.PagingToolbar.prototype.setTotalCount(totalRow);同时,Ext.grid.GridPanel的store会去后台load数据,传给后台的参数包括start:1,limit:50,表示取第一页的50行数据。这样就实现了后台分页的效果。

     如果数据很多,统计总行数会耗时较长,这时Ext.grid.GridPanel上显示的总页数为0,但是可以点击<下一页>,继续访问下一页的数据。直到总行数统计完成后,才能看到总页数,才能翻到最后一页。

     这种方式避免了数据过多时的内存溢出问题,也提高了数据的访问速度,最起码是页数比较靠前的数据的访问速度。友好度也要好很多。

分享到:
评论
2 楼 softfn 2014-01-15  
没解决本质问题 你这个只是为了页面分页而已
1 楼 tangmin823 2013-02-19  
这个根本算不上LDAP分页查询啊,不也是先查询出所有的记录吗?!

相关推荐

Global site tag (gtag.js) - Google Analytics