#問題 1(100分)有一個在線交易電商平臺,有兩張表,分別是庫存表和訂單表,以下:java
如今買家XiaoMing在該平臺購買bag一個,須要同時在庫存表中對bag庫存記錄減一,同時在訂單表中生成該訂單的相關記錄。 請編寫Java程序,實現XiaoMing購買bag邏輯。訂單表ID字段爲自增字段,無需賦值。mysql
#解答 ##購買事務邏輯 XiaoMing購買bag一個,首先檢查是否還有剩餘庫存,若是有庫存,則進行購買,若是沒有庫存,返回沒有庫存異常。當有庫存時,對庫存表中的記錄減一,並生成訂單記錄。 ##事務與鎖的分析 這裏必須進行加鎖,否則會發生各類問題,經過查閱相關資料,肯定悲觀鎖,是相對穩健的形式。 ##初始化數據庫 homework.sqlsql
CREATE TABLE `inventory` ( `ID` int NOT NULL AUTO_INCREMENT , `ProductName` varchar(100) NULL , `Inventory` int NULL , PRIMARY KEY (`Id`) ); INSERT INTO `inventory` (`ID`, `ProductName`, `Inventory`) VALUES ('1', 'watch', '25'); INSERT INTO `inventory` (`ID`, `ProductName`, `Inventory`) VALUES ('2', 'bag', '20'); CREATE TABLE `order` ( `ID` int NOT NULL AUTO_INCREMENT , `buyer` varchar(100) NULL , `ProductName` varchar(100) NULL , PRIMARY KEY (`Id`) );
##執行測試類數據庫
package com.hava.transition; import junit.framework.TestCase; /** * Created by zhanpeng on 2016/10/8. */ public class OrderDAOTest extends TestCase { public void testTransferAccount() throws Exception { OrderDAO orderDAO = new OrderDAO(); orderDAO.init(); orderDAO.buyOne("XiaoMing","bag"); } }
##數據庫訪問類apache
package com.hava.transition; import org.apache.commons.dbcp2.BasicDataSource; import java.sql.*; /** * Created by zhanpeng on 2016/10/8. */ public class OrderDAO { public static BasicDataSource basicDataSource = null; public final static String JDBC_DRIVER = "com.mysql.jdbc.Driver"; public final static String DB_URL = "jdbc:mysql://192.168.1.200/test"; public final static String USER = "root"; public final static String PASSWORD = "dVHJtG0T:pf*"; public void init() { basicDataSource = new BasicDataSource(); basicDataSource.setUrl(DB_URL); basicDataSource.setDriverClassName(JDBC_DRIVER); basicDataSource.setUsername(USER); basicDataSource.setPassword(PASSWORD); } public void buyOne(String buyer,String productName) throws ClassNotFoundException { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { connection = basicDataSource.getConnection(); //開啓事務 connection.setAutoCommit(false); //使用悲觀鎖 String getproduct_sql = "SELECT Inventory FROM inventory WHERE(ProductName = ?) FOR UPDATE"; preparedStatement = connection.prepareStatement(getproduct_sql); preparedStatement.setString(1,productName); resultSet = preparedStatement.executeQuery(); int inventory = -1; while(resultSet.next()) inventory = resultSet.getInt("Inventory"); System.out.println("[inventory]:" + inventory); if(inventory <= 0) { System.out.println("沒有庫存"); throw new SQLException(); } else { //減小庫存 String subproduct_sql = "UPDATE inventory SET Inventory=? WHERE (ProductName = ?)"; preparedStatement = connection.prepareStatement(subproduct_sql); preparedStatement.setInt(1,inventory - 1); preparedStatement.setString(2,productName); preparedStatement.execute(); //新增訂單 String addorder_sql = "INSERT INTO `order` (`buyer`, `ProductName`) VALUES (?, ?)"; preparedStatement = connection.prepareStatement(addorder_sql); preparedStatement.setString(1,buyer); preparedStatement.setString(2,productName); preparedStatement.execute(); //提交事務 connection.commit(); } } catch (SQLException e) { // ignore System.out.println("[SQLException]:" + e.toString()); //若是發生異常則回滾事務 if(connection != null) try { //發生異常回滾 connection.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } finally { if (preparedStatement != null) { try { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }
#參考文獻 mysql處理高併發,防止庫存超賣
電商 對於特定數量的商品 如何在高併發下進行庫存鎖定呢?
訂單系統中併發問題和鎖機制的探討
企業應用架構模式架構