Эта секция объясняет признаки и решения для проблем, с которыми обычно сталкиваются с приложениями, используя 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
и
, см.
Using Option Files. Если ваш сервер MySQL был защищен
брандмауэром, необходимо будет формировать брандмауэр, чтобы позволить связи
TCP/IP от хоста, где работает код Java, к серверу MySQL на порт, который
MySQL слушает (по умолчанию это 3306).datadir
/my.cnf
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 в базу данных. Есть две вещи, которые должны быть сделаны, чтобы избежать этого:
Установите опцию строки подключения
useServerPrepStmts
=
true
.
Установите SQL_MODE
=
NO_BACKSLASH_ESCAPES
.