我正在开发一个会员管理软件,我使用的是spring boot,spring data与JAVA-FX集成。 一切都很好,直到我创建了一个视图,该视图有一个表,其中包含与主要成员关联的所有依附成员。当我打开视图时,它加载得很好,但是在我用于加载依从成员的数据的方法中,它抛出了异常。
这是我的代码:
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="522.0" prefWidth="834.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.ebernet.bomberos_socios.controller.VistaAdherenteSocioController">
<top>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="859.0" BorderPane.alignment="CENTER">
<children>
<Label fx:id="lblTitulo" text="Socios adherentes de: ">
<font>
<Font name="Arial" size="18.0" />
</font>
</Label>
</children>
</HBox>
</top>
<center>
<VBox prefHeight="280.0" prefWidth="784.0" BorderPane.alignment="CENTER">
<children>
<ScrollPane prefHeight="273.0" prefWidth="784.0">
<content>
<TableView fx:id="tblSociosAdh" prefHeight="239.0" prefWidth="782.0">
<columns>
<TableColumn fx:id="colNombre" editable="false" prefWidth="231.0" text="Nombre Completo" />
<TableColumn fx:id="colTipoDoc" editable="false" prefWidth="106.0" text="Tipo Doc." />
<TableColumn fx:id="colNroDoc" editable="false" prefWidth="145.0" text="Nro. Documento" />
<TableColumn fx:id="colFechaNac" editable="false" minWidth="8.0" prefWidth="135.0" text="Fecha de Nac." />
<TableColumn fx:id="colVinculo" prefWidth="87.0" text="Vinculo" />
<TableColumn fx:id="colDeBaja" prefWidth="75.0" text="Baja" />
</columns>
</TableView>
</content>
</ScrollPane>
</children>
<BorderPane.margin>
<Insets left="25.0" right="25.0" />
</BorderPane.margin>
</VBox>
</center>
<bottom>
<VBox alignment="TOP_CENTER" prefHeight="181.0" prefWidth="834.0" BorderPane.alignment="CENTER">
<children>
<Label fx:id="lblSocioSelec" text="Socio adherente seleccionado:">
<VBox.margin>
<Insets bottom="25.0" top="15.0" />
</VBox.margin>
<font>
<Font name="Arial" size="14.0" />
</font>
</Label>
<HBox alignment="TOP_CENTER" prefHeight="38.0" prefWidth="834.0">
<children>
<Button fx:id="btnBaja" mnemonicParsing="false" prefHeight="28.0" prefWidth="65.0" text="Baja">
<HBox.margin>
<Insets left="10.0" right="10.0" />
</HBox.margin>
</Button>
<Button fx:id="btnEditar" mnemonicParsing="false" prefHeight="29.0" prefWidth="62.0" text="Editar">
<HBox.margin>
<Insets left="10.0" right="10.0" />
</HBox.margin>
</Button>
<Button fx:id="btnDetalles" mnemonicParsing="false" prefHeight="28.0" prefWidth="66.0" text="Detalles">
<HBox.margin>
<Insets left="10.0" right="10.0" />
</HBox.margin>
</Button>
</children>
</HBox>
<VBox alignment="CENTER" prefHeight="90.0" prefWidth="844.0">
<children>
<Button fx:id="btnNuevo" mnemonicParsing="false" prefHeight="31.0" prefWidth="235.0" text="Nuevo">
<VBox.margin>
<Insets top="10.0" />
</VBox.margin>
</Button>
<Button fx:id="btnVolver" mnemonicParsing="false" prefHeight="32.0" prefWidth="233.0" text="Volver">
<VBox.margin>
<Insets bottom="10.0" top="10.0" />
</VBox.margin>
</Button>
</children>
</VBox>
</children>
</VBox>
</bottom>
</BorderPane>
控制器:
package com.ebernet.bomberos_socios.controller;
import com.ebernet.bomberos_socios.model.SocioAdherente;
import com.ebernet.bomberos_socios.model.SocioTitular;
import com.ebernet.bomberos_socios.service.ISocioAdherenteService;
import com.ebernet.bomberos_socios.service.ISocioTitularService;
import com.ebernet.bomberos_socios.ui.SocioAdherenteWrapper;
import jakarta.transaction.Transactional;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.ResourceBundle;
import java.util.stream.Collectors;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import org.hibernate.Hibernate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class VistaAdherenteSocioController implements Initializable {
private SocioTitular socio;
private HashMap<Long, SocioAdherente> socios;
private SocioAdherente socioSeleccionado;
//autowired inyection of service classes here
@FXML
private Label lblTitulo;
@FXML
private TableView<SocioAdherenteWrapper> tblSociosAdh;
@FXML
private TableColumn<SocioAdherenteWrapper, String> colNombre;
@FXML
private TableColumn<SocioAdherenteWrapper, String> colTipoDoc;
//other FXML atributes
@Transactional
public void initData(SocioTitular socioTit) {
this.socio = sociotitser.findById(socioTit.getNroSocio());
//Decirle a hibernate que traiga la coleccion
Hibernate.initialize(socio.getSociosAdherentes());
//setear titulo
if (lblTitulo != null) {
lblTitulo.setText("Socios adherentes de: " + socio.getNroDocumento() + " - " + socio.getNombreCompleto());
// Resto del código...
} else {
System.err.println("lblTitulo es nulo en initData.");
}
//obtener socios adherentes del socio en cuestion
List<SocioAdherente> sociosAdherentes = socio.getSociosAdherentes();
// Convertir a wrappers
List<SocioAdherenteWrapper> wrappers = sociosAdherentes.stream()
.map(SocioAdherenteWrapper::new)
.collect(Collectors.toList());
//convertir a hashmap
this.socios = (HashMap<Long, SocioAdherente>) sociosAdherentes.stream()
.collect(Collectors.toMap(SocioAdherente::getNroSocio, socio -> socio));
//Mapeo de columnas :)
colNombre.setCellValueFactory(cell -> cell.getValue().nombreCompletoProperty());
colTipoDoc.setCellValueFactory(cell -> cell.getValue().tipoDocProperty());
colNroDoc.setCellValueFactory(cell -> cell.getValue().nroDocumentoProperty());
colVinculo.setCellValueFactory(cell -> cell.getValue().vinculoProperty());
colDeBaja.setCellValueFactory(cell -> cell.getValue().bajaProperty());
//Setear items
tblSociosAdh.setItems(FXCollections.observableArrayList(wrappers));
//other listeners for tables, buttons, etc.
}
@Override
public void initialize(URL url, ResourceBundle rb) {
//btn y label de acciones disabled por defecto
/*lblSocioSelec.setDisable(true);
btnBaja.setDisable(true);
btnEditar.setDisable(true);
btnDetalles.setDisable(true);*/
}
}
索引: (这是我根据用户选择加载不同视图的位置,例如侧面导航栏)
package com.ebernet.bomberos_socios.controller;
import com.ebernet.bomberos_socios.model.SocioTitular;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.layout.Pane;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class IndexController implements Initializable {`
@Autowired
private ApplicationContext context;
@FXML
private Pane mainContent;
//another methods for opening other views
//this one here works perfectly, and has the same logic behind that the
one im having problems.
public void editarSocio(SocioTitular socioToEdit) {
//se carga la vista
loadView("/fxml/editarSocio.fxml");
//se obtiene el objeto EditarSocioController gracias a el application context (Gracias SPRING!!!)
EditarSocioController controller = context.getBean(EditarSocioController.class);
//se le setea al controlador el objeto socio el cual tiene que editar
controller.setSocioEditar(socioToEdit);
}
//this is the one that doesn't work
public void vistaSociosAdherentes(SocioTitular socio) {
//se carga la vista y el controller!
loadView("/fxml/vistaAdherenteSocio.fxml");
//se obtiene el objeto DetalleSocioController gracias a el application context (Gracias SPRING!!!)
VistaAdherenteSocioController controller = context.getBean(VistaAdherenteSocioController.class);
//se le setea al controlador el objeto socio el cual se quiere ver detalle
if (controller != null) {
System.out.println("CONTROLLER: "+controller);
// Imprime todas las instancias gestionadas por Spring de VistaAdherenteSocioController
String[] beanNames = context.getBeanNamesForType(VistaAdherenteSocioController.class);
System.out.println("Instancias gestionadas por Spring de VistaAdherenteSocioController: " + Arrays.toString(beanNames));
controller.initData(socio);
} else {
System.err.println("El controlador es nulo después de cargar la vista.");
}
}
//general method for loading the view
private void loadView(String view) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource(view));
loader.setControllerFactory(context::getBean);
Pane newLoadedPane = loader.load();
mainContent.getChildren().clear();
mainContent.getChildren().add(newLoadedPane);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void initialize(URL url, ResourceBundle rb) {
}
}
最后出现错误:
引起:java.lang.NullPointerException:无法调用“javafx.scene.control.TableColumn.setCellValueFactory(javafx.util.Callback)”,因为“this.colNombre”为空
它的工作原理如下: 我有一个侧面导航栏,其作用类似于菜单,用户选择其中一个选项,索引控制器负责在中心窗格中加载视图。另外,当需要时,其他视图(实际上是该视图的控制器)可以调用indexcontroller的方法之一(我使用autowired将实例注入其他控制器中)以加载用户需要的视图。
我测试了很多东西,但没有一个起作用。
我还有另外 5 个视图,它们各自的控制器工作得很好! 我不知道为什么这不行。
感谢您阅读本文并尝试帮助我解决我的问题,这真的让我很沮丧。这是我的第一个开发项目,也是第一个将 java fx 与 spring boot 集成的开发项目。
也为我生疏的英语道歉!
我尝试删除控制器和 FXML 视图,以在没有 Spring Boot 的情况下加载视图,多次检查 fx:id 和 @FXML 属性的名称是否匹配。
以防万一,这是我的 @SpringBootApplication 和我的 @Application 类,也是一个 StageListener。
public class JavaFXApplication extends Application{
private ConfigurableApplicationContext context;
@Override
public void init() throws Exception{
//here we do any kind of work that DO NOT runs on the user interface
//to prepare the aplication, to begin, to load
ApplicationContextInitializer<GenericApplicationContext> initializer =
ac->{
ac.registerBean(Application.class, ()->JavaFXApplication.this);
ac.registerBean(Parameters.class, this::getParameters);
ac.registerBean(HostServices.class, this::getHostServices);
};
this.context = new SpringApplicationBuilder()
.sources(BomberosSociosApplication.class)
.initializers(initializer)
.run(getParameters().getRaw().toArray(new String[0]));
}
@Override
public void start(Stage primaryStage) throws Exception {
this.context.publishEvent(new StageReadyEvent(primaryStage));
}
@Override
public void stop() throws Exception{
this.context.close();
Platform.exit();
}
}
class StageReadyEvent extends ApplicationEvent{
public Stage getStage() {
return Stage.class.cast(getSource());
}
public StageReadyEvent(Stage source) {
super(source);
}
}
@SpringBootApplication
public class BomberosSociosApplication {
public static void main(String[] args) {
Application.launch(JavaFXApplication.class, args);
}
}
@Component
public class StageListener implements ApplicationListener<StageReadyEvent>{
private final String appTitle;
private final Resource fxml;
private final ApplicationContext ac;
StageListener(@Value("${spring.application.ui.title}")String appTitle,
@Value("${classpath:/fxml/index.fxml}") Resource resource, ApplicationContext ac) {
this.appTitle = appTitle;
this.fxml = resource;
this.ac = ac;
}
@Override
public void onApplicationEvent(StageReadyEvent event) {
try {
Stage stage = event.getStage();
URL url = this.fxml.getURL();
FXMLLoader fxmlLoader = new FXMLLoader(url);
fxmlLoader.setControllerFactory(ac::getBean);
Parent root = fxmlLoader.load();
Scene scene = new Scene(root, 600, 600);
stage.setScene(scene);
stage.setTitle(appTitle);
stage.show();
} catch (IOException ex) {
Logger.getLogger(StageListener.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
此外,这是我的课程 SocioTitular 和 SocioAdherente:
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Entity(name="socio_titular")
public class SocioTitular {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="nro_socio")
private Long nroSocio;
@Column(name="nombre_completo")
private String nombreCompleto;
@ManyToOne
@JoinColumn(name = "id_tipo_documento")
private TipoDocumento tipoDoc;
@Column(name="nro_documento")
private Long nroDocumento;
@Column(name="fecha_nacimiento")
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate fechaNacimiento;
@Column(name="fecha_ingreso")
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate fechaIngreso;
@Column(name="fecha_baja")
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate fechaBaja;
private boolean baja;
@ManyToOne
@JoinColumn(name = "id_tipo_baja")
private TipoBaja tipoBaja;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "id_domicilio")
private Domicilio domicilio;
@ManyToOne
@JoinColumn(name = "id_categoria")
private Categoria categoria;
@ManyToOne
@JoinColumn(name = "id_cobrador")
private Cobrador cobrador;
@OneToMany(mappedBy = "socioDeudor", cascade = CascadeType.ALL)
private List<Deuda> deudas;
@OneToMany(mappedBy = "socioTitular", cascade = CascadeType.ALL)
private List<SocioAdherente>sociosAdherentes;
}
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Entity(name="socio_adherente")
public class SocioAdherente {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="nro_socio_adherente")
private Long nroSocio;
@Column(name="nombre_completo")
private String nombreCompleto;
@ManyToOne
@JoinColumn(name = "id_tipo_documento")
private TipoDocumento tipoDoc;
@Column(name="nro_documento")
private Long nroDocumento;
@Column(name="fecha_nacimiento")
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate fechaNacimiento;
@Column(name="fecha_baja")
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate fechaBaja;
private boolean baja;
@ManyToOne
@JoinColumn(name = "id_tipo_baja")
private TipoBaja tipoBaja;
@ManyToOne
@JoinColumn(name = "id_socio_titular")
private SocioTitular socioTitular;
@ManyToOne
@JoinColumn(name = "id_vinculo")
private Vinculo vinculo;
}
我解决了问题,就是@Transactional注解的问题。我不知道为什么,但这在某种程度上导致我所有的 @FXML 属性都为空。 我意识到,因为我从头开始重新做了一切,包括视图和控制器......并且对于我添加的每个新组件,我测试了它是否有效。