Эта секция объясняет признаки и решения для проблем, с которыми обычно сталкиваются с приложениями, используя 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.