6

我正在尝试解决使用 JDBC 连接到 MySQL 数据库的 Java 应用程序的问题。表面问题是,当连接到有效数据库时,DriverManager.getConnection 有时会返回 NULL,而几分钟后它会返回与完全相同数据库的有效连接。

我正试图解决这个问题,但我对 Java、JDBC 和 MySQL 在何处相遇的理解相当有限。我一直在对此进行大量研究,但是碰壁了,不知道从哪里开始。

这是我到目前为止所做的:

  • 在 Java 端,我一直跟踪代码到 DriverManager.getConnection()。我已经确定 NULL 连接来自那里,但我不知道 getConnection 背后发生了什么。我一直在努力在网上找到对此的彻底解释。
  • 在 MySQL 端,我已经验证有很多可用的连接(有时大约 1000 个免费连接),所以我知道我没有超过那里的最大连接数。查看日志,我能够确定在我遇到最多问题的时间范围内,有稍多的连接被中止,但我不知道如何确定这些连接被中止的原因(MySQL 是否中止、JDBC、Java 应用程序?)我不确定在 MySQL 端是否还有其他需要寻找的东西。
  • 在中间,使用 JDBC,我很迷茫。我一直在http://dev.mysql.com/doc/refman/5.1/en/connector-j.html阅读 MySQL Connector/J ,但不确定该信息是否与正在使用的 JDBC 驱动程序有关爪哇。

任何关于我可以从这里去哪里的方向都将不胜感激。

谢谢!

编辑 - 中部标准时间 2 月 15 日上午 10:35 我很 抱歉没有更具体。此应用程序是一个生产应用程序,通常可以正常工作。它每天成功处理数万个连接,没有任何问题,只是这个问题会在一天中的随机时间出现,并且在发生时会持续 30 秒到 5 分钟。

这是我一直追踪到 DriverManager.getConnection 的代码:

var dbConn = DatabaseConnectionFactory.createDatabaseConnection('com.mysql.jdbc.Driver','jdbc:mysql://'+ serverName +':' + port + '/' + database, userName, password);

public static DatabaseConnection createDatabaseConnection(String driver, String address, String username, String password) throws SQLException {
        try {
            Class.forName(driver);
        } catch (Exception e) {
            e.printStackTrace();
        }

        Properties info = new Properties();
        info.setProperty("user", username);
        info.setProperty("password", password);

        // this property should only be set if it's for embedded database
        info.setProperty("shutdown", "true");

        return new DatabaseConnection(address, info);
    }

public DatabaseConnection(String address, Properties info) throws SQLException {
        logger.debug("creating new database connection: address=" + address + ", " + info);
        this.address = address;
        connection = DriverManager.getConnection(address, info);
    }

我不相信代码实际上有任何问题,而是 getConnection() 和 MySQL 之间的某个问题。

4

2 回答 2

8

单个驱动程序可以为连接请求返回 null,JDBC 4.1 规范是这样说的:

当 DriverManager 尝试建立连接时,它会调用该驱动程序的 connect 方法并将 URL 传递给驱动程序。如果驱动程序实现理解 URL,它将返回一个 Connection 对象,或者如果无法与数据库建立连接,则抛出一个 SQLException。如果 Driver 实现不理解 URL,它将返回 null。

但是,查看java.sql.DriverManager(在 Java 7 Update 13 中)的代码,当所有可用的驱动程序都已返回以进行调用时,它总是会抛出一个SQLException带有消息没有为 <url> 找到合适的驱动程序:nullconnect(url, properties)

//  Worker method called by the public getConnection() methods.
private static Connection getConnection(
    String url, java.util.Properties info, ClassLoader callerCL) throws SQLException {
// Removed some classloading stuff for brevity
    if(url == null) {
        throw new SQLException("The url cannot be null", "08001");
    }
    // Walk through the loaded registeredDrivers attempting to make a connection.
    // Remember the first exception that gets raised so we can reraise it.
    SQLException reason = null;
    for(DriverInfo aDriver : registeredDrivers) {
        // If the caller does not have permission to load the driver then
        // skip it.
        if(isDriverAllowed(aDriver.driver, callerCL)) {
            try {
                println("    trying " + aDriver.driver.getClass().getName());
                Connection con = aDriver.driver.connect(url, info);
                if (con != null) {
                    // Success!
                    println("getConnection returning " + aDriver.driver.getClass().getName());
                    return (con);
                }
            } catch (SQLException ex) {
                if (reason == null) {
                    reason = ex;
                }
            }
        } else {
            println("    skipping: " + aDriver.getClass().getName());
        }
    }
    // if we got here nobody could connect.
    if (reason != null)    {
        println("getConnection failed: " + reason);
        throw reason;
    }
    println("getConnection: no suitable driver found for "+ url);
    throw new SQLException("No suitable driver found for "+ url, "08001");
}

换句话说:您所描述的事情不会发生(至少在 Java 7 Update 13 中不会发生)。快速浏览一下 Java 5 Update 22 源代码会发现几乎相同的实现,但它根本无法返回 null。

您更有可能吞下异常,然后尝试使用Connection值为 null 的变量或字段。

另一种可能性是您没有获得与 的连接DriverManager.getConnection(url, ...),但由于上面建立的规则,DriverManager.getDriver(url).connect(...)它可以返回。null如果您这样做,那么如果您始终使用完全相同的 URL,它可能指向 Connector/J 驱动程序中的错误:驱动程序无法决定在某一时刻返回特定 URL 的连接,然后返回 null。它应该始终为相同的 URL 返回一个Connection或抛出一个SQLException

于 2013-02-15T16:52:51.743 回答
2

是的,DriverManager是为您获取连接的类。

它使用通过 MySQL Connector-J JAR 获得的 JDBC 驱动程序类来管理它。CLASSPATH当你开始的时候,那个 JAR 必须在你的里面。

首先确保您可以从运行 Java 应用程序的机器连接到 MySQL。成功登录 MySQL 管理应用程序,您已经克服了第一个障碍。

我会根据你的情况给你上一堂医生课。这些方法通常对您有用。根据您的情况修改连接、凭据和查询并试一试。我知道这段代码有效。

package persistence;

import java.sql.*;
import java.util.*;

/**
 * util.DatabaseUtils
 * User: Michael
 * Date: Aug 17, 2010
 * Time: 7:58:02 PM
 */
public class DatabaseUtils {
/*
    private static final String DEFAULT_DRIVER = "oracle.jdbc.driver.OracleDriver";
    private static final String DEFAULT_URL = "jdbc:oracle:thin:@host:1521:database";
    private static final String DEFAULT_USERNAME = "username";
    private static final String DEFAULT_PASSWORD = "password";
*/
/*
    private static final String DEFAULT_DRIVER = "org.postgresql.Driver";
    private static final String DEFAULT_URL = "jdbc:postgresql://localhost:5432/party";
    private static final String DEFAULT_USERNAME = "pgsuper";
    private static final String DEFAULT_PASSWORD = "pgsuper";
*/
    private static final String DEFAULT_DRIVER = "com.mysql.jdbc.Driver";
    private static final String DEFAULT_URL = "jdbc:mysql://localhost:3306/party";
    private static final String DEFAULT_USERNAME = "party";
    private static final String DEFAULT_PASSWORD = "party";

    public static void main(String[] args) {
        long begTime = System.currentTimeMillis();

        String driver = ((args.length > 0) ? args[0] : DEFAULT_DRIVER);
        String url = ((args.length > 1) ? args[1] : DEFAULT_URL);
        String username = ((args.length > 2) ? args[2] : DEFAULT_USERNAME);
        String password = ((args.length > 3) ? args[3] : DEFAULT_PASSWORD);

        Connection connection = null;

        try {
            connection = createConnection(driver, url, username, password);
            DatabaseMetaData meta = connection.getMetaData();
            System.out.println(meta.getDatabaseProductName());
            System.out.println(meta.getDatabaseProductVersion());

            String sqlQuery = "SELECT PERSON_ID, FIRST_NAME, LAST_NAME FROM PERSON ORDER BY LAST_NAME";
            System.out.println("before insert: " + query(connection, sqlQuery, Collections.EMPTY_LIST));

            connection.setAutoCommit(false);
            String sqlUpdate = "INSERT INTO PERSON(FIRST_NAME, LAST_NAME) VALUES(?,?)";
            List parameters = Arrays.asList("Foo", "Bar");
            int numRowsUpdated = update(connection, sqlUpdate, parameters);
            connection.commit();

            System.out.println("# rows inserted: " + numRowsUpdated);
            System.out.println("after insert: " + query(connection, sqlQuery, Collections.EMPTY_LIST));
        } catch (Exception e) {
            rollback(connection);
            e.printStackTrace();
        } finally {
            close(connection);
            long endTime = System.currentTimeMillis();
            System.out.println("wall time: " + (endTime - begTime) + " ms");
        }
    }

    public static Connection createConnection(String driver, String url, String username, String password) throws ClassNotFoundException, SQLException {
        Class.forName(driver);
        if ((username == null) || (password == null) || (username.trim().length() == 0) || (password.trim().length() == 0)) {
            return DriverManager.getConnection(url);
        } else {
            return DriverManager.getConnection(url, username, password);
        }
    }

    public static void close(Connection connection) {
        try {
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


    public static void close(Statement st) {
        try {
            if (st != null) {
                st.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void close(ResultSet rs) {
        try {
            if (rs != null) {
                rs.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void rollback(Connection connection) {
        try {
            if (connection != null) {
                connection.rollback();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static List<Map<String, Object>> map(ResultSet rs) throws SQLException {
        List<Map<String, Object>> results = new ArrayList<Map<String, Object>>();
        try {
            if (rs != null) {
                ResultSetMetaData meta = rs.getMetaData();
                int numColumns = meta.getColumnCount();
                while (rs.next()) {
                    Map<String, Object> row = new HashMap<String, Object>();
                    for (int i = 1; i <= numColumns; ++i) {
                        String name = meta.getColumnName(i);
                        Object value = rs.getObject(i);
                        row.put(name, value);
                    }
                    results.add(row);
                }
            }
        } finally {
            close(rs);
        }
        return results;
    }

    public static List<Map<String, Object>> query(Connection connection, String sql, List<Object> parameters) throws SQLException {
        List<Map<String, Object>> results = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            ps = connection.prepareStatement(sql);

            int i = 0;
            for (Object parameter : parameters) {
                ps.setObject(++i, parameter);
            }
            rs = ps.executeQuery();
            results = map(rs);
        } finally {
            close(rs);
            close(ps);
        }
        return results;
    }

    public static int update(Connection connection, String sql, List<Object> parameters) throws SQLException {
        int numRowsUpdated = 0;
        PreparedStatement ps = null;
        try {
            ps = connection.prepareStatement(sql);

            int i = 0;
            for (Object parameter : parameters) {
                ps.setObject(++i, parameter);
            }
            numRowsUpdated = ps.executeUpdate();
        } finally {
            close(ps);
        }
        return numRowsUpdated;
    }
}

编译后,使用以下命令运行它:

java -classpath .;<Connector-J driver path here> persistence.DatabaseUtils
于 2013-02-15T16:15:55.927 回答