列表操作
新增成功之后会跳转到列表页面,在列表页面中会从多张表中联合查询出所需要的数据。比如需要查询用户的姓名,书籍的类型、书籍的出版社,以及作者的出生地所在等。而这些的查询关键都体现在了mybatis的sql语句的查询中。本次的查询并没有太多注重于性能方面,只是作为一个基础的练习操作。
考虑到需要将查询出来的数据保存在某个地方并且带入到前台页面进行显示,而最好的方式有两种,一种是查询的结果可以设置为Map类型进行返回,另外一种思路就是,将可能会查询出来的数据都放在Book.java实体类中进行保存,然后sql中查询的时候直接返回一个Book类对象,即可将所有的数据带到前台页面。本次会使用第二种操作。
在查询的时候会加入分页操作,而本次的操作通过继承的方式来设置相关的内容,所以首先需要准备出PageUtil.java类,用来保存业务层所需要的内容,并且设置初始值。具体的分页实现会在业务层中进行设置。
1、PageUtil.java类
package cn.mldn.szq.util;
public class PageUtil {
//当前页
private int currentPage = 1;
//每页条数
private int pageSize = 3;
}
2、扩充Book.java类的属性,并且需要继承PageUtil.java类
package cn.mldn.szq.model;
import java.io.Serializable;
import java.util.List;
import cn.mldn.szq.util.PageUtil;
public class Book extends PageUtil implements Serializable{
private Integer bookId;
private String bookName;
private String bookAuthor;
private String bookCover;
private Double bookPrice;
private Integer bookTypeId;
private Integer bookPressId;
private Integer bookStatus;
private String bookAddTime;
private String bookLabel;
private String bookDesc;
private Integer authorProId;
private Integer authorCityId;
private Integer userId;
private User user; //保存用户的信息
private BookType bookType; //保存书籍类型的信息
private BookPress bookPress; //保存出版社信息
private Location cityLocation; //用一个单独的对象来保存市区的信息
private Location proLocation; //保存省的新消息
}
省和市在本系统的设计中是属于同一个类Location.java类的,但是需要保存两个不同的内容,所以有两种做法,一种是直接在该Book.java类中继续添加两个属性用来专门保存查询出来的地区名称,而另一种方式就是如上的方式,分别建立两个Loaction.java类对象来保存其查询出来的内容。这两种方式对比起来,前一种可能会节省一些性能,但是有考虑到本来就是一个多表查询,也就干脆做一个复杂的操作,权当练习了。
2、在IBookDAO.java类中扩充新的方法
·考虑到后续会需要进行条件查询,而条件分为不同的类型,所以为了方便直接使用了Book.java类的对象作为一个参数来传递条件查询所输入的内容。
public List<Book> findAll(Book vo);
3、主要的查询语句需要在book-mapper.xml文件中进行修改
<sql id="bookColumns">
p.book_id AS bookId,p.book_name AS bookName,p.book_author AS bookAuthor,p.book_cover AS bookCover,
p.book_price AS bookPrice,p.book_type_id AS bookTypeId,p.book_press_id AS bookPressId,p.book_status AS bookStatus,to_char(p.book_add_time,'yyyy-MM-dd') AS bookAddTime,
p.book_label AS bookLabel,p.book_desc AS bookDesc,p.author_proid AS authorProId,p.author_cityid AS authorCityId,p.user_id AS userId
</sql>
<sql id="bookTypeColumns">
t.type_id AS "bookType.typeId",t.type_name AS "bookType.typeName"
</sql>
<sql id="bookPressColumns">
s.press_id AS "bookPress.pressId",s.press_name AS "bookPress.pressName"
</sql>
<sql id="userColumns">
u.userid AS "user.userId",u.username AS "user.userName",u.userpwd AS "user.userPass"
</sql>
<sql id="proColumns">
a.area_id AS "proLocation.locationID",a.area_title AS "proLocation.locationName",a.area_parid AS "proLocation.locationPID"
</sql>
<sql id="cityColumns">
m.area_id AS "cityLocation.locationID",m.area_title AS "cityLocation.locationName",m.area_parid AS "cityLocation.locationPID"
</sql>
<select id="findAll" resultType="Book" parameterType="Book">
SELECT <include refid="bookColumns"/>,
<include refid="bookTypeColumns"/>,
<include refid="bookPressColumns"/>,
<include refid="userColumns"/>,
<include refid="proColumns"/>,
<include refid="cityColumns"/>
FROM p_book p,p_booktype t,p_bookpress s,t_user u,p_area a,p_area m
WHERE p.book_type_id=t.type_id
AND p.book_press_id=s.press_id
AND p.author_proid=a.area_id
AND p.author_cityid = m.area_id
AND p.user_id = u.userid
AND p.user_id=#{userId}
</select>
·在设计SQL的语句过程中,为了查询的结果直接与Book实体类进行转换,可以有两种方式,一种是设置resultMap作为查询的返回结果,另一种就是在查询的过程中,为了每一个数据库字段起别名,别名要与实体类(Book)中的属性要一一对应。如果进行多表查询,在设置相关字段的时候,需要使用双引号<">来修饰,因为在Book实体类中引入了其他实体类的引用,所以双引号中的内容格式如:<bookType.typeId>,“bookType”是Book类中的BookType类的引用名称,而typeId是BookType类的属性。这样就可以将查询出来的内容保存在相应的引用实体中。
4、为IBookService.java接口扩充新的查询方法,并且在BookServiceimpl.java类中实现。
public Map<String,Object> list(Book vo);
@Override
public Map<String, Object> list(Book vo) {
if(vo != null){
PageHelper.startPage(vo.getCurrentPage(), vo.getPageSize());
return ServicePageUtil.setListPage(this.bookDAO.findAll(vo), vo.getCurrentPage(), vo.getPageSize());
}
return null;
}
其中使用到的ServicePageUtil.java类如下
package cn.mldn.szq.util;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.github.pagehelper.Page;
public class ServicePageUtil {
/**
* <pre>setListPage
* 用来设置业务层的分页处理
* @param list: 使用DAO查詢出來的所有实体数据返回的List集合
* @param currentPage:当前页
* @param pageSize:每页显示的行数
* @return map中的key如下:
* list:用来存储查询出来的数据
* allRecorders:共有多少条数据
* lineSize:共有多少页
* currentPage:当前页
* pageSize:每页显示的行数</pre>
*/
public static Map<String, Object> setListPage(List list,int currentPage, int pageSize){
Map<String,Object> map = new HashMap<String,Object>();
Page page = (Page) list;
//获取总条数
long total = page.getTotal();
//获取总页数
int pages = page.getPages();
map.put("list", list);
map.put("allRecorders", total);
map.put("lineSize", pages);
map.put("currentPage", currentPage);
map.put("pageSize", pageSize);
return map;
}
}
在业务层的分页也是我第一次接触这样的分页,首先必须使用“PageHelper.startPage()”方法来开启分页,然后才能设置具体的分页内容,这种自动分页的过程中会出现数据重复的问题,因为这种自动分页在查询第一页的以后只是两层分页,而两层分页中并没有加入order by 语句,所以在查询过程中,有可能会出现查询不到某一条数据,或者会重复查询好几条数据的情况。而在这种情况下只需要加入在sql语句的最后面加入“order by”语句即可。当切换到第2页开始,自动分页会进行三层分页,而这种分页往往也是Oracle中最可靠的分页,第一层进行排序,第二层加入Romnum 并且进行筛选rownum<(currentPage *pageSize),第三层利用第二层的ROWNUM>(currentPage - 1) * pageSize 即可进行正确的分页操作。
范例:首次的二层分页查询
SELECT TMP_PAGE.*, ROWNUM ROW_ID
FROM (
SELECT p.book_id AS bookId,p.book_name AS bookName,p.book_author AS bookAuthor,p.book_cover AS bookCover, p.book_price AS bookPrice,p.book_type_id AS bookTypeId,p.book_press_id AS bookPressId,p.book_status AS bookStatus,
to_char(p.book_add_time,'yyyy-MM-dd') AS bookAddTime, p.book_label AS bookLabel,p.book_desc AS bookDesc,p.author_proid AS authorProId,p.author_cityid AS authorCityId,p.user_id AS userId , t.type_id AS "bookType.typeId",t.type_name AS "bookType.typeName" ,
s.press_id AS "bookPress.pressId",s.press_name AS "bookPress.pressName" , u.userid AS "user.userId",u.username AS "user.userName",u.userpwd AS "user.userPass" ,
a.area_id AS "proLocation.locationID",a.area_title AS "proLocation.locationName",a.area_parid AS "proLocation.locationPID" , m.area_id AS "cityLocation.locationID",m.area_title AS "cityLocation.locationName",m.area_parid AS "cityLocation.locationPID"
FROM p_book p,p_booktype t,p_bookpress s,t_user u,p_area a,p_area m
WHERE p.book_type_id=t.type_id AND p.book_press_id=s.press_id AND p.author_proid=a.area_id AND p.author_cityid = m.area_id AND p.user_id = u.userid AND p.user_id=?
) TMP_PAGE
WHERE ROWNUM <= 3
范例:第二页开始进行三层分页
SELECT * FROM (
SELECT TMP_PAGE.*, ROWNUM ROW_ID
FROM (
SELECT p.book_id AS bookId,p.book_name AS bookName,p.book_author AS bookAuthor,p.book_cover AS bookCover, p.book_price AS bookPrice,p.book_type_id AS bookTypeId,p.book_press_id AS bookPressId,p.book_status AS bookStatus,
to_char(p.book_add_time,'yyyy-MM-dd') AS bookAddTime, p.book_label AS bookLabel,p.book_desc AS bookDesc,p.author_proid AS authorProId,p.author_cityid AS authorCityId,
p.user_id AS userId , t.type_id AS "bookType.typeId",t.type_name AS "bookType.typeName" , s.press_id AS "bookPress.pressId",s.press_name AS "bookPress.pressName" ,
u.userid AS "user.userId",u.username AS "user.userName",u.userpwd AS "user.userPass" , a.area_id AS "proLocation.locationID",a.area_title AS "proLocation.locationName",
a.area_parid AS "proLocation.locationPID" , m.area_id AS "cityLocation.locationID",m.area_title AS "cityLocation.locationName",m.area_parid AS "cityLocation.locationPID"
FROM p_book p,p_booktype t,p_bookpress s,t_user u,p_area a,p_area m
WHERE p.book_type_id=t.type_id AND p.book_press_id=s.press_id AND p.author_proid=a.area_id AND p.author_cityid = m.area_id AND p.user_id = u.userid AND p.user_id=?
) TMP_PAGE
WHERE ROWNUM <= 6 )
WHERE ROW_ID > 3
5、扩充控制层BookController.java类的方法
@RequestMapping("list")
public String list(HttpServletRequest request,Model model,Book vo){
Integer uid = (Integer) request.getSession().getAttribute("uid");
Map<String,Object> map = null;
if(vo != null){
vo.setUserId(uid);
map = this.bookService.list(vo);
}
model.addAttribute("allBooks",map.get("list"));
model.addAttribute("allRecorders",map.get("allRecorders"));
model.addAttribute("lineSize",map.get("lineSize"));
model.addAttribute("pageSize",map.get("pageSize"));
model.addAttribute("currentPage",map.get("currentPage"));
return "book/listBooks";
}
@RequestMapping("show")
public String show(HttpServletRequest request,Model model,Book vo){
Integer uid = (Integer) request.getSession().getAttribute("uid");
Map<String,Object> map = null;
if(vo != null){
vo.setUserId(uid);
map = this.bookService.list(vo);
}
model.addAttribute("allBooks",map.get("list"));
model.addAttribute("allRecorders",map.get("allRecorders"));
model.addAttribute("lineSize",map.get("lineSize"));
model.addAttribute("pageSize",map.get("pageSize"));
model.addAttribute("currentPage",map.get("currentPage"));
return "book/showBooks";
}
考虑到前台页面的异步加载问题,而异步加载获取数据并且展示数据有两种方式,一种是在将页面的数据格式作为JSON格式返回,然后在ajax的success:function(){}方法中动态的将数据填充到表格中进行展示,第二种方式,可以设置两个页面,一个是listBooks页面用于一些基本的操作方法调用以及条件查询的设置,而showBooks页面用于动态显示数据,然后将显示的数据使用"<#include>"标签将showBooks页面引入到listBooks页面中,当进行分页或者条件查询之后都可以使用控制层中的“show”方法,然后将查询出来的数据返回到showBooks页面中进行展示,当填充好数据之后,就相当于动态生成了一个table表格,然后可以在listBooks页面中使用html()方法将showBooks.ftl页面直接输出到自己的页面中而不需要重新加载整个页面。
每个用户都只能查询自己的数据,所以在读取数据的过程中必须要保证操作的用户是当前的用户,故每次查询调用业务层方法之前,手下会获取session属性范围中的“uid”并且设置到条件的VO实体类中传到查询方法中,然后在映射文件中设置相应的where条件就可以进行显示当前用户的数据。
6、建立listBooks.ftl页面
<#assign base=request.contextPath />
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<input type="button" value="新增" onclick="to_add()">
<div id="article_list_div">
<#include "showBooks.ftl" />
<div id=layuipage></div>
</div>
<script type="text/javascript" src="${base}/plugins/jquery.min.js"></script>
<script type="text/javascript">
//打开新增页面
function to_add() {
location.href = "${base}/book/toAddftl";
}
//分页
function turn_page(currentPage, pageSize) {
user_search(currentPage, pageSize);
}
//搜索
function user_search(page, rows) {
var data = {
currentPage:page,
pageSize:rows,
};
console.log(data);
$.ajax({
url:"${base}/book/show",
data:data,
type:"post",
success:function(data) {
$("#article_list_div").html(data);
}
});
}
</script>
</body>
</html>
7、建立showBooks.ftl文件
<#assign base=request.contextPath />
<table border="1">
<tr>
<td>书籍ID</td>
<td>书籍名称</td>
<td>书籍作者</td>
<td>书籍封面</td>
<td>书籍价格</td>
<td>书籍类型</td>
<td>书籍出版社</td>
<td>书籍状态</td>
<td>书籍上架时间</td>
<td>书籍标签</td>
<td>书籍描述</td>
<td>作者所在省</td>
<td>作者所在市</td>
<td>操作用户</td>
</tr>
<#list allBooks as vo>
<tr>
<td>${vo.bookId}</td>
<td>${vo.bookName}</td>
<td>${vo.bookAuthor}</td>
<td>
<img height="80px" src="${base}${vo.bookCover!}">
</td>
<td>#{vo.bookPrice}</td>
<td>${vo.bookType.typeName}</td>
<td>${vo.bookPress.pressName}</td>
<td>
<#if vo.bookStatus??>
<#if vo.bookStatus==1 >上架</#if>
<#if vo.bookStatus==2 >下架</#if>
</#if>
</td>
<td>${vo.bookAddTime}</td>
<td>
<#if vo.bookLabel ??>
<#if vo.bookLabel?contains("1")>校园</#if>
<#if vo.bookLabel?contains("2")>青春</#if>
<#if vo.bookLabel?contains("3")>政治</#if>
<#if vo.bookLabel?contains("4")>经济</#if>
</#if>
</td>
<td>${vo.bookDesc}</td>
<td>${vo.proLocation.locationName}</td>
<td>${vo.cityLocation.locationName}</td>
<td>${vo.user.userName}</td>
</tr>
</#list>
<tr>
<td colspan="16">
<#if currentPage <= 1>
<input type="button" value="首页" disabled>
<input type="button" value="上一页" disabled>
<#else>
<input type="button" value="首页" onclick="turn_page(1, 3)">
<input type="button" value="上一页" onclick="turn_page(${currentPage - 1}, 3)">
</#if>
<#if lineSize <= currentPage>
<input type="button" value="下一页" disabled>
<input type="button" value="尾页" disabled>
<#else>
<input type="button" value="下一页" onclick="turn_page(${currentPage + 1}, 3)">
<input type="button" value="尾页" onclick="turn_page(${lineSize}, 3)">
</#if>
当前第${currentPage}页,共${lineSize}页,每页${pageSize}条,共${allRecorders}条
</td>
</tr>
</table>
在数据显示的过程中使用到了一个新的标签“<#if></#if>”,而他的特别新鲜的用法“<#if vo.bookStatus??><#/if>”用来判断当前属性不为空,以及调用函数“<#if vo.bookLabel?contains("1")>校园</#if>”用来回填多选框的内容时候需要使用到函数进行判断,而这也是比较新鲜的操作。
同时在写数据的时候会遇到数据库查询出来的数据为空的情况,而这时候页面往往会报错,所以需要使用“!”来修饰,例如:“${vo.bookCover!}”,这样可以保证即使数据为空也不会报错。
在显示数字的时候还可以使用"#{}"来获取值,在页面中比如价格之类的数字,一般会超过四位数,而这时候如果继续使用“${}”取值,会出现逗号,并且在修改页面进行数据回填的时候也会添加逗号,这样会带来修改不方便以及显示的数据内容不符合观察要求,所以这个时候可以考虑使用“#{}”来取出超过四位的数字类型数据。
同时,在这个showBooks.ftl文件中加入了分页操作,可以发现该分页所调用的方法中最终进行的方法是进行条件查询所使用的方法,而这也是为了方便两种操作同时与查询全部数据进行整合的有效方式。也就是每次调用分页的时候,都会将页面中输入的条件发送到到后台。不过此时我们还没有进行条件的设置。
条件查询
条件查询的目的是为了练习使用不同的方式来设置查询的条件,而在接收这些条件的时候有很多种方式,而我也是通过在Book.java类中设置相应的业务字段来保存可能会用到的查询条件,而业务字段并不会子在数据库中添加。
本次查询可以根据图书名称进行模糊查询,数据类型(下拉)、出版社(下拉)、是否上架(单选框)进行精确查询,并且设置了日期的区间查询,在日期的区间查询中需要设置两个业务字段来进行查询条件的设置,还会根据书籍的标签(复选框)进行模糊查询,而这个查询也需要设置相应的业务字段进行输入条件的保存。
1、扩充Book.java类,同时设置相应的getter、setter方法
private String startTime; //开始时间
private String endTime; //结束时间
private List<String> labels; //所有的标签
2、在book-mapper.xml文件中id为findAll的SQL语句中添加相关的条件
<select id="findAll" resultType="Book" parameterType="Book">
SELECT <include refid="bookColumns"/>,
<include refid="bookTypeColumns"/>,
<include refid="bookPressColumns"/>,
<include refid="userColumns"/>,
<include refid="proColumns"/>,
<include refid="cityColumns"/>
FROM p_book p,p_booktype t,p_bookpress s,t_user u,p_area a,p_area m
WHERE p.book_type_id=t.type_id
AND p.book_press_id=s.press_id
AND p.author_proid=a.area_id
AND p.author_cityid = m.area_id
AND p.user_id = u.userid
AND p.user_id=#{userId}
<if test=" null != bookName and '' != bookName"> AND p.book_name LIKE '%${bookName}%'</if>
<if test=" null != bookTypeId and -1 != bookTypeId"> AND p.book_type_id = #{bookTypeId}</if>
<if test=" null != bookPressId and -1 != bookPressId"> AND p.book_press_id = #{bookPressId}</if>
<if test=" null != bookStatus"> AND p.book_status = #{bookStatus}</if>
<if test=" null != startTime and '' != startTime"> AND p.book_add_time <![CDATA[>=]]> to_date(#{startTime},'yyyy-MM-dd')</if>
<if test=" null != endTime and '' != endTime"> AND p.book_add_time <![CDATA[<=]]> to_date(#{endTime},'yyyy-MM-dd')</if>
<if test=" null != labels">
<foreach collection="labels" open="AND (" close=")" separator="OR" item="label">
p.book_label LIKE '%' || '${label}' || '%'
</foreach>
</if>
</select>
其中需要注意的是:
①实体类中定义的都是包装类,所以都需要进行非空的验证,之后保证非空才可以进行其他类型的判断,否则也会报错。
②同时本次使用到了mybatis中的动态SQL<if><foreach>,在foreach中
·“ collection='labels' ”:为实体类中所定义的接收条件用到的属性
·“ open='AND (' ” :这个是动态拼接SQL 的开头
·“ close=')' ” :SQL的结尾
·“ separator="OR" ”:每个条件中间的分隔符
·“ item="label" ” :每个单独的条件
③在判断过程中会使用到大于号跟小于号,而这两个特殊的字符需要单独额进行表示,否则会与其他标签产生冲突
·“<![CDATA[<=]]>”:表示小于或等于
·“<![CDATA[>=]]>”:表示大于或等于
3、修改业务层操作,将前台传过来存放在bookLabel中的字符串取出来分割然后放在建立的条件属性中
范例:修改BookServiceImpl.java类
@Override
public Map<String, Object> list(Book vo) {
if(vo != null){
if(vo.getBookLabel() != null && !"".equals(vo.getBookLabel())){
String labels = vo.getBookLabel().substring(1);
vo.setLabels(Arrays.asList(labels.split(",")));
}
PageHelper.startPage(vo.getCurrentPage(), vo.getPageSize());
return ServicePageUtil.setListPage(this.bookDAO.findAll(vo), vo.getCurrentPage(), vo.getPageSize());
}
return null;
}
由于在拼接的过程中发现开头多了一个“,”所以会先进行删除逗号,然后在分割。
4、修改listBooks.ftl文件
<#assign base=request.contextPath />
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="${base}/plugins/layui/css/layui.css">
<link rel="stylesheet" href="${base}/plugins/layui/css/modules/layer/default/layer.css">
</head>
<body>
<input type="button" value="新增" onclick="to_add()">
<div id="serch_bar">
<table border="1">
<tr>
<td>书籍名称</td>
<td>
<input name="bookName" id="bookName">
</td>
</tr>
<tr>
<td>书籍类型</td>
<td>
<select id="bookTypeId" name="bookTypeId">
<option value="-1">--请选择--</option>
</select>
</td>
</tr>
<tr>
<td>书籍出版社</td>
<td>
<select id="bookPressId" name="bookPressId">
<option value="-1">--请选择--</option>
</select>
</td>
</tr>
<tr>
<td>文章状态</td>
<td>
<input type="radio" name="bookStatus" value="1"/>上架
<input type="radio" name="bookStatus" value="2"/>下架
</td>
</tr>
<tr>
<td>发布日期</td>
<td>
<input id="startTime" name="startTime">--><input id="endTime" name="endTime">
</td>
</tr>
<tr>
<td>文章标签</td>
<td>
<input type="checkbox" name="bookLabel" value="1"/>校园
<input type="checkbox" name="bookLabel" value="2"/>青春
<input type="checkbox" name="bookLabel" value="3"/>政治
<input type="checkbox" name="bookLabel" value="4"/>经济
</td>
</tr>
<tr>
<td colspan="2">
<input type="button" value="提交" onclick="user_search(1, 3)">
</td>
</tr>
</table>
</div>
<div id="article_list_div">
<#include "showBooks.ftl" />
<div id=layuipage></div>
</div>
<script type="text/javascript" src="${base}/plugins/jquery.min.js"></script>
<script src="${base}/plugins/layui/layui.all.js"></script>
<script>
$(function(){
$("#checkAll").on("click",function(){
$("[name='checks']").prop("checked",$("#checkAll").prop("checked"));
});
//类型
$.ajax({
url:"${base}/booktype/listAll",
type:"post",
dataType:"json",
data:"{}",
success:function(data){
for(var x = 0; x < data.length ; x ++){
add_select_Row("bookTypeId",data[x].typeId,data[x].typeName);
}
}
});
//出版社
$.ajax({
url:"${base}/bookpress/listAll",
type:"post",
dataType:"json",
data:"{}",
success:function(data){
for(var x = 0; x < data.length ; x ++){
add_select_Row("bookPressId",data[x].pressId,data[x].pressName);
}
}
});
});
function add_select_Row(eleId,contentId,contentTitle){
$("#"+eleId).append(
"<option value='"+ contentId +"'>"+ contentTitle +"</option>"
);
}
//初始化layui相关组件
layui.use(['form', 'upload', 'layedit', 'laydate'], function(){
var form = layui.form;
var laydate = layui.laydate;
//初始化日期控件
laydate.render({
elem:"#startTime"
});
//初始化日期控件
laydate.render({
elem:"#endTime"
});
});
//打开新增页面
function to_add() {
location.href = "${base}/book/toAddftl";
}
//分页
function turn_page(currentPage, pageSize) {
user_search(currentPage, pageSize);
}
//搜索
function user_search(page, rows) {
var bookName = $("#bookName").val();
var bookTypeId = $("#bookTypeId").val();
var bookPressId = $("#bookPressId").val();
var bookStatus = $("[name='bookStatus']:checked").val();
var startTime = $("#startTime").val();
var endTime = $("#endTime").val();
var labels = "";
$("[name='bookLabel']:checked").each(function(){
labels += "," + $(this).val();
});
var data = {
bookName:bookName,
bookTypeId:bookTypeId,
bookPressId:bookPressId,
bookStatus:bookStatus,
startTime:startTime,
endTime:endTime,
bookLabel:labels,
currentPage:page,
pageSize:rows,
};
console.log(data);
$.ajax({
url:"${base}/book/show",
data:data,
type:"post",
success:function(data) {
$("#article_list_div").html(data);
}
});
}
</script>
</body>
</html>
此处的数据加载与新增页面是完全一样的,所以不再赘述,而关键的数据发送使用的是ajax记性异步加载,为了实现异步加载显示数据,如之前所说的,会将查询的路径设置为BookController中的show方法中,然后通过其将数据填充到自己的面板中,当异步加载成功调用之后会将其面板中的内容通过jQuery的html()方法来加载到本页面中来。
当然这里的条件查询,可以进行多条件同时管理查询,所以可以看出,条件查询的关键在于SQL语句的条件判断中以及接受条件的属性设置上。
更新上下架状态,删除操作、下载图片
更新操作和删除不再需要扩充实体类的属性。
1、扩充IBookDAO.java类中的方法
public void doUpdateStatus(Book vo);
public void doRemove(List<String> ids);
2、在book-mapper.xml文件中实现对应的方法
<update id="doUpdateStatus" parameterType="Book">
UPDATE p_book SET book_status=#{bookStatus} WHERE book_id=#{bookId}
</update>
<delete id="doRemove" parameterType="java.util.List">
DELETE FROM p_book
WHERE book_id IN
<foreach collection="list" item="i" open="(" close=")" separator=",">
#{i}
</foreach>
</delete>
此处使用的删除与上面的复选框条件查询使用的方式是一致的,业务要在业务层将字符串进行分割出来
3、在业务层IBookService.java接口中扩充方法并且在BookServiceImpl.java类中实现
public void editStatus(Book vo);
public void rm(String ids);
@Override
public void editStatus(Book vo) {
this.bookDAO.doUpdateStatus(vo);
}
@Override
public void rm(String ids) {
if(!"".equals(ids) && ids != null){
List<String> all = Arrays.asList(ids.split(","));
this.bookDAO.doRemove(all);
}
}
4、在控制层扩充新的方法BookController.java类中
@RequestMapping("editStatus")
@ResponseBody
public String editStatus(Book vo){
this.bookService.editStatus(vo);
return "{}";
}
@RequestMapping("rm")
@ResponseBody
public String rm(String ids){
this.bookService.rm(ids);
return "{}";
}
@RequestMapping("downloadBookCover")
public void downLoadFile(HttpServletResponse response, HttpServletRequest request, Book book) {
UpDownPicture.downLoadFile(response, request, book.getBookCover());
}
5、在页面中进行设置相应的操作
范例:修改showBooks.ftl文件
<#assign base=request.contextPath />
<table border="1">
<tr>
<td>
<input type="checkbox" id="checkAll"/>
</td>
<td>书籍ID</td>
<td>书籍名称</td>
<td>书籍作者</td>
<td>书籍封面</td>
<td>书籍价格</td>
<td>书籍类型</td>
<td>书籍出版社</td>
<td>书籍状态</td>
<td>书籍上架时间</td>
<td>书籍标签</td>
<td>书籍描述</td>
<td>作者所在省</td>
<td>作者所在市</td>
<td>操作用户</td>
<td>操作</td>
</tr>
<#list allBooks as vo>
<tr>
<td>
<input type="checkbox" name="checks" value="${vo.bookId}"/>
</td>
<td>${vo.bookId}</td>
<td>${vo.bookName}</td>
<td>${vo.bookAuthor}</td>
<td>
<img height="80px" src="${base}${vo.bookCover!}">
</td>
<td>#{vo.bookPrice}</td>
<td>${vo.bookType.typeName}</td>
<td>${vo.bookPress.pressName}</td>
<td>
<#if vo.bookStatus??>
<#if vo.bookStatus==1 >上架</#if>
<#if vo.bookStatus==2 >下架</#if>
</#if>
</td>
<td>${vo.bookAddTime}</td>
<td>
<#if vo.bookLabel ??>
<#if vo.bookLabel?contains("1")>校园</#if>
<#if vo.bookLabel?contains("2")>青春</#if>
<#if vo.bookLabel?contains("3")>政治</#if>
<#if vo.bookLabel?contains("4")>经济</#if>
</#if>
</td>
<td>${vo.bookDesc}</td>
<td>${vo.proLocation.locationName}</td>
<td>${vo.cityLocation.locationName}</td>
<td>${vo.user.userName}</td>
<td>
<#if vo.bookStatus??>
<#if vo.bookStatus==1 >
<input type="button" value="下架" onclick="edit_status(${vo.bookId},2)">
</#if>
<#if vo.bookStatus==2 >
<input type="button" value="上架" onclick="edit_status(${vo.bookId},1)">
</#if>
</#if>
<input type="button" value="下载" onclick="downloadPic('${vo.bookCover!}')">
<input type="button" value="删除" onclick="delBut(${vo.bookId})">
<input type="button" value="修改" onclick="editBut(${vo.bookId})">
</td>
</tr>
</#list>
<tr>
<td colspan="16">
<#if currentPage <= 1>
<input type="button" value="首页" disabled>
<input type="button" value="上一页" disabled>
<#else>
<input type="button" value="首页" onclick="turn_page(1, 3)">
<input type="button" value="上一页" onclick="turn_page(${currentPage - 1}, 3)">
</#if>
<#if lineSize <= currentPage>
<input type="button" value="下一页" disabled>
<input type="button" value="尾页" disabled>
<#else>
<input type="button" value="下一页" onclick="turn_page(${currentPage + 1}, 3)">
<input type="button" value="尾页" onclick="turn_page(${lineSize}, 3)">
</#if>
当前第${currentPage}页,共${lineSize}页,每页${pageSize}条,共${allRecorders}条
<input type="button" value="批量删除" onclick="delall()">
</td>
</tr>
</table>
范例:修改listBooks.ftl文件
<script>
$(function(){
//全选实现
$("#checkAll").on("click",function(){
$("[name='checks']").prop("checked",$("#checkAll").prop("checked"));
});
//类型
$.ajax({
url:"${base}/booktype/listAll",
type:"post",
dataType:"json",
data:"{}",
success:function(data){
for(var x = 0; x < data.length ; x ++){
add_select_Row("bookTypeId",data[x].typeId,data[x].typeName);
}
}
});
//出版社
$.ajax({
url:"${base}/bookpress/listAll",
type:"post",
dataType:"json",
data:"{}",
success:function(data){
for(var x = 0; x < data.length ; x ++){
add_select_Row("bookPressId",data[x].pressId,data[x].pressName);
}
}
});
});
function add_select_Row(eleId,contentId,contentTitle){
$("#"+eleId).append(
"<option value='"+ contentId +"'>"+ contentTitle +"</option>"
);
}
//修改状态操作
function edit_status(bookid,status) {
$.ajax({
url:"${base}/book/editStatus",
data:{bookId:bookid,bookStatus:status},
type:"post",
dataType:"json",
success:function(data) {
location.href = "${base}/book/list";
}
});
}
//初始化layui相关组件
layui.use(['form', 'upload', 'layedit', 'laydate'], function(){
var form = layui.form;
var laydate = layui.laydate;
//初始化日期控件
laydate.render({
elem:"#startTime"
});
//初始化日期控件
laydate.render({
elem:"#endTime"
});
});
//下载图片
function downloadPic(bookCover){
location.href = "${base}/book/downloadBookCover?bookCover="+bookCover;
}
//删除操作
function delBut(id) {
if (confirm("是否要删除?")) {
$.ajax({
url:"${base}/book/rm",
data:{ids:id},
type:"post",
dataType:"json",
success:function(data) {
location.href = "${base}/book/list";
}
});
}
}
//批量删除操作
function delall() {
var str = "";
$("[name='checks']:checked").each(function(){
str += "," + $(this).val();
});
str = str.substring(1);
if(str == ""){
alert("请选择要删除的数据");
}else{
console.log(str);
if (confirm("是否要删除?")) {
$.ajax({
url:"${base}/book/rm",
data:{ids:str},
type:"post",
dataType:"json",
success:function(data) {
location.href = "${base}/book/list";
}
});
}
}
}
//打开新增页面
function to_add() {
location.href = "${base}/book/toAddftl";
}
//分页
function turn_page(currentPage, pageSize) {
user_search(currentPage, pageSize);
}
//搜索
function user_search(page, rows) {
var bookName = $("#bookName").val();
var bookTypeId = $("#bookTypeId").val();
var bookPressId = $("#bookPressId").val();
var bookStatus = $("[name='bookStatus']:checked").val();
var startTime = $("#startTime").val();
var endTime = $("#endTime").val();
var labels = "";
$("[name='bookLabel']:checked").each(function(){
labels += "," + $(this).val();
});
var data = {
bookName:bookName,
bookTypeId:bookTypeId,
bookPressId:bookPressId,
bookStatus:bookStatus,
startTime:startTime,
endTime:endTime,
bookLabel:labels,
currentPage:page,
pageSize:rows,
};
console.log(data);
$.ajax({
url:"${base}/book/show",
data:data,
type:"post",
success:function(data) {
$("#article_list_div").html(data);
}
});
}
</script>
至此,操作完成。而之前系在图片所调用的方法在新增操作中已经有过引用,此处再次引用UpDownPicture.java:
package cn.mldn.szq.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.multipart.MultipartFile;
public class UpDownPicture {
/**
* <pre>uploadPhoto(这里用一句话描述这个方法的作用)
* @param request : HttpServletRequest的对象
* @param Photo :所要上传的图片文件
* @return 将保存好的文件名称以Map形式返回</pre>
*/
public static Map<String,Object> uploadPhoto(HttpServletRequest request,MultipartFile Photo){
Map<String,Object> map = new HashMap<String,Object>();
if(Photo.getSize() > 0){
String realpath = request.getServletContext().getRealPath("/") + "/upload/";
File file = new File(realpath);
if(!file.exists()){
file.mkdirs();
}
String originalFilename = Photo.getOriginalFilename();
String newFileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
try {
Photo.transferTo(new File(realpath + newFileName));
map.put("filepath", "/upload/" + newFileName);
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return map;
}
/**
* <pre>downLoadFile(这里用一句话描述这个方法的作用)
* 用于图片下载操作
* @param response :HttpServletResponse对象
* @param request:HttpServletRequest对象
* @param photoTitle:要下载的图片名称</pre>
*/
public static void downLoadFile(HttpServletResponse response, HttpServletRequest request, String photoTitle) {
String realPath = request.getServletContext().getRealPath("/") + photoTitle;
File file = new File(realPath);
if (file.exists()) {
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Disposition", "attachment;filename=" + file.getName());
response.setHeader("Content-Type", "application/octet-stream");
FileInputStream inputStream = null;
ServletOutputStream outputStream = null;
try {
inputStream = new FileInputStream(file);
outputStream = response.getOutputStream();
byte[] b = new byte[1024];
int read = inputStream.read(b);
while (-1 != read) {
outputStream.write(b);
outputStream.flush();
//读取下一次
read = inputStream.read(b);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (null != outputStream) {
try {
outputStream.close();
outputStream = null;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (null != inputStream) {
try {
inputStream.close();
inputStream = null; //方便GC更快回收
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}