Scala项目sbt-assembly导致MariaDB驱动连接MySQL 8失败求助
我正在将Scala 2.12项目中的MySQL Connector/J替换为MariaDB Connector/J 2.7.12。原本以为是MariaDB驱动存在bug,经过一周排查发现,实际问题是sbt-assembly的合并逻辑替换了相关库,导致MariaDB驱动连接MySQL 8时抛出错误。
报错信息
java.sql.SQLNonTransientConnectionException: Could not connect to address=(host=192.168.240.7)(port=3306)(type=master) : Client does not support authentication protocol requested by server. plugin type was = 'caching_sha2_password'
at org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.createException(ExceptionFactory.java:73)
我确认不是驱动本身的问题:在一个小型测试应用中,使用相同的驱动、Scala和Java版本,移除build.sbt中的合并逻辑并手动排除重复依赖后可以正常运行。但实际应用依赖的库更多,没法这么简单处理。
请问是否有办法确保sbt-assembly优先选用MariaDB Connector/J jar中的类?如果没有,有没有包含该驱动的可用build.sbt示例?
已尝试操作
- 替换库;
- 尝试不同版本的MariaDB Connector/J;
- 使用mysql_native_password认证插件时可以连接MySQL 8;
- 可以连接MySQL 5.7服务器,但默认情况下无法连接MySQL 8服务器。
期望实现
用MariaDB驱动无缝替换MySQL驱动。
补充测试代码
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("Start...") // DriverManager.registerDriver(new org.mariadb.jdbc.Driver()) Class.forName("org.mariadb.jdbc.Driver") println("Registered driver") connection = DriverManager.getConnection( "jdbc:mariadb://maria-mysql2-1:3306/test?allowPublicKeyRetrieval=true", "root", "password") println(s"Connection valid: ${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("Close...") connection.close() println(s"Connection valid: ${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") )
注:若移除excludeDependencies部分并替换为标准合并逻辑,就会出现连接错误。
1. 配置sbt-assembly优先选用MariaDB的类
核心是通过assemblyMergeStrategy指定MariaDB相关包的优先级,确保合并时保留MariaDB驱动的类,覆盖其他依赖中可能存在的冲突类(比如旧MySQL驱动的残留类)。
在build.sbt中添加以下合并策略:
assemblyMergeStrategy in assembly := { // 优先保留MariaDB驱动的所有类 case PathList("org", "mariadb", xs @ _*) => MergeStrategy.first // 处理JDBC驱动服务配置文件,避免合并冲突 case "META-INF/services/java.sql.Driver" => MergeStrategy.filterDistinctLines // 其他默认合并策略 case x => val oldStrategy = (assemblyMergeStrategy in assembly).value oldStrategy(x) }
2. 完整的build.sbt示例
结合依赖排除和合并策略,确保MariaDB驱动正常工作:
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", // 引入MariaDB驱动,同时排除可能冲突的依赖 "org.mariadb.jdbc" % "mariadb-java-client" % "2.7.12" excludeAll( ExclusionRule("org.slf4j", "slf4j-api"), ExclusionRule("org.slf4j", "jcl-over-slf4j") ) ) // 配置assembly合并策略,优先保留MariaDB的类 assemblyMergeStrategy in assembly := { case PathList("org", "mariadb", xs @ _*) => MergeStrategy.first case "META-INF/services/java.sql.Driver" => MergeStrategy.filterDistinctLines // 处理其他常见冲突文件 case "META-INF/LICENSE" => MergeStrategy.first case "META-INF/NOTICE" => MergeStrategy.first case x => val oldStrategy = (assemblyMergeStrategy in assembly).value oldStrategy(x) }
3. 额外注意事项
- 用
sbt dependencyTree命令检查项目依赖,确保没有残留的MySQL Connector/J依赖,如有则手动排除; - JDBC URL必须使用
jdbc:mariadb://前缀,不要混用jdbc:mysql://; - 连接参数
allowPublicKeyRetrieval=true要保留,这是MySQL 8使用caching_sha2_password认证时的必要参数。
内容的提问来源于stack exchange,提问作者AndrewBurton

