javafxjdbcmodulemysql-connector

MySQL driver does not follow when project folder copied


My project uses JavaFX and MySQL Connector/J 9, which is located in the lib folder within the IDEA project folder. I back up this project daily by copying the IDEA project folder and renaming it.

When I open the new folder in the IDE, the first build/run fails with error "No suitable driver found." This is despite the driver showing as present in External Libraries. I have to go to Project Structure, Libraries, scroll to MySQL-connector, right-click and choose "Add to Modules". Then it builds/runs fine.

I don't understand why when I copy the entire folder the MySQL connector gets lost from modules. Is there some way to get the connector into modules so it stays there?

When this first started happening I was able to troubleshoot as far as finding the need to Add to Modules, but this happens every time and I haven't been able to take it further.

POM.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>JE.Spex</groupId>
    <artifactId>Drift</artifactId>
    <version>1.0</version>
    <name>Drift</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <junit.version>5.8.2</junit.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>18</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>18</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-web</artifactId>
            <version>18</version>
        </dependency>
        <dependency>
            <groupId>org.controlsfx</groupId>
            <artifactId>controlsfx</artifactId>
            <version>11.1.1</version>
        </dependency>
        <dependency>
            <groupId>com.dlsc.formsfx</groupId>
            <artifactId>formsfx-core</artifactId>
            <version>11.3.2</version>
            <exclusions>
                <exclusion>
                    <groupId>org.openjfx</groupId>
                    <artifactId>*</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.kordamp.ikonli</groupId>
            <artifactId>ikonli-javafx</artifactId>
            <version>12.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.kordamp.bootstrapfx</groupId>
            <artifactId>bootstrapfx-core</artifactId>
            <version>0.4.0</version>
        </dependency>
        <dependency>
            <groupId>eu.hansolo</groupId>
            <artifactId>tilesfx</artifactId>
            <version>11.48</version>
            <exclusions>
                <exclusion>
                    <groupId>org.openjfx</groupId>
                    <artifactId>*</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jetbrains</groupId>
            <artifactId>annotations</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>uk.co.caprica</groupId>
            <artifactId>vlcj</artifactId>
            <version>4.8.3</version>
        </dependency>
        <dependency>
            <groupId>uk.co.caprica</groupId>
            <artifactId>vlcj-javafx</artifactId>
            <version>1.2.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
             <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.10.1</version>
                <configuration>
                    <source>18</source>
                    <target>18</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.8</version>
                <executions>
                    <execution>
                        <!-- Default configuration for running with: mvn clean javafx:run -->
                        <id>default-cli</id>
                        <configuration>
                            <mainClass>je.spex.MainApp</mainClass>
                            <launcher>app</launcher>
                            <jlinkZipName>app</jlinkZipName>
                            <jlinkImageName>app</jlinkImageName>
                            <noManPages>true</noManPages>
                            <stripDebug>true</stripDebug>
                            <noHeaderFiles>true</noHeaderFiles>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.6.0</version>
                <configuration>
                    <!-- put your configurations here -->
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>je.spex.Main</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

module-info.java

module je.spex.Drift {
    requires javafx.controls;
    requires javafx.fxml;
    requires javafx.web;

    requires org.controlsfx.controls;
    requires com.dlsc.formsfx;
    requires org.kordamp.ikonli.javafx;
    requires org.kordamp.bootstrapfx.core;
    requires eu.hansolo.tilesfx;
    requires java.sql;
    requires java.desktop;
    requires java.net.http;
    requires org.jetbrains.annotations;
    requires uk.co.caprica.vlcj;

    opens je.spex.Drift to javafx.fxml;
    exports je.spex.Drift;
}

Solution

  • Issues with your approach

    You have multiple issues:

    1. You don't have a dependency on the MySQL driver in your pom.xml.
      • This means that the required jars for the driver will not be included in your Idea project when you synchronize it with Maven.
    2. You don't require the MySQL driver in your module-info.java.
      • This means that even if the driver is provided as a Maven dependency, your Java platform module rules don't provide access to the driver code.
      • Slaw mentions in comments:

      the requires mysql.connector.j is only needed if you want to directly use code from the (automatic) module (which your example does). If you only use it via the SPI/API of java.sql, then you can forgo the requires directive (though you'll still need to requires java.sql).

    3. You are shading a modular project.
      • This means that you will lose Java platform modularity and (even if it worked) require running JavaFX in an unsupported (non-modular) mode.

    Required fixes

    In pom.xml add:

    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>9.0.0</version>
    </dependency>
    

    In module-info.java add:

    requires mysql.connector.j;
    

    Example project

    This example project fixes the above-identified issues.

    mysql driver version

    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>org.example</groupId>
        <artifactId>mysqlfx</artifactId>
        <version>1.0-SNAPSHOT</version>
        <name>mysqlfx</name>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-controls</artifactId>
                <version>23</version>
            </dependency>
            <dependency>
                <groupId>com.mysql</groupId>
                <artifactId>mysql-connector-j</artifactId>
                <version>9.0.0</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.13.0</version>
                    <configuration>
                        <source>21</source>
                        <target>21</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    

    src/main/java/module-info.java

    The MySQL driver as of version 9 is unfortunately still an automatic module, so it cannot be used with packaging tools such as jlink. As an automatic module without any module info in its metadata, the module name mysql.connector.j is derived from the jar file name.

    module org.example.mysqlfx {
        requires javafx.controls;
        requires mysql.connector.j;
        requires java.sql;
    
        exports org.example.mysqlfx;
    }
    

    src/main/java/org/example/mysqlfx/MySqlDriverVersionApp

    Normally you wouldn't directly instantiate an SQL driver like this. Modern JDBC implementations are smart enough to determine the driver to use from the JDBC URL. But for demo purposes, I access the driver directly here.

    package org.example.mysqlfx;
    
    import com.mysql.cj.jdbc.Driver;
    import javafx.application.Application;
    import javafx.geometry.Insets;
    import javafx.scene.Scene;
    import javafx.scene.control.Label;
    import javafx.stage.Stage;
    
    import java.sql.SQLException;
    
    public class MySqlDriverVersionApp extends Application {
        @Override
        public void start(Stage stage) throws SQLException {
            Driver driver = new Driver();
            String driverVersion = driver.getMajorVersion() + "." + driver.getMinorVersion();
    
            Label info = new Label(
                    "MySQL Driver Version: " + driverVersion
            );
            info.setStyle("-fx-font-size:20px");
            info.setPadding(new Insets(20));
    
            stage.setScene(new Scene(info));
            stage.show();
        }
    
        public static void main(String[] args) {
            launch();
        }
    }
    

    Unrelated issues

    These issues aren't core to your main problem of MySQL connector driver access, but are unrelated things that you may consider addressing:

    1. You have many dependencies in your pom.xml and module-info.java that you likely aren't using -> remove them.
    2. You are providing a jlink configuration to your Maven JavaFX plugin.
    3. You are providing mainClass references to your Maven JavaFX plugin that are not qualified by your module name.
      • A module qualified class name would be modulename/classname, e.g. je.spex.Drift/je.spex.MainApp.
    4. Your module name does not follow naming conventions.
      • By convention, the module name should be lowercase (similar to the way packages are named), e.g. je.spex.drift instead of je.spex.Drift, though technically, both will work.