一:商品查询分页展示:
实例如下:,当我们点击上面文学,生活等字样,就会在下面展示出下面的商品。点击发送请求
<a
href="${pageContext.request.contextPath}/pageServlet?category=wenxue">文学</a>
<a
所以啦,咱们得建一个分页的pageServlet并且带着分类的参数去请求。但是之前我们必须新建一个pageBean,这个类虽然属于domain,但是数据库中并没有这个表,他的存在只是存一些我需要的字段,如上面的总页数,当前页,和传递来的参数等.
private int currentPage; //当前页
private int pageSize; //每页显示的记录数
private int count; //总数
private int totalPage; //总页数
private List<Product> products; //商品放入list集合!
private String category; //筛选参数
先写出来后面有用。
servlet
//导航按钮的查询条件
String category = request.getParameter("category");
if(category==null){
category="";
}
//初始化每页显示的记录数
int pageSize = 4;
int currentPage = 1;//当前页
String currPage = request.getParameter("currentPage");//从上一页或下一页得到的数据
if(currPage!=null&&!"".equals(currPage)){//第一次访问资源时,currPage可能是null
currentPage = Integer.parseInt(currPage);
}
ProductService bs = new ProductService();
//分页查询,并返回PageBean对象
PageBean pb = bs.findBooksPage(currentPage,pageSize,category);
request.setAttribute("pb", pb);
request.getRequestDispatcher("/product_list.jsp").forward(request, response);
这里可以发现,获取的参数会有是否为空的判断,因为当查询所有商品时,就是不传参数,所以在这里给他加上一个空字符。
这里pagesize就是你分的每页多少个,默认当前页是1;因为第一次访问时从currentPage中没有取出当前页(这个值,在点击文学时,不会传递,这个是在点击下一页时传过来的),所以默认给1.如果currentpage里面有值,那么就赋给他,而不是使用1。
service层:
写一个findbookspage方法返回pageBean,将这个pageBean存入域中,那么在前端就可以在域中取出跟页数有关的参数了,
传入的参数有(当前页,一页展示的数量,文学)
//分页查询
public PageBean findBooksPage(int currentPage, int pageSize,String category) {
try {
int count = productDao.count(category);//得到总记录数
int totalPage = (int)Math.ceil(count*1.0/pageSize); //求出总页数
List<Product> products= productDao.findBooks(currentPage,pageSize,category);
//把5个变量封装到PageBean中,做为返回值
PageBean pb = new PageBean();
pb.setProducts(products);
pb.setCount(count);
pb.setCurrentPage(currentPage);
pb.setPageSize(pageSize);
pb.setTotalPage(totalPage);
//在pageBean中添加属性,用于点击上一页或下一页时使用
pb.setCategory(category);
return pb;
} catch (SQLException e) {
e.printStackTrace();
先利用要category查找出所有这类的商品。得到总数。然后求出总页数。并通过当前页,页数,文学三个参数找出第一页的所有商品,并把他们放入list集合中。这样的话,pageBean中的所有变量都齐了,可以封装成一个pageBean返回了。因为servlet就是要这个,他把这个存入域中,前端取出来显示。
dao层:productDao
/**
* 得到总记录数
* @return
* @throws SQLException
*/
public int count(String category) throws SQLException {
QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
String sql ="select count(*) from products";
//如果category不是空,就把条件加上
if(!"".equals(category)){
sql+=" where category='"+category+"'";
}
long l = (Long)qr.query(sql, new ScalarHandler(1));
return (int)l;
}
/**
* 查找分页数据
* @param currentPage
* @param pageSize
* @return
* @throws SQLException
*/
public List<Product> findBooks(int currentPage, int pageSize,String category) throws SQLException {
QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
String sql = "select * from products where 1=1";
List list = new ArrayList();
if(!"".equals(category)){
sql+=" and category=?";
list.add(category);
}
sql+=" limit ?,?";
// select * from products where 1=1 and category=? limit ?,?;
list.add((currentPage-1)*pageSize);
list.add(pageSize);
return qr.query(sql, new BeanListHandler<Product>(Product.class),list.toArray());
}
就count中很奇怪,用的是qr.query(sql, new ScalarHandler(1));所以就记住吧。
然后findBooks这个sql语句有点厉害,where 1 =1 ,这样做其实很牛逼。可以防止关键字冲突。就比如这里,如果category为空
那么直接加上limit了。这肯定不行的,所以加上where 1=1就可以,具体讲解如下。
在不使用where 1=1的情况下
if(params.containsKey("name")){
String key = params.get("name").toString();
sql+="where a.name='"+key +"'";
}
if(params.containsKey("age")){
String key = params.get("age").toString();
sql+="where a.age='"+key +"'";
}
if(params.containsKey("class ")){
String key = params.get("class ").toString();
sql+="where a.class ='"+key +"'";
}
这样同时存在两个属性及以上是就会发生冲突
当时用where 1=1 的时候
String sql = “select * from table a where 1=1”;
if(params.containsKey("name")){
String key = params.get("name").toString();
sql+=" and a.name='"+key +"'";
}
if(params.containsKey("age")){
String key = params.get("age").toString();
sql+=" and a.age='"+key +"'";
}
if(params.containsKey("class ")){
String key = params.get("class ").toString();
sql+=" and a.class ='"+key +"'";
}
注意啊,这个方法中,是用list封装了要传入的参数。记下这个方法。返回值的是一个list <product>。正是封装pageBean中需要的。至此,封装的pageBean完成了。那么servlet将它存入了request域中。且页面跳转到了展示商品的页面。
<c:forEach items="${pb.products }" var="b">
<td>
<div class="divbookpic">
<p>
<a href="#"><img
src="${pageContext.request.contextPath }/upload/${b.img_url}"
width="115" height="129" border="0" /> </a>
</p>
</div>
<div class="divlisttitle">
<a
href="${pageContext.request.contextPath }/findBookInfoServlet?id=${b.id}">书名:${b.name
}<br />售价:${b.price } </a>
</div>
</td>
</c:forEach>
因为之前,我们存入的pageBean中含有list集合,我们可以取通过foreach标签取出来。
上下页,跳转有讲究。只传两个参数。当前页和筛选条件(文学,这个不就是咱们开始查询分页的条件吗?只不过这次带了当前页,还是请求原来的servlet。)这页数也是有讲究,
上页:${pb.currentPage==1?1:pb.currentPage-1}&category=${pb.category}"如果当前页是1,那么上页还是1,否则就减一
下页:${pb.currentPage==pb.totalPage?pb.totalPage:pb.currentPage+1}如果到了顶页,那么下页还是当前页,否则加一。
<li class="disablepage"><a
href="${pageContext.request.contextPath }/pageServlet?currentPage=${pb.currentPage==1?1:pb.currentPage-1}&category=${pb.category}"><<上一页</a>
</li>
<li>第${pb.currentPage }页/共${pb.totalPage }页</li>
<li class="nextPage"><a
href="${pageContext.request.contextPath }/pageServlet?currentPage=${pb.currentPage==pb.totalPage?pb.totalPage:pb.currentPage+1}&category=${pb.category}"><<下一页</a>
</li>
加入购物车:
当我们点击查看物品详情时,拿着商品id去查找。找出来,存入域中,然后页面回显出来。这个过程就不写了。
商品详情下面就是加入购物车功能。当点击加入购物车时,带着商品id,发送请求
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
String id = request.getParameter("id");
ProductService bs = new ProductService();
Product b = bs.findBookById(id);
//从session中的购物车取出 来
HttpSession session = request.getSession();
Map<Product, String> cart = (Map<Product, String>) session.getAttribute("cart");
int num = 1;
//如何是第一次访问,没有购物车对象,我们就创建 一个购物车对象
if(cart==null){
cart = new HashMap<Product, String>();
}
//查看当前集合中是否存在b这本书,如果有就把数据取出来加1;
if(cart.containsKey(b)){
num=Integer.parseInt(cart.get(b))+1;
}
//把图书放入购物车
cart.put(b, num+"");
//把cart对象放回到session作用域中
session.setAttribute("cart", cart);
out.print("<a href='"+request.getContextPath()+"/pageServlet'>继续购物</a>,<a href='"+request.getContextPath()+"/cart.jsp'>查看购物车</a>");
}
注意我怎么存下购物车的商品呢?使用map最好。先通过id查出商品,然后将商品加入map中。但是加入前有许多步骤。
1.这个map咱们肯定不能新创啊,必须从request域中找啊。因为还有可能存在其他的商品被添加了,咱们要在原来的基础上进行添加啊。
2.新建个num默认=1(后面如果发现这个商品在购物车中,就用购物车的num+1.这个是数量)如果为空,就是第一次加入购物车,new个新的map<Product,Stirng>,将product作为key,num做为值。
3.如果取出来的map,包含即将要添加的商品,就把num在原基础上+1.
OK,存入map后,加入到request域中,然后输出一个简单的简单页面,用于继续购买和查看购物车(内含下订单).
继续购买,就是回到原来的页面。如果查看购物车,那么就要把map遍历出来显示在前端了。
<c:set var="sum" value="0" > </c:set>
<c:forEach items="${cart }" var="entry" varStatus="vs">
<table width="100%" border="0" cellspacing="0">
<tr>
<td width="10%">${vs.count }</td>
<td width="30%">${entry.key.name }</td>
<td width="10%">${entry.key.price }</td>
<td width="20%"><input type="button" value='-'
style="width:20px"
onclick="changeNum('${entry.key.id}','${entry.value-1 }','${entry.key.pnum }')">
<input name="text" type="text" value="${entry.value }"
style="width:40px;text-align:center" /> <input
type="button" value='+' style="width:20px"
onclick="changeNum('${entry.key.id}','${entry.value+1 }','${entry.key.pnum }')">
</td>
<td width="10%">${entry.key.pnum }</td>
<td width="10%">${entry.value*entry.key.price }</td>
<td width="10%"><a href="${pageContext.request.contextPath}/changeNumServlet?id=${entry.key.id}&num=0"
style="color:#FF0000; font-weight:bold">X</a></td>
</tr>
</table>
<c:set var="sum" value="${sum+entry.value*entry.key.price }"> </c:set>
</c:forEach>
map的遍历好好看看下。其中<c:forEach items="${cart }" var="entry" varStatus="vs">这个varStatus怎么用的还是不懂。
当点击结账时,那么就关于生成订单的事了。
生成订单:
这个就设计了三张表。订单表,订单关系表,商品表。
一条订单对应多个订单关系表,
一个商品同时也对应着多个订单关系表。
这里需要说明下,这是java中多对多的写法,数据库中存的都是orderid和productid。
那为啥这里存的是对象呢?因为以后可以随时取出来对象,并且这样并不影响对数据库的操作。直接在dao层通过order
的id也可以操作,更加有扩展性。
订单关系:orderitem
private Order order;// 订单
private Product p; // 商品
private int buynum; // 购物数量
订单:因为是一个订单有多个orderitem,所以这里用list集合存,也可以用set。
private String id; // 订单编号
private double money; // 订单总价
private String receiverAddress; // 送货地址
private String receiverName; // 收货人姓名
private String receiverPhone; // 收货人电话
private int paystate; // 订单状态
private Date ordertime; // 下单时间
private User user;
private List<OrderItem> orderItems;//表示一个order对象,对应多个orderitem
商品:那么商品也可以有list啊?为啥没有,额,因为我们是把订单作为主表了,所以这张表可以不要,当然你可以要,但是一般都是这样的,根据需求来。
private String id;
private String name;
private double price;
private int pnum;
private String category;
private String description;
private String img_url;
定交订单时:只有订单的总价,地址,姓名电话等....,订单的状态不需要改动,用默认的就好,因为他还没付钱。然后发出请求
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//1、封装Order对象
Order order = new Order();
try {
BeanUtils.populate(order, request.getParameterMap());
order.setId(UUID.randomUUID().toString());
order.setUser((User)request.getSession().getAttribute("user"));//把session对象中的用户信息保存到order对象中
} catch (Exception e) {
e.printStackTrace();
}
//2、获取session对象中的购物车数据
Map<Product, String> cart = (Map<Product, String>) request.getSession().getAttribute("cart");
//3、遍历购物车中的数据,添加到orderItem对象中,同时把多个orderItem添加到list集合中
List<OrderItem> list = new ArrayList<OrderItem>();
for (Product p : cart.keySet()) {
OrderItem oi = new OrderItem();
oi.setOrder(order);//把Order对象添加到OrderItem中
oi.setP(p); //把购物车中的商品对象添加到OrderItem中
oi.setBuynum(Integer.parseInt(cart.get(p)));//购物车中的商品数量
list.add(oi);//把每个定单项添加到集合中
}
//4、把集合放入到Order对象 中
order.setOrderItems(list);
//调用 业务逻辑
OrderService os = new OrderService();
os.addOrder(order);
//
request.getRequestDispatcher("/pay.jsp").forward(request, response);
}
由于提交的表单没有,订单号和用户,我们可以手动设置,订单号:uuid,用户,从session取。
注意我们是要做什么?是为了封装一条订单,但是封装一条订单,需要封装多个orderitem(因为这是个list集合)。同时,还设计到多个商品的改动,为啥呢?因为,生成订单的我们需要更新商品的数量,所以orderitem里面还有个num属性。
一发而动全身,一条订单,批量添加ordreitem,批量修改product。
那么接下来,就是把list封装好就行了,咱们新建一个list<orderitem>集合,这个封装的方法记下来。我们将购物车的map全部的key取出来(因为key存的是product),通过遍历key值,把所有product取出来。将product放进去,然后把当前order放进去(此时的order不是完整的,因为这个order没有list属性,但是也的确不能有,因为我们现在做的就是封装好list。),然后将购买商品数量放进去。(这些终于知道我们为啥存对象,而不存id了吧,对象直接放进去就好了,因为域中存的也是对象)这就完成了一个orderitem,把他放进list中。一直循环。
然后这个list就封装好了,直接丢进order,就OK。
然后就是addOrder了。这个过程,就需要改动三张表了。
OrderDao orderDao = new OrderDao();
OrderItemDao orderItemDao = new OrderItemDao();
ProductDao productDao = new ProductDao();
public void addOrder(Order order){
try {
ManagerThreadLocal.startTransacation();
orderDao.addOrder(order);
orderItemDao.addOrderItem(order);
productDao.updateProductNum(order);
ManagerThreadLocal.commit();
} catch (SQLException e) {
e.printStackTrace();
ManagerThreadLocal.rollback();
}
}
发现没有,这里用到了事务。调用事务。而且,三个表的修改都是通过一个参数order,这个有点牛逼。
那就详细看看这三个方法吧。
注意,这里获取数据库连接不能像开始那样,通过C3P0Utils获取数据库连接,这里要用事务的数据库链接。知道为啥要存User
了吧,因为,数据库中有UserID,所以他java对象就直接存了个User对象,所以以后看见要存别的表的ID,我们在java中我们
就直接存对象好了。
// 添加定单
public void addOrder(Order order) throws SQLException {
QueryRunner qr = new QueryRunner();
qr.update(ManagerThreadLocal.getConnection(),
"INSERT INTO orders VALUES(?,?,?,?,?,?,?,?)", order.getId(),
order.getMoney(), order.getReceiverAddress(), order
.getReceiverName(), order.getReceiverPhone(), order
.getPaystate(), order.getOrdertime(), order.getUser()
.getId());
}
存orderitem时,注意这里是批量插入,我们不知道有多少个要插入,取决于list的长度。所以这里插入的方法也不一样。
定义了二维数组,一维的长度正是我们要插入的数量。通过循环然后确定另外二维数组。这个二维里面放的就是真正的值。
也很简单。这里用的也是事务的数据库链接。
//添加定单项
public void addOrderItem(Order order) throws SQLException{
List<OrderItem> orderItems = order.getOrderItems();//得到所有定单项的集合
QueryRunner qr = new QueryRunner();
Object[][] params = new Object[orderItems.size()][];
for (int i = 0; i < params.length; i++) {
//数组中的第一个参数代表主单id, 第二个参数:商品id 第三个参数 :商品的购买数量
params[i] = new Object[]{order.getId(),orderItems.get(i).getP().getId(),orderItems.get(i).getBuynum()};
}
qr.batch(ManagerThreadLocal.getConnection(),"INSERT INTO orderitem VALUES(?,?,?)", params );
}
还是原来的方法,封装。
//修改商品数量
public void updateProductNum(Order order) throws SQLException{
List<OrderItem> orderItems = order.getOrderItems();
QueryRunner qr = new QueryRunner();
Object[][] params = new Object[orderItems.size()][];
for (int i = 0; i < params.length; i++) {
params[i] = new Object[]{orderItems.get(i).getBuynum(),orderItems.get(i).getP().getId()};
}
qr.batch(ManagerThreadLocal.getConnection(),"UPDATE products SET pnum=pnum-? WHERE id=?", params);
}
OK,一条订单就插入完成了。将这个订单存在域中。接下来就支付的事了.......