Search code examples
javaspringjavafx

spring with javafx exception when load fxml


Maybe the question is stupid. But I haven't worked with JavaFX before, so I'm asking for help. I tried to create an application that allows you to create a document by clicking on the button.But when I try to run it, I get the following error:

2023-11-06T10:52:27.409+05:00  INFO 17156 --- [JavaFX-Launcher] o.s.boot.SpringApplication               : Starting application using Java 17.0.2 with PID 17156 (started by днс in E:\MaximDocuments)
2023-11-06T10:52:27.412+05:00  INFO 17156 --- [JavaFX-Launcher] o.s.boot.SpringApplication               : No active profile set, falling back to 1 default profile: "default"
2023-11-06T10:52:27.486+05:00  INFO 17156 --- [JavaFX-Launcher] o.s.boot.SpringApplication               : Started application in 0.454 seconds (process running for 1.741)
javafx.fxml.LoadException: 
/E:/MaximDocuments/target/classes/mainWindow.fxml:10

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2727)
    at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:946)
    at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:983)
    at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:230)
    at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:757)
    at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2858)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2654)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2568)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2536)
    at documents.JavaFxApplication.start(JavaFxApplication.java:32)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:839)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:483)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:456)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:455)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:185)
    at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'documents.controller.MainWindowController' available
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:341)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:332)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1176)
    at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:943)
    ... 17 more

Here are my classes:

mainWindow.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>

<BorderPane xmlns:fx="http://javafx.com/fxml" fx:controller="documents.controller.MainWindowController">
    <top>
        <HBox spacing="10" padding="10">
            <Button text="Счет" onAction="#handleInvoiceAction"/>
            <Button text="Платежка" fx:id="paymentButton" onAction="#handlePaymentAction"/>
            <Button text="Заказ на оплату" fx:id="paymentOrderButton" onAction="#handlePaymentOrderAction"/>
            <Button text="Сохранить" fx:id="saveButton" onAction="#handleSaveAction"/>
            <Button text="Загрузить" fx:id="loadButton" onAction="#handleLoadAction"/>
            <Button text="Просмотр" fx:id="viewButton" onAction="#handleViewAction"/>
        </HBox>
    </top>
    <center>
        <VBox spacing="10" padding="10">
            <ListView fx:id="documentListView" />
        </VBox>
    </center>
    <bottom>
        <HBox spacing="10" padding="10" alignment="bottom_right">
            <Button text="Выход" fx:id="exitButton" onAction="#handleExitAction"/>
        </HBox>
    </bottom>
</BorderPane>

MainWindowController:

package documents.controller;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import documents.model.DisplayableDocument;
import documents.model.Invoice;
import documents.model.Payment;
import documents.model.PaymentOrder;
import org.springframework.stereotype.Controller;

import java.io.*;
import java.math.BigDecimal;
import java.time.LocalDate;

@Controller
public class MainWindowController {

    private DisplayableDocument currentDocument;
    @FXML
    private void handleInvoiceAction(ActionEvent event) {
        loadWindow("/invoice.fxml", "Счет");
    }

    @FXML
    private void handlePaymentAction(ActionEvent event) {
        loadWindow("/payment.fxml", "Платеж");
    }

    @FXML
    private void handlePaymentOrderAction(ActionEvent event) {
        loadWindow("/paymentOrder.fxml", "Платежное поручение");
    }

    @FXML
    private void handleSaveAction(ActionEvent event) {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("Сохранить документ");
        fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Text Files", "*.txt"));
        File selectedFile = fileChooser.showSaveDialog(((Node) event.getSource()).getScene().getWindow());

        if (selectedFile != null) {
            saveDocumentToFile(selectedFile);
        }
    }

    private void saveDocumentToFile(File file) {
        if (currentDocument == null) {
            return;
        }
        try (FileWriter fileWriter = new FileWriter(file)) {
            if (currentDocument instanceof Invoice invoice) {
                fileWriter.write(convertInvoiceToString(invoice));
            } else if (currentDocument instanceof Payment payment) {
                fileWriter.write(convertPaymentToString(payment));
            } else if (currentDocument instanceof PaymentOrder paymentOrder) {
                fileWriter.write(convertPaymentOrderToString(paymentOrder));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private String convertInvoiceToString(Invoice invoice) {
        return String.format(
                "Invoice,%s,%s,%s,%s,%s,%s,%s,%s%n",
                invoice.getId(),
                invoice.getNumber(),
                invoice.getDate(),
                invoice.getUser(),
                invoice.getAmount(),
                invoice.getCurrency(),
                invoice.getCurrencyRate(),
                invoice.getProduct(),
                invoice.getQuantity()
        );
    }

    private String convertPaymentToString(Payment payment) {
        return String.format(
                "Payment,%s,%s,%s,%s,%s,%s%n",
                payment.getId(),
                payment.getNumber(),
                payment.getDate(),
                payment.getUser(),
                payment.getAmount(),
                payment.getEmployee()
        );
    }

    private String convertPaymentOrderToString(PaymentOrder paymentOrder) {
        return String.format(
                "PaymentOrder,%s,%s,%s,%s,%s,%s,%s,%s,%s%n",
                paymentOrder.getId(),
                paymentOrder.getNumber(),
                paymentOrder.getDate(),
                paymentOrder.getUser(),
                paymentOrder.getContractor(),
                paymentOrder.getAmount(),
                paymentOrder.getCurrency(),
                paymentOrder.getCurrencyRate(),
                paymentOrder.getCommission()
        );
    }

    @FXML
    private void handleLoadAction(ActionEvent event) {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("Загрузить документ");
        fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Text Files", "*.txt"));
        File selectedFile = fileChooser.showOpenDialog(((Node) event.getSource()).getScene().getWindow());

        if (selectedFile != null) {
            loadDocumentFromFile(selectedFile);
        }
    }

    private void loadDocumentFromFile(File file) {
        try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.startsWith("Invoice:")) {
                    Invoice invoice = parseInvoice(String.valueOf(reader));
                } else if (line.startsWith("Payment:")) {
                    Payment payment = parsePayment(String.valueOf(reader));
                } else if (line.startsWith("PaymentOrder:")) {
                    PaymentOrder paymentOrder = parsePaymentOrder(String.valueOf(reader));
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private Invoice parseInvoice(String line) {
        String[] data = line.split(",");

        return new Invoice(
                Integer.parseInt(data[1]),
                data[2],
                LocalDate.parse(data[3]),
                data[4], 
                new BigDecimal(data[5]),
                data[6], 
                new BigDecimal(data[7]),
                data[8],
                Integer.parseInt(data[9])
        );
    }

    private Payment parsePayment(String line) {
        String[] data = line.split(",");

        return new Payment(
                Integer.parseInt(data[1]),
                data[2],
                LocalDate.parse(data[3]),
                data[4],
                new BigDecimal(data[5]),
                data[6]
        );
    }

    private PaymentOrder parsePaymentOrder(String line) {
        String[] data = line.split(",");

        return new PaymentOrder(
                Integer.parseInt(data[1]),
                data[2],
                LocalDate.parse(data[3]),
                data[4],
                data[5],
                new BigDecimal(data[6]),
                data[7],
                new BigDecimal(data[8]),
                new BigDecimal(data[9])
        );
    }


    @FXML
    private void handleViewAction(ActionEvent event) {
        loadWindow("/documentDetails.fxml", "Детали документа");
    }

    @FXML
    private void handleExitAction(ActionEvent event) {
        cancel(event);
    }

    private void loadWindow(String path, String title) {
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource(path));
            Parent root = loader.load();
            Stage stage = new Stage();
            stage.setTitle(title);
            stage.setScene(new Scene(root));
            stage.show();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void cancel(ActionEvent event) {
        Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow();
        stage.close();
    }
}

ApplicationRunner:

package documents;

import javafx.application.Application;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ApplicationRunner {

    public static void main(String[] args) {
        Application.launch(JavaFxApplication.class, args);
    }
}

JavaFxApplication :

package documents;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;

import java.io.IOException;


public class JavaFxApplication extends Application {

    private ConfigurableApplicationContext context;

    @Override
    public void init() {
        SpringApplicationBuilder builder = new SpringApplicationBuilder(ApplicationRunner.class);
        String[] args = getParameters().getRaw().toArray(new String[0]);
        this.context = builder.run(args);
    }

    @Override
    public void start(Stage stage) {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/mainWindow.fxml"));
        fxmlLoader.setControllerFactory(context::getBean);

        try {
            Parent root = fxmlLoader.load();
            Scene scene = new Scene(root);
            stage.setScene(scene);
            stage.setTitle("Main Window");
            stage.show();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void stop() {
        this.context.close();
        Platform.exit();
    }
}

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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.maxim.documents</groupId>
    <artifactId>TestMaximDociments</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.4</version>
    </parent>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>22-ea+16</version>
        </dependency>

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>22-ea+16</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>8.0.1.Final</version>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.10.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.8</version>
                <configuration>
                    <mainClass>org/maxim/documents/JavaFxApplication</mainClass>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                </configuration>
            </plugin>


            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

I tried using other fxml files, but the error is the same. Adding a scheme, but it didn't help either. I read that the problem can occur if you use constructors. I tried to implement this through the fields, but the error didn't change.


Solution

  • You are trying to run the wrong ApplicationRunner.

    You have a class named documents.ApplicationRunner but instead you import org.springframework.boot.ApplicationRunner from Spring and try to use that to initialize your application. Your own ApplicationRunner is in your own package and initializes all the beans in the package and subpackage by default, but the ApplicationRunner from Spring doesn't know about your package and won't do that.

    To prevent confusion I recommend calling your ApplicationRunner something different (I used MySpringApplication in the example below but you could use some name specific to your application if you wanted).

    Reference

    Futher info on this topic is provided in the answer here:

    Working Code

    To create this, I created a new JavaFX project in Idea, removed the generated module-info.java to make the project non-modular (Java modularity and Spring 6 mix poorly unfortunately). Downloaded and extracted the JavaFX 21 SDK from Gluon. Right clicked on the class MySpringApplication to choose "More Run/Debug: Modify the Run Configuration..." set the VM parameters of the run configuration as:

    --module-path /Users/<username>/Downloads/javafx-sdk-21/lib --add-modules javafx.controls,javafx.fxml
    

    I modified the code to call your spring application MySpringApplication and import and use that in your JavaFXApplication.

    I removed all of the references to missing classes (most of that stuff is probably better handled in a service layer rather than in the Controller class anyway).

    I changed the database dependency to h2 from postgres so I didn't have to setup postgres.

    I removed the lombok dependency as I have seen other experienced developers note issues with it when used in the context of JavaFX and also recommend against its use in general.

    I modified the fxml so that it would load (it defined insets incorrectly, so I deleted those references).

    An FXML controller is not a Spring controller, which is for use in a Java Servlet style framework, don't annotate your FXML controller with the Spring @Controller annotation, a @Component annotation with prototype scope is probably best. (I used the recommended annotations in my example).

    Don't use the JavaFX versions with ea in the name, they are early access versions, which may not be stable. (I used a stable version - 21 - in my example).

    Outstanding issue

    There is another likely issue with your code, which I didn't try to address. Although you correctly set the controller factory to use the spring context to inject Spring bean references into the controller when you load the main window FXML:

    fxmlLoader.setControllerFactory(context::getBean);
    

    You don't set the controller factor for subsequent page loads which you perform in the MainWindowController.loadWindow method. This means that none of these subsequent page loads will have spring beans injected into their related controllers.

    I'll leave fixing this outstanding issue as an exercise for the reader ;-)

    Working Source Code

    src/main/java/documents/MySpringApplication.java

    package documents;
    
    import javafx.application.Application;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class MySpringApplication {
        public static void main(String[] args) {
            Application.launch(JavaFxApplication.class, args);
        }
    }
    

    src/main/java/documents/JavaFxApplication.java

    package documents;
    import javafx.application.Application;
    import javafx.application.Platform;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.stage.Stage;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    import org.springframework.context.ConfigurableApplicationContext;
    
    import java.io.IOException;
    
    
    public class JavaFxApplication extends Application {
    
        private ConfigurableApplicationContext context;
    
        @Override
        public void init() {
            SpringApplicationBuilder builder = new SpringApplicationBuilder(MySpringApplication.class);
            String[] args = getParameters().getRaw().toArray(new String[0]);
            this.context = builder.run(args);
        }
    
        @Override
        public void start(Stage stage) {
            FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/mainWindow.fxml"));
            fxmlLoader.setControllerFactory(context::getBean);
    
            try {
                Parent root = fxmlLoader.load();
                Scene scene = new Scene(root);
                stage.setScene(scene);
                stage.setTitle("Main Window");
                stage.show();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void stop() {
            this.context.close();
            Platform.exit();
        }
    }
    

    src/main/java/documents/controller/MainWindowController.java

    package documents.controller;
    
    import javafx.event.ActionEvent;
    import javafx.fxml.FXML;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Node;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.stage.FileChooser;
    import javafx.stage.Stage;
    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Component;
    
    import java.io.File;
    import java.io.IOException;
    
    import static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;
    
    @Component
    @Scope(value=SCOPE_PROTOTYPE)
    public class MainWindowController {
    
        @FXML
        private void handleInvoiceAction(ActionEvent event) {
            loadWindow("/invoice.fxml", "Счет");
        }
    
        @FXML
        private void handlePaymentAction(ActionEvent event) {
            loadWindow("/payment.fxml", "Платеж");
        }
    
        @FXML
        private void handlePaymentOrderAction(ActionEvent event) {
            loadWindow("/paymentOrder.fxml", "Платежное поручение");
        }
    
        @FXML
        private void handleSaveAction(ActionEvent event) {
            FileChooser fileChooser = new FileChooser();
            fileChooser.setTitle("Сохранить документ");
            fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Text Files", "*.txt"));
            File selectedFile = fileChooser.showSaveDialog(((Node) event.getSource()).getScene().getWindow());
    
            if (selectedFile != null) {
                saveDocumentToFile(selectedFile);
            }
        }
    
        private void saveDocumentToFile(File file) {
        }
    
        @FXML
        private void handleLoadAction(ActionEvent event) {
            FileChooser fileChooser = new FileChooser();
            fileChooser.setTitle("Загрузить документ");
            fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Text Files", "*.txt"));
            File selectedFile = fileChooser.showOpenDialog(((Node) event.getSource()).getScene().getWindow());
    
            if (selectedFile != null) {
    //            loadDocumentFromFile(selectedFile);
            }
        }
    
        @FXML
        private void handleViewAction(ActionEvent event) {
            loadWindow("/documentDetails.fxml", "Детали документа");
        }
    
        @FXML
        private void handleExitAction(ActionEvent event) {
            cancel(event);
        }
    
        private void loadWindow(String path, String title) {
            try {
                FXMLLoader loader = new FXMLLoader(getClass().getResource(path));
                Parent root = loader.load();
                Stage stage = new Stage();
                stage.setTitle(title);
                stage.setScene(new Scene(root));
                stage.show();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        private void cancel(ActionEvent event) {
            Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow();
            stage.close();
        }
    }
    

    src/main/resources/mainWindow.fxml

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.scene.control.Button?>
    <?import javafx.scene.control.ListView?>
    <?import javafx.scene.layout.BorderPane?>
    <?import javafx.scene.layout.HBox?>
    <?import javafx.scene.layout.VBox?>
    
    <BorderPane xmlns:fx="http://javafx.com/fxml" fx:controller="documents.controller.MainWindowController">
        <top>
            <HBox spacing="10">
                <Button text="Счет" onAction="#handleInvoiceAction"/>
                <Button text="Платежка" fx:id="paymentButton" onAction="#handlePaymentAction"/>
                <Button text="Заказ на оплату" fx:id="paymentOrderButton" onAction="#handlePaymentOrderAction"/>
                <Button text="Сохранить" fx:id="saveButton" onAction="#handleSaveAction"/>
                <Button text="Загрузить" fx:id="loadButton" onAction="#handleLoadAction"/>
                <Button text="Просмотр" fx:id="viewButton" onAction="#handleViewAction"/>
            </HBox>
        </top>
        <center>
            <VBox spacing="10">
                <ListView fx:id="documentListView" />
            </VBox>
        </center>
        <bottom>
            <HBox spacing="10" alignment="bottom_right">
                <Button text="Выход" fx:id="exitButton" onAction="#handleExitAction"/>
            </HBox>
        </bottom>
    </BorderPane>
    

    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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>org.maxim.documents</groupId>
        <artifactId>TestMaximDociments</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>3.1.4</version>
        </parent>
    
        <properties>
            <maven.compiler.source>17</maven.compiler.source>
            <maven.compiler.target>17</maven.compiler.target>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
        <dependencies>
    
            <dependency>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-controls</artifactId>
                <version>21.0.1</version>
            </dependency>
    
            <dependency>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-fxml</artifactId>
                <version>21.0.1</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
    <!--        <dependency>-->
    <!--            <groupId>org.postgresql</groupId>-->
    <!--            <artifactId>postgresql</artifactId>-->
    <!--        </dependency>-->
    
            <dependency>
                <groupId>com.h2database</groupId>
                <artifactId>h2</artifactId>
                <scope>runtime</scope>
            </dependency>
    
            <dependency>
                <groupId>org.hibernate.validator</groupId>
                <artifactId>hibernate-validator</artifactId>
                <version>8.0.1.Final</version>
            </dependency>
    
            <dependency>
                <groupId>org.junit.jupiter</groupId>
                <artifactId>junit-jupiter-api</artifactId>
                <version>5.10.0</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.openjfx</groupId>
                    <artifactId>javafx-maven-plugin</artifactId>
                    <version>0.0.8</version>
                    <configuration>
                        <mainClass>org/maxim/documents/JavaFxApplication</mainClass>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.1</version>
                    <configuration>
                        <source>17</source>
                        <target>17</target>
                    </configuration>
                </plugin>
    
    
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    

    Output

    output

      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::                (v3.1.4)
    
    2023-11-06T14:35:38.053-08:00  INFO 27963 --- [JavaFX-Launcher] o.s.boot.SpringApplication               : Starting application using Java 21.0.1 with PID 27963 (started by <username> in /Users/<username>/dev/documents)
    2023-11-06T14:35:38.056-08:00  INFO 27963 --- [JavaFX-Launcher] o.s.boot.SpringApplication               : No active profile set, falling back to 1 default profile: "default"
    2023-11-06T14:35:38.493-08:00  INFO 27963 --- [JavaFX-Launcher] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
    2023-11-06T14:35:38.530-08:00  INFO 27963 --- [JavaFX-Launcher] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 26 ms. Found 0 JPA repository interfaces.
    2023-11-06T14:35:38.865-08:00  INFO 27963 --- [JavaFX-Launcher] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
    2023-11-06T14:35:39.118-08:00  INFO 27963 --- [JavaFX-Launcher] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:28b3894c-1aa7-4d13-9a1d-5afed0a7ada6 user=SA
    2023-11-06T14:35:39.120-08:00  INFO 27963 --- [JavaFX-Launcher] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
    2023-11-06T14:35:39.177-08:00  INFO 27963 --- [JavaFX-Launcher] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
    2023-11-06T14:35:39.253-08:00  INFO 27963 --- [JavaFX-Launcher] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 6.2.9.Final
    2023-11-06T14:35:39.255-08:00  INFO 27963 --- [JavaFX-Launcher] org.hibernate.cfg.Environment            : HHH000406: Using bytecode reflection optimizer
    2023-11-06T14:35:39.424-08:00  INFO 27963 --- [JavaFX-Launcher] o.h.b.i.BytecodeProviderInitiator        : HHH000021: Bytecode provider name : bytebuddy
    2023-11-06T14:35:39.614-08:00  INFO 27963 --- [JavaFX-Launcher] o.s.o.j.p.SpringPersistenceUnitInfo      : No LoadTimeWeaver setup: ignoring JPA class transformer
    2023-11-06T14:35:39.787-08:00  INFO 27963 --- [JavaFX-Launcher] o.h.b.i.BytecodeProviderInitiator        : HHH000021: Bytecode provider name : bytebuddy
    2023-11-06T14:35:40.055-08:00  INFO 27963 --- [JavaFX-Launcher] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
    2023-11-06T14:35:40.060-08:00  INFO 27963 --- [JavaFX-Launcher] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
    2023-11-06T14:35:40.346-08:00  INFO 27963 --- [JavaFX-Launcher] o.s.boot.SpringApplication               : Started application in 2.629 seconds (process running for 3.963)