Java 大作业————使用MySQL的购物车
一、团队介绍
姓名 | 任务 |
---|---|
李天明、康友煌 | GUI设计及代码编写 |
谢晓淞 | 业务代码编写、MySQL服务器平台部署、git代码库 |
严威 | 类和包的结构关系设计以及流程设计、PPT制作 |
二、项目git地址
码云地址 (数据库用户目前已经删除)
三、项目主要使用技术
- DAO模式
- MVC模式
- MySQL数据库
四、项目其余特点
- 界面美观
- 可在不同设备登陆同一账号
五、项目功能架构图与主要功能流程图
六、UML类图
七、项目运行截图
- 登录界面
- 登录默认页面(无显示)
- 商城界面
- 商品详情界面
- 购物车界面
- 订单界面
- 订单详情界面
八、项目整体流程
数据库
使用的是MySQL数据库,数据库采用的DAO模式,其中订单,商城,购物车,用户账号密码,商家账号密码后台存储结构都是数据库。他们的表结构如下:
用户表(user)
用户表有三个字段,一个是用户id,一个是用户名,另一个是用户密码的md5值
商家表(merchant)
商家表同用户表
订单表(user_order)
订单表这个信息比较多,实际上订单表中都是以单种商品存在的,什么意思呢,当一个订单中有两件商品时,我会将这两件商品分开以相同的订单id存入数据库,这样设计有一个好处,就是商家也可以通过查询这个表当中的merchant_name字段来查询看谁买了自己商品,然后发货。
商城商品表(product)
这个也没什么特别的,只有一点是值得一提的,那就是details字段,我想让不同的商品体现不同的属性,比如买书会有对应的出版社,出版日期,买电脑有cpu信息这一类,我总不能每种都新建一个类来存储,所以我这边是用map来存储的,而map是一个类,要存入数据库就必须序列化,java要序列化好像还要自己写,比较麻烦,所以我干脆直接用来json格式化字符串来保存。
购物车表(cart)
cart表同上其中item_array同样使用的json格式化字符串来存储不确定数量的商品
DAO模式体现
DAO模式是什么,在我看来,DAO模式是用户操作和后端存储结构操作之间的一条铰链。我们先定义好了这样的一个接口:
package model.dao;
import java.util.ArrayList;
import model.order.Order;
public interface OrderDao {
public List<Order> getOrders();
public boolean payOrder(String orderId);
public boolean cancelOrder(String orderId);
public boolean deleteOrder(String orderId);
}
对于GUI的编写人员来说,我只需要知道这样的一个接口就能对后端的存储进行读取,写入,修改等操作,我不需要知道这些操作的具体实现。并且如果后面想要换存储结构,又或者说换一种数据库,比如说MySQL要换成Oracle,那么存储结构的编写人员只要根据这个接口对这些操作进行重写就好了,对GUI的代码不用做太多的改变,就能达到目的,这样将存储结构与其他业务代码分开来,减少了代码的耦合度的模式,就是DAO模式。
MVC模式体现
MVC又是什么,好像学习java总是会学到一些奇怪的缩写。我就拿项目中搜索商品举例吧:
MVC分为Model, View, Controler。其中Controler即为监听器,即上图中的searchButtonActionPerformed
,当用户输入完商品关键字,并点击搜索Button时,就会触发这个Controler,Controler从searchTextField
,即视图层View,获取到了用户输入,然后Controler转而将得到的用户输入交给了malljdbimpl
,即模型层Model,并得到了Model对该关键字的搜索结果,最后再将搜索结果交给了视图层,fillTable
方法中的tableModel
,进行搜索结果的显示,即视图层更新。
如果上面讲的太复杂听不懂,那么我们可以从以下角度理解这段代码的MVC模式:
假设公司中有一个老板叫监听器(Controler),我们就叫他总监吧,他底下有三个小喽啰,一个是干苦力专门找东西的模范员工老莫(Model),一个是老板的传话秘书S小姐(View1,即上面代码中的asearchTextField),还有一个是负责跟顾客交谈的秘书T小姐(View2,即上述代码中fillTable方法中的tableModel)。这天来了个客户:
S小姐(View1) -> 总监(Controler):“老板有一个客户要找xxx商品。”(传递用户输入)
总监(Controler) -> 老莫(Model):“欸老莫啊,你去找找xxx这个商品的相关资料。”(传递用户输入)
老模玩命翻箱倒柜中......(获得搜索结果)
老莫(Model) -> 总监(Controler):“老板找到了,给你。”(传递搜索结果)
总监(Controler) -> T小姐(View2) :“商品资料找到了,你去跟顾客谈生意吧。”(传递搜索结果)
随后T小姐向顾客展示了搜索结果。(视图更新)
以上,即为我对MVC模式的理解。也是MVC在项目中的体现。附上一张MVC图:
九、项目关键代码
搜索商品
public List<Product> searchProduct(String name, int page) {
List<Product> resultList = new ArrayList<>();
Connection conn = null;
PreparedStatement pstat = null;
ResultSet result ;
// 每页条数
int pieces = 10;
int limitStart = page * pieces - pieces;
int limitEnd = page * pieces - 1;
String sqlValue = "%" + name + "%";
String sql = "select * from product where name like ? or description like ? or brand like ? limit " + limitStart + "," + limitEnd + ";";
String pageSql = "select count(*) from product where name like ? or description like ? or brand like ?;";
try {
conn = Mysql.getConnection();
pstat = conn.prepareStatement(sql);
pstat.setString(1, sqlValue);
pstat.setString(2, sqlValue);
pstat.setString(3,sqlValue);
// 设置参数写入
result = pstat.executeQuery();
while(result.next()) {
String productId = result.getString(1);
String productName = result.getString(2);
String productDescription = result.getString(3);
double productPrice = result.getDouble(4);
String productBrand = result.getString(5);
String merchant_name = result.getString(6);
String detailsString = result.getString(7);
HashMap<String, String> details = jsonToMap(detailsString);
resultList.add(new Product(productId, productName, productDescription, productPrice, productBrand, details,merchant_name));
}
pstat.close();
pstat = conn.prepareStatement(pageSql);
pstat.setString(1, sqlValue);
pstat.setString(2, sqlValue);
pstat.setString(3,sqlValue);
result = pstat.executeQuery();
if (result.next()) {
this.pageNumber = result.getInt(1);
if (this.pageNumber % 10 == 0) {
this.pageNumber /= 10;
}
else {
this.pageNumber = this.pageNumber / 10 + 1;
}
}
} catch (SQLException sqle) {
sqle.printStackTrace();
} catch (Exception e){
e.printStackTrace();
} finally {
Mysql.realeaseAll(null,pstat, conn);
}
System.out.println(resultList.size());
this.products = resultList;
return resultList;
}
生成订单
public boolean generalOrder(CartItemsJDBCImpl cart, String address, long phoneNumber) {
Order newOrder = new Order(
cart.getItems(),
cart.totalPrice(),
cart.userNameGet(),
address,
System.currentTimeMillis(),
phoneNumber,
"unpaid"
);
this.orders.add(newOrder);
Connection conn = null;
PreparedStatement pstat = null;
boolean result = false;
long maxId = 0;
String sql = "insert into user_order(order_id, user_name, product_id,product_name, product_description, product_price, product_brand, product_details, merchant_name, amount, address, timetamp, phone_number, order_status) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?);";
String sqlMaxOrderId = "select max(order_id) from user_order;";
try {
conn = Mysql.getConnection();
pstat = conn.prepareStatement(sqlMaxOrderId);
ResultSet idResult = pstat.executeQuery();
if(idResult.next()) {
maxId = idResult.getLong(1);
}
pstat.close();
newOrder.setOrderId(maxId + 1 + "");
// 插入数据库
for(CartItem eItem : newOrder.getItems()) {
System.out.println(eItem.getProduct());
pstat = conn.prepareStatement(sql);
pstat.setLong(1, maxId + 1);
pstat.setString(2, this.userName);
pstat.setString(3, eItem.getProduct().getProductId());
pstat.setString(4, eItem.getProduct().getProductName());
pstat.setString(5, eItem.getProduct().getProductDescription());
pstat.setDouble(6, eItem.getProduct().getProductPrice());
pstat.setString(7, eItem.getProduct().getBrand());
pstat.setString(8, JSONObject.fromObject(eItem.getProduct().getDetails()).toString());
pstat.setString(9, eItem.getProduct().getMerchant_name());
pstat.setInt(10, eItem.getAmount());
pstat.setString(11, newOrder.getAddress());
pstat.setLong(12, newOrder.getTimesTamp());
pstat.setLong(13, phoneNumber);
pstat.setString(14, newOrder.getOrderStatus());
if(pstat.executeUpdate() > -1) {
result = true;
}
else {
result = false;
}
pstat.close();
}
} catch (SQLException sqle) {
sqle.printStackTrace();
} catch (Exception e){
e.printStackTrace();
} finally {
Mysql.realeaseAll(null,pstat, conn);
}
return result;
}
支付订单
public boolean payOrder(String orderId) {
Connection conn = null;
PreparedStatement pstat = null;
int result = -1;
String sql = "update user_order set order_status = ? where user_name = ? and order_id = ? and order_status = 'unpaid';";
try {
conn = Mysql.getConnection();
pstat = conn.prepareStatement(sql);
pstat.setString(1, "paid");
pstat.setString(2, this.userName);
pstat.setString(3, orderId);
result = pstat.executeUpdate();
} catch (SQLException sqle) {
sqle.printStackTrace();
} catch (Exception e){
e.printStackTrace();
} finally {
Mysql.realeaseAll(null,pstat, conn);
}
if(result > 0) {
for(int i = 0; i < this.orders.size(); i++) {
Order e = this.orders.get(i);
if(e.getOrderId().equals(orderId)) {
this.orders.get(i).setOrderStatus("paid");
break;
}
}
}
return result > 0 ? true : false;
}
从购物车中读取订单信息到本地
public void readData() {
Connection conn = null;
PreparedStatement pstat = null;
ResultSet r = null;
String sql = "select item_array from cart where user_name = ?;";
try {
conn = Mysql.getConnection();
pstat = conn.prepareStatement(sql);
pstat.setString(1, this.name);
r = pstat.executeQuery();
// 数据不存在该用户的购物车,使用insert增添一个空的购物车
if(r.next() == false) {
this.items = new ArrayList<>();
sql = "insert into cart(user_name, item_array) values (?,NULL);";
pstat.close();
pstat = conn.prepareStatement(sql);
pstat.setString(1, this.name);
pstat.execute();
}
else {
String text = r.getString(1);
// 数据库中购物车为NULL,为目前对象新建空购物车
if(text == null) {
this.items = new ArrayList<>();
}
else {
JSONArray cartItemJSONArray = JSONArray.fromObject(text);
this.items = (List<CartItem>) (JSONArray.toList(cartItemJSONArray, CartItem.class));
}
}
} catch (SQLException sqle) {
sqle.printStackTrace();
} catch (Exception e){
e.printStackTrace();
} finally {
Mysql.realeaseAll(null,pstat, conn);
}
}
往购物车添加商品并更新到数据库
public boolean addItem(Product other, int num) {
for (CartItem e : this.items) {
// 已存在该商品,则在原本数量的基础上继续添加
if (e.getProduct().getProductId().equals(other.getProductId())) {
return this.changeItemAmount(other.getProductId(), num);
}
}
this.items.add(new CartItem(other, num));
// 连接数据库更新数据
return this.databaseUpdate();
}
用户登录
public boolean login(String name, String password) {
password = getMD5(password);
if(password == "") {
return false;
}
Connection conn = null;
PreparedStatement pstat = null;
ResultSet r = null;
String sql = "select * from user where user = ? and password = ?;";
try {
conn = Mysql.getConnection();
pstat = conn.prepareStatement(sql);
pstat.setString(1, name);
pstat.setString(2, password);
r = pstat.executeQuery();
this.loginStatus = r.next();
} catch (SQLException sqle) {
sqle.printStackTrace();
} catch (Exception e){
e.printStackTrace();
} finally {
Mysql.realeaseAll(null,pstat, conn);
}
if(this.loginStatus == true) {
this.currentUser = name;
this.accountInit();
}
return this.loginStatus;
}
十、尚待改进或未实现的内容
- GUI的变量命名其实还不太规范
- 实际上商家的业务代码我也已经写完了,但是GUI部分尚未实现,原本的想法是为商家也设计一个客户端
- 安全性不好,因为这个程序是在本地运行的,包括数据库连接部分,所以不法分子可以通过反编译class文件得到数据库的账号密码,从而进行不花钱买东西这种情况,又或者数据库污染。