如何正确关闭数据源连接?

8

我有这个类,但我不确定如何正确关闭连接,因为我仍然会遇到错误,即使只有3个用户登录,但有多个SQL查询。

> com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException:
> Data source rejected establishment of connection,  message from
> server: "Too many connections"
import java.io.File;
import java.io.IOException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.sql.DataSource;

public class UserDaoImpl implements UserDao
{

    DataSource dataSource;

    public DataSource getDataSource()
    {
            return this.dataSource;
    }

    public void setDataSource(DataSource dataSource)
    {
            this.dataSource = dataSource;
    }


    public boolean isValidUser(String username, String password) throws SQLException
    {       
        PreparedStatement pstmt = null;
        ResultSet resultSet = null;
        boolean rt = false;
        try{
            PasswordEncryptor pws = new PasswordEncryptor();
            String encryptedPass = pws.encrypt(password);

            String query = "Select count(1) from userdetails where username = ? and password = ?";
            pstmt = dataSource.getConnection().prepareStatement(query);
            pstmt.setString(1, username);
            pstmt.setString(2, encryptedPass);
            resultSet = pstmt.executeQuery();
            if (resultSet.next()){
                    rt =  (resultSet.getInt(1) > 0);
            }
            else{
                rt = false;
            }
    }
    catch(Exception e){
        e.printStackTrace();

    }
    finally{
        resultSet.close();
        pstmt.close();
        dataSource.getConnection().close();
    }

        return rt;  
    }
}

SpringConfiguration.xml

    <bean name="userDao" class="com.spring.acadconnect.services.UserDaoImpl">
   <property name="dataSource" ref="dataSource"></property>
   </bean>

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">

    <property name="driverClassName" value="com.mysql.jdbc.Driver" />

    <property name="url" value="jdbc:mysql://localhost:3306/acadconnect" />

    <property name="username" value="root" />

    <property name="password" value="" />

</bean>
3个回答

5
请注意,您正在多次调用.getConnection()。虽然文档在这方面可能不够清晰DataSource.getConnection()实际上会打开一个新的连接(而不是返回一个现有的连接),因此您需要关闭从该方法返回的每个实例。
由于.getConnection()每次调用时都创建一个新实例,因此以下行是连接泄漏,因为它没有关闭返回的连接:
pstmt = dataSource.getConnection().prepareStatement(query);

而这行代码浪费地打开了一个新的连接,然后立即关闭它:

dataSource.getConnection().close();

看起来你正在尝试为每次调用 isValidUser() 打开和关闭一个单独的连接(因为你在该方法调用结束时关闭了连接)。即使你修复了上述泄漏,这也不是连接应该使用的方式。相反,你应该在整个应用程序中共享一个连接(或一小部分连接)。因此,当程序启动时,应打开此类连接,而在整个程序(通常是在终止前不久)不再需要连接时关闭它。
这种行为通常通过依赖注入实现,其中你构建连接和其他资源,然后将它们传递给需要它们的任何对象-这将资源管理与使用这些资源的代码解耦。以下是一个简单的示例:
public static void main(String[] args) {
  DataSource dataSource = createDataSource();
  try (Connection connection = dataSource.getConnection()) {
    runProgram(connection);
  }
}


/**
 * this method doesn't need to worry about closing the Connection,
 * it trusts that its caller will be responsible for that.
 */
private static void runProgram(Connection connection) {
  // ...
}

作为一项基本原则,对象仅应负责关闭它们构造的对象,并避免关闭传递给它们的对象。在您当前的代码中,UserDaoImpl正在打开连接,因此应该负责关闭它,但我建议改为传入Connection

谢谢,我会尝试你的建议。 - Dark Falcon

4

dataSource.getConnection() 将始终返回新的连接,因此您没有关闭您认为的连接。 您必须使用 DataSourceUtils.getConnection() 来获取当前线程的活动连接,否则存储返回的引用,例如 conn = dataSource.getConnection() 并调用 conn.close()


谢谢,我会尝试你的建议。 - Dark Falcon
请参考这个问题,其中更详细地描述了DataSourceDataSourceUtils的区别。请注意,如果您使用DataSourceUtils,那么UserDaoImpl不应该调用Connection.close()(但是应用程序的某些部分仍然需要管理关闭连接)。 - dimo414

0

试试这个。

conn = dataSource.getConnection();
pstmt = conn.prepareStatement(query);

当你不再需要与数据库连接时,只需使用关闭命令即可。

conn.close();

更新:

Connection conn = dataSource.getConnection();
PreparedStatement ps = pstmt = conn.prepareStatement(query);

try {
    // ur code
}
catch (SQLException ex) 
{
    // Exception handling
} 
finally 
{
    if (rs != null) 
{
    if (ps != null) {
        try {
            ps.close();
        } catch (SQLException e) { out.print(e)} // This will print exception to help better
    }
    if (conn != null) {
        try {
            conn.close();
        } catch (SQLException e) { out.print(e)}
    }
}

我现在的问题是,当使用war文件将项目部署到tomcat服务器时,连接没有被正确关闭,但当我在IDE中运行项目时,连接会正确关闭。我正在检查mysql中的“show processlist”的信息。 - Dark Falcon
@DarkFalcon 如果你还没有使用 try catch 语句,请尝试一下我的更新答案。 - Ashish Sharma

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接