JavaFx+Mysql实现图书管理系统(实现思路+部分代码讲解)

本文详细描述了一个基于MySQL和JavaFX可视化的图书管理系统,涉及ER图设计、页面跳转逻辑、登录验证、注册提示、数据初始化、录入限制、信息同步以及数据库操作等关键模块,展示了作者的逻辑思维提升过程。
摘要由CSDN通过智能技术生成

前言

  本人所写的图书管理系统,是基于mysql、数据库和用scenebuilder实现javafx的可视化界面所实现的,我的作品分类较多(如下图,上面方框中每个类都对应一个图形化界面),但只要搞清楚一个他们之间相似的对应操作,剩下的就是照葫芦画瓢一般的系统书写,对于提升我的逻辑思维能力是一次巨大的挑战。
  全部具体代码可参考我的这篇文章:https://blog.csdn.net/Mrs_Lupin/article/details/135732021?spm=1001.2014.3001.5501
  在这里仅做我认为重要部分的讲解,希望可以对你有所帮助!

分类展示

一、ER图

ER图

二、系统结构图

在这里插入图片描述

三、页面跳转关系

页面跳转关系

四、页面跳转实现

以登陆成功跳入管理员页面为例。

总的来说就是通过这个页面的id获取这个页面的stage,再建立另一个页面的根节点,为新页面设置宽高和标题,关闭之前的页面之后,打开新的页面

    //管理员登录按钮(此按钮绑定点击事件)
    public void ma_login_bt() throws Exception {
    //获取m_login此id所在页面
        Stage previousStage = (Stage) m_login.getScene().getWindow();
        if (ma()) {
            //账号密码验证成功
            //管理员状态下
            state=1;
            //得到um_id的内容
            String umId=um_id.getText();
            //将umId的值传给Manager对象并存储到静态变量loginManager中
			//方便我们在另一个页面使用此对象信息	
            loginManager=new Manager(umId);
            //关闭前一个窗口
            previousStage.close();
            //创建新的根节点(此页面链接写你所要跳转的那个页面)
            Parent root = FXMLLoader.load(getClass().getResource("../view/maLogin.fxml"));
            //创建新的舞台
            Stage stage = new Stage();
            //创建新的场景
            Scene scene = new Scene(root, 790.0, 522.0);
            //设置窗口不可改变大小
            stage.setResizable(false);
            //设置窗口标题
            stage.setTitle("管理员操作");
            //设置窗口视图
            stage.setScene(scene);
            //展现窗口
            stage.show();
        } else {
            //验证失败,重新加载当前窗口
            um_id.setText("");
            um_psw.setText("");
            //创建一个弹窗
            Main.tips("数据输入有误或为空,请重新输入", previousStage);
        }

    }
    //管理员登录
    public boolean ma() throws Exception {
    //输入不为空
        if(um_id.getText()!=null&&um_psw.getText()!=null){
        //管理员账号
            String maId=um_id.getText();
            //管理员密码
            int maPsw;
            try {
            //将密码转为int型
                maPsw = Integer.parseInt(um_psw.getText());
            } catch (NumberFormatException e) {
                // 输入的文本不是一个有效的整数,处理异常情况
                return false;
            }
            //返回和数据库的比较结果
            boolean maPass = managerOp.maLogin(maId,maPsw);
            return maPass;
        }else{
            System.out.println("数据输入为空或者错误,请重新输入");
            return false;
        }
    }

五、注册提示框

注册逻辑如下:当用户输入未注册过的账号登录时,将弹出注册提示框询问用户是想以此账号新注册一个账号,还是想取消返回

将registerNumber设为静态变量,用以监测整个页面中此窗口数

 //注册提示框
    public void registerTips(Stage stage){
//        窗口数+1
        registerNumber++;
//        添加文本和按钮
        Label l1 = new Label("检测到您输入的数据有误");
        Label l2 = new Label("您是否想以当前输入的数据注册账号?");
        Button b1 = new Button("确定");
        Button b2 = new Button("取消");
//        创建面板
//        将文本和按钮加入盒子中
        VBox vb = new VBox(l1,l2,b1,b2);
        Scene sc = new Scene(vb, 400, 250);
        //为文本和按钮设置位置和间隔
         b1.setTranslateX(-100);
         b1.setTranslateY(40);
         b2.setTranslateX(100);
         b2.setTranslateY(-15);
        vb.setSpacing(30);
        vb.setAlignment(Pos.CENTER);
        Stage stage1 = new Stage();
        stage1.setScene(sc);
//        设置窗口大小不可调节
        stage1.setResizable(false);
        //点击关闭按钮之后窗口数减一,即此窗口关闭
        stage1.setOnCloseRequest(windowEvent -> {
            registerNumber--;

        });
        //此按钮实现关闭此页面打开下一页面
        b1.setOnAction(actionEvent -> {
            registerNumber--;
            stage1.close();
            Parent root = null;
            try {
            //创建下一个页面的根节点
                root = FXMLLoader.load(getClass().getResource("../view/userRegister.fxml"));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            stage.close();
            //创建新的舞台
            Stage stage3 = new Stage();
            //创建新的场景
            Scene scene = new Scene(root, 468.0, 584.0);
            stage3.setResizable(false);
            stage3.setTitle("用户注册");
            stage3.setScene(scene);
            stage3.show();
        });
        //点击取消按钮后关闭窗口
        b2.setOnAction(actionEvent -> {
            registerNumber--;
            stage1.close();
        });
        //只能操作该窗口
        stage1.initOwner(stage);
        //设置窗口模态,使用户只能与此窗口交互,直到对话框被关闭。
        stage1.initModality(Modality.WINDOW_MODAL);
        //设置在窗口永远在最上层
        stage1.setAlwaysOnTop(true); 
        stage1.setTitle("提示");
        stage1.show();
    }

六、初始化加载数据

在页面显示出来的时候就将对应的数据加载出来,如下图在这里插入图片描述

初始化表格(下面的loadData()),在页面加载的时候就将查询到的数据插入
当FXML文件加载并关联控制器类时,JavaFX会自动创建控制器类的实例,
并自动调用initialize()方法。在initialize()方法内部,loadData()方法被调用,
从而实现在加载FXML文件时自动加载数据。

@FXML
//fxml的初始化方法
    public void initialize() throws Exception{
        loadData(Controller.loginUser.getId());
    }
    //初始化表格
    public void loadData(String userId) throws Exception{
    //查询语句,查询表中所有数据
        String sql = "SELECT book_id,name,author,press,price,book_status,book_class,borrow_time FROM borrow_log,book where user_id=? AND borrow_log.book_id=book.id;";
        //ResultSet接收查询结果
        ResultSet rs = jdbc.executeQuery(sql,userId);
        //声明一个集合
        ObservableList<BrwLog> brwList = FXCollections.observableArrayList();
		//循环ResultSet中的数据
        while (rs.next()) {
        //根据数据库中的属性存储数据到变量中(按列存储)
            String bookId = rs.getString("book_id");
            String bookName = rs.getString("name");
            String author = rs.getString("author");
            String press = rs.getString("press");
            double price = rs.getDouble("price");
            String bookStatus = null;
            //改变存储内容
            if(rs.getInt("book_status")==0){
                 bookStatus ="已归还";
            }else if(rs.getInt("book_status")==1){
                 bookStatus="借阅中";
            }
            String bookClass = rs.getString("book_class");
            String brwTime= rs.getString("borrow_time");
			//创建实体类的一个对象,存储变量
            BrwLog log = new BrwLog(bookId, bookName, author, press, price,bookStatus,bookClass,brwTime);
            //每个对象为一行,将此行放入集合中
            brwList.add(log);
        }
		//将实体类的属性与每一列对应设置
        brw_id.setCellValueFactory(new PropertyValueFactory<>("bookId"));
        brw_name.setCellValueFactory(new PropertyValueFactory<>("bookName"));
        brw_au.setCellValueFactory(new PropertyValueFactory<>("author"));
        brw_press.setCellValueFactory(new PropertyValueFactory<>("press"));
        brw_price.setCellValueFactory(new PropertyValueFactory<>("price"));
        brw_status.setCellValueFactory(new PropertyValueFactory<>("bookStatus"));
        brw_class.setCellValueFactory(new PropertyValueFactory<>("bookClass"));
        brw_wartime.setCellValueFactory(new PropertyValueFactory<>("borrowTime"));
		//数据添加到表格中
        book_brw_list.setItems(brwList);
    }

七、数据录入限制

与第六点相同,将数据限制写进初始化方法中,使用户在输入信息同时被限制

//初始化限制数据输入
    @FXML
    public void initialize() throws Exception{
        numLimit(um_id,6);
        // 创建一个整数输入格式的 TextFormatter
        TextFormatter<Integer> textFormatter = new TextFormatter<>(change -> {
            String newText = change.getControlNewText();
            // 仅允许输入数字且位数小于6
            if ((newText.matches("\\d*"))&&(change.getControlNewText().length() <= 6)) { 
                return change;
            } else {
                return null; // 不接受非数字字符,不接受超过最大字符限制的输入
            }
        });
        // 将 TextFormatter 应用到 TextArea
        um_psw.setTextFormatter(textFormatter);
    }
 //检查字数是否合法
    public static void numLimit(TextField textField,Integer num){
        // 创建一个字符限制的文本过滤器
        textField.setTextFormatter(new TextFormatter<String>(change -> {
            if (change.getControlNewText().length() <= num) {
                return change;
            } else {
                return null; // 不接受超过最大字符限制的输入
            }
        }));
    }

八、选中修改并实现信息同步

首先用静态变量BookUpdate.oldStage2存储图书管理界面的stage,在此中获取选中行的信息,并存入到一个静态对象BookUpdate.selectRow中,当用户点击修改按钮后,这本书的信息会显示在修改页面,并保持可修改的状态,用户修改过后,点击确认按钮,获取新修改的信息存入数据库中,修改页面关闭同时重新加载原页面

 //修改图书信息按钮
    public  Book  book_update_bt() throws IOException {
            //获取选中行
            Book selectedBook= (Book) book_list.getSelectionModel().getSelectedItem();
            //选中行不为空就打开界面
            if(selectedBook!=null){
            //获取选中行的所有信息
                String oldId=selectedBook.getId();
                String oldName=selectedBook.getName();
                String oldAuthor=selectedBook.getAuthor();
                String oldPress=selectedBook.getPress();
                BigDecimal oldPrice=selectedBook.getPrice();
                int oldNum=selectedBook.getNum();
                String oldBookClass=selectedBook.getBookClass();
			//将选中行的信息保存在静态变量selectRow中,这样在下一个页面中可以初始化加载要更改的信息
                BookUpdate.selectRow=new Book(oldId,oldName,oldAuthor,oldPress,oldPrice,oldNum,oldBookClass);
			//存储此页面的stage
                BookUpdate.oldStage2 = (Stage) book_add.getScene().getWindow();
            // 重新获取这个页面的根节点
                FXMLLoader fxmlLoader = new FXMLLoader();
                Parent root = fxmlLoader.load(BookMa.class.getResource("../view/book_update.fxml"));
                Scene scene = new Scene(root, 532.0, 489.0);
                Stage stage = new Stage();
                stage.setResizable(false);
                stage.setTitle("修改图书信息");
                stage.setScene(scene);
                stage.show();
                return null;
            }else{
            //获取当前页面的stage并弹出提示框
                Stage stage = (Stage) book_update.getScene().getWindow();
                Main.tips("修改失败,请重新操作",stage);
                return null;
            }
        }
        
 //修改状态下的确认
    public void update_con_bt(MouseEvent mouseEvent) throws IOException {
    //在各个输入框信息都不为空的情况下
        if(!update_name.getText().isEmpty()&&!update_author.getText().isEmpty()&&!update_press.getText().isEmpty()&&!update_author.getText().isEmpty()&&!update_price.getText().isEmpty()&&!update_num.getText().isEmpty()&&!update_type.getText().isEmpty()){
        //存储各个输入框的信息
            String newBookName=update_name.getText();
            String newAuthor=update_author.getText();
            String newPress=update_press.getText();
            double newPrice= Double.parseDouble(update_price.getText());
            int newBookNum= Integer.parseInt(update_num.getText());
            String newBookType=update_type.getText();
            //修改数据库中的数据
            if(managerOp.bookUpdate(newBookName,newAuthor,newPress,newPrice,newBookNum,newBookType,selectRow.getId())){
            //获取当前页面stage并关闭页面
                Stage stage = (Stage) update_con.getScene().getWindow();
                stage.close();
                // 加载book_ma.fxml文件
                Parent root = FXMLLoader.load(getClass().getResource("../view/book_ma.fxml"));
                // 设置舞台的场景
                BookUpdate.oldStage2.setScene(new Scene(root));
            }
        }else{
            Stage stage = (Stage) update_con.getScene().getWindow();
            Main.tips("录入图书信息不能为空", stage);
        }
    }
        

九、封装通用的更新和查询操作

如下操作中只需提供sql语句和与sql语句匹配的参数即可

  public static boolean exeUpdate(String sql, Object... params) {
        //获取连接对象
        Connection conn = getConn();
        PreparedStatement ps = null;
        try {
            //获取预编译对象
            ps = conn.prepareStatement(sql);
            //执行参数赋值操作
            if (Objects.nonNull(params)) {
                //循环将所有的参数赋值
                for (int i = 0; i < params.length; i++) {
                    ps.setObject(i + 1, params[i]);
                }
            }
            //执行更新
            return ps.executeUpdate() > 0;
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            close(null, ps, conn);
        }
        return false;
    }

    public static ResultSet executeQuery(String sql , Object... params) {
        Connection conn = getConn();
        PreparedStatement ps = null;
        try {
            //获取预编译对象
            ps = conn.prepareStatement(sql);
            //执行参数赋值操作
            if (Objects.nonNull(params)) {
                //循环将所有的参数赋值
                for (int i = 0; i < params.length; i++) {
                    ps.setObject(i + 1, params[i]);
                }
            }
            //执行更新
            return ps.executeQuery();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            close(null, ps, conn);
        }
        return null;
    }

十、两个bug修改

如何将一个页面的数据传输给另一个页面?

我原本的想法是将fxml中的绑定的id属性设为静态的,进而在其他类中直接调取信息,但会爆出如下的线程错误,这bug即便我将想要传信息的两个类写在一个类中也无法解决。其解决方法是将要传输的信息存为静态变量

在这里插入图片描述

如何实现在当前页面修改数据,在另一个页面同步更新?

先静态存储页面1的stage,打开页面2后存储已经修改过的数据,并存储到数据库中,在页面2关闭的同时,从根节点加载页面1的scene,并放在中页面1的stage中,而页面1的重新加载初始化是从数据库赋值,相当于“画板”不变,改变的是“画布”

  • 13
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值