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 分、CST より具体的でないことをお詫びします。このアプリケーションは、通常は問題なく動作する本番アプリケーションです。1 日に数万件の接続を問題なく正常に処理していますが、この問題は 1 日のランダムな時間に発生し、発生しても 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 を渡します。Driver 実装が URL を理解すると、接続オブジェクトを返すか、データベースに接続できない場合は SQLException をスローします。Driver の実装が URL を理解できない場合、null が返されます。

java.sql.DriverManagerただし、 (Java 7 Update 13の) のコードを見ると、使用可能なすべてのドライバーが呼び出しに対して返されたときに、<url> に適したドライバーが見つかりませんでしたというメッセージが常にスローされます。SQLExceptionnullconnect(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 の変数またはフィールドを使用しようとしている可能性が高くなります。

もう 1 つの可能性は、 との接続を取得していないが、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 ドライバー クラスを使用して管理します。その JAR は、CLASSPATH開始時にあなたの中になければなりません。

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 に答える