在多线程系统中使用静态的java.sql.Connection实例是否安全?

64

我正在Tomcat上运行一个Web应用程序。我有一个处理所有DB查询的类。 该类包含Connection对象和返回查询结果的方法。

这是连接对象:

private static Connection conn = null;

它只有一个实例(单例)。

此外,我还有执行查询的方法,例如在数据库中搜索用户:

public static ResultSet searchUser(String user, String pass) throws SQLException

这个方法使用静态的Connection对象。我的问题是,我在静态Connection对象中的使用是否线程安全?或者当很多用户调用searchUser方法时,它会导致问题吗?

2个回答

95

我的静态连接对象在使用中是线程安全的吗?

绝对不是!

这样,连接将在所有用户发送的所有请求之间共享,因此所有查询都会互相干扰。但线程安全并不是你唯一的问题,资源泄漏也是你的另一个问题。你在整个应用程序的生命周期中保持单个连接处于打开状态。平均数据库将在连接打开时间过长时回收连接,这通常在30分钟至8小时之间,具体取决于数据库的配置。因此,如果你的Web应用程序运行时间超过这个时间,连接将丢失,你将无法再执行查询。

当这些资源作为类实例的非static实例变量被多次重用时,此问题也适用。

你应该在尽可能短的范围内获取和关闭连接、语句和结果集,最好在与执行查询相同的try-with-resources中按照以下JDBC惯例:

public User find(String username, String password) throws SQLException {
    User user = null;

    try (
        Connection connection = dataSource.getConnection();
        PreparedStatement statement = connection.prepareStatement("SELECT id, username, email FROM user WHERE username=? AND password=md5(?)");
    ) {
        statement.setString(1, username);
        statement.setString(2, password);

        try (ResultSet resultSet = statement.executeQuery()) {
            if (resultSet.next()) {
                user = new User();
                user.setId(resultSet.getLong("id"));
                user.setUsername(resultSet.getString("username"));
                user.setEmail(resultSet.getString("email"));
            }
        }
    }       

    return user;
}

请注意,此处不应返回 ResultSet。您应立即读取并将其映射到非 JDBC 类,然后返回它,以便可以安全地关闭 ResultSet

如果您还没有使用 Java 7,则应使用 try-finally 块,在其中手动关闭可关闭资源,关闭顺序与获取顺序相反。您可以在此处找到一个示例:JDBC 中应该多久关闭 Connection、Statement 和 ResultSet?

如果您担心连接性能,则应改用连接池。这已内置于许多 Java EE 应用程序服务器中,甚至裸机 servlet 容器(如 Tomcat)也支持它。只需在服务器本身中创建 JNDI 数据源,然后让您的 Web 应用程序作为 DataSource 获取它。它已经透明地是一个连接池。您可以在下面列表的第一个链接中找到一个示例。

另请参阅:


@BaluC:如果我们在网页中使用像这个链接中所示的代码呢?它使用了C3P0- http://www.javatips.net/blog/2013/12/c3p0-connection-pooling-example - PeakGen
1
@JustCause:请查看答案底部的“另请参阅”链接。请注意,您的链接没有使用静态“Connection”,而这个问题正是关于它的。 - BalusC
@BalusC:我知道,我只是想知道那段代码是否更安全适用于网络。有什么想法吗? - PeakGen
@BalusC:也许这样更好,因为它会检查连接是否存在- http://syntx.io/configuring-c3p0-connection-pooling-with-jdbc/ - PeakGen
@BalusC,您的回答与此回答相矛盾-https://dev59.com/q3I_5IYBdhLWcg3wFu_L。根据这个答案,连接对象是线程安全的,可以被多个线程同时使用。您能否帮忙解释一下这些答案之间的混淆或差异? - pjj
显示剩余2条评论

1

如果您只运行Select查询(searchUser听起来只选择数据),除了线程争用之外,不会有任何问题。

据我所知,一个Connection一次只能处理一个查询,因此使用单个实例将基本上序列化数据库访问。但这并不一定意味着在多线程环境中像这样访问数据库总是安全的。如果并发访问交错,仍然可能存在问题。


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