主要是分页——在此使用Mybatis
的PageHelper
分页插件
POM添加依赖
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelperartifactId>
<version>5.1.8version>
dependency>
Spring
修改 Mybatis
配置部分
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:Mapper/*.xml" />
<property name="configuration" >
<bean class="org.apache.ibatis.session.Configuration" >
<property name="mapUnderscoreToCamelCase" value="true" />
<property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/>
bean>
property>
<property name="plugins" >
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<value>
offsetAsPageNum=true
rowBoundsWithCount=true
reasonable=true
value>
property>
bean>
array>
property>
bean>
<mybatis:scan base-package="com.bookstore.Dao" />
ProductController.java
package com.bookstore.Controller;
import com.bookstore.Domain.Product;
import com.bookstore.Service.ProductService;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@Controller
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping("/productlist")
public String productList(@RequestParam(required = false) String catagory, @RequestParam(value="page", required = false, defaultValue = "1") Integer curPage, Model model) {
Integer pageSize = 5;
List<Product> productList = productService.findProductsOnPage(curPage, pageSize, catagory); //实际类型分页插件修改后的Page,它扩展了ArrayList
model.addAttribute("pageInfo", new PageInfo(productList));
model.addAttribute("productList", productList);
return "product_list";
}
}
ProductService.java
package com.bookstore.Controller;
import com.bookstore.Domain.Product;
import com.bookstore.Service.ProductService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
@Service
public class ProductService {
@Autowired
private ProductMapper productDao;
public List<Product> findProductsOnPage(Integer curPage, Integer pageSize, String catagory) {
ProductExample productExample = new ProductExample();
ProductExample.Criteria criteria = productExample.createCriteria();
if(catagory == null || "".equals(catagory)){
criteria.andCategoryIsNotNull();
}else{
criteria.andCategoryEqualTo(catagory);
}
PageHelper.startPage(curPage, pageSize, true);
return productDao.selectByExample(productExample); //分页时,实际返回的结果list类型是Page,如果想取出分页信息,需要强制转换为Page或者使用PageInfo(list)类包装
}
}
JSP部分省略
ProductController.java
(改写后利用Json保存购物车信息)
@GetMapping("/productinfo")
public String productInfo(@RequestParam String id, Model model, HttpServletResponse response) throws JsonProcessingException, UnsupportedEncodingException {
Product product = productService.findProductByID(id);
if (product == null) {
model.addAttribute("message", "该产品不存在#-1");
return "message";
}
//将产品信息(排除库存)json化后返回给前端,在pnum字段添加JsonIgnore注解
ObjectMapper mapper = new ObjectMapper();
Cookie cookie = new Cookie("product_" + id, URLEncoder.encode(mapper.writeValueAsString(product),"UTF-8"));
cookie.setMaxAge(30 * 24 * 60 * 60);
response.addCookie(cookie);
model.addAttribute("product", product);
return "product_info";
}
ProductService.java
public Product findProductByID(String id) {
return productDao.selectByPrimaryKey(id);
}
产品详情页购买的JS实现——使用JS创建cookie利用Json保存购物车信息
使用encodeURIComponent、decodeURIComponent
编解码Cookie与tomcat有关,cookie含有某些字符时tomcat会报错故而需要转义
<script src="js/jquery.js"></script>
<script type="text/javascript">
function getQueryVariable(variable) { //处理URL问号后面的部分
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split("=");
if (pair[0] == variable) {
return pair[1];
};
}
return false;
}
var pid = getQueryVariable("id");
cartCookie(0);
function addCart() { //添加到购物车,同步保存在服务端session的cart
cartCookie(1);
$.ajax({
type: "post",
withCredential: true, //ajax提交带上cookie
url: "${pageContext.request.contextPath}/changeCart"
});
}
function cartCookie(bnum) {
var cart={};
if(getCookie("cart")=="") {
cart[pid] = bnum;
}else{
cart = JSON.parse(decodeURIComponent(getCookie("cart")));
if(pid in cart){
cart[pid] += bnum;
}else{
cart[pid] = bnum;
}
}
setCookie("cart", encodeURIComponent(JSON.stringify(cart)), 30);
var num = JSON.parse(decodeURIComponent(getCookie("cart")))[pid];
if(num == 0) return;
$("#buynum").val(num);
$(".buy").attr("style", "display:inline");
}
function setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires;
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) == 0) return c.substring(name.length, c.length);
};
return "";
}
</script>
原本想将产品详情部分的id改成URL网址的一部分,但改动后静态资源的相对路径变了读取不到,故而又改回来
html的链接href属性,相对路径时以"/“打头直接连接到domain即"域名+href”,不以"/“打头连接到当前目录即"当前URL(去除当前页面文件名及QueryString部分)+href”,最好还是利用浏览器工具看看其到底怎么解析地址
服务器端的相对地址指的是相对于你的web应用的地址,客户端的地址即所有的html中的相对地址都是相对于域名的
ShoppingController.java
添加到购物车——调整前需要多次查询数据库:
package com.bookstore.Controller;
import com.bookstore.Domain.Product;
import com.bookstore.Service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@Controller
public class ShoppingController {
@Autowired
private ProductService productService;
@ResponseBody
@PostMapping("/addCart") //@CookieValue能否和变量绑定,@SessionAttribute为空还得创建
public int addCart(@RequestParam("id") String productID, HttpServletRequest request, HttpServletResponse response) {
// 原来是每次调整购物车都会查询数据库,将其改造为下单时统一查询通过后再提交订单
Product product = productService.findProductByID(productID);
@SuppressWarnings("unchecked")
Map<Product, Integer> cart = (Map<Product, Integer>) request.getSession().getAttribute("cart");
if (cart == null) {
cart = new HashMap<>();
}
//从cookie中读取采购信息,原来是分cookie单独保存,得重新刷新到相关页面才会将原有的购物信息添加到session
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if (cookie.getName().equals( {
cart.put(product, Integer.valueOf(cookie.getValue()));
break;
}
}
if (cart.containsKey(product)) { // 得重写Product的hashcode和equal方法,不然每次都是新的不会加1
int num = cart.get(product) + 1;
if (num <= product.getPnum()) {
cart.put(product, num);
}
} else {
cart.put(product, 1);
}
request.getSession().setAttribute("cart", cart);
Cookie cookie = new Cookie(String.valueOf(product.getId()), String.valueOf(cart.get(product)));
cookie.setHttpOnly(true);
cookie.setMaxAge(15 * 24 * 60 * 60);
response.addCookie(cookie);
return cart.get(product);
}
}
调整后尽量减少数据库查询,将购物车写到一个cookie里方便读取
package com.bookstore.Controller;
import com.bookstore.Domain.Product;
import com.bookstore.Service.ProductService;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
@Controller
public class ShoppingController {
@Autowired
private ProductService productService;
@ResponseBody
// 返回值为void时要不加上ResponeseBody或者HttpServletResponse入参,要不Spring MVC就会查找RequestMapping中路径对应的视图 https://blog.csdn.net/yh_zeng2/article/details/75136614
@PostMapping("/changeCart") //名字由addCart改为changeCart
public void changeCart(@CookieValue("cart") String cartCookie, HttpSession session) throws IOException {
ObjectMapper mapper = new ObjectMapper();
Map<String, Integer> cartID = mapper.readValue(URLDecoder.decode(cartCookie, "UTF-8"), new TypeReference<Map<String, Integer>>() {});
session.setAttribute("cartID", cartID); //若要持久化保存得将其写入数据库即可,这里仅由浏览器端保存
}
//打开购物车,基于cookie生成
@GetMapping("/cart")
public String showCart(@SessionAttribute Map<String, Integer> cartID, HttpServletRequest request) throws IOException {
Cookie[] cookies = request.getCookies();
if(cartID == null) { //未选购商品直接打开购物车时
String cartCookie = "";
for(Cookie cookie : cookies){
if(cookie.getName().equals("cart")){
cartCookie = cookie.getValue();
break;
}
}
changeCart(cartCookie, request.getSession()); //handler方法也是可以调用的
cartID = (Map<String, Integer>) request.getSession().getAttribute("cartID");
}
Map<Product, Integer> cart = new HashMap<>();
Cookie[] cookies = request.getCookies();
ObjectMapper mapper = new ObjectMapper();
for (String productID : cartID.keySet()) {
for (Cookie cookie : cookies) {
Product product;
if(!cookie.getName().startsWith("product_"))continue;
if (productID.equals(cookie.getName().split("_")[1])) {
product = mapper.readValue(URLDecoder.decode(cookie.getValue(), "UTF-8"), Product.class);
} else {
product = productService.findProductByID(productID); //排除cookie过期情况
}
cart.put(product, cartID.get(product.getId()));
}
}
request.setAttribute("cart", cart); //不用model看看效果是否一样
return "cart";
}
//结算,以显示的表单为准
@GetMapping("/settleaccounts")
public String settleAccounts(@RequestParam List<String> id, @RequestParam("amount") List<Integer> buyNum, Model model, HttpSession session){ // 表单是否可以将两个list转化为map呢 —— 变成 "产品id = 采购数量"
Map<Product, Integer> cart = new HashMap<>();
Map<String,String> msg = new HashMap<>();
Integer length = id.size();
for(int i=0; i<length; i++){
if(buyNum.get(i)<=0)continue;
Product product = productService.findProductByID(id.get(i));
cart.put(product, buyNum.get(i));
if(product.getPnum() < buyNum.get(i)){
msg.put(id.get(i), "库存不足,仅剩"+ product.getPnum()+"件");
}
}
if(msg.size() != 0){
model.addAttribute("cart", cart);
model.addAttribute("msg", msg);
return "cart";
}
session.setAttribute("cart", cart);
return "redirect:order";
}
}
/* 改变数量时不再查询数据库
@ResponseBody
@PostMapping("changeItemAmount")
public boolean changeItemAmount(@SessionAttribute Map cart, HttpServletRequest request, HttpServletResponse response) {
String id = request.getParameter("id");
int amount = Integer.parseInt(request.getParameter("amount"));
Product product = productService.findProductByID(id);
cart.put(product, amount);
if (amount == -1) { // -1时删除从购物车删除商品
cart.remove(product);
}
Cookie cookie = new Cookie(String.valueOf(product.getId()), String.valueOf(cart.get(product)));
cookie.setMaxAge(amount == -1 ? 0 : 15 * 24 * 60 * 60);
response.addCookie(cookie);
if (amount > product.getPnum()) {
return false;
}
return true;
}
*/
购物车相关JS代码
$("table .amount").change(function(){ //直接修改数量
changeAmount(this);
});
$("table :button[value='-']").click(function(){ // “-”绑定事件
var t = $(this).next(); //元素的选取定位请按需调整
var value = $(t).val()-1;
if(value>=0){
$(t).val(value);
changeAmount(this);
}
});
$("table :button[value='+']").click(function(){ //"-" 绑定事件
var t = $(this).prev();
var value = Number($(t).val())+1;
$(t).val(value);
changeAmount(this);
});
$("table a.removeItem").click(function(){removeItem(this);}); // 删除操作
function removeItem(item){ //删除条目函数
//console.log(item);
//修改cookie并同步
var cart = JSON.parse(decodeURIComponent(getCookie("cart")));
delete cart[$(item).parent().siblings(".pid").children(":first").attr("value")];
setCookie("cart", encodeURIComponent(JSON.stringify(cart)), 30);
$.ajax({
type: "post",
withCredential: true,
url: "${pageContext.request.contextPath}/changeCart"
});
//更新价格显示
var amountPriceNode = $(item).parent().siblings(".amountPrice");
var totalPriceNode = $("#totalPrice");
totalPriceNode.text((Number(totalPriceNode.text())-Number(amountPriceNode.text())).toFixed(2)); //保留两位有效小数
$(item).parent().parent().remove();
};
function changeAmount(elem){
//控制“-”键有效性
var amount = Number($(elem).parent().children(".amount").val());
if(amount==0){
$(elem).parent().children(".amount").prev().attr("disabled","disabled");
if(confirm("是否要删除此商品?")){
removeItem(elem);
return;
}
}else{
$(elem).parent().children(".amount").prev().removeAttr("disabled");
};
//调整价格显示
var amountPriceNode = $(elem).parent().siblings(".amountPrice");
var priceNode= $(elem).parent().siblings(".price");
//console.log(price);
var totalPriceNode = $("#totalPrice");
var oldAmountPrice = Number(amountPriceNode.text());
amountPriceNode.text((amount*Number(priceNode.text())).toFixed(2));
totalPriceNode.text((Number(totalPriceNode.text())+Number(amountPriceNode.text())-oldAmountPrice).toFixed(2));
// cookie部分
var cart = JSON.parse(decodeURIComponent(getCookie("cart")));
cart[$(elem).parent().siblings(".pid").children(":first").attr("value")] = amount;
setCookie("cart", encodeURIComponent(JSON.stringify(cart)), 30);
$.ajax({
type: "post",
withCredential: true,
url: "${pageContext.request.contextPath}/changeCart"
});
}
function setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires;
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) == 0) return c.substring(name.length, c.length);
};
return "";
}
写代码有时有点突发奇想,突然冒出个主意似乎这样做可以优化下,然后动手但有可能做着做着又觉得不对了删掉,但一回头却发现删掉的部分还是有用处的,如果没有保存就悲剧了,所以得用好版本管理工具 —— 我这次就有点悲剧,不大用IDE的版本管理,一般用rsync同步到git目录然后再用git管理,没保存的结果就是来回折腾浪费了好久!可以用IDE的git管理本地,需要提交到github再同步嘛