I have a personal program that uses Springboot and Javafx. I've been trying to gain more skills in creating springboot and javafx apps. Program launches an fxml page where a person can update a person's records, clicks save, and is then saved to an mssql database.
I'm trying to save a new person record to a mssql database.
PersonEntity.java
@Entity
@Getter
@NoArgsConstructor(force = true)
@Data
@Table(name = "persons")
public class PersonEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "personID", nullable = false)
private final Integer personID;
@Column(name = "first_name")
@Setter
private String firstName;
@Column(name = "last_name")
@Setter
private String lastName;
@Column(name = "dob")
@Setter
private String dob;
PersonRepo.java
@Repository
public interface PersonRepo extends JpaRepository<PersonEntity, String> {
List<PersonEntity> findAll();
}
PersonService.java
public interface PersonService{
Optional<PersonEntity> save(PersonEntity personEntity);
PersonEntity update(PersonEntity personEntity);
PersonEntity findAll();
}
PersonServiceImpl.java
@Service
@RequiredArgsConstructor
public class PersonServiceImplimplements PersonService {
@Autowired
private PersonRepo repo;
@Override
public Optional<PersonEntity> save(PersonEntity personEntity) {
return Optional.of(repo.save(personEntity));
}
@Override
public PersonEntity update(PersonEntity personEntity){ return repo.save(personEntity);}
@Override
public PersonEntity findAll() {
return (PersonEntity ) repo.findAll();
}
}
PersonController.java
@Component
public class PersonController {
@Autowired
PersonService personService;
public TextField lName;
public TextField fName;
public TextField dob;
public Button onSaveNewPerson;
@FXML
public void onSaveNewPerson(ActionEvent event) throws SQLException {
if(fName != null){
PersonEntity newPerson = new PersonEntity ();
fName.setText(newPerson.getFirstName());
personService.save(newPerson);
}
}
PersonsRecord.fxml
<AnchorPane fx:id="scenePane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/20.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.controllers.PersonController">
<Pane layoutX="-2.0" layoutY="91.0" prefHeight="800.0" prefWidth="1004.0">
<Label layoutX="14.0" layoutY="14.0" text="New Person Form">
<font>
<Font size="20.0"/>
</font>
</Label>
<Label layoutX="14.0" layoutY="69.0" text="First Name"/>
<Label layoutX="16.0" layoutY="130.0" text="Last name"/>
<Label layoutX="18.0" layoutY="130.0" text="Date of Birth"/>
<Label layoutX="20.0" layoutY="130.0" text="Address"/>
<TextField fx:id="fName" layoutX="98.0" layoutY="66.0"/>
<TextField fx:id="lName" layoutX="98.0" layoutY="126.0"/>
<TextField fx:id="dob" layoutX="98.0" layoutY="146.0"/>
<TextField fx:id="personAddress" layoutX="98.0" layoutY="166.0"/>
<Button fx:id="saveNewPerson" layoutX="803.0" layoutY="333.0" mnemonicParsing="false"
onAction="#onSaveNewPerson" prefHeight="58.0" prefWidth="187.0" text="Save New Person"/>
<Label layoutX="597.0" layoutY="70.0" text="DOB"/>
<TextField fx:id="dob" layoutX="691.0" layoutY="66.0"/>
</Pane>
</AnchorPane>
Currently after clicking the onSaveNewPersonbutton I receive the following error –
Cannot invoke "com.example.services.PersonService.save(com.example.entity.persons.PersonEntity)" because "this.personService" is null
I do want all the textfields to save but for right now I've just been testing with the fname field.
I've ensured the @Autowired is set for the personService in the controller. I also made sure the @Repository is set for the PersonRepo.
I also added the implements CommandLineRunner to the FxApplication file and had a test person added to the database to ensure proper connectivity. That ran just fine and a person was entered successfully. Code for investigation –
@SpringBootApplication
@EnableJpaRepositories("com.example.repo")
public class FxApplication extends Application implements CommandLineRunner {
private ConfigurableApplicationContext springContext;
private Parent root;
@Autowired
PersonRepo personRepo;
public static void main(String[] args){
launch(FxApplication.class, args);
}
@Override
public void start(Stage stage) throws Exception {
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setControllerFactory(springContext::getBean);
fxmlLoader.setLocation(getClass().getResource("/PersonsRecord.fxml"));
Parent root = fxmlLoader.load();
stage.setTitle("Add NewPerson");
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
@Override
public void init() throws Exception{
springContext = SpringApplication.run(FxApplication.class);
}
@Override
public void stop() throws Exception{
springContext.close();
Platform.exit();
}
@Override
public void run(String... args) throws Exception {
PersonEntity person = new PersonEntity ();
person.setFirstName("TestSpring3");
person.setLastName("LastNameTest4");
person.setDOB("1/1/2000")
personRepo.save(person);
}
}
I edited my code and used the template that was provided via @David Weber.
This is now my FxApplicationLauncher.java class that I have edited.
package com.example;
import javafx.application.Application;
import javafx.application.HostServices;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Stage;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.stereotype.Component;
import java.io.IOException;
@SpringBootApplication
@EnableJpaRepositories("com.example.repo")
public class FxApplicationLauncher {
public static void main(String[] args) {
Application.launch(FxApplication.class, args);
}
public static class FxApplication extends Application {
private ConfigurableApplicationContext context;
@Override
public void init(){
ApplicationContextInitializer<GenericApplicationContext> initializer = applicationContext -> {
applicationContext.registerBean(Application.class, () -> FxApplication.this);
applicationContext.registerBean(Parameters.class, this::getParameters);
applicationContext.registerBean(HostServices.class, this::getHostServices);
};
this.context = new SpringApplicationBuilder()
.sources(FxApplicationLauncher.class)
.initializers(initializer)
.run(getParameters().getRaw().toArray(new String[0]));
}
@Override
public void start(Stage stage) { this.context.publishEvent(new StageIsReadyEvent(stage)); }
@Override
public void stop() throws Exception {
context.close();
Platform.exit();
System.exit(0);
}
@Component
public static class FxApplicationStageIsReadyListener implements ApplicationListener<StageIsReadyEvent>{
@Value("${spring.application.name}")
private String applicationTitle;
private ApplicationContext applicationContext;
public void JfxSpringBootStageIsReadyListener(
@Value("${spring.application.name}") String applicationTitle,
ApplicationContext applicationContext) {
this.applicationTitle = applicationTitle;
this.applicationContext = applicationContext;
}
@Override
public void onApplicationEvent(StageIsReadyEvent event) {
try {
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("/PersonsRecord.fxml"));
fxmlLoader.setControllerFactory(applicationContext::getBean);
Parent root = fxmlLoader.load();
Scene scene = new Scene(root, 600, 600);
Stage stage = event.getStage();
stage.setScene(scene);
stage.setTitle(this.applicationTitle);
stage.show();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public static class StageIsReadyEvent extends ApplicationEvent {
public Stage getStage() {
return (Stage) getSource();
}
public StageIsReadyEvent(Stage source) {
super(source);
}
}
}
}
For some reason the applicationContext is null.
java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:208)
at com.example.gaitlabapp.FxApplicationLauncher$FxApplication$FxApplicationStageIsReadyListener.onApplicationEvent(FxApplicationLauncher.java:84)
at com.example.gaitlabapp.FxApplicationLauncher$FxApplication$FxApplicationStageIsReadyListener.onApplicationEvent(FxApplicationLauncher.java:65)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:185)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:178)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:156)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:451)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:384)
at com.example.gaitlabapp.FxApplicationLauncher$FxApplication.start(FxApplicationLauncher.java:51)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:847)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:484)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:457)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:456)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)
at java.base/java.lang.Thread.run(Thread.java:842)
Best Answer
Explanation:
I am quite sure, that your service is null because your controller is not a
Spring Component
. You marked it with@Component
, which is a nice idea, but because you useFXML
I bet, that your controller is not created bySpring Boot
, but by theFxmlLoader
. This leads to "ignoring" the annotation, because the FxmlLoader creates a new instance of your controller by using the keywordnew
.Example repos of mine which demonstrate the use of Spring Boot and JavaFx with Maven and Gradle. I would appreciate a star for supporting free open source:
JFX with Spring Boot and Gradle (better and more up to date)
JFX with Spring Boot and Maven
What to do next:
You have several options from here.
Spring Boot Application context
to a static variable and get your service in a non Spring Component by usingService service = springContext.getBean(Service.class)
.SpringFxmlLoader
class).Additional information:
Be aware when using Spring components. They can be stateless or stateful. In easier words: They can be a Singleton (scope singleton) or an own instance for every injection (scope prototype).