先贴一个模板模式的图
[img]图在最下面,不会排版:funk: [img]
先说明下使用场景:因为数据量过大,所以在mysql对表进行了按月切分(并且是跨库),那么分页查询这些表时就有点蛋疼了,下面使用java代码来做下分页(有其他更好的方案请各位提供,一定感谢)
先来做一个abstract类
package cn.egame.data.core.common; import cn.egame.common.exception.ExceptionCommonBase; import cn.egame.common.model.PageData; import org.apache.log4j.Logger; import java.util.ArrayList; import java.util.List; /** * Created by dinghw on 2015/4/20. * * @desc 此类为模板模式的抽象类,主要用于主从分表查询分页的方法 */ public abstract class TablesTemplate { Logger logger = Logger.getLogger(TablesTemplate.class); /** * 模板方法的子类必须为此dao赋值,并且相应的类必须实现{@link TemplateOnlineServiceDao} */ public TemplateOnlineServiceDao templateOnlineServiceDao; public TemplateOfflineServiceDao templateOfflineServiceDao; /** * @param bean 查询条件对象 * @param currPage * @param pageSize * @param返回对象 * @return * @throws ExceptionCommonBase */ public PageData getPageDataFromOnlineAndOffline(B bean, int currPage, int pageSize) throws ExceptionCommonBase { int totalCount = 0; PageData pageData = templateOnlineServiceDao.getPageDataFromOnline(bean, currPage, pageSize); int onlineTotalCount = pageData.getTotal(); totalCount += onlineTotalCount; List tableNames = templateOfflineServiceDao.getOfflineTables(); List pageList = (List ) pageData.getContent(); for (String tableName : tableNames) { try { int offlineTotalCount = templateOfflineServiceDao.totalCount(bean, tableName); if (offlineTotalCount == 0) continue; int indexNum = (currPage - 1) * pageSize + 1; int endNum = currPage * pageSize; if (totalCount >= endNum) { totalCount = totalCount + offlineTotalCount; continue; } List offlineTableList = templateOfflineServiceDao.listFromOffline(bean, tableName); List tempList = getSubList(offlineTableList, pageSize, totalCount, offlineTotalCount, indexNum, endNum); pageList.addAll(tempList); totalCount = totalCount + offlineTotalCount; } catch (ExceptionCommonBase e) { logger.error(e.getMessage(), e); } } pageData.setContent(pageList); pageData.setTotal(totalCount); return pageData; } /** * 分页,获取从表的数据并切分 * * @param offlineTableList * @param pageSize * @param totalCount * @param offlineTotalCount * @param indexNum * @param endNum * @return */ private List getSubList(List offlineTableList, int pageSize, int totalCount, int offlineTotalCount, int indexNum, int endNum) { if (offlineTableList == null || offlineTableList.size() == 0) { return new ArrayList(); } int fromIndex = 0; int toIndex = 0; if (totalCount >= indexNum) { if ((totalCount + offlineTotalCount) > endNum) { toIndex = endNum - totalCount; } else { toIndex = offlineTotalCount; } } else if (totalCount < indexNum && (totalCount + offlineTotalCount) >= indexNum) { fromIndex = indexNum - 1 - totalCount; if ((totalCount + offlineTotalCount) >= endNum) { toIndex = fromIndex + pageSize; } else { toIndex = offlineTotalCount; } } return offlineTableList.subList(fromIndex, toIndex); } }
上面代码实现规则:先从主表中取数据,然后依次从从表中查询表的总数,当满足当前页的数据则加入到tempList中,直至加满(=pageSize),如果数据不满足则不做处理
其中TemplateOnlineServiceDao、TemplateOfflineServiceDao 均为接口提供方法如下
package cn.egame.data.core.common; import cn.egame.common.exception.ExceptionCommonBase; import cn.egame.common.model.PageData; import cn.egame.data.model.itv.ItvFee; import cn.egame.data.model.itv.ItvFeeBean; import java.util.List; /** * Created by dinghw on 2015/4/20. */ public interface TemplateOnlineServiceDao { /** * @param bean * @param currPage * @param pageSize * @return * @throws ExceptionCommonBase * @throws ExceptionCommonBase */ publicPageData getPageDataFromOnline(B bean, int currPage, int pageSize) throws ExceptionCommonBase; /** * @param bean * @param * @return * @throws ExceptionCommonBase */ public List listFromOnline(B bean) throws ExceptionCommonBase; }
package cn.egame.data.core.common; import cn.egame.common.exception.ExceptionCommonBase; import java.util.List; /** * Created by dinghw on 2015/4/20. */ public interface TemplateOfflineServiceDao { /** * 获取线下所有表名 * * @return * @throws cn.egame.common.exception.ExceptionCommonBase */ public ListgetOfflineTables() throws ExceptionCommonBase; /** * 获取表中的总数 * * @param bean * @param tableName * @return * @throws ExceptionCommonBase */ public int totalCount(B bean, String tableName) throws ExceptionCommonBase; /** * 查询出线下库表的数据 * * @param bean * @param tableName * @param 返回类型 * @return * @throws ExceptionCommonBase */ public List listFromOffline(B bean, String tableName) throws ExceptionCommonBase; }
这边TemplateOfflineServiceDao类中getOfflineTables方法,其实可以放到抽象类TablesTemplate,因为这个方法在一定程度上属于service层而不是dao层
上层的架子搭好了,下面就是具体的实现了,拿itv的代码做例子
/** * */ package cn.egame.data.core.itv; import cn.egame.common.exception.ExceptionCommonBase; import cn.egame.common.model.PageData; import cn.egame.data.core.app.AppServiceFactory; import cn.egame.data.core.common.TablesTemplate; import cn.egame.data.core.common.TemplateOfflineServiceDao; import cn.egame.data.core.common.TemplateOnlineServiceDao; import cn.egame.data.core.cp.CpServiceFactory; import cn.egame.data.interfaces.IObjectVisitor; import cn.egame.data.interfaces.ItvFeeService; import cn.egame.data.model.ContentProvider; import cn.egame.data.model.app.AppConsume; import cn.egame.data.model.app.AppInfo; import cn.egame.data.model.itv.ItvFee; import cn.egame.data.model.itv.ItvFeeBean; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.List; /** * itv计费相关业务实现类 * * @author houqq * @date 2014年11月28日 上午9:43:22 */ public class ItvFeeServiceImpl extends TablesTemplateimplements ItvFeeService { Logger logger = Logger.getLogger(ItvFeeServiceImpl.class); private static ItvFeeServiceImpl instance; private static byte[] syncRoot = new byte[1]; private ItvFeeServiceDao itvFeeServiceDao; private ItvFeeOfflineDao itvFeeOfflineDao; private ItvFeeServiceImpl() throws ExceptionCommonBase { itvFeeServiceDao = new ItvFeeServiceDao(); itvFeeOfflineDao = new ItvFeeOfflineDao(); templateOnlineServiceDao = new ItvFeeServiceDao(); templateOfflineServiceDao = new ItvFeeOfflineDao(); } public static ItvFeeServiceImpl getInstance() throws ExceptionCommonBase { if (instance == null) { synchronized (syncRoot) { if (instance == null) { instance = new ItvFeeServiceImpl(); } } } return instance; } /** * 依据条件查询itv计费列表 * * @param itvFeeBean itv计费实体 * @return itv计费列表 * @throws ExceptionCommonBase */ private List listItvFee(ItvFeeBean itvFeeBean) throws ExceptionCommonBase { List itvFees = itvFeeServiceDao.listFromOnline(itvFeeBean); List offLineItvFees = itvFeeOfflineDao.listAllFromOffline(itvFeeBean); itvFees.addAll(offLineItvFees); return itvFees; } /** * 从主从表中获取数据 * * @param appId * @param userId * @param itvfeebean * @param currPage * @param pageSize * @return * @throws RemoteException */ public PageData listItvFee(int appId, long userId, ItvFeeBean itvfeebean, int currPage, int pageSize) throws RemoteException { if (StringUtils.isEmpty(itvfeebean.getLinkageAccountId()) && StringUtils.isEmpty(itvfeebean.getPhone())) { PageData pageData = new PageData(); pageData.setCurrentPage(currPage); pageData.setRowsOfPage(pageSize); return pageData; } //此处调用父类方法 return this.getPageDataFromOnlineAndOffline(itvfeebean, currPage, pageSize); } }
上述代码有一个让我感觉还不够完美的地方,就是dao定义,我必须得重复定义,一方面为了兼容之前的方法,另外一方面,较少TemplateOnlineServiceDao、TemplateOfflineServiceDao两个接口中的方法数量,让子类的dao可存在自己的特性方法
itvFeeServiceDao = new ItvFeeServiceDao(); itvFeeOfflineDao = new ItvFeeOfflineDao(); templateOnlineServiceDao = new ItvFeeServiceDao(); templateOfflineServiceDao = new ItvFeeOfflineDao();