2013-02-15 9 views
5

Sto tentando di risolvere un problema con un'applicazione Java che utilizza JDBC per connettersi a un database MySQL. Il problema di superficie è che quando si connette a un database valido, DriverManager.getConnection a volte restituisce NULL, mentre solo pochi minuti dopo restituirà una connessione valida allo stesso database esatto.Java/JDBC/MySQL: Come si risolve il problema con cui DriverManager.getConnection() restituisce NULL?

Sono nella posizione di provare a risolvere questo problema, ma la mia comprensione di dove Java, JDBC e MySQL si incontrano è piuttosto limitata. Ho fatto molte ricerche su questo, ma ho colpito un muro e non so dove andare da qui.

Ecco che cosa ho fatto finora:

  • Sul lato Java, ho rintracciato il codice fino a DriverManager.getConnection(). Ho determinato che la connessione NULL sta venendo da lì, ma non ho idea di cosa succede sotto il cofano di getConnection. Ho faticato a trovare una spiegazione approfondita di questo online.
  • Sul lato MySQL, ho verificato che ci sono molte connessioni disponibili (circa 1000 connessioni libere a volte), quindi so che non sto superando le connessioni massime lì. Guardando i registri, sono stato in grado di determinare che ci sono un numero leggermente più alto di connessioni interrotte durante i periodi di tempo in cui ho più problemi, ma non so come determinare il motivo per cui queste connessioni sono state interrotte (MySQL abort, JDBC, l'applicazione Java?) Non sono sicuro se c'è qualcos'altro che devo cercare sul terminale MySQL.
  • Nel mezzo, con JDBC, sono piuttosto perso. Ho letto su MySQL Connector/J allo http://dev.mysql.com/doc/refman/5.1/en/connector-j.html, ma non sono sicuro che tali informazioni riguardino il driver JDBC utilizzato da Java.

Qualsiasi direzione su dove posso andare da qui sarebbe molto apprezzata.

Grazie!

EDIT - 2/15, 10:35 CST Mi scuso per non essere più specifico. Questa applicazione è un'applicazione di produzione che normalmente funziona bene. Elabora con successo decine di migliaia di connessioni al giorno senza alcun problema, è solo che questo problema si presenterà in momenti casuali durante il giorno e continuerà a persistere da 30 secondi a 5 minuti quando accadrà.

Ecco il codice che ho tracciato tutta la strada fino a 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); 
    } 

Non credo ci sia in realtà alcun problema con il codice, ma piuttosto un problema da qualche parte tra getConnection() e MySQL.

+1

Posta un po 'di codice e sarà più facile aiutarti. – duffymo

+0

sembra che la connessione (rete) sia inaffidabile e la macchina non sia sempre connessa alla macchina, il db è acceso, quindi a volte la connessione non può essere stabilita –

+0

Jeff - Questo è il mio sospetto, ma è difficile dirlo dove ciò potrebbe accadere. Se esiste un modo per determinare i diversi motivi per cui DriverManager.getConnection restituisce NULL, ciò mi aiuterà a rintracciare in modo significativo questo problema. – TACHEON

risposta

7

Un driver individuo può restituire null per una richiesta di connessione, le specifiche JDBC 4.1 dice questo:

Quando il DriverManager sta provando a stabilire una connessione, chiama quel metodo di connessione del driver e passa il driver all'URL. Se l'implementazione del driver comprende l'URL, restituirà un oggetto Connection o genererà una SQLException se una connessione non può essere inoltrata al database. Se l'implementazione del driver fa sì che non capisca l'URL, restituirà null.

Tuttavia, guardando il codice di java.sql.DriverManager (in Java 7 Aggiornamento 13), sarà sempre lanciare un SQLException con il messaggio Nessun driver adatto trovata per <url> quando tutti i driver disponibili sono tornati null per una chiamata connect(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"); 
} 

in altre parole: ciò che si descrive non può accadere (almeno non in Java 7 Aggiornamento 13). Una rapida occhiata alle fonti di Java 5 Update 22 mostra un'implementazione quasi identica se semplicemente non è possibile restituire null.

È più probabile che si stia ingoiando un'eccezione e quindi si tenti di utilizzare una variabile o un campo Connection con valore nullo.

Un'altra possibilità potrebbe essere che non si sta ottenendo la connessione con DriverManager.getConnection(url, ...), ma con DriverManager.getDriver(url).connect(...) che può restituire null a causa delle regole stabilite sopra. Se è quello che fai, probabilmente punta ad un bug nel Connector/J driver se usi sempre lo stesso esatto URL: un driver non può decidere in un punto di restituire una connessione per un URL specifico e il successivo return null. Dovrebbe sempre restituire uno Connection o lanciare uno SQLException per lo stesso URL.

+2

Grazie per aver chiarito che non restituirà mai NULL, ma invece un'eccezione. Ho scavato un po 'di più e ho scoperto dove si sta inghiottendo l'eccezione. Ho letto tutto il codice così tante volte che non sono sicuro di quanto mi sia mancato. – TACHEON

+0

Felice di poterti aiutare! –

2

Sì, DriverManager è la classe che ottiene la connessione per te.

Gestisce questo utilizzando le classi di driver JDBC ottenute con il connettore MySQL J JAR. Quel JAR deve essere nel tuo CLASSPATH quando si avvia.

Inizia assicurandosi che sia possibile connettersi a MySQL dalla macchina su cui si esegue l'app Java. Accedi correttamente all'app di amministrazione MySQL e hai superato il primo ostacolo.

Ti offrirò un corso di dottorato per la tua situazione. I metodi potrebbero essere generalmente utili per te.Modifica la connessione, le credenziali e la query per la tua situazione e provalo. I sa questo codice funziona.

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; 
    } 
} 

Dopo la compilazione, eseguirlo con questo comando:

java -classpath .;<Connector-J driver path here> persistence.DatabaseUtils 
Problemi correlati