Примерна задача
Да се разшири приложението за управление на данни за софтуерни разработчици, така че да поддържа работа с база от данни и редактиране информацията на софтуерен разработчик.
Приложението трябва да реализира следните функционалности:
Създаване на база данни за съхранение информацията за разработчиците.
- Записване на нов разработчик
- Визуализация на всички разработчици
- Изтриване на съществуващ разработчик
- Редактиране на съществуващ разработчик
За редайтирането трябва да се добави:
- бутон в menu и context menu команда „Редакция“
- нов edit-developer-dialog.fxml
- нов controller за този прозорец
- логика в основния DeveloperController, която:
- взима избрания програмист
- отваря диалога
- зарежда текущите му данни
- записва промените
Стъпка 1: Доцавяне на библиотека за работа с базата данни:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.4.240</version>
</dependency>
Стъпка 2: Създаване връзката към базата данни:
public class DatabaseConnection {
private static final String URL = "jdbc:h2:./data/developersdb";
private static final String USER = "javafx";
private static final String PASSWORD = "fxjava";
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USER, PASSWORD);
}
}
Стъпка 3: Създаване на базата данни:
public class DatabaseInitializer {
public static void initDatabase() {
String sql = """
CREATE TABLE IF NOT EXISTS developers (
id IDENTITY PRIMARY KEY,
name VARCHAR(255) NOT NULL,
level VARCHAR(50) NOT NULL,
skills VARCHAR(500) NOT NULL
)
""";
try (Connection connection = DatabaseConnection.getConnection();
Statement statement = connection.createStatement()) {
statement.execute(sql);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Стъпка 4: Имплементиране на методите за селектиране, записване и изтриване от базата:
public class DeveloperDAO {
public List<Developer> findAll() {
List<Developer> developers = new ArrayList<>();
String sql = "SELECT id, name, level, skills FROM developers";
try (Connection connection = DatabaseConnection.getConnection();
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery(sql)) {
while (rs.next()) {
Long id = rs.getLong("id");
String name = rs.getString("name");
Level level = Level.valueOf(rs.getString("level"));
List<Technology> skills = parseSkills(rs.getString("skills"));
developers.add(new Developer(id, name, level, skills));
}
} catch (SQLException e) {
throw new RuntimeException("Грешка при зареждане на разработчиците.", e);
}
return developers;
}
public Developer save(Developer developer) {
String sql = "INSERT INTO developers(name, level, skills) VALUES (?, ?, ?)";
try (Connection connection = DatabaseConnection.getConnection();
PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
ps.setString(1, developer.getName());
ps.setString(2, developer.getLevel().name());
ps.setString(3, skillsToString(developer.getSkills()));
ps.executeUpdate();
try (ResultSet rs = ps.getGeneratedKeys()) {
if (rs.next()) {
developer.setId(rs.getLong(1));
}
}
return developer;
} catch (SQLException e) {
throw new RuntimeException("Грешка при запис на разработчик.", e);
}
}
public Developer update(Developer developer) {
String sql = "UPDATE developers SET name = ?, level = ?, skills = ? WHERE id = ?";
try (Connection connection = DatabaseConnection.getConnection();
PreparedStatement ps = connection.prepareStatement(sql)) {
ps.setString(1, developer.getName());
ps.setString(2, developer.getLevel().name());
ps.setString(3, skillsToString(developer.getSkills()));
ps.setLong(4, developer.getId());
int affectedRows = ps.executeUpdate();
if (affectedRows == 0) {
throw new RuntimeException("Няма намерен разработчик за обновяване.");
}
return developer;
} catch (SQLException e) {
throw new RuntimeException("Грешка при обновяване на разработчик.", e);
}
}
public void deleteById(Long id) {
String sql = "DELETE FROM developers WHERE id = ?";
try (Connection connection = DatabaseConnection.getConnection();
PreparedStatement ps = connection.prepareStatement(sql)) {
ps.setLong(1, id);
ps.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException("Грешка при изтриване на разработчик.", e);
}
}
private String skillsToString(List<Technology> skills) {
return skills.stream()
.map(Enum::name)
.collect(Collectors.joining(","));
}
private List<Technology> parseSkills(String skillsText) {
if (skillsText == null || skillsText.isBlank()) {
return new ArrayList<>();
}
return Arrays.stream(skillsText.split(","))
.map(String::trim)
.map(Technology::valueOf)
.collect(Collectors.toList());
}
}
Стъпка 5: Актуализиране на DeveloperController:
private void loadDevelopersFromDatabase() {
developersData.clear();
developersData.addAll(developerDAO.findAll());
}
@FXML
public void initialize() {
initComboBox();
initListView();
initTableView();
loadDevelopersFromDatabase();
}
private boolean createDeveloper(String name, Level level, List<Technology> selectedTechs) {
try {
Developer newDev = new Developer(name, level, selectedTechs);
Developer savedDeveloper = developerDAO.save(newDev);
return developersData.add(savedDeveloper);
} catch (IllegalArgumentException exception) {
throw exception;
}
}
public void handleDeleteSelected() {
Developer selectedDeveloper = developerTable.getSelectionModel().getSelectedItem();
if (selectedDeveloper == null) {
showAlert(Alert.AlertType.WARNING, "Предупреждение",null, "Моля, изберете разработчик от таблицата.");
} else {
Optional<ButtonType> result = showAlert(Alert.AlertType.WARNING, "Предупреждение", "Изтриване на разработчик", "Сигурни ли сте, че искате да изтриете избрания разработчик?");
if (result.isPresent() && result.get() == ButtonType.OK) {
developerDAO.deleteById(selectedDeveloper.getId());
developersData.remove(selectedDeveloper);
}
}
}
Стъпка 6: Добавяне на бутоните в менюто за редактиране
<Menu text="Файл">
<MenuItem text="Нов" onAction="#handleNew"/>
<MenuItem text="Изтрий избран" onAction="#handleDeleteSelected"/>
<MenuItem text="Редакция избран" onAction="#handleEditDeveloper"/>
<MenuItem text="Изход" onAction="#handleExit"/>
</Menu>
<ContextMenu>
<items>
<MenuItem text="Редакция" onAction="#handleEditDeveloper"/>
<MenuItem text="Изтрий" onAction="#handleDeleteSelected"/>
</items>
</ContextMenu>
Стъпка 7: Създаване на метода за редктиране в DeveloperController:
public void handleEditDeveloper(ActionEvent actionEvent) { }
Стъпка 8: Създаване на fxml файла за редактиране:
<VBox stylesheets="@css/styles.css"
xmlns:fx="http://javafx.com/fxml"
fx:controller="bg.tu_varna.sit.ps.lab7.task1.controllers.EditDeveloperDialogController"
spacing="10" prefWidth="350" styleClass="main-container">
<Label text="Редакция на програмист"/>
<Label text="Име и фамилия:"/>
<TextField fx:id="nameField"/>
<Label text="Ниво:"/>
<ComboBox fx:id="levelCombo"/>
<Label text="Умения:"/>
<ListView fx:id="techListView" prefHeight="150"/>
<Label fx:id="errorLabel" style="-fx-text-fill: red;" visible="false" wrapText="true"/>
<HBox spacing="10">
<Button text="Запази" onAction="#handleSave"/>
<Button text="Отказ" onAction="#handleCancel"/>
</HBox>
</VBox>
Стъпка 9: Обновяване на Developer класа:
public class Developer {
private String name;
private Level level;
private List<Technology> skills;
public Developer(String name, Level level, List<Technology> skills) {
setName(name);
setLevel(level);
setSkills(skills);
}
public String getName() {
return name;
}
public Level getLevel() {
return level;
}
public List<Technology> getSkills() {
return skills;
}
public void setName(String name) {
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("Моля, въведете име.");
}
this.name = name.trim();
}
public void setLevel(Level level) {
if (level == null) {
throw new IllegalArgumentException("Моля, изберете ниво.");
}
this.level = level;
}
public void setSkills(List<Technology> skills) {
if (skills == null || skills.isEmpty()) {
throw new IllegalArgumentException("Моля, изберете поне едно умение.");
}
this.skills = new ArrayList<>(skills);
}
}
Стъпка 10: Създаване на контролера за редактиране:
public class EditDeveloperDialogController {
@FXML
private TextField nameField;
@FXML
private ComboBox<Level> levelCombo;
@FXML
private ListView<Technology> techListView;
@FXML
private Label errorLabel;
private Developer developer;
private DeveloperDAO developerDAO;
private boolean saved = false;
@FXML
public void initialize() {
levelCombo.setItems(FXCollections.observableArrayList(Level.values()));
techListView.setItems(FXCollections.observableArrayList(Technology.values()));
techListView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
}
public void setDeveloper(Developer developer) {
this.developer = developer;
nameField.setText(developer.getName());
levelCombo.setValue(developer.getLevel());
techListView.getSelectionModel().clearSelection();
for (Technology technology : developer.getSkills()) {
techListView.getSelectionModel().select(technology);
}
}
public void setDeveloperDao(DeveloperDAO developerDAO) {
this.developerDAO = developerDAO;
}
public boolean isSaved() {
return saved;
}
private void closeWindow() {
Stage stage = (Stage) nameField.getScene().getWindow();
stage.close();
}
public void handleSave(ActionEvent actionEvent) {
try {
String name = nameField.getText();
Level level = levelCombo.getValue();
List<Technology> skills = techListView.getSelectionModel().getSelectedItems();
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("Името е задължително.");
}
if (level == null) {
throw new IllegalArgumentException("Изберете ниво.");
}
if (skills == null || skills.isEmpty()) {
throw new IllegalArgumentException("Изберете поне едно умение.");
}
developer.setName(name);
developer.setLevel(level);
developer.setSkills(skills);
if(developerDAO.update(developer) != null) {
saved = true;
}
closeWindow();
} catch (IllegalArgumentException e) {
errorLabel.setText(e.getMessage());
errorLabel.setVisible(true);
}
}
public void handleCancel(ActionEvent actionEvent) {
closeWindow();
}
}
Стъпка 11: Отваряне на прозореца за редкатиране от DeveloperController:
public void handleEditDeveloper(ActionEvent actionEvent) {
Developer selectedDeveloper = developerTable.getSelectionModel().getSelectedItem();
if (selectedDeveloper == null) {
showAlert(Alert.AlertType.WARNING, "Предупреждение", null, "Моля, изберете програмист от таблицата.");
} else {
try {
FXMLLoader loader = new FXMLLoader(
getClass().getResource("/bg/tu_varna/sit/ps/lab7/task1/edit-developer-dialog.fxml")
);
Parent root = loader.load();
EditDeveloperDialogController controller = loader.getController();
controller.setDeveloper(selectedDeveloper);
controller.setDeveloperDao(developerDAO);
Stage stage = new Stage();
stage.setTitle("Редакция на програмист");
stage.initModality(Modality.APPLICATION_MODAL);
stage.setScene(new Scene(root));
stage.showAndWait();
if (controller.isSaved()) {
developerTable.refresh();
showAlert(Alert.AlertType.INFORMATION, "Успешно", null, "Данните са редактирани успешно.");
}
} catch (IOException e) {
showAlert(Alert.AlertType.ERROR, "Грешка", "Проблем при отваряне на прозореца", e.getMessage());
}
}
}