Connectionconn=null;PreparedStatementpstmt=null;ResultSetrs=null;try{conn=DriverManager.getConnection(url,user,password);conn.setAutoCommit(false);// 锁住这本书StringlockSql="SELECT stock FROM book WHERE book_id = ? FOR UPDATE";pstmt=conn.prepareStatement(lockSql);pstmt.setInt(1,bookId);rs=pstmt.executeQuery();if(!rs.next()){thrownewRuntimeException("图书不存在");}intstock=rs.getInt("stock");if(stock<=0){thrownewRuntimeException("库存不足");}rs.close();pstmt.close();// 插入借书记录StringborrowSql="INSERT INTO borrow(card_id, book_id, borrow_time) VALUES (?, ?, ?)";pstmt=conn.prepareStatement(borrowSql);pstmt.setInt(1,cardId);pstmt.setInt(2,bookId);pstmt.setLong(3,borrowTime);pstmt.executeUpdate();pstmt.close();// 更新库存StringupdateSql="UPDATE book SET stock = stock - 1 WHERE book_id = ?";pstmt=conn.prepareStatement(updateSql);pstmt.setInt(1,bookId);pstmt.executeUpdate();conn.commit();}catch(Exceptione){if(conn!=null)conn.rollback();e.printStackTrace();}finally{if(rs!=null)rs.close();if(pstmt!=null)pstmt.close();if(conn!=null)conn.close();}
@OverridepublicApiResultstoreBook(Bookbook){Connectionconn=connector.getConn();Stringsql="INSERT INTO book (category, title, press, publish_year, author, price, stock) VALUES (?, ?, ?, ?, ?, ?, ?)";try(PreparedStatementpstmt=conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS)){// pstmt写在try()中,能自动调用析构函数,不用自己写closepstmt.setString(1,book.getCategory());pstmt.setString(2,book.getTitle());pstmt.setString(3,book.getPress());pstmt.setInt(4,book.getPublishYear());pstmt.setString(5,book.getAuthor());pstmt.setDouble(6,book.getPrice());pstmt.setInt(7,book.getStock());intaffectedRows=pstmt.executeUpdate();if(affectedRows!=1){// 检查插入是不是只插入了一行,这个是否必要?rollback(conn);returnnewApiResult(false,"Failed to store book");}try(ResultSetgeneratedKeys=pstmt.getGeneratedKeys()){// 插入后形成book_id,写回book中作为主键if(generatedKeys.next()){book.setBookId(generatedKeys,getInt(1));}else{rollback(conn);returnnewApiResult(false,"Failed to fetch generated book id");}}commit(conn);returnnewApiResult(true,null);}catch(Exceptione){rollback(conn);returnnewApiResult(false,e.getMessage());}}
@OverridepublicApiResultincBookStock(intbookId,intdeltaStock){Connectionconn=connector.getConn();StringselectSql="SELECT stock FROM book WHERE book_id = ? FOR UPDATE";// FOR UPDATE加锁,将SELECT变为原子操作,防止并行时读写不一致导致冲突StringupdateSql="UPDATE book SET stock = stock + ? WHERE book_id = ?";try(PreparedStatementpstmtSelect=conn.prepareStatement(selectSql)){pstmtSelect.setInt(1,bookId);ResultSetrs=pstmtSelect.executeQuery();if(!rs.next()){// 用于检查是否有返回结果rollback(conn);returnnewApiResult(false,"Invalid book id");}// 不用检查返回结果是不是大于一行,这个在插入时检查intstock=rs.getInt("stock");// 如果上一条成功,已经指在第一行返回结果前,直接getIntif(stock+deltaStock<0){// 注意deltaStock可能为负数!检查更新后stock是不是为负rollback(conn);returnnewApiResult(false,"Result stock cannot be negative");}try(PreparedStatementpstmtUpdate=conn.prepareStatement(updateSql)){pstmtUpdate.setInt(1,deltaStock);pstmtUpdate.setInt(2,bookId);intaffectedRows=pstmtUpdate.executeUpdate();if(affectedRows!=1){rollback(conn);returnnewApiResult(false,"Failed to update book stock");}conn.commit();returnnewApiResult(true,null);}}catch(Exceptione){// 内层try失败,同样会跳到外层的catch,因此只需写一个catchrollback(conn);returnnewApiResult(false,e.getMessage());}}
@OverridepublicApiResultremoveBook(intbookId){Connectionconn=connector.getConn();StringremoveSql="DELETE FROM book WHERE book_id = ?";StringcheckSql="SELECT 1 FROM borrow WHERE book_id = ? AND return_time = 0";// return_time=0表示未归还try(PreparedStatementpstmtCheck=conn.prepareStatement(checkSql)){pstmtCheck.setInt(1,bookId);try(ResultSetrsCheck=pstmtCheck.executeQuery()){// ResultSet写在try中,避免手动调用closeif(rsCheck.next()){rollback(conn);returnnewApiResult(false,"Book has unreturned borrow records");}}try(PreparedStatementpstmtRemove=conn.prepareStatement(removeSql)){pstmtRemove.setInt(1,bookId);intaffectedRows=pstmtRemove.executeUpdate();if(affectedRows!=1){rollback(conn);returnnewApiResult(false,"Invalid book id");}}commit(conn);returnnewApiResult(true,null);}catch(Exceptione){rollback(conn);returnnewApiResult(false,e.getMessage());}}