如何通过多个类一次连接到 MySQL 数据库

Posted

技术标签:

【中文标题】如何通过多个类一次连接到 MySQL 数据库【英文标题】:How to connect to MySQL database once over multiple classes 【发布时间】:2021-08-18 03:02:12 【问题描述】:

我有这个程序,它由许多类组成,其中一半使用 mysql 数据库。现在,我繁琐地为每个班级连接到 MySQL。我知道这是一个坏习惯,而且我知道这是一团糟,这就是为什么我想知道是否有办法在一堂课上一劳永逸地联系起来。不过,我不确定如何将这种连接传递给类。是和使用参数类似,还是完全不同?

如果有人想要参考,这里是我的一些课程:

import ~~~

public class LoginVerify 
            
    public LoginVerify() 
        
        String url = "jdbc:mysql://localhost:3306/grades";
        String username = "root";
        String password = "root";
        
        JPasswordField jpf = new JPasswordField(10);
        JLabel jl = new JLabel("Password: ");
        jl.setFont(new Font("Times New Roman", Font.PLAIN, 15));
        
        JTextField username1 = new JTextField(10);
        JLabel label = new JLabel("Username: ");
        label.setFont(new Font("Times New Roman", Font.PLAIN, 15));
        
        final JButton okay = new JButton("Ok");
        okay.setBackground(Color.WHITE);
        okay.setFont(new Font("Times New Roman", Font.PLAIN, 15));
        
        final JButton cancel = new JButton("Cancel");
        cancel.setBackground(Color.WHITE);
        cancel.setFont(new Font("Times New Roman", Font.PLAIN, 15));
        
        Object[] message = 
                label, username1,
                jl, jpf,
        ;
        
        int value = JOptionPane.showOptionDialog(
             null,
             message,
             "LOGIN",
             JOptionPane.YES_NO_OPTION, 
             JOptionPane.QUESTION_MESSAGE, 
             null, 
             new Object[]okay, cancel, 
             okay);
        if (value == 0) 
           String user = username1.getText();
           char[] p = jpf.getPassword();
           String pswrd = String.valueOf(p);
           System.out.println("user: " + user);
           System.out.println("password: " + pswrd);

// CONNECTION STARTS HERE
               
               try     
                   Connection connection = DriverManager.getConnection(url, username, password);
                   System.out.println("connection success!!");
                
                   String sql = "SELECT * FROM user_info WHERE user_id = ? && user_password = ?";
                   PreparedStatement statement = connection.prepareStatement(sql);
                   statement.setString(1, user);
                   statement.setString(2, pswrd);
                   
               ResultSet rs = statement.executeQuery();
               while(rs.next())
                   String name = rs.getString("user_name");
                   System.out.println(name);
                   new UserMain(user, name, password, null);
               
            
               ResultSet rows = statement.executeQuery();
               if (!rows.next() ) 
                   JLabel l = new JLabel("Username or password incorrect, please try again.");
                   l.setFont(new Font("Times New Roman", Font.PLAIN, 15));
                   l.setHorizontalAlignment(SwingConstants.CENTER);
                   JOptionPane.showMessageDialog(null, l, "ERROR", JOptionPane.PLAIN_MESSAGE);
                   new LoginVerify();
                  
               
             catch (SQLException e) 
                System.out.println("& i oop");
                e.printStackTrace();
                  
         else if (value == 1) 
            JOptionPane.getRootFrame().dispose();   
         else System.exit(0);
        
        return; 
    

【问题讨论】:

...是否类似于使用参数, - 是的,这就是您将对象从一个类传递到另一个类或从一个方法传递到另一个方法的方式。你为什么使用while (rs.next)。您的查询应该只返回一个用户/密码,因此您应该使用“if 语句”。 @camickr 抱歉,如果这听起来缺乏知识,但是 - 你将如何传递连接?喜欢int addNumbers (Connection con)?这行得通吗?您将如何处理 try 和 catch 异常? 或者您可以在主类中将 Connection 设为静态变量,并在主类中添加静态方法以访问连接。 【参考方案1】:

您的要求实际上非常复杂。作为一般规则,您确实希望在其他不相关的代码位之间进行紧密绑定,因此从表面上看,“重用连接”是一个可怕的想法。

不幸的是,数据库连接非常繁重,您也不希望一秒钟内创建大量的连接。

因此,解决方案是一团糟。通常,“令人费解的混乱”应该首先让你走:真的吗?这个问题还没解决?但是,这是大多数情况下没有的情况之一。不过也有很好的解决方法。

连接池

您正在寻找的是一种称为数据库连接池库的东西。

这些库为您提供了一个 API 访问点,可让您建立连接。完成后,您不要关闭它,而是将其交还(有时通过关闭它来完成 - 但是您正在使用的库将为您提供代理连接,并且在代理上调用 close() 并没有真正关闭它,它只是将它标记为可供下一个调用者使用)。

真正复杂的原因是你可以对连接做各种有状态的事情(例如,使用mysql的SET命令,你可以改变大量持续整个连接的奇怪事情),而连接是通常会在一段时间后被数据库引擎破坏,所以仅仅给代码一个来自池的连接是不够的——你需要先“测试”它是否仍然“工作”,然后运行一大堆 SET 语句或以其他方式撤消任何损坏之前使用该连接的代码导致。

那个,而且,你需要一个限制器:如果已经有 10 个连接到同一个数据库仍在进行中,那么“获取连接”的调用应该只是阻塞并等待 10 个中的一个正在进行的连接被交回池中。

非常太多方面了,作为一个新手一天之内就无法完成。

因此,获得一个很好的、预先编写的、开源的库来完成这一切。或者,接受你只是建立一个连接,每次都让数据库引擎担心它。您当然可以获取建立新连接的代码并将其粘贴在某个静态方法中,然后调用它。现在您至少只有一个地方可以编辑 JDBC URL 等。

最常用的连接池是HikariCP。

你的代码有些瑕疵

永远不要通过“打印堆栈跟踪并继续”来处理异常。根据定义,您不知道发生了什么(显然是这种情况 - 您的 catch 块没有做任何事情来处理问题),但是您继续执行代码,就好像什么都没发生一样?这会增加代码的复杂性,意味着如果出现问题,您会得到 85 个异常滚动,除了其中一个完全不相关。

正确的“我不知道,我不在乎”捕获块是:throw new RuntimeException("Uncaught", e);。更新您的 IDE。更好的是,只需向前抛出 SQLException。例如psv main() 可以(并且应该!)用throws Exception 声明。更好的应用程序,更少的代码。双赢。

您没有应用 try-with-resources。资源(代表实际资源的对象,除了您的机器有多少 RAM,例如文件句柄、网络套接字,或者,是的,数据库连接)必须关闭。代码退出的原因有很多——你可以return,也可以抛出异常。 try-with-resources 是解决方案。永远不要创建 ResultSet、(Prepared)Statement 或 Connection,除非你这样做:
try (ResultSet rs = ....) 

try 的意思是:如果代码以任何方式退出(返回/中断/继续,只是运行到它的末尾,或异常),请先在rs 上调用close。

如果不这样做,则意味着这些连接只是挂起,很快数据库引擎就会崩溃并死掉。

您将密码明文存储在数据库中。如果你作为一家中型公司这样做,你会收到几百万美元的罚款。我假设您不是这里的公司,但您可能至少想评论一下,这永远不会超过“供个人使用/只是为了学习”阶段。使用 mysql 自己的用户管理(因此,让该密码成为连接本身的密码),或者使用特定于密码的哈希方案,例如 bcrypt。您可以在网上搜索“java bcrypt”以了解更多信息。

【讨论】:

感谢您的回答!这肯定需要我一些时间来阅读和消化所有内容,但是谢谢!至于你的最后一点 - 是的,我知道我不应该将密码存储为纯文本。然而,我才刚写了一个多月的代码(这里的学生,正在为我紧张的第一年大学课程做准备),所以我现在知道和能做的事情是有限的。我绝对打算找到一种方法来保护这些密码,但现在是一个学习阶段。 再次感谢 - 我会按照您的指示尝试修复程序中的漏洞 :)【参考方案2】:

连接很昂贵,并且有一些工具可以帮助维护一组连接并在它们之间进行池化,重新利用一组打开的连接而不是每次都创建一个新连接。

如果您不想或不能使用这些工具之一,至少您可以考虑创建一个连接并在整个过程的生命周期中使用相同的连接。

我更喜欢让像 Hibernate 这样的 ORM 来处理这个问题,但您可以使用 DataSource 并自己实现它。

查看此链接以了解后续操作: https://www.baeldung.com/java-connection-pooling

【讨论】:

以上是关于如何通过多个类一次连接到 MySQL 数据库的主要内容,如果未能解决你的问题,请参考以下文章

PyQt5:如何一次连接到两个 D-Bus 服务?

Presto 与元数据库的集成

如何使用自定义类一次重置 jquery mobile 中的多个下拉列表

使用 EAAccessory 框架的多蓝牙设备连接

通过 ODBC 使用 Python 连接到 MySQL 数据库的问题

在 Node.js 中连接到多个 MySQL 数据库