生成订单
- 需求与步骤分析
- 准备工作
1)需求与步骤分析
需求分析:
在购物车页面上,有一个提交订单按钮,点击的时候将用户购物车中的商品添加到数据库中
实体:
用户
订单
订单项
商品
我们在第一章分析时已经分析过了他们的关系:
步骤分析:
- 点击生成订单:${path}/order/add
- 创建OrderServlet:
- 处理add,创建add方法。1. 判断用户是否登录,2. 封装数据Order
- 调用orderService生成订单。1. 向订单表中插入一条数据,2. 向订单项表中插入多条数据
要么成功,要么失败,所以要使用事务控制
2)准备工作
① 创建数据表
② 创建实体类
订单实体Order:
public class Order implements Serializable{
/**
*
*/
private static final long serialVersionUID = -8541282704453921798L;
private String oid;
/**
* 下单时间
*/
private Date ordertime;
/**
* 总价
*/
private Double total;
/**
* 状态 0 未支付,1 已支付 2
*/
private Integer state;
/**
* 订单收件地址
*/
private String address;
/**
* 收件人
*/
private String name;
/**
* 收件号码
*/
private String telephone;
/**
* 属于哪个用户
*/
private User user;
/**
* 一个订单包含多个订单项
*/
private List<OrderItem> items=new ArrayList<>();
//getter和setter...
}
订单项实体OrderItem:
public class OrderItem implements Serializable{
/**
*
*/
private static final long serialVersionUID = -2760996836863504723L;
/**
* 订单项id
*/
private String itemid;
/**
* 订单项商品总数量
*/
private Integer count;
/**
* 当前订单项金额
*/
private Double subtotal;
/**
* 包含哪个实体
*/
private Product product;
/**
* 属于哪个订单
*/
private Order order;
//getter和setter...
}
③ 创建Order与OrderItem的dao层和service层接口和实现类
略:
3)代码实现
① 编写CardServlet处理add,生成订单
/**
* 生成订单
* @param request
* @param response
* @throws IOException
* @throws ServletException
*/
private void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1、判断用户是否登录
User user=(User) request.getSession().getAttribute("user");
if(user == null) {
request.setAttribute("msg", "请先登录~~");
request.getRequestDispatcher("/jsp/msg.jsp").forward(request, response);
return;
}
//2、封装数据
Order order=new Order();
//2.1 订单id
order.setOid(UUIDUtils.getId());
//2.2 订单时间
order.setOrdertime(new Date());
//2.3 总金额
//获取session中的cart
Cart cart=(Cart) request.getSession().getAttribute("cart");
order.setTotal(cart.getTotal());
//2.4 订单的所有订单项
/*
* 先获取cart中的items
* 遍历items 组装成orderItem
* 将orderItem添加到list(itmes)中
*/
for(CartItem cartItem:cart.getItems()) {
//创建订单项
OrderItem orderItem=new OrderItem();
//2.4.1 设置订单项id
orderItem.setItemid(UUIDUtils.getId());
//2.4.2 设置购买数量
orderItem.setCount(cartItem.getCount());
//2.4.3 设置小计
orderItem.setSubtotal(cartItem.getSubtotal());
//2.4.5 设置商品
orderItem.setProduct(cartItem.getProduct());
//2.4.6 设置order
orderItem.setOrder(order);
//2.4.7 添加到list
order.getItems().add(orderItem);
}
//2.5 设置用户
order.setUser(user);
//3、调用service 添加订单
orderService.add(order);
//4、将order绑定到request中,请求转发到/jsp/order_info.jsp
request.setAttribute("order", order);
request.getRequestDispatcher("/jsp/order_info.jsp").forward(request, response);
}
② 更改DBUtil,提供操作事务控制的方法
开启事务方法:
/**
* 开启事务
*/
public static void startTransaction() {
try {
getConnection().setAutoCommit(false);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
事务提交并释放资源:
/**
* 事务提交并关闭
*/
public static void commitAndClose() {
Connection connection = getConnection();
try {
connection.commit();
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
事务回滚并释放资源:
/**
* 事务回滚并关闭
*/
public static void rollbackAndClose() {
Connection connection = getConnection();
try {
connection.rollback();
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
③ 在service层生成订单,使用事务控制
@Override
public void add(Order order) throws Exception{
try {
//1、开启事务
DBUtil.startTransaction();
//2、向orders表中添加一条记录
orderDao.add(order);
//3、向orderitem表中添加多条记录
for(OrderItem orderItem:order.getItems()) {
orderDao.addItem(orderItem);
}
//4、事务处理
DBUtil.commitAndClose();
}catch(Exception e) {
e.printStackTrace();
//错误回滚
DBUtil.rollbackAndClose();
throw e;
}
}
④ 在dao层实现增加
public class OrderDaoImpl implements OrderDao{
//QueryRunner
private QueryRunner qr=new QueryRunner(DBUtil.getDataSource());
@Override
public void add(Order order) throws SQLException {
String sql="insert into `orders` values(?,?,?,?,?,?,?,?)";
qr.update(sql,order.getOid(),order.getOrdertime(),order.getTotal(),
order.getState(),order.getAddress(),order.getName(),
order.getTelephone(),order.getUser().getUid());
}
@Override
public void addItem(OrderItem orderItem) throws SQLException {
String sql="insert into `orderitem` values(?,?,?,?,?)";
qr.update(sql,orderItem.getItemid(),orderItem.getCount(),orderItem.getSubtotal()
,orderItem.getProduct().getPid(),orderItem.getOrder().getOid());
}
}
⑤ 先测试事务
先把商品加入购物车,点击提交订单:
查看数据库是否添加成功:
订单表:
订单项表:
修改service,手动添加异常测试事务回滚:
...
//2、向orders表中添加一条记录
orderDao.add(order);
int a=1/0;
//3、向orderitem表中添加多条记录
...
再进行测试:
提交订单,查看数据库:
增加成功,事务并没有回滚!!!把这个没有任何关联的数据删除掉
原因:因为在dao层执行的时候时重新获取的Connection,跟Service层处理事务时用的不是同一个连接,当然不会成功
先修改DBUtil的getConnection方法从线程池中获取
//1、定义一个连接池
private static BasicDataSource ds;
//2、定义一个线程池
private static ThreadLocal<Connection> tl=new ThreadLocal<>();
/**
* 从线程中获取连接
* @return
* @throws SQLException
*/
public static Connection getConnection() {
//从线程中获取connection
Connection conn = tl.get();
if(conn==null){
try {
conn=ds.getConnection();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//和当前线程绑�?
tl.set(conn);
}
return conn;
}
//...
再修改Dao层获取连接的方式
在测试发现事务成功!
⑥ 在order_info.jsp上展示数据
⑦ 在生成订单之后清空购物车
...
//4、将order绑定到request中,请求转发到/jsp/order_info.jsp
request.setAttribute("order", order);
//5、清空购物车
request.getSession().removeAttribute("cart");
...