今天来写Order模块,这个模块可以说是最复杂模块了,因为它和前面所有模块都有很大的关联,所以务必将这一块拿下,我们先进入到showCart页面,点击结算生成订单时,网页会跳转到order.jsp页面,在页面展示我们订单中的信息。当我们点击生成订单时,order.jsp
页面会向${pageContext.request.contextPath}/order提交, 表单中有一个隐藏域 <input type="hidden" name="method" value="add">
然后我们开始正式写Order模块,首先,我们还是先写domain,因为在数据库中order模块有order、orderItem(多对多操作)两张表,所以我们需要将order和orderitem全部写出来,另外,在Order用于查询订单时,我们也要将用户信息封装到Order中,并且由于一个用户的订单包含多个订单项,所以我们用List来储存订单项。在orderitem类中,要查询订单中商品信息时,可以将商品信息封装到OrderItem中。
下面的Order和OrderItem类
package cn.itcast.estore.domain;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
public class Order implements Serializable {
private String id;
private double money;
private String receiverinfo;
private int paystate;
private Date orderTime;
private int user_id;
// 用于订单查询时,可以将用户信息也封装到Order中
private String username;
private String nickname;
// 订单中包含多个订单项
private List<OrderItem> orderItems;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public String getReceiverinfo() {
return receiverinfo;
}
public void setReceiverinfo(String receiverinfo) {
this.receiverinfo = receiverinfo;
}
public int getPaystate() {
return paystate;
}
public void setPaystate(int paystate) {
this.paystate = paystate;
}
public Date getOrderTime() {
return orderTime;
}
public void setOrderTime(Date orderTime) {
this.orderTime = orderTime;
}
public int getUser_id() {
return user_id;
}
public void setUser_id(int user_id) {
this.user_id = user_id;
}
public List<OrderItem> getOrderItems() {
return orderItems;
}
public void setOrderItems(List<OrderItem> orderItems) {
this.orderItems = orderItems;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
}
package cn.itcast.estore.domain;
public class OrderItem {
private String order_id; // 订单号
private String product_id; // 商品号
private int buynum; // 购买数量
// 要查询订单中商品信息时,可以将商品信息封装到OrderItem中.
private String name;
private double price;
public String getOrder_id() {
return order_id;
}
public void setOrder_id(String order_id) {
this.order_id = order_id;
}
public String getProduct_id() {
return product_id;
}
public void setProduct_id(String product_id) {
this.product_id = product_id;
}
public int getBuynum() {
return buynum;
}
public void setBuynum(int buynum) {
this.buynum = buynum;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
接着我们开始写DAO层,OrderDao主要写对订单的增删查改,OrderItemDao主要写根据订单对订单项的增删查改。需要注意的是因为我们提交订单或者删除订单,Product数量要相应变化,所以我们也要对Product进行操作,增加相应操作的ProductDao类后面也会重新更新一下。
package cn.itcast.estore.dao;
import java.sql.SQLException;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import cn.itcast.estore.domain.Order;
import cn.itcast.estore.domain.User;
import cn.itcast.estore.utils.DataSourceUtils;
public class OrderDao {
// 创建订单
public void createOrder(Order order) throws SQLException {
String sql = "insert into orders values(?,?,?,0,null,?)";
QueryRunner runner = new QueryRunner();//不需要使用带参数构造方法
runner.update(DataSourceUtils.getConnection(), sql, order.getId(),
order.getMoney(), order.getReceiverinfo(), order.getUser_id());
}
// 根据用户查询订单
public List<Order> findOrder(User user) throws SQLException {
String sql = null;
List<Order> orders = null;
QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSource());
if ("admin".equals(user.getRole())) {
sql = "select orders.*,username,nickname from orders,users where orders.user_id=users.id";
orders = runner.query(sql, new BeanListHandler<Order>(Order.class));
} else if("user".equals(user.getRole())) {
sql = "select orders.*,username,nickname from orders,users where orders.user_id=users.id and user_id=?";
orders = runner.query(sql, new BeanListHandler<Order>(Order.class),
user.getId());
}
return orders;
}
//删除订单
public void delOrder(String id) throws SQLException {
String sql = "delete from orders where id=?";
QueryRunner runner = new QueryRunner();
runner.update(DataSourceUtils.getConnection(), sql, id);
}
//修改订单状态
public void updateState(String id) throws SQLException {
String sql="update orders set paystate=1 where id=?";
QueryRunner runner = new QueryRunner();
runner.update(sql,id);
}
}
package cn.itcast.estore.dao;
import java.sql.SQLException;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import cn.itcast.estore.domain.Order;
import cn.itcast.estore.domain.OrderItem;
import cn.itcast.estore.utils.DataSourceUtils;
public class OrderItemDao {
// 添加订单项
public void addOrderItem(Order order) throws SQLException {
List<OrderItem> items = order.getOrderItems();
Object[][] params = new Object[items.size()][3];
for (int i = 0; i < items.size(); i++) {
OrderItem item = items.get(i);
params[i][0] = item.getOrder_id();
params[i][1] = item.getProduct_id();
params[i][2] = item.getBuynum();
}
String sql = "insert into orderitem values(?,?,?)";
QueryRunner runner = new QueryRunner();
runner.batch(DataSourceUtils.getConnection(), sql, params);
}
// 根据订单查询订单项
public List<OrderItem> findOrderItemByOrderId(Order order)
throws SQLException {
String sql = "select * from orderitem,products where orderitem.product_id=products.id and order_id=?";
QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSource());
return runner.query(sql,
new BeanListHandler<OrderItem>(OrderItem.class), order.getId());
}
// 根据订单id查询所有订单项
public List<OrderItem> findOrderItemByOrderId(String id)
throws SQLException {
String sql = "select * from orderitem where order_id=?";
QueryRunner runner = new QueryRunner();
return runner.query(DataSourceUtils.getConnection(),sql,
new BeanListHandler<OrderItem>(OrderItem.class), id);
}
//删除订单项
public void delOrderItem(String id) throws SQLException {
String sql="delete from orderItem where order_id=?";
QueryRunner runner = new QueryRunner();
runner.update(DataSourceUtils.getConnection(),sql,id);
}
}
package cn.itcast.estore.dao;
import java.sql.SQLException;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import cn.itcast.estore.domain.Order;
import cn.itcast.estore.domain.OrderItem;
import cn.itcast.estore.domain.Product;
import cn.itcast.estore.utils.DataSourceUtils;
public class ProductDao {
public void addProduct(Product p) throws SQLException {
QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "insert into products values(?,?,?,?,?,?,?)";
runner.update(sql, p.getId(), p.getName(), p.getPrice(),
p.getCategory(), p.getPnum(), p.getImgurl(), p.getDescription());
}
public List<Product> findAll() throws SQLException {
QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "select * from products";
return runner.query(sql, new BeanListHandler<Product>(Product.class));
}
public Product findById(String id) throws SQLException {
QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSource());
String sql = "select * from products where id=?";
return runner.query(sql, new BeanHandler<Product>(Product.class), id);
}
// 修改商品的数量
public void updateProductCount(Order order) throws SQLException {
// 要修改的数量在哪?
List<OrderItem> items = order.getOrderItems();
Object[][] params = new Object[items.size()][2];
for (int i = 0; i < items.size(); i++) {
OrderItem item = items.get(i);
params[i][0] = item.getBuynum();
params[i][1] = item.getProduct_id();
}
String sql = "update products set pnum=pnum-? where id=?";
QueryRunner runner = new QueryRunner();
runner.batch(DataSourceUtils.getConnection(), sql, params);
// for(OrderItem item:items){
//
// runner.update(sql,item.getBuynum(),item.getProduct_id());
//
// }
}
// 当删除订单时,修改商品数量
public void updateProductCount(List<OrderItem> items) throws SQLException {
Object[][] params = new Object[items.size()][2];
for (int i = 0; i < items.size(); i++) {
OrderItem item = items.get(i);
params[i][0] = item.getBuynum();
params[i][1] = item.getProduct_id();
}
String sql = "update products set pnum=pnum+? where id=?";
QueryRunner runner = new QueryRunner();
runner.batch(DataSourceUtils.getConnection(), sql, params);
}
public List<Product> findSell() throws SQLException {
String sql = "select products.name,sum(buynum) as totalSaleNum from orders,orderItem,products where orders.id=orderItem.order_id and products.id=orderITem.product_id and orders.paystate=1 group by products.id order by totalSaleNum desc";
QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSource());
return runner.query(sql, new BeanListHandler<Product>(Product.class));
}
}
现在我们整理一下注意事项:
当订单生成后,需要对以下的表进行操作.
1.订单表中要插入数据
2.商品表中的商品数量要进行修改(修改商品的库存)
3.订单与用户之间也存在关系,添加订单时,也需要得到当前用户的id.
这时我们应想到,如果对数据库夺标进行操作时,就需要一个事务控制(DataSourceUtils)。
所以以上操作需要进行事务控制。
1.获取Connection时,要使用同一个,需要在DataSourceUtils中对获取Connection对象操作进行修改,将其放入到ThreadLocale中.
2.DbutilsQueryRunner 直接使用带参数的 参数类型是DataSource类型。new QueryRunner(DataSource ds);这个操作,在调用update,query方法,一般不会带Connection参数,这样,它就是一条sql一个事务。而现在我们需要事务管理,所以我们在使用QueryRunner时,就会不带参数 new QueryRunner(),而使用带Connection参数的update,query方法.
现在我们需要事务控制,所以我们在service层进行了事务的开启
大家看这张图,就拿add方法来说,一个add方法就要调用DAO的多个方法,都需要与数据库操作,而且操作方法两个是insert一个update,如果在添加过程中出现问题,就要对数据进行回滚,保证数据完整。所以我们需要做开启事务、调用操作、提交(如果出现问题,需要事务回滚),同时在添加订单项时,使用了批处理,因为订单与商品之间存在多对多关系,那么我们的中间表orderItem,它就有可能有多条数据,所以我们使用了QueryRuner的batch方法完成添加订单项操作。注意注意的是:当我们操作完成后,一定要将Connection对象从ThreadLocale中remove掉,这样我们就完成了add方法
接着还有查看订单操作,查看订单,会根据用户的role去显示出不同的订单。如果role=admin 它查询出所有的订单,如果role=user 它只查询出当前用户的订单,这个功能的实现是在用户添加完订单订单成功后,会显示查看订单操作。<a href="${pageContext.request.contextPath}/order method=search">
现在我们开始写service,还是先写接口:
package cn.itcast.estore.service;
import java.sql.SQLException;
import java.util.List;
import cn.itcast.estore.annotation.PrivilegeInfo;
import cn.itcast.estore.domain.Order;
import cn.itcast.estore.domain.User;
import cn.itcast.estore.exception.PrivilegeException;
public interface OrderService {
// 添加订单
@PrivilegeInfo("生成订单")
public void add(User user, Order order) throws PrivilegeException,
Exception;
// 根据用户查找订单
@PrivilegeInfo("查看订单")
public List<Order> find(User user) throws PrivilegeException, Exception;
// 根据id删除订单
public void delete(String id) throws Exception;
public void updateState(String id) throws Exception;
}
接着开始实现接口,并用到上面说的那些注意事项
package cn.itcast.estore.service;
import java.sql.SQLException;
import java.util.List;
import cn.itcast.estore.dao.OrderDao;
import cn.itcast.estore.dao.OrderItemDao;
import cn.itcast.estore.dao.ProductDao;
import cn.itcast.estore.domain.Order;
import cn.itcast.estore.domain.OrderItem;
import cn.itcast.estore.domain.User;
import cn.itcast.estore.exception.OrderException;
import cn.itcast.estore.exception.PrivilegeException;
import cn.itcast.estore.utils.DataSourceUtils;
public class OrderServiceImpl implements OrderService {
// 添加订单
public void add(User user, Order order) throws PrivilegeException {
OrderDao dao = new OrderDao();
OrderItemDao idao = new OrderItemDao();
ProductDao pdao = new ProductDao();
try {
// 开启事务
DataSourceUtils.startTransaction();
// 1.向orders表中添加数据
dao.createOrder(order);
// 2.向orderitem表中添加数据
idao.addOrderItem(order);
// 3.修改products表中数据
pdao.updateProductCount(order);
} catch (SQLException e) {
e.printStackTrace();
try {
DataSourceUtils.rollback(); // 事务回滚
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
try {
DataSourceUtils.commitAndReleased(); // 事务提交与释放
} catch (SQLException e) {
e.printStackTrace();
}
}
// 根据用户查找订单
public List<Order> find(User user) throws PrivilegeException, SQLException {
List<Order> orders = new OrderDao().findOrder(user); // 查询订单信息,不包含订单中的商品信息
OrderItemDao idao = new OrderItemDao();
// 根据得到的订单,查询订单中商品信息.
for (Order order : orders) {
List<OrderItem> items = idao.findOrderItemByOrderId(order);
order.setOrderItems(items);
}
return orders;
}
// 根据id删除订单
public void delete(String id) throws OrderException {
OrderDao dao = new OrderDao();
OrderItemDao idao = new OrderItemDao();
ProductDao pdao = new ProductDao();
// 1.修改商品表中商品数量
try {
DataSourceUtils.startTransaction(); //开启事务
// 1.1 得到商品的数量
List<OrderItem> items = idao.findOrderItemByOrderId(id);
// 1.2修改商品的数量
pdao.updateProductCount(items);
// 2.删除订单项
idao.delOrderItem(id);
// 3.删除订单
dao.delOrder(id);
} catch (SQLException e) {
e.printStackTrace();
try {
DataSourceUtils.rollback(); //事务回滚
} catch (SQLException e1) {
e1.printStackTrace();
}
throw new OrderException("删除订单失败");
} finally {
try {
DataSourceUtils.commitAndReleased(); //事务提交
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// 根据订单号修改订单状态
public void updateState(String id) throws SQLException {
OrderDao dao = new OrderDao();
dao.updateState(id);
}
}
最后我们开始写Servlet,因为admin可以查看所有用户的订单,而用户只可以查看自己的订单,所以在这需要一个factory实例化一下对象
package cn.itcast.estore.web.servlet;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.BeanUtils;
import cn.itcast.estore.domain.Order;
import cn.itcast.estore.domain.OrderItem;
import cn.itcast.estore.domain.Product;
import cn.itcast.estore.domain.User;
import cn.itcast.estore.exception.OrderException;
import cn.itcast.estore.factory.OrderServiceFactory;
import cn.itcast.estore.service.OrderService;
/**
* Servlet implementation class OrderServlet
*/
public class OrderServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public OrderServlet() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.getWriter().append("Served at: ").append(request.getContextPath());
String method = request.getParameter("method");
if ("add".equals(method)) {
add(request, response);
} else if ("del".equals(method)) {
del(request, response);
} else if ("search".equals(method)) {
search(request, response);
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
public void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.将请求参数封装到Order对象中.
Order order = new Order();
// 1.1 表单数据
try {
BeanUtils.populate(order, request.getParameterMap()); // 只封装了表单数据到javaBean,简单说,只有receiverinfo
// money两项
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
// 1.2 订单编号 当前用户id
order.setId(UUID.randomUUID().toString());
User user = (User) request.getSession().getAttribute("user");
if (user == null) {
response.sendRedirect(request.getContextPath() + "/error/error.jsp");
return;
}
order.setUser_id(user.getId());
// 1.3 将订单项封装到订单中.
Map<Product, Integer> cart = (Map<Product, Integer>) request.getSession().getAttribute("cart"); // 得到购物车
List<OrderItem> items = new ArrayList<OrderItem>();
for (Product p : cart.keySet()) {
OrderItem item = new OrderItem();
item.setOrder_id(order.getId());
item.setProduct_id(p.getId());
item.setBuynum(cart.get(p));
items.add(item);
}
order.setOrderItems(items);
// 2.调用OrderService中方法,创建订单
OrderService service = OrderServiceFactory.getInstance();
try {
service.add(user, order);
response.getWriter().write("下单成功,<a href='" + request.getContextPath() + "/index.jsp'>继续购物</a>,<a href='"
+ request.getContextPath() + "/order?method=search'>查看订单</a>");
} catch (Exception e) {
e.printStackTrace();
}
}
public void del(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String id = request.getParameter("id"); // 得到要删除的订单的id。
// 调用OrderService中删除订单操作
OrderService service = OrderServiceFactory.getInstance();
try {
service.delete(id);
// 在次查询订单
response.sendRedirect(request.getContextPath() + "/order?method=search");
return;
} catch (OrderException e) {
e.printStackTrace();
response.getWriter().write(e.getMessage());
return;
} catch (Exception e) {
e.printStackTrace();
}
}
public void search(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 1.得到当前用户
User user = (User) request.getSession().getAttribute("user");
if (user == null) {
response.getWriter().write("请先<a href='" + request.getContextPath() + "/index.jsp'>登录</a>");
return;
}
// 2.调用OrderService中查询订单操作
OrderService service = OrderServiceFactory.getInstance();
try {
List<Order> orders = service.find(user);
request.setAttribute("orders", orders);
request.getRequestDispatcher("/showOrder.jsp").forward(request, response);
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
这样我们就完成了目前来说最难的Order模块设计。