批处理打包JavaFX+JDBC项目报MySQL无合适驱动错误求助
问题说明
基于JavaFX开发的JDBC连接MySQL应用,在NetBeans IDE环境中运行完全正常,自行通过批处理脚本完成代码编译、自定义JRE制作、JAR打包流程后启动,抛出如下错误:
SQLException on database connection: No suitable driver found for jdbc:mysql://localhost:3306/smtbiz
SQLState: 08001
已确认将mysql-connector.jar加入项目依赖库、配置了编译阶段类路径,查阅相关资料仍未定位根因。
问题根因
报错本质是运行时JVM未加载到MySQL JDBC驱动,由三个脚本配置漏项共同导致:
- 编译阶段未将MySQL驱动加入类路径,IDE内置的类路径配置不会自动传递到命令行编译流程
- 启动脚本使用
-jar参数启动时,会忽略全局CLASSPATH环境变量和Manifest外的类路径配置,原脚本未将lib目录下的mysql-connector.jar纳入运行时类路径,JVM根本找不到驱动包 - 自定义精简JRE场景下,JDBC的SPI自动驱动注册机制容易失效,未主动触发驱动类加载时,即使驱动包存在也无法被DriverManager识别
相关代码与原脚本
主类代码
public class CustomerManagementGUI extends Application { @Override public void start(Stage stage) throws Exception { Parent root = FXMLLoader.load(getClass().getResource("CustomerManagementUI.fxml")); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } public static void main(String[] args) { //launch(args); Scanner sc = new Scanner(System.in); CustomerManagementGUI.launch(args); CreateDatabase.createCustomerDB(); }
数据库工具类代码
public class DBUtil { private static final String URL_DB = "jdbc:mysql://localhost:3306/smtbiz"; private static final String USER = "root"; private static final String PASSWORD = ""; private static Connection con = null; public static void connectDatabase() { try { con = DriverManager.getConnection(URL_DB, USER, PASSWORD); con.setAutoCommit(false); } catch (SQLException ex) { System.out.println("SQLException on database connection: " + ex.getMessage()); System.out.println("SQLState: " + ex.getSQLState()); System.out.println("VendorError: " + ex.getErrorCode()); } } public static void closeDatabase() { try { if (con != null && !con.isClosed()) { con.close(); } } catch (SQLException ex) { System.out.println("SQLException on database close: " + ex.getMessage()); } } public static ResultSet executeQuery(String queryStmt) { Statement stmt = null; ResultSet resultSet = null; CachedRowSet crs = null; try { connectDatabase(); stmt = con.createStatement(); resultSet = stmt.executeQuery(queryStmt); crs = RowSetProvider.newFactory().createCachedRowSet(); crs.populate(resultSet); } catch (SQLException ex) { System.out.println("SQLException on executeQuery: " + ex.getMessage()); } finally { try { if (resultSet != null) { resultSet.close(); } if (stmt != null) { stmt.close(); } closeDatabase(); } catch (SQLException ex) { System.out.println("SQLException caught on database closing: " + ex.getMessage()); } } return crs; } public static int executeUpdate(String sqlStmt) { Statement stmt = null; int count; try { connectDatabase(); stmt = con.createStatement(); count = stmt.executeUpdate(sqlStmt); con.commit(); return count; } catch (SQLException ex) { System.out.println("SQLException on executeUpdate: " + ex.getMessage()); return 0; } finally { try { if (stmt != null) { stmt.close(); } closeDatabase(); } catch (SQLException ex) { System.out.println("SQLException caught on database closing: " + ex.getMessage()); } } }
DAO数据访问类代码
public class CustomerDAO { public static void insertCustomer(String name, String email, String mobile) { String insertSQL = String.format("INSERT INTO customer (Name, Email, Mobile) VALUES ('%s', '%s', '%s');", name, email, mobile); int count = DBUtil.executeUpdate(insertSQL); if (count == 0) { System.out.println("Failed to add new customer."); } else { System.out.println("\nNew customer added successfully."); } } public static void deleteCustomer(int customerID) { String deleteSQL = "DELETE FROM customer WHERE id='" + customerID + "';"; int count = DBUtil.executeUpdate(deleteSQL); if (count == 0) { System.out.println("ID not found, delete unsuccessful."); } else { System.out.println("\nID successfully deleted."); } } public static void editCustomer(int customerID, String customerName, String customerEmail, String customerMobile) { String editCustomer = "UPDATE customer " + "SET name = " + "\"" + customerName + "\", " + "email = " + "\"" + customerEmail + "\", " + "mobile = " + "\"" + customerMobile + "\" " + "WHERE ID = " + "" + customerID + ";"; int count = DBUtil.executeUpdate(editCustomer); if (count == 0) { System.out.println("ID not found, edit unsuccessful."); } else { System.out.println("\nID successfully edited."); } } public static Customer searchCustomerID(int id) throws SQLException { String query = "SELECT * FROM customer WHERE ID =" + id + ";"; Customer c = null; try { ResultSet rs = DBUtil.executeQuery(query); if (rs.next()) { System.out.println("\nCustomer found: "); c = new Customer(); c.setId(rs.getInt("ID")); c.setName(rs.getString("Name")); c.setEmail(rs.getString("Email")); c.setMobile(rs.getString("Mobile")); } else if (c == null){ System.out.println("Unfortunately that customer ID: " + id + " was not found."); } else { System.out.println("Unfortunately that customer ID: " + id + " was not found."); } } catch (SQLException ex) { System.out.println("SQLException on executeQuery: " + ex.getMessage()); } return c; } public static ObservableList<Customer> getAllCustomers() throws ClassNotFoundException, SQLException { String query = "SELECT * FROM customer;"; try { ResultSet rs = DBUtil.executeQuery(query); ObservableList<Customer> customerDetails = getCustomerModelObjects(rs); return customerDetails; } catch (SQLException ex) { System.out.println("Error!"); ex.printStackTrace();; throw ex; } } public static ObservableList<Customer> getCustomerModelObjects(ResultSet rs) throws SQLException, ClassNotFoundException { try { ObservableList<Customer> customerList = FXCollections.observableArrayList(); while (rs.next()) { Customer customer = new Customer(); customer.setId(rs.getInt("ID")); customer.setName(rs.getString("Name")); customer.setEmail(rs.getString("Email")); customer.setMobile(rs.getString("Mobile")); customerList.add(customer); } return customerList; } catch (SQLException ex) { throw ex; } }
注:客户实体类代码因篇幅原因未附完整内容
数据库创建类代码
public class CreateDatabase { public static void createCustomerDB() { String url = "jdbc:mysql://localhost:3300/"; // no database yet String user = "root"; String password = ""; Connection con = null; Statement stmt = null; String query; ResultSet result = null; try { con = DriverManager.getConnection(url, user, password); stmt = con.createStatement(); query = "DROP DATABASE IF EXISTS smtbiz;"; stmt.executeUpdate(query); query = "CREATE DATABASE smtbiz;"; stmt.executeUpdate(query); query = "USE smtbiz;"; stmt.executeUpdate(query); query = """ CREATE TABLE customer ( ID INTEGER NOT NULL AUTO_INCREMENT, Name VARCHAR(32), Email VARCHAR(25), Mobile VARCHAR(15), PRIMARY KEY(ID) ); """; stmt.executeUpdate(query); query = """ INSERT INTO customer (Name,Email,Mobile) VALUES ("Kyle","Kyle.Henry@gmail.com","0489358318"), ("John","John.Potter@outlook.com","0460358348"), ("Liam","Liam.Stone@hotmail.com","0417335874"), ("Argon","Argon.Chung@gmail.com","0422358320"), ("Kris","Kris.Frank@gmail.com","0494358923"); """; stmt.executeUpdate(query); query = "SELECT * FROM customer;"; result = stmt.executeQuery(query); // execute the SQL query } catch (SQLException ex) { System.out.println("SQLException on database creation: " + ex.getMessage()); } finally { try { if (result != null) { result.close(); } if (stmt != null) { stmt.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { System.out.println("SQLException caught: " + ex.getMessage()); } } }
原使用的批处理脚本
编译脚本
@echo off javac --module-path %PATH_TO_FX% --add-modules=javafx.base,javafx.controls,javafx.fxml,javafx.graphics src\customermanagementgui\*.java -d classes copy src\customermanagementgui\*.fxml classes\customermanagementgui\*.fxml pause
JAR打包脚本
@echo off md app jar --create --file=app/CustomerManagementGUI.jar --main-class=customermanagementgui.CustomerManagementGUI -m Manifest.mf -C classes . REM md app\lib REM copy lib\mysql-connector-java.jar app\lib xcopy .\lib\ .\app\lib /E /I pause
自定义JRE生成脚本
@echo off jdeps -s --module-path %PATH_TO_FX% app\CustomerManagementGUI.jar jlink --module-path ../jmods;%PATH_TO_FX_JMOD% --add-modules java.base,java.sql,java.sql.rowset,javafx.base,javafx.controls,javafx.fxml,javafx.graphics --output jre echo ############ echo # Finished # echo ############ pause
应用启动脚本
@echo off REM java -classpath classes customermanagementgui.CustomerManagementGUI jre\bin\java -jar app/CustomerManagementGUI.jar pause
修复方案
按以下步骤修改即可解决问题:
- 修改编译脚本,将MySQL驱动加入类路径,把编译行调整为:
javac --module-path %PATH_TO_FX% -cp "lib/*" --add-modules=javafx.base,javafx.controls,javafx.fxml,javafx.graphics src\customermanagementgui\*.java -d classes - 修改Manifest.mf文件,添加类路径配置,确保打包后能识别lib目录下的驱动,在文件中新增一行
Class-Path: lib/mysql-connector-java.jar,注意文件末尾必须保留一个空行 - 主动加载MySQL驱动类,避免自定义JRE下SPI自动注册失效,在
DBUtil类中添加静态代码块:static { try { // 8.x版本驱动用com.mysql.cj.jdbc.Driver,5.x版本用com.mysql.jdbc.Driver Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { System.out.println("MySQL driver not found: " + e.getMessage()); } } - 修改启动脚本,显式指定类路径,避免
-jar参数覆盖类路径配置,把启动行调整为:jre\bin\java -cp "app/CustomerManagementGUI.jar;app/lib/*" customermanagementgui.CustomerManagementGUI - (可选)如果需要把MySQL驱动直接打包进自定义JRE,在jlink的
--module-path参数后追加lib目录下mysql驱动的路径,同时在--add-modules中加入MySQL驱动对应的模块名即可。
内容的提问来源于stack exchange,提问作者TurtleTaco
相关产品推荐
相关产品推荐

