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文件得到數據庫的賬號密碼,從而進行不花錢買東西這種情況,又或者數據庫污染。