一. 什么是通用分页
在J2EE中,通用分页指的是能够适用于多种数据库、多种ORM框架和多种编程语言的分页技术。J2EE的通用分页主要通过查询总记录数和查询一页记录数来计算总页数和当前页的起始位置,从而实现分页。
通俗来讲就是:通用分页就是把我们经常使用的分页方法进行封装,然后用泛型传递对象,利用反射获取对象的所有属性并且赋值。
通用分页的核心思路:
将上一次查询的请求再发一次,只不过页码发生了变化。
通用分页的好处:
一旦对分页进行封装后,在使用时就只需要改变sql语句和传递的对象,节省开发时间,提高效率,提高代码的复用性。
二. 通用分页的特点
通用性强
通用分页技术可以适用于多种数据库、多种ORM框架和多种编程语言,可以在不同的开发环境中使用。
实现简单
通用分页技术可以通过使用分页插件或自定义分页类来实现,实现过程相对简单。
易于维护
通过使用通用分页技术,开发者可以将通用的分页代码提取出来,避免了每次开发都需要重新编写分页代码,便于后续的维护和更新。
性能较高
通过使用通用分页技术,可以有效降低数据库负载,提高数据查询性能。
用户体验好
使用通用分页技术可以方便用户浏览数据,逐页浏览,减少数据的加载时间,提升用户体验。
三. 通用分页实例
由于我这里使用的是Mysql数据库,有分页的语法,因此重点在于basedao的实现
首先,需要用到的jar包
实体类Book
package com.xissl.entity; public class Book { private int bid; private String bname; private float price; @Override public String toString() { return "Book [bid=" + bid + ", bname=" + bname + ", price=" + price + "]"; } public int getBid() { return bid; } public void setBid(int bid) { this.bid = bid; } public String getBname() { return bname; } public void setBname(String bname) { this.bname = bname; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } }
数据库通用的工具类DBAccess
package com.xissl.utils; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; /** * 提供了一组获得或关闭数据库对象的方法 * */ public class DBAccess { private static String driver; private static String url; private static String user; private static String password; static {// 静态块执行一次,加载 驱动一次 try { InputStream is = DBAccess.class .getResourceAsStream("config.properties"); Properties properties = new Properties(); properties.load(is); driver = properties.getProperty("driver"); url = properties.getProperty("url"); user = properties.getProperty("user"); password = properties.getProperty("pwd"); Class.forName(driver); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } /** * 获得数据连接对象 * * @return */ public static Connection getConnection() { try { Connection conn = DriverManager.getConnection(url, user, password); return conn; } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } public static void close(ResultSet rs) { if (null != rs) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } } public static void close(Statement stmt) { if (null != stmt) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } } public static void close(Connection conn) { if (null != conn) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } } public static void close(Connection conn, Statement stmt, ResultSet rs) { close(rs); close(stmt); close(conn); } public static boolean isOracle() { return "oracle.jdbc.driver.OracleDriver".equals(driver); } public static boolean isSQLServer() { return "com.microsoft.sqlserver.jdbc.SQLServerDriver".equals(driver); } public static boolean isMysql() { return "com.mysql.cj.jdbc.Driver".equals(driver); } public static void main(String[] args) { Connection conn = DBAccess.getConnection(); System.out.println(conn); DBAccess.close(conn); System.out.println("isOracle:" + isOracle()); System.out.println("isSQLServer:" + isSQLServer()); System.out.println("isMysql:" + isMysql()); System.out.println("数据库连接(关闭)成功"); } }
测试数据库连接是否成功
运行结果如下:
帮助类config.properties
#mysql driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis_ssm?useUnicode=true&characterEncoding=UTF-8&useSSL=false user=root pwd=123456
mybatis_ssm:数据库名
乱码处理过滤器EncodingFiter
package com.xissl.utils; import java.io.IOException; import java.util.Iterator; import java.util.Map; import java.util.Set; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 中文乱码处理 * */ public class EncodingFiter implements Filter { private String encoding = "UTF-8";// 默认字符集 public EncodingFiter() { super(); } public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; // 中文处理必须放到 chain.doFilter(request, response)方法前面 res.setContentType("text/html;charset=" + this.encoding); if (req.getMethod().equalsIgnoreCase("post")) { req.setCharacterEncoding(this.encoding); } else { Map map = req.getParameterMap();// 保存所有参数名=参数值(数组)的Map集合 Set set = map.keySet();// 取出所有参数名 Iterator it = set.iterator(); while (it.hasNext()) { String name = (String) it.next(); String[] values = (String[]) map.get(name);// 取出参数值[注:参数值为一个数组] for (int i = 0; i < values.length; i++) { values[i] = new String(values[i].getBytes("ISO-8859-1"), this.encoding); } } } chain.doFilter(request, response); } public void init(FilterConfig filterConfig) throws ServletException { String s = filterConfig.getInitParameter("encoding");// 读取web.xml文件中配置的字符集 if (null != s && !s.trim().equals("")) { this.encoding = s.trim(); } } }
String封装工具类StringUtils
判断Stirng值是否为空
package com.xissl.utils; public class StringUtils { // 私有的构造方法,保护此类不能在外部实例化 private StringUtils() { } /** * 如果字符串等于null或去空格后等于"",则返回true,否则返回false * * @param s * @return */ public static boolean isBlank(String s) { boolean b = false; if (null == s || s.trim().equals("")) { b = true; } return b; } /** * 如果字符串不等于null或去空格后不等于"",则返回true,否则返回false * * @param s * @return */ public static boolean isNotBlank(String s) { return !isBlank(s); } }
分页助手工具类PageBean
package com.xissl.utils; /** * 分页工具类 * */ public class PageBean { private int page = 1;// 页码 private int rows = 10;// 页大小 private int total = 0;// 总记录数 private boolean pagination = true;// 是否分页 public PageBean() { super(); } public int getPage() { return page; } public void setPage(int page) { this.page = page; } public int getRows() { return rows; } public void setRows(int rows) { this.rows = rows; } public int getTotal() { return total; } public void setTotal(int total) { this.total = total; } public void setTotal(String total) { this.total = Integer.parseInt(total); } public boolean isPagination() { return pagination; } public void setPagination(boolean pagination) { this.pagination = pagination; } /** * 获得起始记录的下标 * * @return */ public int getStartIndex() { return (this.page - 1) * this.rows; } @Override public String toString() { return "PageBean [page=" + page + ", rows=" + rows + ", total=" + total + ", pagination=" + pagination + "]"; } }
dao层BookDao
package com.xissl.dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; import com.xissl.entity.Book; import com.xissl.utils.BaseDao; import com.xissl.utils.DBAccess; import com.xissl.utils.PageBean; import com.xissl.utils.StringUtils; public class BookDao{ public List<Book> query(Book book,PageBean pageBean) throws Exception{ List<Book> list = new ArrayList<Book>(); Connection conn = DBAccess.getConnection(); String sql = "select *from t_mvc_book where 1=1"; String bname = book.getBname(); if(StringUtils.isNotBlank(bname)) { sql += " and bname like '%"+bname+"%'"; } PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery(); while(rs.next()) { Book b = new Book(); b.setBid(rs.getInt("bid")); b.setBname(rs.getString("bname")); b.setPrice(rs.getFloat("price")); list.add(b); } return list; } public static void main(String[] args) throws Exception { BookDao bookdao = new BookDao(); Book book = new Book(); book.setBname("圣墟"); PageBean pagebean = new PageBean(); List<Book> query = bookdao.query(book, pagebean); for (Book book2 : query) { System.out.println(book2); } } }
查一个表就要写一个方法,效率太慢,因此需要一个通用的查询方法
运行结果如下:
BaseDao通用的查询方法
BaseDao的作用
BaseDAO是一个用于封装数据库操作的类,在这个类中封装了一些基本的数据库操作方法,比如读取和写入数据。我们可以通过使用这个类来减少代码重复,并提高数据访问的可维护性和可扩展性。在具体的应用中,我们可以通过扩展BaseDAO来支持特定的业务逻辑和数据访问需求。通常情况下,BaseDAO会和ORM框架一起使用,来简化数据库操作和提高开发效率。
T:要查询的实体类对应表
sql:查询不同的实体类,对应的sql不同,需要传递
clz:不同的实体类对应的实例,返回不同的对象集合
pageBean:决定是否分页
package com.xissl.utils; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; import com.xissl.entity.Book; /** * 泛型类 * T:要查询的实体类对应表 * sql:查询不同的实体类,对应的sql不同,需要传递 * clz:不同的实体类对应的实例,返回不同的对象集合 * pageBean:决定是否分页 * * @author xissl * * @param <T> */ public class BaseDao<T> { public List<T> query(String sql,Class clz,PageBean pageBean) throws Exception{ List<T> list = new ArrayList<T>(); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; //需要分页 if(pageBean !=null && pageBean.isPagination()) { //算符合条件的总记录数 String countSql = getCountSql(sql); conn = DBAccess.getConnection(); ps = conn.prepareStatement(countSql); rs = ps.executeQuery(); if(rs.next()) { pageBean.setTotal(rs.getObject("n").toString()); } //查询出符合条件的结果集 String pageSql = getPageSql(sql,pageBean); conn = DBAccess.getConnection(); ps = conn.prepareStatement(pageSql); rs = ps.executeQuery(); }else { conn = DBAccess.getConnection(); ps = conn.prepareStatement(sql); rs = ps.executeQuery(); } /** * 实例化一个对象 * 给这个空对象的每个属性赋值 * 将赋值完的对象添加到集合中返回 */ while(rs.next()) { T t = (T) clz.newInstance(); //拿到clz对应的所有属性对象 Field[] fields = clz.getDeclaredFields(); for (Field f : fields) { f.setAccessible(true); f.set(t, rs.getObject(f.getName())); } list.add(t); } return list; } /** * 利用原生sql拼接出符合条件的结果集的查询sql * @param sql * @param pageBean * @return */ private String getPageSql(String sql, PageBean pageBean) { // TODO Auto-generated method stub return sql+" LIMIT "+pageBean.getStartIndex()+","+pageBean.getRows(); } /** * 获取符合条件的总记录数的sql * @param sql * @return */ private String getCountSql(String sql) { return "SELECT COUNT(1) as n from ("+sql+")t"; } }