Documente Academic
Documente Profesional
Documente Cultură
Breviar teoretic
În industrie trendul aplicațiilor grafice cu interfața definită printr-o variantă a limbajului XML (Extensible Markup
Language) este în continuă creștere. Pentru a satisface aceste cerințe Microsoft au inventat XAML, Flex – MXML, iar
acum și Oracle are o variantă proprietară numită Java FXML.
Java FXML este un limbaj bazat pe XML folosit la construirea de grafuri de obiecte Java. Este o alternativă la
construirea acestor grafuri prin metode de cod procedural și este ideal pentru definirea de interfețe cu utilizatorul,
deoarece structura unui document XML este foarte asemănătoare cu structura de cod folosită la definirea unui graf
de scenă într-o aplicație Java FX obișnuită.
FXML nu are o schemă, dar are o structură predefinită. Ceea ce poate fi exprimat în FXML și cum este aplicat grafului
scenei depinde de interfața de cod a obiectelor construite. Deoarece FXML este mapat direct în cod Java, aproape
toate clasele de Java FX au un element XML corespondent cu atributele descrise în clasa Java mapată.
Java FX
border.setTop(topPaneText);
border.setCenter(centerPaneText);
Java FXML
<BorderPane>
<top>
</top>
<center>
</center>
</BorderPane>
Pentru a eficientiza procesul de dezvoltare al aplicațiilor cu interfețe grafice au fost create șabloane (patternuri) și
arhitecturi de aplicații. Cel mai popular pattern pentru aplicațiile desktop este Model View Controller, care a devenit
folosit chiar și în aplicațiile web.
Patternul Model View Controller este o metodă de a implementa interfețe cu utilizatorul, care divide software-ul
implementat în trei părți interconectate, pentru a separa reprezentările interne de informațiile prezentate
utilizatorilor.
Din perspectiva patternului Model View Controller, fișierul care conține descrierea XML a interfeței este View-ul.
Controllerul este o clasă de Java care este declarată ca și controllerul fișierului XML. Modelul este constituit din
obiectele domeniului definite în Java. Modelul se leagă de interfață folosind Controllere.
Pentru a interacționa cu o bază de date vom încapsula logica necesară interogărilor și extragerii primare de date
folosind un alt pattern numit Data Access Object. Această abstractizare permite operațiuni specifice (de exemplu
operațiile CRUD) fără a divulga detaliile mecanismului de persistență a datelor.
Aceste două patternuri ne vor ajuta să ne structurăm aplicația într-o manieră scalabilă și ușor de înțeles.
Aplicație
Realizarea unei aplicații grafice folosind Java FXML și conectarea acesteia la o bază
de date SQLite
Aplicația pe care o vom dezvolta se va conecta la o bază de date SQLite, care conține o tabelă Person
(FirstName, LastName), și va pune la dispoziția utilizatorului o interfața prietenoasă prin care se poate realiza
operațiile de CRUD (Create, Read, Update, Delete). Interfața va arăta în felul următor:
Vom folosi mediul de programare NetBeans și vom crea un nou proiect Java – Application cu numele
SqliteJavaFxml. Pentru aplicația noastră avem nevoie de pachetul sqlite-jdbc, pe care îl putem downloada de la
acest link sqlite-jdbc-3.8.11.2.jar.
După ce am descărcat sqlite-jdbc, trebuie adăugat la referințele proiectului in directorul Libraries – click dreapta →
Add JAR/Folder → Alegem fișierul nou descărcat sqlite-jdbc-3.8.11.2.jar.
Următorii pași care sunt sugerați în dezvoltarea acestei aplicații sunt stabilirea structurii de fișiere. Ne vom crea
module (packages) astfel încât să urmăm principiile MVC și DAO.
Vom redenumi packageul implicit sqlite.java.fxml în model pentru a respecta convenția noastră.
Apoi vom crea și următoarele module view și dataaccess. Restul fișierelor vor fi adăugate în modulele aferente
folosind meniul contextual click dreapta pe modul și Add Other…
Fișierul Person.java conține definiția clasei Person, care va corespunde ca și proprietăți tabelei Person din baza de
date folosită în aplicație.
package model;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public Person() {
this.id = id;
return id;
this.id = id;
}
return firstName.get();
this.firstName.set(firstName);
return firstName;
return lastName.get();
this.lastName.set(lastName);
return lastName;
După definirea proprietăților, vom putea scrie mai departe modulul de acces la baza de date. Acest modul se va
ocupa de crearea tabelei în cazul în care nu există deja și de interacționarea din cod Java cu tabela Person (Create,
Read, Update, Delete). Conținutul fișierului PersonDAO va fi următorul:
package dataaccess;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import model.SqliteJavaFxml;
import model.Person;
Class.forName("org.sqlite.JDBC");
this.databaseConnectionString = databaseConnectionString;
this.ensureTables();
+ "LAST_NAME NVARCHAR(32));";
statement.execute(query);
while (rs.next()) {
int id = rs.getInt("ID");
return persons;
}
public Person getPerson(int personId) {
PreparedStatement statement
statement.setInt(1, personId);
while (rs.next()) {
int id = rs.getInt("ID");
return person;
PreparedStatement statement
statement.setString(1, person.getFirstName());
statement.setString(2, person.getLastName());
statement.executeUpdate();
success = false;
return success;
PreparedStatement statement
statement.setString(1, person.getFirstName());
statement.setString(2, person.getLastName());
statement.setInt(3, person.getId());
statement.executeUpdate();
success = false;
return success;
PreparedStatement statement
= connection.prepareStatement("DELETE FROM PERSON WHERE ID = ?;")) {
statement.setInt(1, person.getId());
statement.executeUpdate();
success = false;
return success;
Fișierul principal al aplicației noastre îl constituie SqliteJavaFxml.java și va conține codul pentru gestionarea scenei și
a viewurilor. În acest fișier vom scrie următoarele:
package model;
import dataaccess.PersonDAO;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
import view.PersonEditDialogController;
import view.PersonOverviewController;
return this.personDAO;
@Override
this.primaryStage = primaryStage;
initRootLayout();
showPersonOverview();
try {
loader.setLocation(SqliteJavaFxml.class.getResource("/view/RootLayout.fxml"));
primaryStage.show();
} catch (IOException e) {
try {
loader.setLocation(SqliteJavaFxml.class.getResource("/view/PersonOverview.fxml"));
rootLayout.setCenter(personOverview);
controller.setMainApp(this);
} catch (IOException e) {
try {
loader.setLocation(SqliteJavaFxml.class.getResource("/view/PersonEditDialog.fxml"));
dialogStage.setTitle("Edit Person");
dialogStage.initModality(Modality.WINDOW_MODAL);
dialogStage.initOwner(primaryStage);
dialogStage.setScene(scene);
controller.setDialogStage(dialogStage);
controller.setPerson(person);
dialogStage.showAndWait();
return controller.isOkClicked();
} catch (IOException e) {
return false;
return primaryStage;
launch(args);
Observăm că în constructorul clasei principale se află instanțierea Data Access Objectului pentru persoane care
primește ca parametru adresa bazei de date SQLite. Reamintim că această baze de date se creează automat la
rularea programului direct în directorul proiectului.
Pentru a lega controllerele de aceste viewuri, apelăm tot la loaderul FXML, folosind metoda getController.
Fișierul RootLayout.fxml va conține doar un cadru pentru aplicația noastră, va fi practic containerul care
dimensionează fereastra principală a aplicației.
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.BorderPane?>
</BorderPane>
Acest View nu are nevoie de un controller asociat deoarece nu are legături de date, el fiind folosit doar pentru a
aranja layoutul aplicației.
Fișierul PersonOverview.fxml va conține descrierea tabelului FXML corespunzător tabelei Person dintr-o bază de
date SQLite, care va fi folosit desigur la extragerea și afișarea datelor din acea tabelă. În acest View vom regăsi și trei
butoane de acțiune care au rolul de a manipula datele din tabel, respectiv baza de date, prin intermediul de ferestre
intermediare sau acțiuni asupra modelului.
De remarcat sunt legarea proprietăților din controller la view cu atributul fx:id (fx:id="firstNameColumn") și
legarea funcțiilor cu prefixul "#" (#handleNewPerson);
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<children>
<columns>
</columns>
<columnResizePolicy>
</columnResizePolicy>
</TableView>
<buttons>
</buttons>
</ButtonBar>
</children>
</AnchorPane>
Controllerul acestui View se regăsește în fișierul PersonOverviewController.java și va face legătura dintre interfața
grafica și model, persistând rezultatul acțiunilor utilizatorului în baza de date prin intermediul Person Data Access
Objectului.
De remarcat sunt decoratorii (@FXML) folosiți pentru a marca proprietățile și funcțiile ce interacționează în corpul lor
cu elemente din fișierul FXML pe care îl controlează. Dacă metodele, respectiv proprietățile nu sunt decorate cu
@FXML, aplicația va avea erori de funcționare. În cazul în care nu este selectată nicio înregistrare din tabel, apăsarea
butoanelor de acțiune pentru editare și ștergere vor declanșa apariția unui mesaj de eroare (metoda showAlert).
package view;
import dataaccess.PersonDAO;
import java.util.List;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import model.SqliteJavaFxml;
import model.Person;
@FXML
@FXML
@FXML
public PersonOverviewController() {
@FXML
firstNameColumn.setCellValueFactory(cellData ->
cellData.getValue().firstNameProperty());
lastNameColumn.setCellValueFactory(cellData ->
cellData.getValue().lastNameProperty());
this.mainApp = mainApp;
handleRefreshPersons();
@FXML
if (selectedPerson != null) {
mainApp.getPersonDAO().deletePerson(selectedPerson);
handleRefreshPersons();
} else {
showAlert();
@FXML
if (okClicked) {
mainApp.getPersonDAO().insertPerson(tempPerson);
handleRefreshPersons();
@FXML
if (selectedPerson != null) {
if (okClicked) {
mainApp.getPersonDAO().updatePerson(selectedPerson);
handleRefreshPersons();
} else {
showAlert();
@FXML
personTable.setItems(items);
alert.initOwner(mainApp.getPrimaryStage());
alert.setTitle("No Selection");
alert.showAndWait();
Ultimul view folosit în aplicație este cel care descrie fereastra de tip dialog prin care un utilizator poate introduce
datele unei persoane noi sau poate edita pe cele ale uneia existente în baza de date. În funcție de metoda de unde a
fost apelat controllerul acestui view, rezultatul acțiunii de a apăsa butonul OK va declanșa o acțiune de adăugare sau
de editare a tabelei Person.
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<children>
<columnConstraints>
</columnConstraints>
<rowConstraints>
<children>
</children>
</GridPane>
<buttons>
</buttons>
</ButtonBar>
</children>
</AnchorPane>
Controllerul pentru acest view, PersonEditDialogController.java , este folosit pentru a verifica introducerea corectă
de date și validarea modificarărilor la nivel de interfață. Acesta comunică schimbările modelului controllerului
PersonOverviewController, iar acesta din urmă va acționa și în baza de date.
package view;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import model.Person;
@FXML
@FXML
this.dialogStage = dialogStage;
this.person = person;
firstNameField.setText(person.getFirstName());
lastNameField.setText(person.getLastName());
return okClicked;
@FXML
private void handleOk() {
if (isInputValid()) {
person.setFirstName(firstNameField.getText());
person.setLastName(lastNameField.getText());
okClicked = true;
dialogStage.close();
@FXML
dialogStage.close();
if (errorMessage.length() == 0) {
return true;
} else {
alert.initOwner(dialogStage);
alert.setTitle("Invalid Fields");
alert.setHeaderText("Please correct invalid fields");
alert.setContentText(errorMessage);
alert.showAndWait();
return false;
Putem vedea relația dintre aceste doua controllere ca Master-Slave, unul fiind utilizat pentru controlul la nivel doar
de interfață (PersonEditDialogController), iar celălalt pentru a gestiona logica aplicației.
Așadar, am realizat o aplicație cu interfață grafică, susținută de o bază de date SQLite. Concluzia este că aplicând o
arhitectură MVC putem scrie codul într-un mod modular, ușor de extins, care ține cont de patternuri aplicate în
tehnicile moderne de programare și putem îl refolosi pentru aplicații asemănătoare.