最近在开发客服第三方支付功能的时候,发现了主从表分页查询存在问题,然后解决了这个问题后,想将修改后的核心代码迁移到itv、短代、游戏包历史订购等相关接口时,发现这个完全适用于模板模式。好处就是当有类似的主从表分页查询时,只需要继承父类,实现相应的dao方法即可
先贴一个模板模式的图
[img]图在最下面,不会排版:funk: [img]
先说明下使用场景:因为数据量过大,所以在mysql对表进行了按月切分(并且是跨库),那么分页查询这些表时就有点蛋疼了,下面使用java代码来做下分页(有其他更好的方案请各位提供,一定感谢)
先来做一个abstract类
上面代码实现规则:先从主表中取数据,然后依次从从表中查询表的总数,当满足当前页的数据则加入到tempList中,直至加满(=pageSize),如果数据不满足则不做处理
其中TemplateOnlineServiceDao、TemplateOfflineServiceDao 均为接口提供方法如下
这边TemplateOfflineServiceDao类中getOfflineTables方法,其实可以放到抽象类TablesTemplate,因为这个方法在一定程度上属于service层而不是dao层
上层的架子搭好了,下面就是具体的实现了,拿itv的代码做例子
上述代码有一个让我感觉还不够完美的地方,就是dao定义,我必须得重复定义,一方面为了兼容之前的方法,另外一方面,较少TemplateOnlineServiceDao、TemplateOfflineServiceDao两个接口中的方法数量,让子类的dao可存在自己的特性方法
先贴一个模板模式的图
[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<B> {
Logger logger = Logger.getLogger(TablesTemplate.class);
/**
* 模板方法的子类必须为此dao赋值,并且相应的类必须实现{@link TemplateOnlineServiceDao}
*/
public TemplateOnlineServiceDao templateOnlineServiceDao;
public TemplateOfflineServiceDao templateOfflineServiceDao;
/**
* @param bean 查询条件对象
* @param currPage
* @param pageSize
* @param <T> 返回对象
* @return
* @throws ExceptionCommonBase
*/
public <T> 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<String> tableNames = templateOfflineServiceDao.getOfflineTables();
List<T> pageList = (List<T>) 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<T> offlineTableList = templateOfflineServiceDao.listFromOffline(bean, tableName);
List<T> 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 <T> List<T> getSubList(List<T> 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<B> {
/**
* @param bean
* @param currPage
* @param pageSize
* @return
* @throws ExceptionCommonBase
* @throws ExceptionCommonBase
*/
public <T> PageData getPageDataFromOnline(B bean, int currPage, int pageSize) throws ExceptionCommonBase;
/**
* @param bean
* @param <T>
* @return
* @throws ExceptionCommonBase
*/
public <T> List<T> 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<B> {
/**
* 获取线下所有表名
*
* @return
* @throws cn.egame.common.exception.ExceptionCommonBase
*/
public List<String> getOfflineTables() throws ExceptionCommonBase;
/**
* 获取表中的总数
*
* @param bean
* @param tableName
* @return
* @throws ExceptionCommonBase
*/
public int totalCount(B bean, String tableName) throws ExceptionCommonBase;
/**
* 查询出线下库表的数据
*
* @param bean
* @param tableName
* @param <T> 返回类型
* @return
* @throws ExceptionCommonBase
*/
public <T> List<T> 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 TablesTemplate<ItvFeeBean> implements 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<ItvFee> listItvFee(ItvFeeBean itvFeeBean) throws ExceptionCommonBase {
List<ItvFee> itvFees = itvFeeServiceDao.listFromOnline(itvFeeBean);
List<ItvFee> 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();