`
tan4836128
  • 浏览: 27750 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

钢炮级 持久层 —— 下篇

阅读更多
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

先到数据库里查询一下:

得到数据集之后,在解析它,再封装为以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);
		}

看图看真相:

    总结,对于返回的数据结构,可以自定义,方式方法很多,但最终目的是如何方便的使用查询得到的数据,而不用考虑数据怎么来的,这就是持久层存在的意义
分享到:
评论

相关推荐

    钢炮级 持久层 —— 中篇

    标题 "钢炮级 持久层 —— 中篇" 提到的是关于持久层技术的深入探讨,这通常指的是在软件开发中用于处理数据库交互的框架或库。持久层是应用程序与数据库之间的桥梁,它负责数据的存储和检索,使得业务逻辑与数据...

    小钢炮蓝牙音箱PADS9.5设计硬件原理图+PCB文件.zip

    《小钢炮蓝牙音箱PADS9.5设计详解——硬件原理与PCB解析》 在电子设备领域,蓝牙音箱凭借其无线便捷性受到广大消费者的喜爱。"小钢炮"蓝牙音箱,以其小巧便携、音质出色的特点,成为市场上的一款热门产品。本资料...

    【MicroPython】读取小钢炮的HTS221传感器

    在介绍MicroPython读取小钢炮开发板上的HTS221温湿度传感器的文章中,我们首先需要了解HTS221传感器的基本信息。HTS221是一款由STMicroelectronics生产的数字输出相对湿度和温度传感器,该传感器能够提供高精度的...

    Skylake平台小钢炮Z170芯片组ITX主板.pdf

    Skylake平台小钢炮Z170芯片组ITX主板.pdf

    【PADS9.5】小钢炮蓝牙音箱BGA两层板设计.zip

    在本项目中,我们关注的是使用PADS 9.5软件进行小钢炮蓝牙音箱的BGA(Ball Grid Array)两层板设计。这涉及到电子工程中的多个关键知识点,包括硬件设计、电路板布局、BGA封装处理以及PCB设计流程。 首先,硬件设计...

    【PADS9.5】小钢炮蓝牙音箱BGA两层板设计-电路方案

    市场上各种蓝牙音箱数不胜数,小钢炮系列的蓝牙音箱也颇受欢迎。 这个是我收集的蓝牙音箱原理图和PCB,之所有拿出来分享,这个PCB是两层板,但是包含了BGA封装的蓝牙模块,layout的...PCB是两层板,也省下了一笔成本。

    基于PADS9.5设计的小钢炮蓝牙音箱硬件(原理图+PCB)文件+RDA58硬件应用指南.zip

    基于PADS9.5设计的小钢炮蓝牙音箱硬件(原理图+PCB)文件+RDA58硬件应用指南,可供学习及设计参考。

    NAS机箱,迷你机箱,小钢炮机箱图纸

    2.5寸硬盘抽取式机箱DWG图 大小: 101103 字节 MD5: A5972B2DC6CD17A6D0169A02BB7B6AC8 SHA1: B11C8ED0B7A7AD9D655554B4FB65B0257E8D1758

    黑苹果_10.14.4_i7-8850H_UHD630_EFI完整文件.zip

    描述中提到的“小钢炮 I7-8850H,UHD 630黑苹果10.14.4成功”,意味着有人成功地在配备 i7-8850H 和 UHD 630 显卡的紧凑型高性能电脑(可能指小型台式机或笔记本电脑)上安装了 macOS 10.14.4,并且提供了详细的教程...

    高CP值的小钢炮组合! 全汉FSP CST350机壳+金钢弹850W SFX电源开箱 FSP CST350 Case Review

    【Huan】_高CP值的小鋼砲組合!_全漢FSP_CST350機殼+金鋼彈850W_SFX電源開箱_FSP_CST350_Cas

    MicroPython中文教程-用于STM32

    在Windows环境下编译STM32的Micropython固件时,需要注意gcc编译器路径是否已经添加到系统路径中。如果未添加且不希望修改makefile文件,可以在编译时输入命令: ``` make CROSS_COMPILE=e:/gcc-arm/bin/arm-none-...

    基于ssm的物流网站的设计与实现源码

    MyBatis则是一个轻量级的持久层框架,实现了SQL语句与Java代码的解耦。 二、前端设计 1. HTML5与CSS3:项目采用现代Web技术,提供响应式布局,确保在不同设备上都能良好显示。 2. Bootstrap框架:用于快速构建美观...

    HTS221温湿度传感器.rar

    在本项目中,我们将基于开源电子网的官方库函数,通过串口打印HTS221所测得的温湿度值,并利用小钢炮开发板进行IIC通信,以16MHz的晶振频率为系统提供时钟支持。 首先,我们需要了解HTS221的基本特性。这款传感器...

    网上银行系统的设计与实现源码

    Hibernate作为持久层框架,简化了数据库操作。它提供了对象关系映射(Object-Relational Mapping,ORM),使得开发人员可以使用面向对象的方式处理数据库事务,而无需编写大量的SQL语句。Hibernate支持事务管理,...

    小米蓝牙耳机、音响驱动

    直接驱动小米所有蓝牙设备~ 找了很久,只有这个才真正有用~

    kali for android让你的安卓设备跑起kali.docx

    Kali for Android 是一种将 Kali 系统安装到安卓设备上的解决方案,将安卓设备变成渗透测试小钢炮。以下是实现 Kali for Android 的详细步骤和知识点: 一、前提条件 * 安卓设备已经获取了 root 权限 * 安卓设备...

    【毕业设计】多目相机的视频拼接.zip

    研究目的 主要研究内容是基于多目相机,利用图像拼接技术...摄像头:采用淘宝上最便宜的小钢炮摄像头,单个价格25元左右 摄像头固定装置:计算摄像头的视场,使用Autodesk 3ds Max设计固定装置,并使用3D打印技术打印

    20210511-平安证券-汽车行业:2021上海车展看点及新车梳理.pdf

    2. **细分市场多样化**:除了传统的车型之外,本次车展还展示了多款针对特定细分市场设计的新车型,例如A00级敞篷车(五菱宏光MINIEV敞篷版)、两厢钢炮(第八代高尔夫GTI、领克02 Hatchback)、敞篷跑车(MG ...

    招标文件 招标文件(办公用品)

    4. 开标后,遵循最低价中标原则,但在满足招标方所有需求的前提下。 5. 中标单位拒签合同或提出重大修改将导致投标保证金被没收。 【合同签订】 中标单位将与贺兰县农村信用合作联社签订为期两年的《贺兰联社办公...

    汽车行业:2021上海车展看点及新车梳理(28页).pdf

    此外,小众细分市场的新车如A00级敞篷车、两厢钢炮、敞篷跑车、豪华SUV和乘用化皮卡等也吸引了不少关注。 新品牌和新标识的出现是本次车展的另一大亮点。长城汽车的坦克品牌独立,上汽智己、吉利极氪首次参展,现代...

Global site tag (gtag.js) - Google Analytics