【问题标题】:Close MySQL connection via Thread通过 Thread 关闭 MySQL 连接
【发布时间】:2021-05-12 03:53:43
【问题描述】:

我的代码有问题,我的程序使用线程从 MySQL DB 中填充 JTable,程序运行正常,但出现此错误

//错误,com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException:数据源拒绝建立连接,来自服务器的消息:“连接太多” 错误 java.lang.NullPointerException//

我认为我需要关闭连接或者可能不使用线程来填充 JTable,我的想法是不使用 JButton 事件来填充表格,您对此有什么建议吗?

public class VentanaAdministrador extends javax.swing.JFrame{

    public static final String URL = "jdbc:mysql://localhost:3306/floreria?autoReconnet=true&useSSL=false";
    public static final String Usuario = "root";
    public static final String Contraseña = "";
    PreparedStatement ps;
    ResultSet rs;
    
    public Connection getConnection(){
        Connection conexion = null;
        
        try{
            Class.forName("com.mysql.jdbc.Driver");
            conexion = (Connection) DriverManager.getConnection(URL,Usuario,Contraseña);
        }catch(Exception ex){
            System.err.println("Error, "+ex);
        }
        
        return conexion;
    }
    ///////////////////////////////////////////////
    Thread llenado = new Thread(){
        public void run(){
        DefaultTableModel modeloTabla = new DefaultTableModel();
        TablaVentanaAsignar.setModel(modeloTabla);
        
        PreparedStatement ps = null;
        ResultSet rs = null;
        
        
        try{
            VentanaAdministrador con = new VentanaAdministrador();
            Connection VentanaAdministrador = con.getConnection();
            
            ps = VentanaAdministrador.prepareStatement("select NumeroEmpleado,Nombre,Punto_de_venta,TiempoTrabajo from empleados where Puesto=?");
            ps.setString(1, "Diseñador");
            rs = ps.executeQuery();
            
            modeloTabla.addColumn("No. Empleado");
            modeloTabla.addColumn("Nombre");
            modeloTabla.addColumn("Sucursal");
            modeloTabla.addColumn("Tiempo trabajado");
            
            while(rs.next()){
                Object fila[] = new Object[4];
                fila[0] = rs.getObject(1);
                fila[1] = rs.getObject(2);
                fila[2] = rs.getObject(3);
                fila[3] = rs.getObject(4);
                
                modeloTabla.addRow(fila);
            }   
            }catch(Exception ex){
                    System.err.println("Error "+ex);
                    
        }
        }
    };    
    
    public VentanaAdministrador() {
        initComponents();
        llenado.start();
    }

【问题讨论】:

  • 您需要在完成连接后立即关闭连接,即在您的 catch 块之后,在 finally 块中:或者最好还是使用 try-with-resources。注意 PreparedStatementResultSet 都不应该是成员变量。 NB 2 Class.forName() 行自 2007 年以来就不再需要了。

标签: java mysql jdbc


【解决方案1】:

通常最好在数据库工作中专注于尽可能快速和直接地执行以下操作:

  • (a) 连接到数据库,
  • (b) 执行查询,
  • (c) 检索数据,以及
  • (d) 终止连接(如果您正在使用连接池,则将连接返回到池中)。

因此,如您的代码所示,无需在您的类中携带 PreparedStatementResultSet 作为成员字段。这样的对象应该被快速实例化、利用和丢弃(一般来说)。

Separate 你的数据库工作来自你的GUI 工作。与数据库交互可能需要一段时间,因此请在后台线程上运行。完成后,在您的 GUI 中使用适当的访问点,要求它使用新数据更新其小部件 - 但在其自己的 GUI 线程中更新自身。 永远不要从其他线程访问 GUI 小部件。 无论您使用的是 JavaFX/OpenJFXSwingSWTVaadin Flow 还是 Android,每个 GUI 框架都有一个用于询问GUI 在 GUI 自己的线程上更新其小部件。

在现代 Java 中,我们很少需要直接寻址 Thread 类。对于并发工作,请使用 Java 5 添加的 Executors 框架。

如果您使用DataSource 接口包含连接信息而不是代码中的字符串,您将在开发、测试和部署方面获得最大的灵活性。通常JDBC driver 提供DataSource 的基本实现。例如,Connector/J driver providescom.mysql.cj.jdbc.MysqlDataSource 类。

private DataSource prepareDataSource() {
    MysqlDataSource ds = new com.mysql.cj.jdbc.MysqlDataSource();
    ds.setServerName( "1.2.3.4" );
    ds.setPortNumber( 5432 );
    ds.setUser( "scott" );
    ds.setPassword( "tiger") ;
    ds.setDatabaseName( "invoicing" );
    … Set all your relevant properties …
    return ds ;
}

将此DataSource 保存在您的应用中方便的位置,以供任何需要数据库连接的代码使用。

还要随身携带一个ExecutorService 的实现,以用于您的后台线程工作。使用Executors 实用程序类可以方便地获取实例。请务必在您的应用退出之前最终关闭执行器服务,否则它的后台线程池可能会像僵尸一样无限期地继续运行。

ExecutorService executorService = Executors.newFixedThreadPool( 3 ) ;

或者,将来Project Loom技术到来时:

ExecutorService executorService = Executors.newVirtualThreadExecutor() ;

在进行 JDBC 工作时使用 try-with-resources 语法。它以有用的方式简化您的代码,自动关闭您的数据库资源。

将您的数据库工作定义为实现Runnable(或Callable)接口的任务对象。向您的执行器服务提交一个实例。

/// Code in your GUI detects need to load data.
/// … Fetch the `ExecutorService` from wherever you stored it.
executorService.submit( new DatabaseWorkRunnable() ) ;

Runnablerun 方法可能类似于下面所示的不完整且未经测试的代码。

请注意,这段代码有一对嵌套的 try-with-resources 语句,涵盖三个 AutoCloseable 资源对象:ConnectionPreparedStatementResultSet。如果实例化,无论您的代码是否成功完成,每个资源对象都将关闭。资源按照声明它们的相反顺序关闭。

DataSource 对象不是资源并且永远不会“打开”。所以它永远不会关闭。 DataSource 只是保存打开数据库连接的信息。

String sql = "SELECT … ;";
try (
        Connection conn = dataSource.getConnection() ;
        PreparedStatement ps = conn.prepareStatement( sql ) ;
)
{
    … Make `PreparedStatement::set…` calls here.
    try (
            ResultSet rs = ps.executeQuery() ;
    )
    {
        if ( rs.next() )
        {
            LocalDate ld = rs.getObject( 1 , LocalDate.class );
            … Retrieve your data , add to collection to be delivered to GUI later.
        }
    }
}
catch ( SQLException e )
{
    e.printStackTrace();
}
// At this point, all three `Connection`, `PreparedStatement`, and `ResultSet` objects are closed automatically whether your code ran successfully or whether an exception/error was thrown.

… Use your GUI framework’s hook to ask the GUI to update its table widget with the fresh data in collection. 

所有这些都已经在 Stack Overflow 上介绍过很多次了。搜索以了解更多信息并查看许多示例。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-04
    • 1970-01-01
    • 2014-12-18
    • 2012-11-03
    相关资源
    最近更新 更多