RussianLDP Рейтинг@Mail.ru
WebMoney: 
WMZ Z294115950220 
WMR R409981405661 
WME E134003968233 
Visa 
4274 3200 2453 6495 

Глава 15. Проблемы с приложениями Connector/J

Эта секция объясняет признаки и решения для проблем, с которыми обычно сталкиваются с приложениями, используя MySQL Connector/J.

Вопросы и ответы

15.1: Когда я пытаюсь соединиться с базой данных с MySQL Connector/J, я получаю следующее исключение:

SQLException: Server configuration denies access to data source
SQLState: 08001
VendorError: 0

Я могу соединиться клиентом командной строки MySQL.

Connector/J обычно использует сокеты TCP/IP, чтобы соединиться с MySQL (см. разделы 6.8 и 6.9 для исключений). Менеджер безопасности на сервере MySQL использует свои таблицы привилегий, чтобы определить, разрешена ли связь TCP/IP. Необходимо поэтому добавить необходимые параметры безопасности серверу MySQL для связи командой GRANT на вашем сревере MySQL, см. GRANT Statement.

Неправильное изменение привилегий и разрешений на MySQL может потенциально заставить вашу установку сервера иметь неоптимальные свойства безопасности.

Тестирование вашей возможности соединения клиентом командной строки mysql не будет работать, если вы не добавите опцию --host и используете что-то другое, чем localhost для хоста. Клиент mysql попытается использовать сокет Unix, если вы будете использовать специальное имя хоста localhost. Если вы проверяете возможность соединения TCP/IP к localhost, используйте как имя хоста 127.0.0.1.

15.2: Мое приложение бросает SQLException 'No Suitable Driver'. Почему это происходит?

Есть три возможных причины для этой ошибки:

  • Драйвер Connector/J не находится в вашем CLASSPATH, см. главу 4.

  • Формат вашей связи URL неправильный, или вы ссылаетесь на ошибочный драйвер JDBC.

  • Используя DriverManager, системное свойство jdbc.drivers не было задано с местоположением драйвера Connector/J.

15.3: Я пытаюсь использовать MySQL Connector/J в апплете или приложении, и получаю исключение, подобное

SQLException: Cannot connect to MySQL server on host:3306.
Is there a MySQL server running on the machine/port you
are trying to connect to?

(java.security.AccessControlException)
SQLState: 08S01
VendorError: 0

Ваш сервер MySQL был настроен с включенной опцией skip_networking или у вашего сервера MySQL есть неправильно настроенный брандмауэр.

Апплеты могут установить сетевые связи только назад с машиной, которая управляет веб-сервером, который обрабатывает файл .class апплета. Это означает, что MySQL должен работать на той же самой машине (или у вас должно быть своего рода переназначение порта). Это также означает, что вы не будете в состоянии проверить апплеты от своей локальной файловой системы, но должны всегда развертывать их к веб-серверу.

Connector/J обычно использует сокеты TCP/IP, чтобы соединиться с MySQL normally uses TCP/IP sockets to connect to MySQL (см. разделы 6.8 и 6.9 для исключений). Связь TCP/IP с MySQL может быть затронута переменной skip_networking или брандмауэром сервера. Если MySQL был запущен с включенной опцией skip_networking, необходимо закомментировать ее в файле /etc/mysql/my.cnf или /etc/my.cnf, чтобы включить соединения по TCP/IP. Обратите внимание на то, что ваш конфигурационный файл сервера мог бы также существовать в каталоге data сервера MySQL или где-то в другом месте, в зависимости от того, как MySQL был собран, двоичные пакеты Oracle всегда ищут /etc/my.cnf и datadir /my.cnf, см. Using Option Files. Если ваш сервер MySQL был защищен брандмауэром, необходимо будет формировать брандмауэр, чтобы позволить связи TCP/IP от хоста, где работает код Java, к серверу MySQL на порт, который MySQL слушает (по умолчанию это 3306).

15.4: У меня есть сервлет/приложение, который хорошо работает в течение дня, а затем прекращает работать в течение ночи.

MySQL закрывает связи после 8 часов бездеятельности. Или необходимо использовать пул связи, который обращается с несвежими связями, или используйте опцию autoReconnect (см. раздел 6.3).

Кроме того, обработайте SQLExceptions в вашем приложении вместо того, чтобы размножить их полностью до вылета ваших программ. Это просто хорошая практика программирования. MySQL Connector/J установит SQLState (см. java.sql.SQLException.getSQLState() в ваших описаниях API) в 08S01, когда это сталкивается с проблемами сетевого соединения во время обработки запроса.

Следующий (упрощенный) пример показывает то, что код, который может обращаться с этими исключениями, мог бы быть похожим на это:

Пример 15.1. Connector/J: Пример транзакции с логикой повторной попытки

public void doBusinessOp() throws SQLException {
  Connection conn = null;
  Statement stmt = null;
  ResultSet rs = null;

  // How many times do you want to retry the transaction
  // (or at least _getting_ a connection)?
  int retryCount = 5;
  boolean transactionCompleted = false;
  do {
    try {
      conn = getConnection();
      // assume getting this from a
      // javax.sql.DataSource, or the
      // java.sql.DriverManager
      conn.setAutoCommit(false);

      // Okay, at this point, the 'retry-ability' of the
      // transaction really depends on your application logic,
      // whether or not you're using autocommit (in this case
      // not), and whether you're using transactional storage engines
      //
      // For this example, we'll assume that it's _not_ safe
      // to retry the entire transaction, so we set retry
      // count to 0 at this point
      //
      // If you were using exclusively transaction-safe tables,
      // or your application could recover from a connection going
      // bad in the middle of an operation, then you would not
      // touch 'retryCount' here, and just let the loop repeat
      // until retryCount == 0.
      retryCount = 0;
      stmt = conn.createStatement();
      String query = "SELECT foo FROM bar ORDER BY baz";
      rs = stmt.executeQuery(query);
      while (rs.next()) { }
      rs.close();
      rs = null;
      stmt.close();
      stmt = null;
      conn.commit();
      conn.close();
      conn = null;
      transactionCompleted = true;
    } catch (SQLException sqlEx) {
      // The two SQL states that are 'retry-able' are 08S01
      // for a communications error, and 40001 for deadlock.
      //
      // Only retry if the error was due to a stale connection,
      // communications problem or deadlock
      String sqlState = sqlEx.getSQLState();
      if ("08S01".equals(sqlState) || "40001".equals(sqlState)) {
         retryCount -= 1;
      } else {
        retryCount = 0;
      }
    } finally {
      if (rs != null) {
         try {
           rs.close();
         } catch (SQLException sqlEx) {
           // You'd probably want to log this...
         }
      }
      if (stmt != null) {
         try {
           stmt.close();
         } catch (SQLException sqlEx) {
           // You'd probably want to log this as well...
         }
      }
      if (conn != null) {
         try {
           // If we got here, and conn is not null, the
           // transaction should be rolled back, as not
           // all work has been done
           try {
             conn.rollback();
           } finally {
             conn.close();
           }
         } catch (SQLException sqlEx) {
           // If we got an exception here, something
           // pretty serious is going on, so we better
           // pass it up the stack, rather than just logging it...
           throw sqlEx;
         }
      }
    }
  } while (!transactionCompleted && (retryCount > 0));
}

Использование опции autoReconnect не рекомендуется, потому что нет никакого надежного метода повторного подключения к серверу MySQL, не рискуя повреждением состояния связи или информации о статусе базы данных. Вместо этого используйте пул связи, который позволит вашему запросу соединиться с сервером MySQL, используя доступную связь из пула. Опция autoReconnect устарела и может быть удалена в будущем выпуске.

15.5: Я не могу соединиться с сервером MySQL, используя Connector/J, и я уверен, что параметры связи правильны.

Удостоверьтесь, что переменная skip_networking выключена на на вашем сервере. Connector/J должен быть в состоянии общаться с вашим сервером по TCP/IP, именованные сокеты не поддерживаются. Также гарантируйте, что вы не пропускаете связи через брандмауэр или другую систему защиты сети. Для получения дополнительной информации посмотрите Can't connect to [local] MySQL server.

15.6: Приложение развернуто через JBoss, и я использую транзакции, чтобы обращаться с запросами к базе данных MySQL. Под большими нагрузками я получаю ошибку и трассировку стека, но они происходят только после установленного срока тяжелой деятельности.

Это проблема JBoss, а не Connector/J, связана с использованием транзакций. Под большими нагрузками может увеличиться время, потраченное для транзакций, и ошибка вызывается, потому что вы превысили предопределенный тайм-аут.

Можно увеличить тайм-аут, установив атрибут TransactionTimeout в TransactionManagerService в файле /conf/jboss-service.xml (pre-4.0.3) или /deploy/jta-service.xml для JBoss 4.0.3 или позже, см. TransactionTimeout в JBoss wiki для получения дополнительной информации.

15.7: Обновление таблицы, которая содержит primary key, который является также FLOAT или составным первичным ключом, который использует FLOAT, не обновляет таблицу и поднимает исключение.

Connector/J добавляет условия к WHERE во время UPDATE, чтобы проверять старые значения первичного ключа. Если там нет совпадения, то Connector/J считает это условием неудачи и поднимает исключение.

Проблема состоит в том, что округление различий между значениями, поставляемыми и сохраненными в базе данных, может означать, что значения никогда не соответствуют, и следовательно обновление терпит неудачу. Проблема затронет все запросы, а не только от Connector/J.

Чтобы предотвратить эту проблему, используйте первичный ключ, который не использует FLOAT. Если необходимо использовать столбец с плавающей точкой в первичном ключе, можно использовать типы DOUBLE или DECIMAL вместо FLOAT.

15.8: Я получаю исключение ER_NET_PACKET_TOO_LARGE даже при том, что размер blob, который я хочу вставить с использованием JDBC, безопасно ниже размера max_allowed_packet.

Это потому, что метод hexEscapeBlock() в com.mysql.cj.AbstractPreparedQuery.streamToBytes() почти удваивает размер ваших данных.

15.9: Что делать, если я получаю сообщения об ошибках, подобные следующему: Communications link failure - Last packet sent to the server was X ms ago?

Вообще говоря, эта ошибка предполагает, что сетевое соединение было закрыто. Может быть несколько первопричин:

  • Брандмауэры или маршрутизаторы могут пресечь неработающие связи.

  • MySQL Server может закрывать неработающие связи, которые превышают wait_timeout или interactive_timeout.

Хотя сетевые соединения могут быть изменчивыми, следующее может быть полезным в предотвращении проблем:

  • Гарантируйте, что связи действительны, когда используются из пула связи. Используйте запрос, который начинается с /* ping */ для пинга вместо полного запроса. Отметьте, синтаксис должен быть точно как определен здесь.

  • Минимизируйте время, которое объект связи оставляют неработающим, в то время как другая прикладная логика выполняется.

  • Явно проверьте связь перед использованием, если связь оставили неработающей в течение длительного периода времени.

  • Гарантируйте, что wait_timeout и interactive_timeout установлены достаточно высоко.

  • Гарантируйте, что включена опция tcpKeepalive.

  • Гарантируйте, что любые конфигурируемые параметры брандмауэра или маршрутизатора допускают максимальное ожидаемое время простоя связи.

Не ожидайте, что будете в состоянии снова использовать связь без проблем, если у нее был длительный простой. Если связь должна быть снова использована, будучи неработающей в течение какого-либо отрезка времени, надо гарантировать, чтобы вы явно проверили ее прежде, чем снова использовать.

15.10: Почему не делает Connector/J реконнект с MySQL после сбоя связи вместо того, чтобы бросить исключение, даже при том, что я использую опцию autoReconnect строки подключения?

Есть несколько причин этого. Первой является целостность транзакций. Справочник MySQL указывает, что нет никакого надежного метода повторного подключения к серверу MySQL, не рискуя повреждением состояния связи или информации о статусе базы данных. Рассмотрите следующий ряд запросов, например:

conn.createStatement().execute(
    "UPDATE checking_account SET balance = balance - 1000.00
    WHERE customer='Smith'");
conn.createStatement().execute(
    "UPDATE savings_account SET balance = balance + 1000.00
    WHERE customer='Smith'");
conn.commit();

Рассмотрите случай, где связь с сервером прерывается после UPDATE для checking_account. Если никакое исключение не будет брошено, и приложение никогда не узнает о проблеме, оно продолжит выполняться. Однако сервер не передал первую транзакцию в этом случае, так что она была отменена. Но выполнение продолжает следующую транзакцию и увеличивает savings_account на 1000. Приложение не получило исключение, таким образом, оно продолжалось независимо, в конечном счете передавая вторую транзакцию, поскольку передача относится только к изменениям, внесенным в новой связи.

Отметьте, что работа с включенной autocommit не решает эту проблему. Когда Connector/J сталкивается с проблемой коммуникации, нет никакого средства определить, обработал ли сервер в настоящее время выполняющийся запрос или нет. Следующие теоретические состояния одинаково возможны:

  • Сервер никогда не получал запрос, поэтому никакая связанная обработка не произошла на сервере.

  • Сервер получил запрос, выполнил его полностью, но ответ не был получен клиентом.

Если вы раболтаете с включенной autocommit, невозможно гарантировать статус данных по серверу, когда с коммуникационным исключением сталкиваются. Запрос, возможно, достиг сервера. Все, что вы знаете: коммуникация прервана в какой-то момент прежде, чем клиент получил подтверждение (или данные) от сервера. Это не только затрагивает autocommit. Если проблема коммуникации произошла во время Connection.commit(), возникает вопрос: была ли транзакция передана на сервере прежде, чем коммуникация прервалась.

Вторая причина для создания исключений: рассмотренные транзакцией контекстные данные могут быть уязвимыми, например:

  • Временные таблицы.

  • Пользовательские переменные.

  • Серверная сторона подготовленного запроса.

Эти пункты потеряны, когда связь прерывается, и если связь тихо снова соединяется, не производя исключение, это могло бы быть вредно для правильного выполнения вашего запроса.

Таким образом, ошибки связи производят условия, которые для Connector/J может быть небезопасно просто проигнорировать, тихо снова соединившись. Необходимо для приложения обработать ситуацию. Для разработчика приложений важно решить, как продолжить в случае ошибок связи.

15.11: Как я могу использовать 3-байтовый UTF8 с Connector/J?

8.0.12 и раньше: чтобы использовать 3-байтовый UTF8 с Connector/J, установите characterEncoding=utf8 и useUnicode=true в строке подключения.

8.0.13 и позже: Поскольку нет никакого названия набора символов Java-стиля для utfmb3, который можно использовать с опцией связи charaterEncoding, единственный способ использовать utf8mb3 как ваш набор символов связи, это использовать сопоставление utf8mb3 (например, utf8_general_ci) для опции connectionCollation, что вызывает набор символов utf8mb3, который будет использоваться. См. раздел 6.6.

15.12: Как использовать 4-байтовый UTF8 (utf8mb4) с Connector/J?

Чтобы использовать 4-байтовый UTF8 с Connector/J настройте сервер MySQL с character_set_server=utf8mb4. Connector/J будет тогда использовать эту настройку, если characterEncoding и connectionCollation не были установлены в строке подключения. Это эквивалентно автообнаружению набора символов. Посмотрите раздел 6.6 для деталей. 8.0.13 позже: можно использовать characterEncoding=UTF-8, чтобы использовать utf8mb4, даже если character_set_server на сервере установлена во что-то еще.

15.13: Использование useServerPrepStmts=false и определенные кодировки символов могут привести к повреждению, вставляя BLOB. Как этого можно избежать?

Используя определенные кодировки символов, такие как SJIS, CP932 и BIG5, возможно, что данные BLOB содержат знаки, которые могут интерпретироваться как управляющие символы, например, наклонная черта влево, '\'. Это может привести к испорченным данным, вставляя BLOB в базу данных. Есть две вещи, которые должны быть сделаны, чтобы избежать этого:

  1. Установите опцию строки подключения useServerPrepStmts = true.

  2. Установите SQL_MODE = NO_BACKSLASH_ESCAPES.

Поиск

 

Найди своих коллег!

Вы можете направить письмо администратору этой странички, Алексею Паутову. mailto:alexey.v.pautov@mail.ru