Вопрос или проблема
Я пытаюсь перейти с использования MySQL Connector/J на MariaDB Connector/J 2.7.12 в проекте на Scala 2.12. После недели подозрений в баге в драйвере MariaDB, я обнаружил, что настоящая проблема заключается в том, что логика объединения для sbt-assembly заменяет библиотеки, из-за чего драйвер MariaDB выдает ошибку при подключении к MySQL 8.
Ошибка, о которой идет речь:
java.sql.SQLNonTransientConnectionException: Не удалось подключиться к address=(host=192.168.240.7)(port=3306)(type=master) : Клиент не поддерживает протокол аутентификации, запрошенный сервером. тип плагина был="caching_sha2_password"
в org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.createException(ExceptionFactory.java:73)
Я знаю, что это не драйвер, потому что я использовал тот же драйвер, Scala и Java в небольшом тестовом приложении. Все работало хорошо, как только я убрал логику объединения в этом файле build.sbt
и вручную исключил дубликаты. К сожалению, в реальном приложении используется гораздо больше библиотек.
Есть ли способ гарантировать, что sbt-assembly сначала выбирает классы из jar-файла MariaDB Connector/J? Если нет, есть ли у кого-то файл build.sbt
, включающий этот драйвер?
Попробовал:
- Заменил библиотеку.
- Использовал разные версии MariaDB Connector/J.
- Подключился к MySQL 8, когда использую плагин аутентификации
mysql_native_password
. - Подключился к серверу MySQL 5.7, но постоянно не удается подключиться к серверу MySQL 8 “из коробки”.
Ожидание: бесшовная замена драйвера MySQL на драйвер MariaDB.
ИЗМЕНИЛ: Вот пример приложения, которое работает. Чтобы его сломать, удалите раздел excludeDependencies
и замените его на стандартную логику объединения.
Maria.scala
package com.test
import java.sql.Connection
import java.sql.DriverManager
import java.sql.SQLException
import org.mariadb.jdbc.Driver
object Maria {
def main(args: Array[String]) = {
var connection:Connection = null
try {
println("Начало...")
Class.forName("org.mariadb.jdbc.Driver")
println("Драйвер зарегистрирован")
connection = DriverManager.getConnection(
"jdbc:mariadb://maria-mysql2-1:3306/test?allowPublicKeyRetrieval=true",
"root", "password")
println(s"Подключение корректно: ${connection.isValid(5)}")
val statement = connection.createStatement()
val resultSet = statement.executeQuery("SELECT id, name FROM test")
while ( resultSet.next() ) {
val id = resultSet.getInt("id")
val name = resultSet.getString("name")
println(s"> ${id}. ${name}")
}
} catch {
case e: Exception => {
println(e.printStackTrace)
}
} finally {
println("Закрытие...")
connection.close()
println(s"Подключение корректно: ${connection.isValid(5)}")
}
}
}
build.sbt
version := "0.1.0-SNAPSHOT"
organization := "com.test"
scalaVersion := "2.12.19"
name := "Maria"
Compile / mainClass := Some("com.test.Maria")
assembly / mainClass := Some("com.test.Maria")
assembly / assemblyJarName := s"${name.value}-${version.value}.jar"
libraryDependencies ++= Seq(
"org.scala-lang.modules" %% "scala-parser-combinators" % "2.3.0",
"org.mariadb.jdbc" % "mariadb-java-client" % "2.7.12"
)
excludeDependencies ++= Seq(
ExclusionRule("org.slf4j", "slf4j-api"),
ExclusionRule("org.slf4j", "jcl-over-slf4j")
)
Ответ или решение
Проблема, с которой вы столкнулись, связана с конфликтами классов при использовании плагина sbt-assembly. Этот плагин отвечает за объединение зависимостей в один JAR файл, и когда в различных библиотеках есть дублирующиеся классы, логика объединения может заменить нужные классы на не нужные, что и вызывает ошибку подключения, связанную с аутентификацией.
Решение
Для того чтобы избежать конфликтов и гарантировать, что классы из JAR файла MariaDB Connector/J будут использоваться в первую очередь, вы можете воспользоваться следующими шагами:
-
Настройте порядок объединения зависимостей – для этого вы можете использовать параметр
assembly / mergeStrategy
. Он позволяет указать, как действовать с конфликтующими классами. -
Обновите файл
build.sbt
следующим образом:
version := "0.1.0-SNAPSHOT"
organization := "com.test"
scalaVersion := "2.12.19"
name := "Maria"
Compile / mainClass := Some("com.test.Maria")
assembly / mainClass := Some("com.test.Maria")
assembly / assemblyJarName := s"${name.value}-${version.value}.jar"
libraryDependencies ++= Seq(
"org.scala-lang.modules" %% "scala-parser-combinators" % "2.3.0",
"org.mariadb.jdbc" % "mariadb-java-client" % "2.7.12"
)
excludeDependencies ++= Seq(
ExclusionRule("org.slf4j", "slf4j-api"),
ExclusionRule("org.slf4j", "jcl-over-slf4j")
)
// Добавляем mergeStrategy для правильной обработки классов
assembly / mergeStrategy := {
case PathList("META-INF", xs @ _*) => MergeStrategy.discard // Удаляем дубликаты файлов META-INF
case "reference.conf" => MergeStrategy.concat // Объединяем файлы конфигурации
case x =>
val isDuplicateClass = Seq("org.mariadb.jdbc.Driver").exists(x.contains)
if (isDuplicateClass) MergeStrategy.first // Сохраняем класс из JAR с MariaDB драйвером
else MergeStrategy.first
}
Объяснение настроек mergeStrategy
-
*PathList("META-INF", xs @ _) => MergeStrategy.discard**: Удаляет все дубликаты файлов в каталоге META-INF, так как они часто не нужны для работы приложения и могут вызвать проблемы.
-
case "reference.conf" => MergeStrategy.concat: Удаляет конфликты в файлах конфигурации
reference.conf
, объединяя их. -
val isDuplicateClass = Seq("org.mariadb.jdbc.Driver").exists(x.contains): Этот участок кода проверяет, является ли текущий класс соответствующим классу драйвера MariaDB, и если это так, сохраняет его как первый.
Итоги
С помощью настройки mergeStrategy
вы можете гарантировать, что нужный класс будет использоваться. При выполнении вышеуказанных шагов вы должны избежать проблем, связанных с конфликтом библиотек при использовании sbt-assembly, и успешно подключаться к MySQL 8 с помощью MariaDB Connector/J.
Если после этих изменений проблема не решится, рекомендую также проверить конфигурацию вашего подключаемого сервера MySQL, убедившись, что он настроен на использование поддерживаемого механизма аутентификации.