目录
订单模块
创建数据表
创建订单表
首先创建一个订单表,用于存放订单id、订单创建时间、订单价格、订单状态、订单所属的用户的id。
create table t_order(
`order_id` varchar(50) primary key,
`create_time` datetime,
`price` decimal(11,2),
`status` int,
`user_id` int,
foreign key(`user_id`) references t_user(`id`)
);
创建订单项的表
创建一个订单项的表,用来存放商品项id、商品名称、商品数量、商品单价、商品总价、订单id。
create table t_order_item(
`id` int primary key auto_increment,
`name` varchar(100),
`count` int,
`price` decimal(11,2),
`total_price` decimal(11,2),
`order_id` varchar(50),
foreign key(`order_id`) references t_order(`order_id`)
);
编写javaBean
编写Order类
编写Order类,该类与数据库中的表项对应,提供相应的get、set方法,空参和满参的构造器,tostring方法。
/**
* @author glq
* @create 2020-04-21 20:21
*/
public class Order {
private String orderId;
private Date creatTime;
private BigDecimal price;
private Integer status = 0;
private Integer userId;
@Override
public String toString() {
return "Order{" +
"orderId='" + orderId + '\'' +
", creatTime=" + creatTime +
", price=" + price +
", status=" + status +
", userId=" + userId +
'}';
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public Date getCreatTime() {
return creatTime;
}
public void setCreatTime(Date creatTime) {
this.creatTime = creatTime;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public Order(String orderId, Date creatTime, BigDecimal price, Integer status, Integer userId) {
this.orderId = orderId;
this.creatTime = creatTime;
this.price = price;
this.status = status;
this.userId = userId;
}
public Order() {
}
}
编写OrderItem类
编写OrderItem类,与数据库中的表项对应,提供相应的get、set方法,空参和满参的构造器,tostring方法。
package glq.pojo;
import java.math.BigDecimal;
/**
* @author glq
* @create 2020-04-21 20:24
*/
public class OrderItem {
private Integer id;
private String name;
private Integer count;
private BigDecimal price;
private BigDecimal totalPrice;
private String orderId;
@Override
public String toString() {
return "OrderItem{" +
"id=" + id +
", name='" + name + '\'' +
", count=" + count +
", price=" + price +
", totalPrice=" + totalPrice +
", orderId='" + orderId + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public BigDecimal getTotalPrice() {
return totalPrice;
}
public void setTotalPrice(BigDecimal totalPrice) {
this.totalPrice = totalPrice;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public OrderItem(Integer id, String name, Integer count, BigDecimal price, BigDecimal totalPrice, String orderId) {
this.id = id;
this.name = name;
this.count = count;
this.price = price;
this.totalPrice = totalPrice;
this.orderId = orderId;
}
public OrderItem() {
}
}
编写DAO层
编写OrderDao
实现saveOrder方法,用来保存订单信息
public int saveOrder(Order order) {
String sql = "insert into t_order(`order_id`,`create_time`,`price`,`status`,`user_id`) values(?,?,?,?,?)";
int update = update(sql, order.getOrderId(), order.getCreatTime(), order.getPrice(), order.getStatus(), order.getUserId());
return update;
}
编写OrderItemDao
实现saveOrderItem方法,用来保存订单项信息
public int saveOrderItem(OrderItem orderItem) {
String sql = "insert into t_order_item(`name`,`count`,`price`,`total_price`,`order_id`) values(?,?,?,?,?)";
return update(sql,orderItem.getName(),orderItem.getCount(),orderItem.getPrice(),orderItem.getTotalPrice(), orderItem.getOrderId());
}
实现service层
创建OrderServiceImpl类,然后实现creatOrder方法用来生成订单号并且保存订单和订单项。
public class OrderServiceImpl implements OrderService {
private OrderDaoImpl orderDao = new OrderDaoImpl();
private OrderItemDaoImpl orderItemDao = new OrderItemDaoImpl();
@Override
public String creatOrder(Cart cart, Integer userId) {
String orderId = System.currentTimeMillis() + "" + userId;
Order order = new Order(orderId, new Date(), cart.getTotalPrice(), 0, userId);
orderDao.saveOrder(order);
// int i = 10/0;
for(Map.Entry<Integer,CartItem> entry:cart.getItems().entrySet()){
CartItem cartItem = entry.getValue();
OrderItem orderItem = new OrderItem(null, cartItem.getName(), cartItem.getCount(), cartItem.getPrice(), cartItem.getTotalPice(), orderId);
orderItemDao.saveOrderItem(orderItem);
}
cart.clear();
return orderId;
}
}
实现web层
实现servlet程序,获取购物车信息和用户信息,然后调用service层中的creatOrder方法保存订单信息,再将订单号返回用于显示。
public class OrderServlet extends BaseServlet {
private OrderService orderService = new OrderServiceImpl();
protected void createOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cart cart = (Cart) req.getSession().getAttribute("cart");
User user = (User)req.getSession().getAttribute("user");
if (user == null){
req.getRequestDispatcher("/pages/user/login.jsp").forward(req,resp);
return;
}
Integer id = user.getId();
String orderId = orderService.creatOrder(cart, id);
req.getSession().setAttribute("orderId",orderId);
resp.sendRedirect("pages/cart/checkout.jsp");
}
}
修改jsp页面
1.cart.jsp中的修改结账的跳转地址
<span class="cart_span"><a href="orderServlet?action=createOrder">去结账</a></span>
2.checkout.jsp中修改订单号的显示
<h1>你的订单已结算,订单号为${sessionScope.orderId}</h1>
事务的管理
在创建订单的时候如果保存订单以后发生错误就会导致保存订单项失败,。我们需要将整个操作设置为一个事务。
这里我们要设置不能自动提交数据。而多个操作中想要正确保存数据就需要使用同一个连接。这里可以使用ThreadLocal来保存和获取连接。
1.需要重写JdbcUtils,获取连接保存到ThreadLoal中、使之不自动提交并且写好提交和回滚的函数。
package com.glq.utils;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
* @author glq
* @create 2020-03-23 14:59
*/
public class JdbcUtils {
private static DruidDataSource dataSource;
private static ThreadLocal<Connection> conns = new ThreadLocal<Connection>();
static {
try {
Properties properties = new Properties();
InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
properties.load(inputStream);
dataSource = (DruidDataSource)DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取数据库连接池的连接
* 如果返回null说明获取连接失败
* @return
*/
public static Connection getConnect(){
Connection conn = conns.get();
if(conn == null)
{
try {
conn = dataSource.getConnection();
conns.set(conn);
conn.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
return conn;
}
// public static void close(Connection conn)
{
if(conn != null)
{
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void commitAndClose(){
Connection connection = conns.get();
if(connection != null)
{
try {
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
conns.remove();
}
public static void roolbackAndClose(){
Connection connection = conns.get();
if(connection != null)
{
try {
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
conns.remove();
}
}
2.使用filter同一管理事务。只需要创建一个TransactionFilter类,然后继承Filter接口,实现doFilter方法,在doFilter中提交(回滚)。要注意的是要保证异常能够抛到该类之中。
public class TransactionFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
try {
filterChain.doFilter(servletRequest,servletResponse);
JdbcUtils.commitAndClose();
} catch (Exception e) {
e.printStackTrace();
JdbcUtils.rollbackAndClose();
throw new RuntimeException(e);
}
}
@Override
public void destroy() {
}
}
还需要配置xml文件
<filter-mapping>
<filter-name>TransactionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<error-page>
错误页面的管理
只需要在XML文件之中配置一下不同的错误码对应的跳转页面即可。
<error-page>
<error-code>404</error-code>
<location>/pages/error/error404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/pages/error/error500.jsp</location>
</error-page>
测试
至此,此项目已经全部完成,然后测试一下有没有什么问题。