Глава 16 Устранение неполадок в приложениях Connector/J
В этом разделе описываются симптомы и решения наиболее распространенных проблем, возникающих в приложениях, использующих MySQL Connector/J.
Вопросы
-
16.1: При попытке подключения к базе данных с помощью MySQL Connector/J я получаю следующее исключение:
SQLException: Server configuration denies access to data source SQLState: 08001 VendorError: 0
Что происходит? Я могу подключиться без проблем с помощью клиентской утилики командной строки MySQL.
16.2: Мое приложение выдает исключение SQLException 'No Suitable Driver'. Почему это происходит?
-
16.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
16.4: У меня есть сервлет/приложение, которое работает нормально в течение дня, а затем перестает работать ночью
16.5: Я не могу подключиться к серверу MySQL с помощью Connector/J, и я уверен, что параметры подключения верны.
16.6: Обновление таблицы, содержащей , которая является либо , либо составным первичным ключом, использующим , не обновляет таблицу и вызывает исключение.
16.7: Я получаю исключение , хотя размер бинарного BLOB, который я хочу вставить с помощью JDBC, безопасно меньше размера .
16.8: Что мне делать, если я получаю сообщения об ошибках, подобные следующим: “Communications link failure – Last packet sent to the server was X ms ago”?
16.9: Почему Connector/J не переподключается к MySQL и не повторяет выполнение инструкции после сбоя связи, а вместо этого выдает исключение, даже если я использую параметр строки соединения
autoReconnect?16.10: Как я могу использовать 3-байтовый UTF8 с Connector/J?
16.11: Как я могу использовать 4-байтовый UTF8 (
utf8mb4) с Connector/J?16.12: Использование
useServerPrepStmts=falseи некоторых кодировок символов может привести к повреждению данных при вставке BLOB. Как этого избежать?
Вопросы и ответы
16.1: При попытке подключения к базе данных с помощью MySQL Connector/J я получаю следующее исключение:
SQLException: Server configuration denies access to data source
SQLState: 08001
VendorError: 0
Что происходит? Я могу подключиться без проблем с помощью клиентской утилики командной строки MySQL.
Connector/J обычно использует TCP/IP сокеты для подключения к MySQL (см. Раздел 6.10, «Подключение с использованием сокетов Unix» и Раздел 6.11, «Подключение с использованием именованных каналов» для исключений). Менеджер безопасности на сервере MySQL использует свои таблицы предоставления прав для определения того, разрешено ли TCP/IP подключение. Поэтому необходимо добавить необходимые учетные данные безопасности на сервер MySQL для подключения, выполнив инструкцию на вашем сервере MySQL. См. , для получения дополнительной информации.
Неправильное изменение прав и разрешений на MySQL может привести к тому, что ваша установка сервера будет иметь неоптимальные свойства безопасности.
Тестирование подключения с помощью клиентской утилики командной строки mysql не будет работать, если вы не добавите флаг и не будете использовать что-то другое, кроме localhost для хоста. Клиентская утилика командной строки mysql попытается использовать сокеты Unix, если вы используете специальное имя хоста localhost. Если вы тестируете TCP/IP подключение к localhost, используйте 127.0.0.1 в качестве имени хоста.
16.2: Мое приложение выдает исключение SQLException 'No Suitable Driver'. Почему это происходит?
Есть три возможные причины этой ошибки:
Драйвер Connector/J не находится в вашем
CLASSPATH, см. Главу 4, Установка Connector/J.Формат вашего URL подключения неверен, или вы ссылаетесь на неправильный JDBC драйвер.
При использовании DriverManager системное свойство
jdbc.driversне было заполнено местоположением драйвера Connector/J.
16.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 был установлен с включенной системной переменной , либо перед вашим сервером MySQL установлен брандмауэр.
Апплеты могут устанавливать сетевые соединения только обратно на машину, которая запускает веб-сервер, обслуживающий .class файлы для апплета. Это означает, что MySQL должен работать на той же машине (или у вас должна быть какая-либо переадресация портов), чтобы это работало. Это также означает, что вы не сможете тестировать апплеты из вашей локальной файловой системы, а всегда должны развертывать их на веб-сервере.
Connector/J обычно использует TCP/IP сокеты для подключения к MySQL (см. Раздел 6.10, «Подключение с использованием сокетов Unix» и Раздел 6.11, «Подключение с использованием именованных каналов» для исключений). TCP/IP связь с MySQL может быть затронута системной переменной или брандмауэром сервера. Если MySQL был запущен с включенной переменной , вам нужно закомментировать ее в файле /etc/mysql/my.cnf или /etc/my.cnf для работы TCP/IP соединений. (Обратите внимание, что ваш конфигурационный файл сервера может также находиться в каталоге data вашего сервера MySQL или где-то еще, в зависимости от того, как был скомпилирован MySQL; бинарные файлы, созданные Oracle, всегда ищут /etc/my.cnf и ; см. для подробностей.) Если ваш сервер MySQL был защищен брандмауэром, вам потребуется настроить брандмауэр, чтобы разрешить TCP/IP соединения с хоста, на котором работает ваш Java код, на сервер MySQL по порту, который прослушивает MySQL (по умолчанию 3306). datadir/my.cnf
16.4: У меня есть сервлет/приложение, которое работает нормально в течение дня, а затем перестает работать ночью
MySQL закрывает соединения после 8 часов бездействия. Вам необходимо либо использовать пул соединений, который обрабатывает устаревшие соединения, либо использовать параметр autoReconnect (см. Раздел 6.3, «Свойства конфигурации»).
Кроме того, перехватывайте исключения SQLExceptions в вашем приложении и обрабатывайте их, а не распространяйте их до выхода вашего приложения. Это просто хорошая практика программирования. MySQL Connector/J установит значение SQLState (см. java.sql.SQLException.getSQLState() в вашей документации по API) в 08S01, когда он столкнется с проблемами сетевого подключения во время обработки запроса. Попробуйте переподключиться к MySQL в этот момент.
Следующий (упрощенный) пример показывает, как может выглядеть код, который может обрабатывать эти исключения:
Пример 16.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 устарел и может быть удален в будущих версиях.
16.5: Я не могу подключиться к серверу MySQL с помощью Connector/J, и я уверен, что параметры подключения верны.
Убедитесь, что системная переменная не включена на вашем сервере. Connector/J должен иметь возможность общаться с вашим сервером по TCP/IP; именованные сокеты не поддерживаются. Также убедитесь, что вы не фильтруете соединения через брандмауэр или другую систему сетевой безопасности. Для получения дополнительной информации, см. .
16.6: Обновление таблицы, содержащей , которая является либо , либо составным первичным ключом, использующим , не обновляет таблицу и вызывает исключение.
Connector/J добавляет условия к предложению WHERE во время для проверки старых значений первичного ключа. Если совпадений нет, то Connector/J рассматривает это как условие сбоя и вызывает исключение.
Проблема в том, что различия в округлениях между предоставленными значениями и значениями, хранящимися в базе данных, могут означать, что значения никогда не совпадают, и, следовательно, обновление не выполняется. Эта проблема будет затрагивать все запросы, а не только запросы от Connector/J.
Чтобы предотвратить эту проблему, используйте первичный ключ, который не использует . Если вы должны использовать столбец с плавающей точкой в вашем первичном ключе, используйте типы или вместо .
16.7: У меня возникает исключение, хотя размер двоичного блока, который я хочу вставить с помощью JDBC, находится в пределах безопасного диапазона.
Это происходит потому, что метод hexEscapeBlock() в com.mysql.cj.AbstractPreparedQuery.streamToBytes() может почти удвоить размер ваших данных.
16.8: Что мне делать, если я получаю сообщения об ошибках, похожие на следующее: “Ошибка связи — последний пакет был отправлен на сервер X мс назад”?
Как правило, эта ошибка указывает на то, что сетевое подключение было закрыто. Причин может быть несколько:
Брандмауэры или маршрутизаторы могут блокировать неактивные подключения (протокол MySQL клиент/сервер не отправляет сигналы ping).
Сервер MySQL может закрывать неактивные подключения, превышающие порог
wait_timeoutилиinteractive_timeout.
Хотя сетевые подключения могут быть нестабильными, следующее может помочь избежать проблем:
Убедитесь, что соединения валидны при использовании из пула соединений. Используйте запрос, начинающийся с
/* ping */, чтобы выполнить лёгкий ping вместо полного запроса. Обратите внимание, синтаксис ping должен быть точно таким же, как указано здесь.Минимизируйте время простоя объекта подключения, в то время как выполняется другая логика приложения.
Явно валидируйте подключение перед использованием, если подключение простояло в течение длительного времени.
Убедитесь, что
wait_timeoutиinteractive_timeoutустановлены достаточно высокими значениями.Убедитесь, что
tcpKeepaliveвключен.Убедитесь, что любые настраиваемые параметры времени ожидания брандмауэра или маршрутизатора позволяют поддерживать максимальное ожидаемое время простоя соединения.
Не ожидайте, что подключение можно будет повторно использовать без проблем, если оно простояло в течение определённого периода времени. Если необходимо повторно использовать подключение после длительного простоя, убедитесь, что вы проверили его перед повторным использованием.
16.9: Почему 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 путём молчаливого переподключения. Приложение должно быть уведомлено. Затем разработчик приложения решает, как поступить в случае ошибок и сбоев соединения.
16.10: Как использовать 3-байтовый UTF8 с Connector/J?
Поскольку нет имени набора символов Java-стиля для utfmb3, которое можно использовать с опцией подключения charaterEncoding, единственный способ использовать utf8mb3 в качестве набора символов подключения — использовать кодировку utf8mb3 (например, utf8_general_ci) для опции подключения connectionCollation, что принудительно использует набор символов utf8mb3. Подробнее см. Раздел 6.7 «Использование наборов символов и Юникода».
16.11: Как использовать 4-байтовый UTF8 (utf8mb4) с Connector/J?
Для использования 4-байтового UTF8 с Connector/J настройте сервер MySQL. Connector/J затем будет использовать эту настройку, если characterEncoding и connectionCollation не были заданы в строке подключения. Это эквивалентно автоматическому определению набора символов. Подробнее см. Раздел 6.7 «Использование наборов символов и Юникода». Вы можете использовать characterEncoding=UTF-8 для использования utf8mb4, даже если на сервере установлено другое значение.
16.12: Использование useServerPrepStmts=false и определённых кодировок символов может привести к повреждению данных при вставке BLOB. Как этого избежать?
При использовании определённых кодировок символов, таких как SJIS, CP932 и BIG5, возможно, что данные BLOB содержат символы, которые могут интерпретироваться как управляющие символы, например, обратный слэш, '\'. Это может привести к повреждению данных при вставке BLOB в базу данных. Для избежания этого необходимо выполнить два действия:
Установите опцию строки подключения
useServerPrepStmtsв значениеtrue.Установите
SQL_MODEв значениеNO_BACKSLASH_ESCAPES.
© 2025 Oracle
Licensed under the GPLv2 License.