目录
二:首先需要开发的是,两个工具类:XmlDataSource类和PageModel类:
1.XmlDataSource类:将整个xml文件读取到内存中
三:在底层工具类做好了以后:编写数据访问对象类:PaintingDao类:
一:概述:
本部分主要是MVC架构的Model(模型)层中的Dao部分:然后,本篇博客的具体内容是分页功能的Dao部分;
主要包括:
(1)XmlDataSource类:工具类,将整个xml文件读取到内存中;
(2)Painting类:entity实体类,承载油画数据的javaBean;
(3)PageModel类:工具类,数据分页类;这个PageModel其实也需要是一个标准的javaBea,这样才能被前台的注入jsp的el表达式等识别获取;
(4)PaintingDao类:数据访问对象类,提供完整的数据访问业务;一个汇总的类;
二:首先需要开发的是,两个工具类:XmlDataSource类和PageModel类:
1.XmlDataSource类:将整个xml文件读取到内存中
因为本项目没有使用数据库,数据都存放在了XML中,所以需要Dom4j来提供对xml文件读写的支持;
Dom4j内容如果忘记了,可以参考使用Dom4j读取XML文档,使用Dom4j写(追加)XML文档,XPath路径表达式查询XML这几篇博客;
需要读写xml文件,为了提升效率,引入Dom4j的jar包:
这个类的目的就是,读取xml文件的内容,将xml内容读进内存中,其背后是使用JavaBean类对象存储xml中的一条一条的数据,然后使用一个List存储xml所有的数据;
在utils包下创建XmlDataSource类;
package com.imooc.mgallery.utils;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import com.imooc.mgallery.entity.Painting;
/**
* 数据源类,将xml文件解析为Java对象
*
* @author dell
*
*/
public class XmlDataSource {
// 为了保证在同一时间,内存中只有一份xml的Document对象,即为了保证加载的xml对象全局唯一
// 通过static静态关键字保证数据的全局唯一性;(自然也可以通过单例模式)
private static List<Painting> data = new ArrayList<Painting>(); // 在XmlDataSource类加载的时候,就会创建data对象,这个对象隶属于XmlDataSource类,可以保证是全局唯一;
private static String dataFile; // xml文件的地址;
static {
// 程序运行编译后,src目录下得java文件会被编译为class文件,这些class文件会被放在classes目录下,而同样处于src目录下的painting.xml文件也会放在classes目录下;
// XmlDataSource.class.getResoure()得到classes目录的根路径,然后在classes目录的根路径下找到painting.xml,然后getPath()获得painting.xml文件的完整的物理磁盘的地址;
dataFile = XmlDataSource.class.getResource("/painting.xml").getPath();
//System.out.println(dataFile);
// 如painting.xml文件的地址是:c:\new style\painting.xml;可以发现,new和style之间有一个空格,这个空格是个特殊字符;
// datFile得到painting.xml文件地址的时候,会进行base64转换,实际dataFile的值会是:c:\new%20style\painting.xml,即空格被转化成了%20;
// 但是如果在后续中,利用JavaIO对painting.xml文件按照“c:\new%20style\painting.xml”读取时,会提示路径找不到,因为%20不会被JavaIO解析;需要手动的将%20转换为空格;
// URLDecoder的作用就是:将base64转回普通的字符串;
URLDecoder decoder = new URLDecoder();
try {
dataFile = decoder.decode(dataFile, "UTF-8"); // 这个需要捕获“不支持的编码格式”异常
//System.out.println(dataFile);
SAXReader reader = new SAXReader();
Document document = reader.read(dataFile); // 需要捕获“DocumentException”异常
List<Node> nodes = document.selectNodes("/root/painting");
for(Node node:nodes) {
Element element = (Element)node;
// 提取数据,如何将数据转换成Java对象?通过什么载体来保存油画的数据?所以,需要开发对应的JavaBean承载油画数据;
String id = element.attributeValue("id");
String pname = element.elementText("pname");
//
Painting painting = new Painting();
painting.setId(Integer.parseInt(id));
painting.setPname(pname);
// 剩余几个采用紧凑的写法
painting.setCategory(Integer.parseInt(element.elementText("category")));
painting.setPrice(Integer.parseInt(element.elementText("price")));
painting.setPreview(element.elementText("preview"));
painting.setDescription(element.elementText("description"));
// 将对象存储到data集合中;
data.add(painting); // 这样以后,当XmlDataSource这个类被加载以后,data集合中就保存了完整的油画信息;
}
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 为了保证外部可以访问data集合,需要给data增加一个出口;;;
* 这儿添加一个方法,getRawData():即获取最原始的信息;
* @return
*/
public static List<Painting> getRawData(){
return data;
}
// public static void main(String[] args) { // 这个main方法主要目的,是测试一下
// //new XmlDataSource(); // 创建这个对象之前,需要加载XmlDataSource这个类,一旦加载这个类,那么和这个类的static块就会初始dataFile对象;
// List<Painting> list = XmlDataSource.getRawData();
// System.out.println(list);
// }
}
XmlDataSource类需要注意:
(1)使用了private static List<Painting> data = new ArrayList<Painting>();来存储xml的文件内容;私有的,静态的;然后提供了一个public static List<Painting> getRawData(),公有静态方法来提供访问data的入口;设置成静态的,也可以保证内存中,只有一份xml的内容;
(2)XmlDataSource.class.getResource("/painting.xml").getPath();:这种获取类编译后的classes目录的地址,获取某个文件的地址;注意这种用法啦;
(3)使用URLDecoder将base64编码的字符串转成普通的字符串;
(4)读取xml是固定流程,没什么好说的啦;本业务内容简单,所以此处的xPath的很简单;;;
(5)用一个JavaBean类的对象来存储xml中的一条一条的数据,这种做法是约定俗成采取的策略,司空见惯,也没什么好说的;
……………………………………………………
注解:
……………………………………………………
2.JavaBean:Painting类:承载数据
在entity目录下创建JavaBean:Painting类:这个类就是个普通的javaBean没什么好说的,只需要注意(1)别忘了无参构造;(2)属性的含义最好注释一下;
package com.imooc.mgallery.entity;
/**
* 保存油画数据的JavaBean
* @author dell
*
*/
public class Painting {
private Integer id; // 油画编号
private String pname; // 名称
private Integer category; // 分类 1-现实主义,2-抽象主义;
private Integer price; // 通常在油画中,油画的价格都是整数,所以这儿价格采用Integer而不是Float;
private String preview; // 油画图片地址
private String description; // 描述
public Painting() {}
public Painting(Integer id, String pname, Integer category, Integer price, String preview, String description) {
super();
this.id = id;
this.pname = pname;
this.category = category;
this.price = price;
this.preview = preview;
this.description = description;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getPname() {
return pname;
}
public void setPname(String pname) {
this.pname = pname;
}
public Integer getCategory() {
return category;
}
public void setCategory(Integer category) {
this.category = category;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
public String getPreview() {
return preview;
}
public void setPreview(String preview) {
this.preview = preview;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
3.PageModel类:用于分页的类:
PageModel工具类的主要作用是:为分页服务的基础类;这个类的有参构造很给力!!!传入的参数是:全部数据的List,当前页码,每页的记录数;;;;就能得到:不仅仅是当前页需要显示的记录的内容,还要有有关分页的所有相关信息(比如是否有上页,是否有下页,当前页码等等);而这些信息在页面显示的时候是很关键的。
在某种程度上,PageModel类可以看成是存储分页各种信息的javaBean;其实这个PageModel就是一个标准的javaBea,这样才能被前台的注入jsp的el表达式等识别获取;(因为后面Controller部分调用Service部分后,向请求中添加的参数就是PageModel类型的对象,所以PageModel必须是一个JavaBean)
在utils包下,创建PageModel类
package com.imooc.mgallery.utils;
import java.util.ArrayList;
import java.util.List;
/**
* 分页类
* @author dell
*
*/
public class PageModel {
private int page; // 页号:当前是第几页;
private int totalPages; //总页数:一共有多少页,这个需要
private int rows; // 每页记录数:每页有几条数据;
private int totalRows; // 总记录数:原始的数据易容有多少条;totalRows/rows:就可以得到总页数了;;;
private int pageStartRow; // 当前页从第n行开始:当前页是从原始数据的第几行开始的;
private int pageEndRow; // 当前页到第n行结束:当前页的最后一条数据是原始数据的第几行;
private boolean hasNextPage; // 是否有下一页;
private boolean hasPreviousPage; //是否有上一页;
private List pageData; // 当前页的数据
public PageModel() {}
/**
* @param data:原始完整的数据;
* @param page:要查询第几页数据;
* @param rows:每一页有几条;
*/
public PageModel(List data,int page,int rows) {
this.page = page;
this.rows = rows;
totalRows = data.size();
// 总页数,向上取整;Math.ceil():作用是向上取整;Math.ceil()返回的数据类型是double,所以下面转成了int;
// 注:Math.floor():向下取整,返回的数据类型也是double类型;
// Java中如8/3 = 2;而不是2.66666;;;;(rows * 1f)的作用是把除数转成浮点数,这样(totalRows/(rows * 1f))得到的就是浮点数了;
totalPages = new Double(Math.ceil(totalRows/(rows * 1f))).intValue();
pageStartRow = (page-1)*rows; // 0
pageEndRow = page*rows; // 6
// totalRows:20 | totalPage:4 | rows:6
// pageEndRow = 4*6=24>20 执行subList会抛出下标越界异常;所以做个判断处理
if(pageEndRow > totalRows) {
pageEndRow = totalRows;
}
pageData = data.subList(pageStartRow, pageEndRow); // subList()截取,可以取到pageStartRow,取不到pageEndRow,即是左闭右开的;
if(page > 1) { // 如果当前页号大于1,存在上一页;
hasPreviousPage = true;
}else {
hasPreviousPage = false;
}
if(page < totalPages) { // 如果当前页号小于总页数,存在下一页;
hasNextPage = true;
}else {
hasNextPage = false;
}
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public int getTotalPages() {
return totalPages;
}
public void setTotalPages(int totalPages) {
this.totalPages = totalPages;
}
public int getRows() {
return rows;
}
public void setRows(int rows) {
this.rows = rows;
}
public int getTotalRows() {
return totalRows;
}
public void setTotalRows(int totalRows) {
this.totalRows = totalRows;
}
public int getPageStartRow() {
return pageStartRow;
}
public void setPageStartRow(int pageStartRow) {
this.pageStartRow = pageStartRow;
}
public int getPageEndRow() {
return pageEndRow;
}
public void setPageEndRow(int pageEndRow) {
this.pageEndRow = pageEndRow;
}
public boolean isHasNextPage() {
return hasNextPage;
}
public void setHasNextPage(boolean hasNextPage) {
this.hasNextPage = hasNextPage;
}
public boolean isHasPreviousPage() {
return hasPreviousPage;
}
public void setHasPreviousPage(boolean hasPreviousPage) {
this.hasPreviousPage = hasPreviousPage;
}
public List getPageData() {
return pageData;
}
public void setPageData(List pageData) {
this.pageData = pageData;
}
// public static void main(String[] args) {
// List sample = new ArrayList();
// for(int i=1;i<=100;i++) {
// sample.add(i);
// }
// PageModel pageModel = new PageModel(sample,6,8);
// System.out.println("当前页数据:"+pageModel.getPageData());
// System.out.println("总页数"+pageModel.getTotalPages());
// System.out.println("起始行号:"+pageModel.getPageStartRow());
// System.out.println("结束行号"+pageModel.getPageEndRow());
//
// }
}
PageModel类需要注意:
(1)这个类是,专门服务于分页的工具类;
(2)List.subList(index x,index y);取不到y,只能取到y-1;
(3)pageStartRow = (page-1)*rows; 和 pageEndRow = page*rows; 在计算开始行数和结束行数的时候,要注意最终的数据是需要从List中获取的,结合List的索引和subList()方法的特性,(做一个小例子)就会很容易理解;
三:在底层工具类做好了以后:编写数据访问对象类:PaintingDao类:
在XmlDataSource类和PageModel这两个工具类开发完成后,需要开发PaintingDao类需要调用XmlDataSource类以获取xml完整的原始的数据信息,然后再创建PageModel对象对数据进行分页处理;这样以后,PaintingDao类就实现了数据分页;然后PaintingDao就会将数据分页的分页对象传递给PaintingService就可以了;由此,底层的数据查询部分就完成了;
创建PaintingDao类:
package com.imooc.mgallery.dao;
import java.util.List;
import com.imooc.mgallery.entity.Painting;
import com.imooc.mgallery.utils.PageModel;
import com.imooc.mgallery.utils.XmlDataSource;
/**
* Dao类;调用PageModel类和XmlDataSource这个两个工具类;
* 数据访问对象类,作用是获取最原始的xml数据,并且对其进行分页;
* @author dell
*
*/
public class PaintingDao {
/**
* 实现分页的方法
* @param page 查询第几页数据;
* @param rows 每一页显示几条
* @return
*/
public PageModel pagination(int page,int rows) {
List<Painting> xmlDataSourceList = XmlDataSource.getRawData(); // 先获取xml完整的数据;
PageModel pageModel = new PageModel(xmlDataSourceList,page,rows);
return pageModel;
}
}
PaintingDao类需要注意:
(1)PaintingDao类先调用XmlDataSource类,获取完整的xml数据;
(2)然后调用PageModel的有参构造获取分页对象;将分页对象返回就好了;
(3)PaintingDao类向上提供完整的数据访问对象;Dao除了调用工具类以外,为了满足数据要求,可能会加一些处理数据的代码,将获取的数据进行稍微加工后,再向后传递;即还是那句话,底层工具类只是完成某些模块化的具体功能,而不参与业务逻辑的编写;Dao类调用底层工具类,获取结果后,加上一些业务逻辑代码(如果需要的话)对从工具类获取的数据进行一下加工,然后向上提供完整的数据访问对象!!!