關於Java小專案——圖書管理系統的總結

關於Java小專案——圖書管理系統的總結

====================

網上看完視訊寫完這個小專案後,總結一下自己遇到的問題。。。

一、開發環境

eclipse   widowbuilder

二、涉及到的技術

  • Java MVC模式
  • Swing
  • MySQL
  • JDBC

Java MVC模式

M——Model(模型)。用於處理應用程式資料邏輯的部分。建立相關的類來連線資料庫儲存資料,例如使用者類,圖書類,圖書類別類,把能夠抽象化成一個一個的類寫在這一部分
V——View(檢視)。所有使用者看到的介面,寫在這一部分。這次用windowbuilder的這一外掛來寫檢視
C——Contraller(控制器)。這部分主要是用來連線Model和View這兩部分的,關於使用者互動的操作的方法函式寫在這一部分

首先,建立了幾個包用來寫不同模組的程式碼!這裡寫圖片描述
com.luo.Model一個包用來寫Model這一模組
com.luo.View一個包用來寫View這一模組
com.luo.Dao一個包用來寫Controller這一模組
另外,還建立com.luo.Util一個包用來封裝相關的工具類,例如我們會重複需要連線資料庫,這時候就先封裝好一個連線資料庫的那一部分的 工具類。後面連線起來就方便多了。
com.luo.Image這個包是用來存放專案中用到的圖示和圖片
http://www.easyicon.net/ —> 用來找圖示的網站

MySQL部分

關聯了bookTypeId和Id
也就是說BookType資料庫的id 代表著 Book中類別的Id,例如型別1,型別7,其中1和7就是BookType中的id

這裡寫圖片描述

在com.luo.Util中封裝了連線資料庫的一個DbUtil類
首先需要4個成員變數:

  • 資料庫的名字,”jdbc:mysql://localhost:3306/(這裡寫資料庫的名字)”
  • MySQL的使用者名稱
  • MySQL的密碼
  • JDBC驅動的名字 “com.mysql.jdbc.Driver”

接下來是連線資料庫的方法函式

  • 通過Class呼叫forName()
  • 然後通過DriverManager呼叫getConnection()
  • 最後返回一個Connection型別值

還有關閉資料庫的方法函式

  • 通過傳入的Connection型別的值呼叫Close()
/**
* 連線資料庫工具
* @author Administrator
*
*/
public class DbUtil {
private String dbUrl = "jdbc:mysql://localhost:3306/db_book";
private String UserName = "root";
private String Password = "123456";
private String jdbcName = "com.mysql.jdbc.Driver";
/**
* 連線資料庫
* @return
* @throws Exception
*/
public Connection getCon() throws Exception {
Class.forName(jdbcName);
Connection conn = DriverManager.getConnection(dbUrl, UserName, Password);
return conn;
}
/**
* 關閉資料庫
* @param conn
* @throws Exception
*/
public void Closecon(Connection conn) throws Exception {
if( conn != null){
conn.close();
}
}

Model模組

這次的小專案分別建立了User類Book類BookType類。其中,後面需要用到這些類的構造方法傳入引數,所以注意,要構造一個不帶引數的建構函式,這樣後面就可以new一個空的類了。

以下是其中一個使用者類的程式碼,

public class User {
private int id;
private String userName;
private String password;
public User() {
super();
} //不帶引數的建構函式
public User(String userName, String password) {
super();
this.userName = userName;
this.password = password;
} //帶兩個引數的建構函式
/********************************** 
分別建立成員變數get()和set()方法
***********************************/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

還有一點,BooType類中重寫了toString()的方法。當把BookType類的物件當作引數傳進去,顯示出來的並不是它裡面的資料,而是它的地址。所以重寫toString()方法來顯示BookType類的物件裡面的資料
後面只需用到BookType類中的一個成員變數BookTypeName

public String toString() {
return BookTypeName; //直接返回BookTypeName
}

Controller模組和JDBC

以下是其中一個Book類的資料互動的程式碼,也就是連線使用者輸入的資料和資料庫裡面的資料的操作

  • MySQL的語句,增刪查改,String型別
  • 呼叫prepareStatement()來預處理,
  • 呼叫setString(),設定MySQL語句中?的內容
  • executUpdate(),返回int型別; 若execuQuer(); 返回ResultSet型別
package com.luo.Dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import com.luo.Model.Book;
import com.luo.Model.BookType;
import com.luo.Util.StringUtil;
public class BookDao {
/**
* 圖書新增
* @param con
* @param book
* @return
* @throws Exception
*/
public int Add(Connection con, Book book) throws Exception {
String sql = "insert into t_book values(null,?,?,?,?,?,?)";
PreparedStatement pstmt = con.prepareStatement(sql);
pstmt.setString(1, book.getBookName());
pstmt.setString(2, book.getAuthor());
pstmt.setString(3, book.getSex());
pstmt.setFloat(4, book.getPrice());
pstmt.setInt(5, book.getBookTypeId());
pstmt.setString(6, book.getBookDesc());
return pstmt.executeUpdate();
}
/**
* 查詢
* @param con
* @param book
* @return
* @throws Exception
*/
public ResultSet list(Connection con, Book book) throws Exception{
StringBuffer sb = new StringBuffer("select * from t_book b,t_booktype bt where b.bookTypeId = bt.id");
if(StringUtil.isNotEmpty(book.getBookName())){
sb.append(" and b.bookName like '%"  book.getBookName()  "%'");
}
if(StringUtil.isNotEmpty(book.getAuthor())){
sb.append(" and b.author like '%"  book.getAuthor()  "%'");
}
if(book.getBookTypeId()!=null && book.getBookTypeId()!=-1){
sb.append(" and b.bookTypeId="  book.getBookTypeId());
}
PreparedStatement pstmt = con.prepareStatement(sb.toString());
return pstmt.executeQuery();
}
/**
* 更新資料
* @param con
* @param book
* @return
* @throws Exception
*/
public int upDate(Connection con, Book book) throws Exception {
String sql = "UpDate t_book set bookName=?, author=?, sex=?, price=?, bookTypeId=?, bookDesc=? where id=?";
PreparedStatement pstmt = con.prepareStatement(sql);
pstmt.setString(1, book.getBookName());
pstmt.setString(2, book.getAuthor());
pstmt.setString(3, book.getSex());
pstmt.setFloat(4, book.getPrice());
pstmt.setInt(5, book.getBookTypeId());
pstmt.setString(6, book.getBookDesc());
pstmt.setInt(7, book.getId());
return pstmt.executeUpdate();
}
/**
* 刪除資料
* @param con
* @param id
* @return
* @throws Exception
*/
public int Delete(Connection con, int id) throws Exception {
String sql = "Delete from t_book where id=?";
PreparedStatement pstmt = con.prepareStatement(sql);
pstmt.setInt(1, id);
return pstmt.executeUpdate();
}
/**
* 判斷是否存在書
* @param con
* @param book
* @return
* @throws Exception
*/
public boolean existBook(Connection con, String bookTypeId) throws Exception {
String sql = "select * from t_book where bookTypeId =?";
PreparedStatement pstmt = con.prepareStatement(sql);
pstmt.setInt(1, Integer.parseInt(bookTypeId));
ResultSet rs = pstmt.executeQuery();
return rs.next();
}
}

View模組

  • 登入介面
  • 主介面
    • 圖書類別新增介面
    • 圖書類別管理介面
    • 圖書新增介面
    • 圖書維護介面

通過windowbuilder外掛來建立一個Frame
登入介面通過登入按鈕跳到主介面
以下是登入按鈕響應事件的操作

/**
* 登入操作
* @param e
*/
private void loginActionPerformed(ActionEvent e) {      
String userName = this.userNameTxt.getText();
String password = new String(this.passwordTxt.getPassword());
//判斷輸入是否為空
if(StringUtil.isEmpty(userName)) {
JOptionPane.showMessageDialog(null, "使用者名稱不能為空");
return;
}
if(StringUtil.isEmpty(password)){
JOptionPane.showMessageDialog(null, "密碼不能為空");
return;
}
Connection conn = null;
try {
//連線資料庫
conn = dbUtil.getCon();
User user = new User(userName, password);
User correntUser = userdao.login(conn, user);
if(correntUser != null){
/**************************************  
這裡先呼叫dispose()的方法,把當前的介面關掉
再通過new出新的主介面,跳轉到主介面上去
***************************************/
dispose();
new MainFrame().setVisible(true);   
} else {
JOptionPane.showMessageDialog(null, "使用者名稱或密碼錯誤!");
}
} catch (Exception e1) {
e1.printStackTrace();
} finally {
try {
dbUtil.Closecon(conn);
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}

登入介面和主介面的類都是繼承JFrame

從工具欄選擇一個JDesktopPane放置到contenPane裡面,將它重新命名為table
點選menuBar的一個選項,讓它彈出一個介面(通過windowbuilder外掛建立一個JInternalFrame,該選項新增響應事件,通過table呼叫add()的方法把這個介面顯示出來),這個介面的類繼承JInternalFrame,只能出現在table裡面

這裡的BookTypeManagerFrame是其中一個繼承JInternalFrame的類顯示出的框會在主介面裡面

//  監聽選單欄上的圖書類別管理這一選項的響應
mntmNewMenuItem_2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
BookTypeManagerFrame bookTypeManagerFrame = new BookTypeManagerFrame();//new出一個新的圖書類別管理類
bookTypeManagerFrame.setVisible(true); //設定可見
table.add(bookTypeManagerFrame); //新增進來主介面上
}
});

這是主介面的結構圖,menuBar和一個contenPane

這裡寫圖片描述

這裡寫圖片描述這裡寫圖片描述

我們可以拖入JTextArea來放置圖這樣一大段文字描述圖書的內容
但是呢,一開始拖入JTextArea進來檢視顯示是沒有邊框
所以呢,要在該介面對應的程式碼中加入

bookTypeDesc.setBorder(new LineBorder(new java.awt.Color(12, 157, 185), 1, false));

點選按鈕監聽事件操作。比如點選新增鍵,就從資料庫中獲取資料顯示在介面上,其實就是對資料庫的操作和資料放在介面上顯示的處理

  • getText()獲取使用者輸入的內容
  • 工具類DbUtil,連線資料庫
  • 呼叫Controller模組裡面的類的方法,實現相關操作
  • 連線完資料庫記得關閉!
  • 中間加上一些業務邏輯判斷

這是專案中,獲取資料在JTextField控制元件顯示的小例子
至於重置編輯框為空的操作就是簡單地重寫setText(“”)一下;

//通過對按鈕的監聽,具體實現程式碼建立一個AddActionPerformed()方法寫在外面,這裡執行AddActionPerformed()方法
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
AddActionPerformed(e);
}
});
/**
* 新增資料
* @param evt
*/
private void AddActionPerformed(ActionEvent evt) {
String Name = bookTypeName.getText();
String Desc = bookTypeDesc.getText();
if(StringUtil.isEmpty(Name)){
JOptionPane.showMessageDialog(null, "圖書類別名稱不能為空");
return;
} 
BookType bookType = new BookType(Name, Desc);
Connection con = null;
try {
con = dbUtil.getCon();
int result = bookTypeDao.addBookType(con, bookType);
if(result == 1){
JOptionPane.showMessageDialog(null, "新增成功!");
reset();
} else {
JOptionPane.showMessageDialog(null, "新增失敗!");
}
} catch (Exception e1) {
// TODO Auto-generated catch block
JOptionPane.showMessageDialog(null, "新增失敗!");
e1.printStackTrace();
} finally{
try {
dbUtil.Closecon(con);
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}

用一個表格來顯示資料。拖入scrollPane,然後在它裡面拖入Jtable,通過Jtable的model屬性設定表的標題與行列數。接著可以通過表格Jtable的mousePressed,來響應滑鼠操作,從而對錶中點選到的那一行的資料操作,顯示在JTextField上

/**
* 填充列表
* @param book
*/
private void fillTable(Book book){
DefaultTableModel dtm = (DefaultTableModel) bookTable.getModel();
dtm.setRowCount(0);
Connection con = null;
try {
con = dbUtil.getCon();
ResultSet rs = bookDao.list(con, book);
while(rs.next()){
Vector v = new Vector();
v.add(rs.getString("id"));
v.add(rs.getString("bookName"));
v.add(rs.getString("author"));
v.add(rs.getString("sex"));
v.add(rs.getString("price"));
v.add(rs.getString("bookDesc"));
v.add(rs.getString("bookTypeName"));
dtm.addRow(v);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.fillTable(new Book()); //呼叫此填充表格的方法函式

點選滑鼠響應事件

bookTypeTable.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
bookTypeTableMousePressed(e);
}
});
/**
* 點選圖表顯示事件
* @param evt
*/
private void bookTypeTableMousePressed(MouseEvent evt) {
int row = bookTypeTable.getSelectedRow(); //獲取當前的行號
//介面上對應控制元件通過呼叫setText()來顯示出那一行的資料
idTxt.setText((String) bookTypeTable.getValueAt(row, 0));
bookTypeNameTxt.setText((String) bookTypeTable.getValueAt(row, 1));
bookTypeDescTxt.setText((String) bookTypeTable.getValueAt(row, 2)); 
}

關於下面這兩種選擇的資料填充:

這裡寫圖片描述

這裡寫圖片描述

這兩個控制元件分別重新命名為s_bookTypeBox和m_bookTypeBox,通過這兩個名字來呼叫addItem()函式填充資料
按照物件導向的思想,直接傳入的引數是BookType類的物件,所以要重寫該類的toString()方法(前面提到的)。

/**
* 填充bookType
* @param type
*/
private void fillBookType(String type){
Connection con = null;
BookType bookType = null;
try {
con = dbUtil.getCon();
ResultSet rs = bookTypeDao.list(con, new BookType());
if("search".equals(type)){
bookType = new BookType();
bookType.setBookTypeName("請選擇...");
bookType.setId(-1);
this.s_bookTypeBox.addItem(bookType);
}
while(rs.next()){
bookType = new BookType();
bookType.setBookTypeName(rs.getString("bookTypeName"));
bookType.setId(rs.getInt("id"));
if("search".equals(type)){
this.s_bookTypeBox.addItem(bookType);
} else if("modify".equals(type)){
this.m_bookTypeBox.addItem(bookType);
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.fillBookType("modify");
this.fillBookType("search");    

補充一下上面用到的list()
模糊查詢

/**
* 查詢
* @param con
* @param bookType
* @return
* @throws Exception
*/
public ResultSet list(Connection con, BookType bookType) throws Exception{
StringBuffer sb = new StringBuffer("select * from t_booktype ");
if(StringUtil.isNotEmpty(bookType.getBookTypeName())){
sb.append("and bookTypeName like '%"  bookType.getBookTypeName()  "%'");
}
PreparedStatement pstmt = con.prepareStatement(sb.toString().replaceFirst("and", "where"));
return pstmt.executeQuery();
}