一:高級查詢+分頁查詢
操作步驟:
0):把pageSize和currentPage封裝到QueryObject對象中(任何查詢對象都得接受用戶傳入的這兩個數據).
1):在IProductDAO接口中定義高級查詢+分頁查詢方法.
2):在ProductDAOImpl中實現該方法.
3):測試代碼.
解決翻頁是丟失高級查詢數據的問題:
造成的原因:翻頁時,會重新發一次請求,該請求和高級查詢表單沒有關係.
解決方案:使用JS解決:在翻頁的時候:同時提交高級查詢表單參數 和 當前第幾頁(currentPage).
<a href="javascript:go(${1})">首頁</a>
<a href="javascript:go(${pageResult.prevPage})">上頁</a>
<a href="javascript:go(${pageResult.nextPage})">下頁</a>
<a href="javascript:go(${pageResult.totalPage})">末頁</a>
跳轉的函數:
<script type="text/javascript">
//提交表單
function go(pageNo){
//把需要跳轉的頁碼設置到<input type="number" name="currentPage"/>上
document.getElementById("currentPage").value = pageNo;
//提交表單
document.forms[0].submit();
}
</script>
DAO
public interface IProductdirDAO {
List<Productdir> list();
}
public interface IProductDAO {
PageResult query(ProductQueryObject qo);
}
Impl
public class BaseDAO {
public PageResult query(Class<?> calssType,IQuery qo) {
//默認把類名作爲表名
String tableName = calssType.getSimpleName();
Table table = calssType.getAnnotation(Table.class);
if(table!=null){
tableName = table.value();
}
//查詢結果總數
String sqlCount = "SELECT count(*) from " + tableName + qo.getQuery();
Integer totalCount = JdbcTemplate.query(sqlCount, new ResultSetHandler<Long>() {
public Long handle(ResultSet rs) throws Exception {
if (rs.next()) {
return rs.getLong(1);
}
return 0L;
}
},qo.getParameters().toArray()).intValue();
if(totalCount == 0){
return PageResult.empty(qo.getPageSize());
}
System.out.println("sqlCount:"+sqlCount);
System.out.println("參數="+qo.getParameters());
//====================================================================================
//查詢結果集
String sqlList = "SELECT * from " + tableName + qo.getQuery()+ " LIMIT ?,?";
qo.getParameters().add((qo.getCurrentPage() -1)*qo.getPageSize());
qo.getParameters().add(qo.getPageSize());
List listdata = JdbcTemplate.query(sqlList, new BeanListHandler<>(calssType), qo.getParameters().toArray());
System.out.println("sqlList:"+sqlList);
System.out.println("參數="+qo.getParameters());
return new PageResult(qo.getCurrentPage(),qo.getPageSize(),listdata,totalCount);
}
}
public class ProductDAOImpl extends BaseDAO implements IProductDAO {
public PageResult query(ProductQueryObject qo) {
return super.query(Product.class, qo);
}
}
query
//表示查詢對象的規範,約束查詢對象應該具有的方法
public interface IQuery {
String getQuery();
List<Object> getParameters();
Integer getPageSize();
Integer getCurrentPage();
}
//封裝了結果集的對象
@Getter
public class PageResult {
private List listData;//結果集數據;通過SQL查詢
private Integer totalCount;//結果總條數;通過SQL查詢
private Integer currentPage = 1;//當前頁;用戶傳入
private Integer pageSize = 5;//每頁條數;用戶傳入
private Integer beginPage = 1;//首頁
private Integer prevPage;//上頁
private Integer nextPage;//下頁
private Integer totalPage;//末頁
private List<Integer> pageItems = Arrays.asList(3,5,8,10);
public static PageResult empty(Integer pageSize){
return new PageResult(1,pageSize,Collections.EMPTY_LIST,0);
}
public PageResult( Integer currentPage, Integer pageSize,List listData, Integer totalCount) {
this.listData = listData;
this.totalCount = totalCount;
this.currentPage = currentPage;
this.pageSize = pageSize;
//-------------------------------------
this.totalPage = totalCount % pageSize == 0 ? totalCount / pageSize : totalCount / pageSize + 1;
this.prevPage = currentPage - 1 >= 1 ? currentPage - 1 : 1;
this.nextPage = currentPage + 1 <= totalPage ? currentPage + 1 : totalPage;
}
}
//封裝了商品對象的查詢條件,並且它繼承了currentPage,pageSize的封裝
@Getter
@Setter
public class ProductQueryObject extends QueryObject{
private String name;
private BigDecimal minSalePrice;
private BigDecimal maxSalePrice;
private Long dir_id = -1L;
private String keyword;
protected void customizedQuery() {
if(StringUtils.isNotBlank(name)){
super.addQuery("productName LIKE ?", "%"+name+"%");
}
//最低價格
if(minSalePrice != null){
super.addQuery("salePrice >= ?", minSalePrice);
}
//最高價格
if(maxSalePrice != null){
super.addQuery("salePrice <= ?", maxSalePrice);
}
if(dir_id != -1){
super.addQuery("dir_id = ?", dir_id);
}
if(StringUtils.isNotBlank(keyword)){//因爲AND優先級大於OR,得使用()括起來
super.addQuery(" (productName LIKE ? OR brand LIKE ? )", "%"+keyword+"%","%"+keyword+"%");
}
}
}
//封裝了商品對象的查詢條件
@Getter
@Setter
public class ProductdirQueryObject extends QueryObject{
private String name;
private Long parent_id = -1L;
//自身的訂製查詢
public void customizedQuery() {
//商品分類名稱
if (StringUtils.isNotBlank(name)) {
super.addQuery("productdir LIKE ?", "%" + name + "%");
}
//父分類
if (parent_id != -1) {
super.addQuery(" parent_id = ?", parent_id);
}
}
}
public class QueryObject implements IQuery{
@Getter@Setter
private Integer currentPage = 1;//當前頁;用戶傳入
@Getter@Setter
private Integer pageSize = 5;//每頁條數;用戶傳入
private List<String> conditions = new ArrayList<>();
//封裝佔位符參數
private List<Object> parameters = new ArrayList<>();
private boolean isBuild = false;//是否已經構建SQL/封裝參數
//初始化操作
public void init(){
if(!isBuild){
isBuild = true;
this.customizedQuery();
}
}
//返回查詢條件,如:WHERE productName LIKE ? AND salePrice >= ?
public String getQuery() {
this.init();
StringBuilder sql = new StringBuilder();
if (conditions.size() == 0) {
return "";
}
String queryString = StringUtils.join(conditions, " AND ");
return sql.append(" Where ").append(queryString).toString();
}
//返回查詢條件中的佔位符參數值
public List<Object> getParameters() {
this.init();
return parameters;
}
//暴露給子類,讓子類編寫自個的查詢方式
protected void customizedQuery(){
}
//暴露給子類:讓子類在customizedQuery中調用,添加自己的查詢條件和參數
protected void addQuery(String condition,Object...param){
this.conditions.add(condition);
this.parameters.addAll(Arrays.asList(param));
}
}
Servlet
@WebServlet("/product")
public class ProductServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private IProductDAO dao;
private IProductdirDAO dao_dir;
public void init() throws ServletException {
dao = new ProductDAOImpl();
dao_dir = new ProductdirDAOImpl();
}
//列表操作
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
//1:接受請求參數,封裝對象
//2:調用業務方法處理請求
ProductQueryObject qo = new ProductQueryObject();
this.request2Object(req,qo);
req.setAttribute("qo",qo);
PageResult pageResult = dao.query(qo);
req.setAttribute("pageResult", pageResult);
req.setAttribute("dir", dao_dir.list());
//3:控制界面跳轉
req.getRequestDispatcher("/WEB-INF/views/product/product.jsp").forward(req, resp);
}
//下面這段代碼可以用commons-beanUtils 可以實現對象拷貝
private void request2Object(HttpServletRequest req, ProductQueryObject qo) {
String name = req.getParameter("name");
String minSalePrice = req.getParameter("minSalePrice");
String maxSalePrice = req.getParameter("maxSalePrice");
String dir_id = req.getParameter("dir_id");
String keyword = req.getParameter("keyword");
String currentPage = req.getParameter("currentPage");
String pageSize = req.getParameter("pageSize");
if(StringUtils.isNotBlank(name)){
qo.setName(name);
}
if(StringUtils.isNotBlank(minSalePrice)){
qo.setMinSalePrice(new BigDecimal(minSalePrice));
}
if(StringUtils.isNotBlank(maxSalePrice)){
qo.setMaxSalePrice(new BigDecimal(maxSalePrice));
}if(StringUtils.isNotBlank(dir_id)){
qo.setDir_id(Long.valueOf(dir_id));
}if(StringUtils.isNotBlank(keyword)){
qo.setKeyword(keyword);
}if(StringUtils.isNotBlank(currentPage)){
qo.setCurrentPage(Integer.valueOf(currentPage));
}if(StringUtils.isNotBlank(pageSize)){
qo.setPageSize(Integer.valueOf(pageSize));
}
}
}
jsp前端頁面
<form action="/product" method="post">
商品名稱:<input type="text" name="name" value="${qo.name}"/>
商品價格:<input type="text" name="minSalePrice" value="${qo.minSalePrice}"/>到
<input type="text" name="maxSalePrice" value="${qo.maxSalePrice}"/>
商品種類:<select name="dir_id">
<option value="-1">全部商品</option>
<c:forEach items="${dir}" var="d">
<option value="${d.id}" ${d.id == qo.dir_id? "selected":""} >${d.name}</option>
</c:forEach>
</select>
關鍵字查詢:<input type="text" name="keyword" placeholder="商品名稱或商品品牌" value="${qo.keyword}"/>
<input type="submit" value=" 提交 " style="background-color: orange;"/>
<table border="1" width="80%" cellpadding="0" cellspacing="0">
<tr style="background-color: orange">
<th>id</th>
<th>productName</th>
<th>brand</th>
<th>supplier</th>
<th>salePrice</th>
<th>costPrice</th>
<th>cutoff</th>
<th>dir_id</th>
</tr>
<c:forEach items="${pageResult.listData}" var="p" varStatus="s">
<tr style='background-color:${s.count % 2 == 0? "gray":""}'>
<td>${p.id}</td>
<td>${p.productName}</td>
<td>${p.brand}</td>
<td>${p.supplier}</td>
<td>${p.salePrice}</td>
<td>${p.costPrice}</td>
<td>${p.cutoff}</td>
<td>
<c:choose>
<c:when test="${p.dir_id == 1}">無線手機</c:when>
<c:when test="${p.dir_id == 3}">遊戲手機</c:when>
<c:when test="${p.dir_id == 5}">有線手機</c:when>
</c:choose>
</td>
</tr>
</c:forEach>
<tr >
<td colspan="8" align="center">
<%@include file="/WEB-INF/views/commons/commons_page.jsp" %>
</td>
</tr>
</table>
</form>
這段代碼是重構出來的代碼,可以重複利用
<script type="text/javascript">
//提交表單
function go(pageNo){
//把需要跳轉的頁碼設置到<input type="number" name="currentPage"/>上
document.getElementById("currentPage").value = pageNo;
//提交表單
document.forms[0].submit();
}
</script>
<a href="javascript:go(${1})">首頁</a>
<a href="javascript:go(${pageResult.prevPage})">上頁</a>
<a href="javascript:go(${pageResult.nextPage})">下頁</a>
<a href="javascript:go(${pageResult.totalPage})">末頁</a>
當前第${pageResult.currentPage}/${pageResult.totalPage}頁,
一共${pageResult.totalCount}條數據,
跳轉到<input type="number" min="1" max="${pageResult.totalPage}"
id="currentPage" style="width: 50px" name="currentPage" value="${pageResult.currentPage}"/>頁
<input type="button" value="GO" onclick="go()"/>
每頁
<select name="pageSize" onchange="go();">
<c:forEach items="${pageResult.pageItems}" var="item">
<option ${item == pageResult.pageSize? "selected":""}>${item}</option>
</c:forEach>
</select>
條數據
domain
* 商品對象
@Data
@Table("product")
public class Product {
private Long id;
//@Comlumn("name2")當列名是name2的時候
private String productName;
private String brand;
private String supplier;
private BigDecimal salePrice;
private BigDecimal costPrice;
private Double cutoff;
private Long dir_id;//分類編號
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Table {
String value() default "";
}
@Data
public class Productdir {
private String name;
private Long id;
private Long parent_id;
}
JDBC重構設計類
public class JdbcTemplate {
private JdbcTemplate() {
}
/**
*
*@param sql DML各自的SQL,由調用者決定
*@param params DML操作需要的參數,由調用者決定
*@return 受影響的行數
*/
public static int update(String sql, Object... params) {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JdbcUtil.INSTANCE.getConn();
ps = conn.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
return ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtil.INSTANCE.close(conn, ps, null);
}
return 0;
}
public static <T>T query(String sql, ResultSetHandler<T> rsh ,Object... params) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtil.INSTANCE.getConn();
ps = conn.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
rs = ps.executeQuery();
return rsh.handle(rs);
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.INSTANCE.close(conn, ps, rs);
}
return null;
}
}
//表示結果集中的一行數據,封裝成一個對象,專門針對結果集中只有一行數據的情況.
public class BeanHandler<T> implements ResultSetHandler<T>{
private Class<T> classType;
public BeanHandler(Class<T> classType){
this.classType = classType;
}
/**
* 規範:
* 1:規定表中的列名必須和對象中的屬性名相同.
* 2:規定表中列名的類型必須和java中的類型要匹配. decimal --->BigDecimal/bigint --->Long
*/
public T handle(ResultSet rs) throws Exception {
//1):創建對應的一個對象
T obj = classType.newInstance();
//2):取出結果集中當前光標所在行的某一列的數據.
BeanInfo beanInfo = Introspector.getBeanInfo(classType,Object.class);
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
if(rs.next()){
for (PropertyDescriptor pd : pds) {
String columnName = pd.getName();//獲取對象的屬性名稱,屬性名和列名相同
Object val = rs.getObject(columnName);
//3):調用該對象的setter方法,把某一列的數據,設置進去.
pd.getWriteMethod().invoke(obj, val);
}
}
return obj;
}
}
//表示把結果集中的多行數據,封裝成一個對象的集合(List<xx>),針對於結果集中有多行數據的.
public class BeanListHandler<T> implements ResultSetHandler<List<T>> {
private Class<T> classType;
public BeanListHandler(Class<T> classType) {
this.classType = classType;
}
public List<T> handle(ResultSet rs) throws Exception {
List<T> list = new ArrayList<>();
while(rs.next()) {
//每一行封裝成一個對象
T obj = classType.newInstance();
BeanInfo beanInfo = Introspector.getBeanInfo(classType, Object.class);
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
//獲取對象的屬性名稱,屬性名和列名相同
String columnName = pd.getName();
Object val = rs.getObject(columnName);
//3):調用該對象的setter方法,把某一列的數據,設置進去.
pd.getWriteMethod().invoke(obj, val);
//把每一行對應的對象,存儲到List集合中.
}
list.add(obj);
}
return list;
}
}
//定義一個專門的約束處理結果集的接口:ResultSetHandler:結果集處理器
public interface ResultSetHandler<T> {
T handle(ResultSet rs) throws Exception;
}