基于javaGUI的书店管理系统小项目
这是一个基于Java GUI的简单的水果管理系统,内容比较简单,主要目的是用于了解JDBC的多层开发模式
该swing项目的除了登陆界面外,还包含4个界面,分别为书籍管理界面,借书记录界面,旧书回收界面和收支帐单界面,它们与工具栏的按钮相互对应。其中除了书籍管理界面的功能( 即:图书的增删改查功能
)实现了之外,其他三个界面都只是假设,还没有任何实现。但这没有关系,书籍管理界面的实现也足够让我们初步认识JDBC的多层开发模式了。
【效果图】
如果有时间有兴趣的朋友可以进行完善补充
【项目结构】
【创建数据库环境】
创建bookMS数据库,再创建好的数据库bookMS中,创建数据库表book,并插入三条数据
/*
SQLyog Ultimate v12.09 (64 bit)
MySQL - 5.5.53
*********************************************************************
*/
/*!40101 SET NAMES utf8 */;
create table `book` (
`id` varchar (30),
`name` varchar (60),
`price` double ,
`number` int (11)
);
insert into `book` (`id`, `name`, `price`, `number`) values('101','红楼梦','82','3');
insert into `book` (`id`, `name`, `price`, `number`) values('102','西游记','63','20');
insert into `book` (`id`, `name`, `price`, `number`) values('103','水浒传','95.8','15');
【创建项目,并导入数据库驱动jar包】
创建一个普通的java项目,然后创建一个src的同级目录lib(我这里已经创建好了),将MySQL驱动jar包(mysql-connector-java-8.0.11.jar
)复制到lib文件夹中
选中mysql-connector-java-8.0.11.jar
,右键,选择Add as Library
,将jar包发布到类路径中
在src目录下创建util包,在util包中创建GUIUtil工具类,该工具类主要定义JFrame窗体的标题图标
package util;
import javax.swing.*;
import java.awt.*;
import java.io.File;
public class GUIUtil {
//给按钮设置图标和文本以及提示文字
public static void setImageIcon(JButton b, String imgFolder, String tip) {
//获取图标对象
ImageIcon icon = new ImageIcon(new File(imgFolder).getAbsolutePath());
//将图标添加到按钮中
b.setIcon(icon);
//设置图片按钮文本
b.setText(tip);
//设置图片按钮大小
b.setPreferredSize(new Dimension(51, 56));
//设置鼠标停留时显示提示信息
b.setToolTipText(tip);
//设置文本在图标下方
b.setVerticalTextPosition(JButton.BOTTOM);
//设置文本与图标居中对齐
b.setHorizontalTextPosition(JButton.CENTER);
}
}
在src目录下创建view包,在view包中创建Welcome登陆窗口类,在登陆成功后就会跳转到图书管理主界面
package view;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public abstract class Welcome extends JFrame {
ImageIcon img=new ImageIcon("img/CD4356.jpg");
private JLabel imglabel=new JLabel(img);
JPanel btnPanel=new JPanel();
private JLabel usernamelabel=new JLabel("账号:");
private JTextField username=new JTextField(8);
private JLabel userpasslabel=new JLabel("密码:");
private JTextField userpass=new JTextField(8);
private JButton btn=new JButton("登陆");
private JLabel report=new JLabel();
public Welcome(){
this.init();
this.addComponent();
this.addListener();
}
// 初始化窗口
private void init(){
this.setTitle("学而乐书店");
this.setSize(600,410);
// 设置窗口标题图标
Toolkit toolkit=Toolkit.getDefaultToolkit();
Image icon = toolkit.getImage("img/titleIcon.jpg");
this.setIconImage(icon);
// 设置窗口再显示器居中显示,还可以使用this.setLayout(null);方式设置窗口居中
Dimension screenSize =toolkit.getScreenSize();
int x=(screenSize.width-this.getWidth())/2;
int y=(screenSize.height-this.getHeight())/2;
this.setLocation(x,y);
// 设置窗口大小不可变
this.setResizable(false);
// 设置单击窗口右上角×时,默认为结束项目运行
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
// 为窗口添加组件
private void addComponent() {
imglabel.setBounds(0,0,img.getIconWidth(),img.getIconHeight());
this.add(imglabel,BorderLayout.NORTH);
btnPanel.setLayout(null);
usernamelabel.setBounds(40,17,50,28);
usernamelabel.setFont(new Font("隶书", Font.PLAIN, 20));
username.setBounds(100,17,150,28);
userpasslabel.setBounds(265,17,50,28);
userpasslabel.setFont(new Font("隶书", Font.PLAIN, 20));
userpass.setBounds(325,17,150,28);
btn.setBackground(Color.lightGray);
btn.setBounds(500,16,60,30);
btnPanel.add(usernamelabel);
btnPanel.add(username);
btnPanel.add(userpasslabel);
btnPanel.add(userpass);
btnPanel.add(btn);
report.setBounds(200,50,200,20);
report.setFont(new Font("隶书", Font.PLAIN, 20));
report.setText("账号密码默认为123!");
report.setForeground(Color.red);
btnPanel.add(report);
this.add(btnPanel);
}
// 为按钮设置监听器
private void addListener() {
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(username.getText().equals("123") && userpass.getText().equals("123")){
showBookAdmin();
// 进入JDialog管理界面后,关闭JFrame窗口
dispose();
}else{
report.setText("账号或密码错误!");
username.setText("");
userpass.setText("");
}
}
});
}
public abstract void showBookAdmin();
}
在view包中创建BookAdmin图书管理界面类
package view;
import util.GUIUtil;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public abstract class BookAdmin extends JFrame {
public JToolBar toolBar=new JToolBar();
JButton adminBtn=new JButton();
JButton lendBtn=new JButton();
JButton recycleBtn=new JButton();
JButton billBtn=new JButton();
private JLabel tableName=new JLabel("图书列表");
public JScrollPane tablePane=new JScrollPane();
public JTable table=new JTable();
private JLabel idLabel=new JLabel("图书编号");
private JLabel nameLabel=new JLabel("图书名称");
private JLabel priceLabel=new JLabel("图书单价");
private JLabel numberLabel=new JLabel("图书余量");
public JTextField addIdText=new JTextField(6);
public JTextField addNameText=new JTextField(6);
public JTextField addPriceText=new JTextField(6);
public JTextField addNumberText=new JTextField(6);
private JButton addBtn=new JButton("新增图书");
public JTextField updateIdText=new JTextField(6);
public JTextField updateNameText=new JTextField(6);
public JTextField updatePriceText=new JTextField(6);
public JTextField updateNumberText=new JTextField(6);
private JButton updateBtn=new JButton("修改图书");
public JTextField delIdText=new JTextField(6);
private JButton delBtn=new JButton("删除图书");
public BookAdmin() {
this.init();
this.addComponent();
this.addListener();
}
// 初始化窗口
private void init() {
this.setTitle("学而乐书店");
Toolkit toolkit=Toolkit.getDefaultToolkit();
Image icon = toolkit.getImage("img/titleIcon.jpg");
this.setIconImage(icon);
this.setSize(600,450);
// 设置窗口大小不可变
this.setResizable(false);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
}
// 为窗口添加组件
private void addComponent() {
//通过GUIUtil类的setImageIcon()方法来设置图片按钮,并传入按钮对象,图片名和鼠标停留的提示信息
GUIUtil.setImageIcon(adminBtn,"img/book.jpg","书籍管理");
GUIUtil.setImageIcon(lendBtn,"img/lend.jpg","借书记录");
GUIUtil.setImageIcon(recycleBtn,"img/recycle.jpg","旧书回收");
GUIUtil.setImageIcon(billBtn,"img/bill.jpg","收支账单");
//将按钮添加到工具栏
toolBar.add(adminBtn);
toolBar.add(lendBtn);
toolBar.add(recycleBtn);
toolBar.add(billBtn);
//setBounds()方法设置组件位置,使用该方法要setLayout(null)清空布局管理器
toolBar.setBounds(20,0,560,65);
this.add(toolBar, BorderLayout.NORTH);
//设置工具栏是否可以移动
toolBar.setFloatable(true);
this.setLayout(null);
tableName.setBounds(250,80,100,25);
// 设置字体样式
tableName.setFont(new Font("华文隶书", Font.PLAIN, 23));
tableName.setForeground(Color.BLACK.brighter());
this.add(tableName);
table.getTableHeader().setReorderingAllowed(false);
table.getTableHeader().setResizingAllowed(false);
table.setEnabled(false);
// 使用setBounds()设置组件位置,但使用之前必须setLayout(null)清空布局管理器
tablePane.setBounds(50,110,500,170);
tablePane.setViewportView(table);
this.add(tablePane);
idLabel.setBounds(50,290,70,25);
nameLabel.setBounds(150,290,70,25);
priceLabel.setBounds(250,290,70,25);
numberLabel.setBounds(350,290,70,25);
idLabel.setFont(new Font("隶书", Font.PLAIN, 17));
nameLabel.setFont(new Font("隶书", Font.PLAIN, 17));
priceLabel.setFont(new Font("隶书", Font.PLAIN, 17));
numberLabel.setFont(new Font("隶书", Font.PLAIN, 17));
this.add(idLabel);
this.add(nameLabel);
this.add(priceLabel);
this.add(numberLabel);
addIdText.setBounds(50,320,80,25);
addNameText.setBounds(150,320,80,25);
addPriceText.setBounds(250,320,80,25);
addNumberText.setBounds(350,320,80,25);
addBtn.setBounds(450,320,100,25);
this.add(addIdText);
this.add(addNameText);
this.add(addPriceText);
this.add(addNumberText);
addBtn.setBackground(Color.lightGray);
addBtn.setFont(new Font("隶书", Font.PLAIN, 16));
this.add(addBtn);
updateIdText.setBounds(50,350,80,25);
updateNameText.setBounds(150,350,80,25);
updatePriceText.setBounds(250,350,80,25);
updateNumberText.setBounds(350,350,80,25);
updateBtn.setBounds(450,350,100,25);
this.add(updateIdText);
this.add(updateNameText);
this.add(updatePriceText);
this.add(updateNumberText);
updateBtn.setBackground(Color.lightGray);
updateBtn.setFont(new Font("隶书", Font.PLAIN, 16));
this.add(updateBtn);
delIdText.setBounds(50,380,80,25);
delBtn.setBounds(450,380,100,25);
this.add(delIdText);
delBtn.setBackground(Color.lightGray);
delBtn.setFont(new Font("隶书", Font.PLAIN, 16));
this.add(delBtn);
}
// 为按钮添加监听器
private void addListener(){
addBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
addBook();
}
});
updateBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
updateBook();
}
});
delBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
deleteBook();
}
});
}
//查询方法
public abstract void queryAll();
//添加方法
public abstract void addBook();
//修改方法
public abstract void updateBook();
//删除方法
public abstract void deleteBook();
}
在src下创建entity包,在包中创建Book实体类
package entiry;
public class Book {
private String id;
private String name;
private Double price;
private int number;
public Book() {
}
public Book(String id, String name, Double price, int number) {
this.id = id;
this.name = name;
this.price = price;
this.number = number;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
在util包下创建DBUtil数据连接辅助类
由于在传统的JDBC中,即使用户执行一条最简单的数据查询操作,都必须执行如下过程:获取连接→创建Statement→执行数据操作→获取结果→关闭Statement→关闭结果集→关闭连接,除此之外还需进行许多异常处理的操作
而在传统的JDBC中,虽然无法做到像Spring JDBC框架那样将可复用的样板式代码都进行隔离封装,更无法hibernate、mybatis框架那样简便,但像获取连接、关闭Statement、关闭结果集、关闭连接这部分的代码还是可以独立出来的,做到部分代码的复用
package util;
import java.sql.*;
public class DBUtil {
private static final String driverClass="com.mysql.cj.jdbc.Driver";
private static final String Url="jdbc:mysql://localhost:3306/bookMS?serverTimezone=GMT%2B8";
private static final String usename="root";
private static final String usepass="root";
/**
* 创建数据库连接
* @returns
*/
public static Connection getConnection(){
Connection conn=null;
try {
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
conn= DriverManager.getConnection(Url,usename,usepass);
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
/**
* 关闭连接,释放资源
* 执行查询操作的方法中使用
* @param conn
*/
public static void close(ResultSet rs, Statement stmt,Connection conn){
try{
if(rs!=null){
rs.close();
}
if(stmt!=null){
stmt.close();
}
if(conn!=null||!conn.isClosed()){
conn.close();
}
}catch (SQLException e){
e.printStackTrace();
}
}
/**
* 关闭连接,释放资源
* 执行增删改操作的方法中使用
* @param conn
*/
public static void close(Statement stmt,Connection conn){
try{
if(stmt!=null){
stmt.close();
}
if(conn!=null||!conn.isClosed()){
conn.close();
}
}catch (SQLException e){
e.printStackTrace();
}
}
}
在src目录下创建dao包(存放持久层的代码
),在包中创建BookDao操作类
package dao;
import entiry.Book;
import util.DBUtil;
import java.sql.*;
import java.util.ArrayList;
/**
* Dao数据访问层
*
* 主要是对数据进行增删改查操作的层,也称持久层
*/
public class BookDao {
/**
* queryAll()方法
* 查询所有图书信息,返回一个集合
* @return
*/
public ArrayList<Book> queryAll(){
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
ArrayList<Book> list=new ArrayList<>();
try{
conn= DBUtil.getConnection();
stmt=conn.createStatement();
String sql="select*from book order by id";
rs=stmt.executeQuery(sql);
while(rs.next()){
Book book=new Book();
// 将获取结果集的信息封装到Book实体类中
book.setId(rs.getString("id"));
book.setName(rs.getString("name"));
book.setPrice(rs.getDouble("price"));
book.setNumber(rs.getInt("number"));
// 将实体类中的信息封装到集合中
list.add(book);
}
}catch (SQLException e){
e.printStackTrace();
}finally {
DBUtil.close(rs,stmt,conn);
}
return list;
}
/**
* addBook()方法
* 以实体类Book作为参数类型,实参封装了装备添加到数据库中的图书信息
*
* 使用PreparedStatement对象执行插入操作,防止SQL语句注入
* @return
*/
public void addBook(Book book){
Connection conn=null;
PreparedStatement prestmt=null;
try{
conn=DBUtil.getConnection();
String sql="insert into book(id,name,price,number)values(?,?,?,?)";
prestmt=conn.prepareStatement(sql);
prestmt.setString(1,book.getId());
prestmt.setString(2,book.getName());
prestmt.setDouble(3,book.getPrice());
prestmt.setInt(4,book.getNumber());
int num=prestmt.executeUpdate();
if(num>0){
System.out.println("插入数据成功!");
}
}catch (SQLException e){
e.printStackTrace();
}finally {
DBUtil.close(prestmt,conn);
}
}
/**
* deleteBook方法
* 删除记录deleteBook()方法中的形参类型为String类型的id,也可改为实体类Book作为形参类型
*
* 使用PreparedStatement对象执行删除操作,防止SQL语句注入
* @return
*/
public void deleteBook(String id){
Connection conn=null;
PreparedStatement prestmt=null;
try{
conn=DBUtil.getConnection();
String sql="delete from book where id=?";
prestmt=conn.prepareStatement(sql);
prestmt.setString(1,id);
int num=prestmt.executeUpdate();
if(num>0){
System.out.println("删除数据成功!");
}
}catch (SQLException e){
e.printStackTrace();
}finally {
DBUtil.close(prestmt,conn);
}
}
}
在src目录下创建service包(存放业务逻辑层的代码
),在包中创建BookService业务逻辑类,即服务类的代码
package service;
import dao.BookDao;
import entiry.Book;
import java.util.ArrayList;
/**
* Service业务逻辑层
*
* 主要是调用Dao数据访问层的基本数据操作来完成业务逻辑处理
*/
public class BookService {
// 创建AdminDao实例化对象
private BookDao bookDao=new BookDao();
// 实现查询逻辑处理
public ArrayList<Book> queryAll(){
ArrayList data=bookDao.queryAll();
return data;
}
// 实现添加逻辑处理
public boolean addBook(String id, String name, double price, int number){
// 遍历数据,判断要插入的水果编号是否存在
ArrayList<Book> data=queryAll();
for(int i=0;i<data.size();i++){
Book book=data.get(i);
if(id.equals(book.getId())){
return false;
}
}
Book thisbook=new Book(id,name,price,number);
bookDao.addBook(thisbook);
return true;
}
/**
* BookDao中没有定义修改操作的方法,我们这个小项目中不是通过执行修改SQL语句来实现数据修改的,
* 而是通过删除要修改的数据后,再添加新的数据的方式来实现数据修改的
* @return
*/
public boolean updateBook(String id, String name, double price, int number) {
// 遍历数据,判断要插入的水果编号是否存在
ArrayList<Book> data=queryAll();
for(int i=0;i<data.size();i++){
Book book=data.get(i);
if(id.equals(book.getId())){
// 如果该图书存在则删除
bookDao.deleteBook(id);
// 添加修改内容作为新的图书内容
Book thisbook=new Book(id,name,price,number);
bookDao.addBook(thisbook);
/**
* 这里不要使用BookService类中的deleteBook()和addBook()方法来删除添加图书,
* 因为这两个方法都要遍历一次数据,如果数据量很大的话就会消耗很多时间,
* 所以应如上面三行代码这样,直接调用BookDao类中的方法来实现图书的删除添加
*/
// deleteBook(id);
// addBook(id,name,price,number);
return true;
}
}
return false;
}
public boolean deleteBook(String id) {
// 遍历数据,判断要插入的水果编号是否存在
ArrayList<Book> data=queryAll();
for(int i=0;i<data.size();i++){
Book book=data.get(i);
if(id.equals(book.getId())){
bookDao.deleteBook(id);
return true;
}
}
return false;
}
}
在src目录下创建controller包(存放控制层/表现层的代码
),在包中创建BookController控制类的代码
package controller;
import entiry.Book;
import service.BookService;
import view.BookAdmin;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import java.util.ArrayList;
/**
* BookController表示层
*
* 对数据库的访问通过业务逻辑层的方法调用来实现
*/
public class BookController extends BookAdmin {
// 创建BookService实例化对象
private BookService bookService=new BookService();
public BookController() {
queryAll();
}
// 将查询的数据转换成二维数组的格式,作为表格中表体的内容
private String[][] list2Array(ArrayList<Book> list){
String[][] body=new String[list.size()][4];
for(int i=0;i<list.size();i++){
Book book=list.get(i);
body[i][0]=book.getId();
body[i][1]=book.getName();
// valueOf()方法返回非字符串类型数据的字符串表现形式
body[i][2]= String.valueOf(book.getPrice());
body[i][3]= String.valueOf(book.getNumber());
}
return body;
}
@Override
public void queryAll() {
String[] head={"图书编号","图书名称","图书价格","图书余量"};
// 将queryAll()方法返回的数据封装到集合中
ArrayList<Book> list=bookService.queryAll();
// 定义一个String类型的二维数组存储list2Array()方法返回的二维数组,这个二位数组就是表格的表体内容
String[][] body=list2Array(list);
TableModel tableModel=new DefaultTableModel(body,head);
table.setModel(tableModel);
}
@Override
public void addBook() {
String id=addIdText.getText();
String name=addNameText.getText();
String price=addPriceText.getText();
String number=addNumberText.getText();
boolean addSuccess=bookService.addBook(id,name,Double.parseDouble(price),Integer.parseInt(number));
if(addSuccess){
queryAll();
}else{
JOptionPane.showMessageDialog(this,"图书已存在!");
}
}
@Override
public void updateBook() {
String id=updateIdText.getText();
String name=updateNameText.getText();
String price=updatePriceText.getText();
String number=updateNumberText.getText();
boolean updateSuccess=bookService.updateBook(id,name,Double.parseDouble(price),Integer.parseInt(number));
if(updateSuccess){
queryAll();
}else{
JOptionPane.showMessageDialog(this,"图书不存在!");
}
}
@Override
public void deleteBook() {
String id=delIdText.getText();
boolean deleteSuccess=bookService.deleteBook(id);
if(deleteSuccess){
queryAll();
}else{
JOptionPane.showMessageDialog(this,"图书不存在!");
}
}
}
在src目录下创建startup包,在包中创建BooKStart类(main函数所在类
),用来启动项目
package startup;
import controller.BookController;
import view.Welcome;
public class BooKStart extends Welcome {
@Override
public void showBookAdmin() {
new BookController().setVisible(true);
}
public static void main(String[] args) {
new BooKStart().setVisible(true);
}
}
下面图片来源于runoob.com
【三层架构】
【三层与实体层之间的依赖关系】
【来自生活中的举例】
【 两层和三层的区别?】
网盘链接:https://pan.baidu.com/s/1r3SFZCldTz4HTPU0Nzh-gQ
提取码:hzwy
Github地址:https://github.com/CD4356/BookStore