返回

WildFly+Hibernate MySQL "数据库未选择" 错误排查与解决

mysql

解决 WildFly + Hibernate + MySQL 环境下 "No database selected" 报错

部署用 Hibernate 和 MySQL 的 Java 应用到 WildFly 时,碰到了个头疼的错误日志:

GenerationTarget encountered exception accepting command : Error executing DDL "
    alter table exam_table
       drop
       foreign key FKgp6h92beial8lknuhckmukak8" via JDBC [No database selected]

看样子,Hibernate 在尝试执行数据定义语言 (DDL) 命令,比如修改表结构、删除外键之类的操作时,找不到要操作的数据库。但怪了,明明检查过 standalone.xml 里的连接 URL 配置没问题,数据库 examdb 也确实存在,persistence.xml 文件里引用的 JTA 数据源 java:jboss/datasources/MySQLDS 也对得上号。那这到底是哪儿出了岔子呢?

为啥会这样?刨根问底找原因

这个 "No database selected" 错误是 MySQL 数据库本身抛出来的,意思很明确:收到了 SQL 命令,但是不知道该在哪个数据库(Schema)上执行。

通常,JDBC 连接在建立时,会通过连接 URL jdbc:mysql://localhost:3306/examdb 里的 examdb 部分来指定默认数据库。后续这个连接上执行的 SQL 语句,就都会在这个 examdb 数据库上操作。

但是,咱们这个场景是在 WildFly 应用服务器里,使用了 JTA 数据源。连接是由 WildFly 的连接池管理的。Hibernate 通过 persistence.xml 里配置的 jta-data-source 来向 WildFly 要连接。

问题出在哪?有几种可能:

  1. WildFly 数据源配置细节有误 :虽然 URL 看起来对,但 standalone.xml 中数据源的其他配置可能干扰了数据库的选择。尤其是那个 <validation> 部分。
  2. Hibernate 的 DDL 自动生成 (hbm2ddl.auto) :你设置了 hibernate.hbm2ddl.autocreate。这意味着 Hibernate 会尝试在应用启动时先删除旧表(如果存在),再创建新表。这个删表、建表的过程涉及多条 DDL 语句。可能在这个过程中,获取到的 JDBC 连接状态出了问题,导致执行到 alter table ... drop foreign key 时,连接失去了选定的数据库上下文。
  3. JDBC 驱动兼容性或 Bug :虽然可能性相对小,但特定版本的 MySQL JDBC 驱动和 WildFly 或 Hibernate 组合时,也可能存在某些边界情况下的问题。
  4. 事务管理 :在 JTA 环境下,事务边界可能影响连接的状态,但对于 DDL 操作来说,影响通常没那么直接。

综合来看,最可疑的是 WildFly 数据源配置中的验证查询 以及 hbm2ddl.auto 设为 create 的影响。咱们提供的 standalone.xml 配置里,那个 <check-valid-connection-sql>USE examdb; SELECT 1</check-valid-connection-sql> 非常扎眼。

为什么说它扎眼?

  • 连接验证的目的 :验证 SQL 的作用是检查连接池里的连接是否还是“活”的,能不能正常跟数据库通信。通常一个简单的 SELECT 1 就够了。
  • USE examdb 的冗余和风险 :连接 URL 里已经指定了 examdb,连接建立后就应该默认在这个库上操作。在验证 SQL 里再执行 USE examdb 不仅多余,还可能带来副作用。不同的 JDBC 驱动或连接池实现,对这种包含多语句或特定命令(如 USE)的验证 SQL 处理方式可能不同。它甚至可能干扰连接状态,或者在某些情况下验证失败导致连接被错误地丢弃或回收。

所以,咱们的排查和解决思路就清晰了:先从最可疑的数据源配置下手。

对症下药:几种靠谱的解决办法

下面提供几个步骤,一步步来排查和解决这个问题。

1. 检查并修正 WildFly 数据源配置 (关键一步)

这是最可能解决问题的步骤。重点是检查并修正 standalone.xml 里数据源的 <validation> 配置。

原理和作用:

确保 WildFly 数据源能正确建立到 examdb 数据库的连接,并且连接池的验证机制不会干扰正常的数据库上下文。连接 URL 里的数据库名应该是指定数据库的主要方式,验证 SQL 只需确认连接可用即可。

操作步骤:

  1. 打开 WildFly 的 standalone/configuration/standalone.xml 文件 (或者你使用的其他配置文件)。

  2. 找到 <subsystem xmlns="urn:jboss:domain:datasources:..."> 部分。

  3. 定位到你的 MySQL 数据源配置,即 jndi-name="java:jboss/datasources/MySQLDS"<datasource> 元素。

  4. 仔细检查 <connection-url> 确保 examdb 数据库名称正确无误,并且格式符合 jdbc:mysql://<host>:<port>/<database_name>?<params>

  5. 关键修改: 找到 <validation> 元素,修改或替换其中的 <check-valid-connection-sql>

    • 推荐做法: 使用简单、标准的验证查询。对于 MySQL,SELECT 1 通常是最佳选择。
    • 删除有问题的验证 SQL:USE examdb; SELECT 1 改为 SELECT 1

代码示例 (修改后的 <validation> 部分):

<datasource jndi-name="java:jboss/datasources/MySQLDS" pool-name="MySQLDS" enabled="true" use-java-context="true">
    <connection-url>jdbc:mysql://localhost:3306/examdb?useUnicode=true&amp;characterEncoding=UTF-8&amp;useSSL=false</connection-url>
    <driver>com.mysql</driver>
    <pool>
        <min-pool-size>5</min-pool-size>
        <max-pool-size>20</max-pool-size>
    </pool>
    <security>
        <user-name>root</user-name>
        <password>pwd</password> <!-- 强烈建议使用更安全的方式管理密码,例如 Vault -->
    </security>
    <validation>
        <!-- 修改这里! -->
        <check-valid-connection-sql>SELECT 1</check-valid-connection-sql>
        <!-- 保留其他有用的验证设置 -->
        <validate-on-match>false</validate-on-match> <!-- 通常设为 false,除非有特殊需求 -->
        <background-validation>true</background-validation>
        <background-validation-millis>10000</background-validation-millis>
        <!-- 可选: 配置 staled-connection-checker 和 exception-sorter -->
        <!--
        <stale-connection-checker class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLStaleConnectionChecker"/>
        <exception-sorter class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter"/>
        -->
    </validation>
    <!-- 根据需要,还可以添加其他超时设置等 -->
    <!--
    <timeout>
        <blocking-timeout-millis>30000</blocking-timeout-millis>
        <idle-timeout-minutes>5</idle-timeout-minutes>
    </timeout>
    -->
</datasource>

安全建议:

  • 配置里的 user-namepassword 直接写在 standalone.xml 中风险较高。考虑使用 WildFly 的 Elytron 子系统、Vault 集成或者环境变量等方式来管理数据库凭据,避免明文密码暴露。

修改完 standalone.xml 后,需要 重启 WildFly 服务器 使配置生效。然后重新部署你的应用,看看错误是否消失。

2. 调整 Hibernate hbm2ddl.auto 策略 (或者干脆不用?)

如果修改数据源配置后问题依旧,或者你想更规范地管理数据库结构,可以考虑调整 Hibernate 的 DDL 策略。

原理和作用:

hibernate.hbm2ddl.auto 属性控制 Hibernate 如何处理数据库 Schema。

  • create: 每次启动都删除旧 Schema (如果存在) 并创建新的。非常适合快速原型开发,但在测试和生产环境极具风险 (丢失数据!)。本次报错可能就与 create 过程中的 drop 操作有关。
  • create-drop: 启动时创建 Schema,应用停止时删除 Schema。仅用于测试。
  • update: 启动时检查 Schema,尝试添加缺失的表、列等。不推荐 在生产环境使用,因为它不能处理复杂的变更(比如重命名列、删除列),可能导致 Schema 不一致。
  • validate: 启动时检查实体映射和数据库 Schema 是否匹配,如果不匹配则报错。适合开发后期和生产环境,确保代码和数据库结构一致。
  • none: 不做任何操作。适用于数据库 Schema 由外部工具(如 Flyway、Liquibase)或 DBA 手动管理的场景,是生产环境推荐 的方式。

操作步骤:

  1. 打开 src/main/resources/META-INF/persistence.xml 文件。
  2. 找到 <properties> 部分。
  3. 修改 hibernate.hbm2ddl.autovalue

代码示例 (改为 validatenone):

将:

<property name="hibernate.hbm2ddl.auto" value="create" />

改为 (推荐用于开发后期和测试):

<property name="hibernate.hbm2ddl.auto" value="validate" />

或者 (推荐用于生产环境,或当 Schema 由其他方式管理时):

<property name="hibernate.hbm2ddl.auto" value="none" />

进阶使用技巧:

  • 使用数据库迁移工具: 对于需要精细控制数据库版本和演进的应用,强烈推荐使用 Flyway 或 Liquibase。这些工具可以让你用 SQL 或 XML/YAML 文件定义数据库结构变更,进行版本控制,并能安全地在不同环境应用这些变更。将 hibernate.hbm2ddl.auto 设为 validatenone,然后集成 Flyway/Liquibase 来处理 Schema。

修改 persistence.xml 后,重新构建并部署你的 .war 文件。如果选用 validate,你需要确保数据库中已存在符合实体定义的表结构。如果选用 none,同样需要确保表结构已存在。

3. 核对 MySQL JDBC 驱动

检查 WildFly 中安装和配置的 MySQL JDBC 驱动是否正确。

原理和作用:

应用服务器需要通过正确的 JDBC 驱动程序才能与数据库通信。驱动版本、配置方式都可能影响连接行为。

操作步骤:

  1. 确认驱动已部署为 WildFly 模块: 通常 MySQL JDBC 驱动 (mysql-connector-java-*.jar) 需要被放置在 WildFly 的 modules 目录下特定的路径结构中 (例如 modules/com/mysql/main),并包含一个 module.xml 文件来定义模块。
    <!-- 示例 module.xml (路径: modules/com/mysql/main/module.xml) -->
    <module xmlns="urn:jboss:module:1.8" name="com.mysql">
        <resources>
            <resource-root path="mysql-connector-java-8.0.x.jar"/> <!-- 使用你实际的 jar 文件名 -->
        </resources>
        <dependencies>
            <module name="jakarta.transaction.api"/>
            <module name="jakarta.resource.api"/>
        </dependencies>
    </module>
    
  2. 检查 standalone.xml 中的驱动定义: 确保 <drivers> 部分的定义指向了正确的模块,并且 <driver-class> (对于新版驱动,通常是 com.mysql.cj.jdbc.Driver) 正确。
    <drivers>
        <driver name="com.mysql" module="com.mysql"> <!-- name 要和 datasource 里 driver 元素内容一致 -->
            <driver-class>com.mysql.cj.jdbc.Driver</driver-class>
            <!-- 可选,但推荐为较新驱动添加 datasource-class -->
            <xa-datasource-class>com.mysql.cj.jdbc.MysqlXADataSource</xa-datasource-class>
        </driver>
    </drivers>
    
    (你提供的配置中包含了 datasource-class,这对于非 XA 数据源也是可以的,但 xa-datasource-class 在使用 XA 事务时是必需的。)
  3. 确认版本兼容性: 查阅 Hibernate、WildFly 和 MySQL 官方文档,确认你使用的 MySQL JDBC 驱动版本与这三者都兼容。有时过于老旧或过于激进的新版本驱动都可能带来问题。

进阶使用技巧:

  • 避免类加载冲突: 确保你的 WAR 包里没有包含 JDBC 驱动 JAR 文件 (WEB-INF/lib),驱动应该由 WildFly 服务器的模块提供,以避免类加载器冲突。

如果发现驱动配置或版本有问题,进行修正后需要重启 WildFly 并重新部署应用。

4. 明确指定数据库 Catalog/Schema (Hibernate 配置)

这是一个补充性的方法,通常在数据源配置正确的情况下不是必需的,但有时能解决一些边缘问题。

原理和作用:

可以通过 Hibernate 的配置属性,明确告诉它操作哪个数据库目录(Catalog)或模式(Schema)。对于 MySQL,通常是指定 Catalog。这可以覆盖或补充 JDBC 连接可能未能正确传递的信息。

操作步骤:

  1. 打开 src/main/resources/META-INF/persistence.xml 文件。
  2. <properties> 部分添加 hibernate.default_catalog 属性。

代码示例:

<properties>
    <!-- ... 其他属性 ... -->
    <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
    <property name="hibernate.hbm2ddl.auto" value="create" /> <!-- 或者 validate / none -->
    <property name="hibernate.show_sql" value="true" />
    <property name="hibernate.format_sql" value="true" />

    <!-- 添加这一行 -->
    <property name="hibernate.default_catalog" value="examdb" />

    <!-- ... 其他属性 ... -->
</properties>

注意:

  • 对于 MySQL 来说,catalogschema 经常可以互换使用,指的就是数据库名。
  • 这个设置通常是最后的手段,或者在需要连接到非默认数据库且无法修改连接 URL 时使用。优先确保 WildFly 数据源配置(特别是连接 URL)的正确性。

添加此属性后,重新构建并部署应用。


通常情况下,第一步修正 WildFly 数据源的验证 SQL 配置 是解决 "No database selected" 问题的最直接有效的方法。结合第二步调整 hbm2ddl.auto 策略(特别是避免在开发环境之外使用 create),可以进一步提高应用的健壮性和安全性。如果问题仍然存在,再考虑检查驱动和尝试指定默认 Catalog。