鋼炮級 持久層 —— 下篇

Title:分頁的擴展
持久層對於分頁功能的缺少,顯然是不能接受,爲彌補這個不足,我做了分頁的擴展,如下:
ExpPublicDao.java
package com.pub.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;

import com.pub.db.DBConnection;
import com.pub.page.Page;
/***
* 擴展PublicDao,處理分頁Sql
* @author Administrator
*
*/
public class ExpPublicDao extends PublicDao {

/***
* 構造方法
*/
public ExpPublicDao(){
super();
}

/***【重】【增】
* 構造分頁sql語句專用方法
* 分頁查詢可以不考慮操作類型,所以減少了操作類型的參數type
* 同時基於Page對象,在Page中做了表名、主鍵的記錄,這裏可以減少表名這個參數
* @param type
* @param page
* @param mk
* @param mv
* @return
*/
public String createPageSql(Map<Object, String> mk,Map<Object, Object> mv, String condition, Page page){

StringBuffer sql = new StringBuffer();
//接收查詢部分
sql.append(this.createSql(PublicDao.SELECT, page.getTableName(), mk, mv, condition));
//SQL條件部分
StringBuffer where = new StringBuffer();
//在查詢狀態下,通過mv判斷條件不爲空
if(mv.size() > 0 ){//配置條件部分
where.append(" where 1=1 and ");
if("".equals(condition) || null == condition){
for(int i=1;i<=mv.size();i++){
where.append(mv.get(i));
if(i != mv.size()){
where.append(" and ");
}
}
}else{
for(int i=1;i<=mv.size();i++){
where.append(mv.get(i)+""+condition.split(", ")[i-1]);//逗號+空格
if(i != mv.size()){
where.append(" and ");
}
}
}
return this.getMysqlPagerSql(sql, page, where).toString();
}else{//條件爲空
return this.getMysqlPagerSql(sql, page, where).toString();
}
}

/***
* 構造SqlServer分頁Sql
* @param sql
* @param page
* @param where
* @return getSqlServerPagerSql
*/
public StringBuffer getSqlServerPagerSql(StringBuffer sql, Page page, StringBuffer where){
//添加
sql = new StringBuffer(sql.toString().replaceFirst("select", "select top "+page.getPageSize()+" "));

StringBuffer condition = new StringBuffer();
//當前頁爲第1頁
if(page.getCurrentPage() <= 1){
if(page.getCurrentPage() < 1){
page.setCurrentPage(1);
}
condition.append(" ORDER BY " + page.getPrimaryKey());
}else{//當前頁不爲第1頁
if(page.getCurrentPage() > page.getPageCount()){
if(page.getPageCount() <= 1){
page.setCurrentPage(1);
getSqlServerPagerSql(sql, page, where);
return sql;
}
//爲便於測試,保證運行,判斷當總頁數小於當前頁數的時候設置當前頁爲總頁數
page.setCurrentPage(page.getPageCount());
}
//對於分頁的條件部分,拼湊SQL語句不算複雜
condition.append(" where "+page.getPrimaryKey() + " > (SELECT MAX(" + page.getPrimaryKey() + ") "+
" FROM (SELECT TOP "+(page.getCurrentPage()-1)*page.getPageSize()+" " + page.getPrimaryKey() + " "+
" FROM " + page.getTableName() + " @where ORDER BY " + page.getPrimaryKey() + ") AS T) "+
"ORDER BY " + page.getPrimaryKey()
);
}
// 最後,替換條件子查詢中條件佔位符
sql = sql.append(condition.toString().replace("@where", (where == null || where.equals(""))?"":where));

return sql;
}

/***
* 構造Mysql分頁Sql
* @param sql
* @param page
* @param where
* @return
*/
public StringBuffer getMysqlPagerSql(StringBuffer sql, Page page, StringBuffer where){

StringBuffer condition = new StringBuffer();

if(page.getCurrentPage() <= 1){
if(page.getCurrentPage() < 1){
page.setCurrentPage(1);
}
condition.append(" ORDER BY " + page.getPrimaryKey());
}else{
if(page.getCurrentPage() > page.getPageCount()){
if(page.getPageCount() <= 1){
page.setCurrentPage(1);
getMysqlPagerSql(sql, page, where);
return sql;
}
//爲便於測試,保證運行,判斷當總頁數小於當前頁數的時候設置當前頁爲總頁數
page.setCurrentPage(page.getPageCount());
}
//構造條件
condition.append(" where "+page.getPrimaryKey() + " > (SELECT MAX(" + page.getPrimaryKey() + ") "+
" FROM (SELECT " + page.getPrimaryKey() + " "+
" FROM " + page.getTableName() + " @where ORDER BY " + page.getPrimaryKey() +
" LIMIT "+(page.getCurrentPage()-1)*page.getPageSize()+" ) AS T) "+
"ORDER BY " + page.getPrimaryKey()
);
}
// 最後,替換條件子查詢中條件佔位符
sql.append(condition.toString().replace("@where", (where == null || where.toString().equals(""))?"":where.toString()) + " LIMIT " + page.getPageSize());

return sql;
}

/***
* 執行Sql,獲取總記錄數<br>
* @param sql
* @return count 總記錄數
*/
public int executeSql(String sql){
Connection con =
//DBConnection.getConnection();
DBConnection.getMySqlConnection();
PreparedStatement ps = null;
ResultSet rs = null;

try {
con = DBConnection.getConnection();

ps = con.prepareStatement(sql);
rs = ps.executeQuery();
int count = 0;
while(rs.next()){
count ++;
}
return count;
} catch (SQLException e) {
e.printStackTrace();
return 0;
} finally {
close(con, ps, rs);
}
}
}


此類繼承了PublicDao,作爲對分頁擴展,比較簡單,共四個方法createPageSql,getSqlServerPageSql,getMysqlPageSql,executeSql。
其中createPageSql方法中根據不同數據庫調用不同的構造分頁條件的SQL的方法;
executeSql方法爲查詢總記錄數的方法,用於分頁時計算總頁數、以及構造分頁條件;

這個類提供了對SQLServer(getSqlServerPageSql)和MySql(getMysqlPageSql)數據庫的分頁支持,我本想做一個通用方法,兼容各類數據庫,但是由於個數據庫間的數據庫語法差異,執行效率還不如分開來好。兩者分頁方案一致,如下:
SELECT TOP 頁大小 *
FROM TestTable
WHERE (ID >
(SELECT MAX(id)
FROM (SELECT TOP 頁大小*頁數 id
FROM 表
ORDER BY id) AS T))
ORDER BY ID

基於這個分頁方案,可以擴展其他可能用到的數據庫分頁方法。
並且由於數據庫差異,DBConnect也應同步一致。在沒有使用多數據源的情況下,編寫一個兼容各數據庫的方法顯得毫無意義。
==========================================
有了分頁的持久層支持,還需要一個負責傳遞分頁信息的中間對象,如下:
page.java
package com.pub.page;

/***
* Page對象
* 設置表名、主鍵字段名,用於拼接分頁Sql字符串,也可以通過Pojo中指定
* @author Administrator
* 2011-05-11
*/
public class Page {

/**當前頁*/
private int currentPage;
/**總頁數*/
private int pageCount;
/**每頁的記錄條數*/
private int pageSize;
/**總的記錄條數*/
private int recordCount;
/**表名*/
private String tableName;
/**主鍵字段名*/
private String primaryKey;
/**數據庫*/
private String DataBaseName;
/***
* 獲取主鍵字段名
* @return
*/
public String getPrimaryKey() {
return primaryKey;
}
public void setPrimaryKey(String primaryKey) {
this.primaryKey = primaryKey;
}
/***
* 獲取表名
* @return
*/
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
/***
* 當前頁
* @return
*/
public int getCurrentPage() {
if(currentPage <= 0){
this.setCurrentPage(1);
}
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
/***
* 總頁數
* @return
*/
public int getPageCount() {
return pageCount;
}
public void setPageCount(int pageCount) {
this.pageCount = pageCount;
}
/***
* 獲取頁顯示記錄數大小,默認爲10
* @return
*/
public int getPageSize() {
if(pageSize <= 0){
this.setPageSize(10);
}
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
/***
* 總記錄數
* @return
*/
public int getRecordCount() {
return recordCount;
}
/***
* 設置總記錄數,同時設置總頁數
* @param recordCount
*/
public void setRecordCount(int recordCount) {

this.setPageCount(
recordCount%this.getPageSize() != 0
?
recordCount/this.getPageSize()+1
:
recordCount/this.getPageSize()
);
this.recordCount = recordCount;
}
public String getDataBaseName() {
if(DataBaseName == null || DataBaseName.equals("")){
return "SQLSERVER";
}
return DataBaseName;
}
public void setDataBaseName(String dataBaseName) {
DataBaseName = dataBaseName;
}
}


仔細看,page中就這一部分,包含了實現分頁的基本信息,這個也是可以擴展的:
    /**當前頁*/
private int currentPage;
/**總頁數*/
private int pageCount;
/**每頁的記錄條數*/
private int pageSize;
/**總的記錄條數*/
private int recordCount;
/**表名*/
private String tableName;
/**主鍵字段名*/
private String primaryKey;
/**數據庫*/
private String DataBaseName;

同時,在給總記錄數recordCount賦值的時候,總頁數pageCount也被賦值了,如下:
	/***
* 設置總記錄數,同時設置總頁數
* @param recordCount
*/
public void setRecordCount(int recordCount) {

this.setPageCount(
recordCount%this.getPageSize() != 0
?
recordCount/this.getPageSize()+1
:
recordCount/this.getPageSize()
);
this.recordCount = recordCount;
}

還是以role表爲例,簡單測試:
		ExpPublicDao dao = new ExpPublicDao();
Page page = new Page();
page.setTableName("role");
page.setPrimaryKey(Role.getPrimaryKey());
page.setCurrentPage(2);
page.setPageSize(5);
//這裏爲了方便,就手寫SQL代碼啦,實際調用不會這樣哈
page.setRecordCount(dao.executeSql("select * from role"));

Map<Object, String> mk = new HashMap<Object, String>();
Map<Object, Object> mv = new HashMap<Object, Object>();
//設置要查詢的列
mk.put(1, Role.RoleId());
mk.put(2, Role.RoleLevel());
mk.put(3, Role.RoleName());
mk.put(4, Role.RoleResource());
mk.put(5, Role.Remark());

String sql = dao.createPageSql(mk, mv, "", page);
System.out.println(sql);
//執行獲得數據集
List list = dao.executeSql(PublicDao.SELECT, sql);

這裏得到SQL:
SELECT role_id,role_level,role_name,role_resource,remark FROM role  
WHERE role_id > (SELECT MAX(role_id) FROM (
SELECT role_id FROM role ORDER BY role_id LIMIT 5 ) AS T
) ORDER BY role_id LIMIT 5

先到數據庫裏查詢一下:
[img]http://dl.iteye.com/upload/picture/pic/97262/bac99743-3968-38c0-881a-d2b721c2f726.jpg[/img]
得到數據集之後,在解析它,再封裝爲以role對象爲內容的數據結構,解析辦法如下:
		//執行獲得數據集
List list = dao.executeSql(PublicDao.SELECT, sql)//接上
/**用於封裝並返回數據的集合對象*/
List<Role> list_t = new ArrayList<Role>();
//取出列名Map
Map mk_n = (Map) list.get(0);
//取出每一行數據
List list_mv = (List) list.get(1);
//封裝的對象
Role role = null;
for(int i=0;i<list_mv.size();i++){
Map mv_n = (Map) list_mv.get(i);
//這裏每循環一次代表每一行數據,即一個對象
role = new Role();
for(int j=1;j<=mk_n.size();j++){
Object temp = mv_n.get(mk_n.get(j));
if(Role.RoleId().equals(mk_n.get(j))){
role.setRoleId(temp==null?null:(Integer)temp);
}
if(Role.RoleName().equals(mk_n.get(j))){
role.setRoleName(temp==null?"":temp.toString());
}
if(Role.RoleResource().equals(mk_n.get(j))){
role.setRoleResource(temp==null?"":temp.toString());
}
if(Role.RoleLevel().equals(mk_n.get(j))){
role.setRoleLevel(temp==null?"":temp.toString());
}
}
list_t.add(role);
}

看圖看真相:
[img]http://dl.iteye.com/upload/picture/pic/97264/0dd83711-74dc-3097-8b3d-a0d96986446d.jpg[/img]
總結,對於返回的數據結構,可以自定義,方式方法很多,但最終目的是如何方便的使用查詢得到的數據,而不用考慮數據怎麼來的,這就是持久層存在的意義
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章